mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 10:28:10 +08:00
Merge branch 'main' into feat/trigger
This commit is contained in:
@ -45,8 +45,8 @@ const ICON_MAP = {
|
||||
</div>,
|
||||
dataset: <AppIcon innerIcon={DatasetSvg} className='!border-[0.5px] !border-indigo-100 !bg-indigo-25' />,
|
||||
webapp: <div className='rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-blue-brand-blue-brand-500 p-1 shadow-md'>
|
||||
<WindowCursor className='h-4 w-4 text-text-primary-on-surface' />
|
||||
</div>,
|
||||
<WindowCursor className='h-4 w-4 text-text-primary-on-surface' />
|
||||
</div>,
|
||||
notion: <AppIcon innerIcon={NotionSvg} className='!border-[0.5px] !border-indigo-100 !bg-white' />,
|
||||
}
|
||||
|
||||
|
||||
@ -62,12 +62,12 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
|
||||
}, [appSidebarExpand, setAppSiderbarExpand])
|
||||
|
||||
if (inWorkflowCanvas && hideHeader) {
|
||||
return (
|
||||
return (
|
||||
<div className='flex w-0 shrink-0'>
|
||||
<AppSidebarDropdown navigation={navigation} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@ -121,7 +121,7 @@ describe('Sidebar Animation Issues Reproduction', () => {
|
||||
}))
|
||||
})
|
||||
|
||||
describe('Issue #1: Toggle Button Position Movement - FIXED', () => {
|
||||
describe('Issue #1: Toggle Button Position Movement - FIXED', () => {
|
||||
it('should verify consistent padding prevents button position shift', () => {
|
||||
let expanded = false
|
||||
const handleToggle = () => {
|
||||
|
||||
@ -84,7 +84,7 @@ const Annotation: FC<Props> = (props) => {
|
||||
setList(data as AnnotationItem[])
|
||||
setTotal(total)
|
||||
}
|
||||
finally {
|
||||
finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,19 +16,26 @@ const VarHighlight: FC<IVarHighlightProps> = ({
|
||||
return (
|
||||
<div
|
||||
key={name}
|
||||
className={`${s.item} ${className} mb-2 flex h-5 items-center justify-center rounded-md px-1 text-xs font-medium text-primary-600`}
|
||||
className={`${s.item} ${className} mb-2 inline-flex h-5 items-center justify-center rounded-md px-1 text-xs font-medium text-primary-600`}
|
||||
>
|
||||
<span className='opacity-60'>{'{{'}</span>
|
||||
<span>{name}</span>
|
||||
<span className='opacity-60'>{'}}'}</span>
|
||||
<span className='opacity-60'>{'{{'}</span><span>{name}</span><span className='opacity-60'>{'}}'}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// DEPRECATED: This function is vulnerable to XSS attacks and should not be used
|
||||
// Use the VarHighlight React component instead
|
||||
export const varHighlightHTML = ({ name, className = '' }: IVarHighlightProps) => {
|
||||
const escapedName = name
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
|
||||
const html = `<div class="${s.item} ${className} inline-flex mb-2 items-center justify-center px-1 rounded-md h-5 text-xs font-medium text-primary-600">
|
||||
<span class='opacity-60'>{{</span>
|
||||
<span>${name}</span>
|
||||
<span>${escapedName}</span>
|
||||
<span class='opacity-60'>}}</span>
|
||||
</div>`
|
||||
return html
|
||||
|
||||
@ -52,13 +52,13 @@ const TypeSelector: FC<Props> = ({
|
||||
>
|
||||
<div className='flex items-center'>
|
||||
<InputVarTypeIcon type={selectedItem?.value as InputVarType} className='size-4 shrink-0 text-text-secondary' />
|
||||
<span
|
||||
className={`
|
||||
<span
|
||||
className={`
|
||||
ml-1.5 ${!selectedItem?.name && 'text-components-input-text-placeholder'}
|
||||
`}
|
||||
>
|
||||
{selectedItem?.name}
|
||||
</span>
|
||||
>
|
||||
{selectedItem?.name}
|
||||
</span>
|
||||
</div>
|
||||
<div className='flex items-center space-x-1'>
|
||||
<Badge uppercase={false}>{inputVarTypeToVarType(selectedItem?.value as InputVarType)}</Badge>
|
||||
|
||||
@ -18,7 +18,7 @@ import s from './style.module.css'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { generateBasicAppFistTimeRule, generateRule } from '@/service/debug'
|
||||
import { generateBasicAppFirstTimeRule, generateRule } from '@/service/debug'
|
||||
import type { CompletionParams, Model } from '@/types/app'
|
||||
import type { AppType } from '@/types/app'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
@ -226,7 +226,7 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
|
||||
let apiRes: GenRes
|
||||
let hasError = false
|
||||
if (isBasicMode || !currentPrompt) {
|
||||
const { error, ...res } = await generateBasicAppFistTimeRule({
|
||||
const { error, ...res } = await generateBasicAppFirstTimeRule({
|
||||
instruction,
|
||||
model_config: model,
|
||||
no_variable: false,
|
||||
|
||||
@ -175,7 +175,6 @@ const ConfigContent: FC<Props> = ({
|
||||
...datasetConfigs,
|
||||
reranking_enable: enable,
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentRerankModel, datasetConfigs, onChange])
|
||||
|
||||
return (
|
||||
|
||||
@ -57,10 +57,10 @@ const ChatUserInput = ({
|
||||
>
|
||||
<div>
|
||||
{type !== 'checkbox' && (
|
||||
<div className='system-sm-semibold mb-1 flex h-6 items-center gap-1 text-text-secondary'>
|
||||
<div className='truncate'>{name || key}</div>
|
||||
{!required && <span className='system-xs-regular text-text-tertiary'>{t('workflow.panel.optional')}</span>}
|
||||
</div>
|
||||
<div className='system-sm-semibold mb-1 flex h-6 items-center gap-1 text-text-secondary'>
|
||||
<div className='truncate'>{name || key}</div>
|
||||
{!required && <span className='system-xs-regular text-text-tertiary'>{t('workflow.panel.optional')}</span>}
|
||||
</div>
|
||||
)}
|
||||
<div className='grow'>
|
||||
{type === 'string' && (
|
||||
|
||||
@ -112,72 +112,72 @@ const getFormattedChatList = (messages: ChatMessage[], conversationId: string, t
|
||||
const newChatList: IChatItem[] = []
|
||||
try {
|
||||
messages.forEach((item: ChatMessage) => {
|
||||
const questionFiles = item.message_files?.filter((file: any) => file.belongs_to === 'user') || []
|
||||
newChatList.push({
|
||||
id: `question-${item.id}`,
|
||||
content: item.inputs.query || item.inputs.default_input || item.query, // text generation: item.inputs.query; chat: item.query
|
||||
isAnswer: false,
|
||||
message_files: getProcessedFilesFromResponse(questionFiles.map((item: any) => ({ ...item, related_id: item.id }))),
|
||||
parentMessageId: item.parent_message_id || undefined,
|
||||
})
|
||||
const questionFiles = item.message_files?.filter((file: any) => file.belongs_to === 'user') || []
|
||||
newChatList.push({
|
||||
id: `question-${item.id}`,
|
||||
content: item.inputs.query || item.inputs.default_input || item.query, // text generation: item.inputs.query; chat: item.query
|
||||
isAnswer: false,
|
||||
message_files: getProcessedFilesFromResponse(questionFiles.map((item: any) => ({ ...item, related_id: item.id }))),
|
||||
parentMessageId: item.parent_message_id || undefined,
|
||||
})
|
||||
|
||||
const answerFiles = item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || []
|
||||
newChatList.push({
|
||||
id: item.id,
|
||||
content: item.answer,
|
||||
agent_thoughts: addFileInfos(item.agent_thoughts ? sortAgentSorts(item.agent_thoughts) : item.agent_thoughts, item.message_files),
|
||||
feedback: item.feedbacks.find(item => item.from_source === 'user'), // user feedback
|
||||
adminFeedback: item.feedbacks.find(item => item.from_source === 'admin'), // admin feedback
|
||||
feedbackDisabled: false,
|
||||
isAnswer: true,
|
||||
message_files: getProcessedFilesFromResponse(answerFiles.map((item: any) => ({ ...item, related_id: item.id }))),
|
||||
log: [
|
||||
...item.message,
|
||||
...(item.message[item.message.length - 1]?.role !== 'assistant'
|
||||
? [
|
||||
{
|
||||
role: 'assistant',
|
||||
text: item.answer,
|
||||
files: item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
] as IChatItem['log'],
|
||||
workflow_run_id: item.workflow_run_id,
|
||||
conversationId,
|
||||
input: {
|
||||
inputs: item.inputs,
|
||||
query: item.query,
|
||||
},
|
||||
more: {
|
||||
time: dayjs.unix(item.created_at).tz(timezone).format(format),
|
||||
tokens: item.answer_tokens + item.message_tokens,
|
||||
latency: item.provider_response_latency.toFixed(2),
|
||||
},
|
||||
citation: item.metadata?.retriever_resources,
|
||||
annotation: (() => {
|
||||
if (item.annotation_hit_history) {
|
||||
return {
|
||||
id: item.annotation_hit_history.annotation_id,
|
||||
authorName: item.annotation_hit_history.annotation_create_account?.name || 'N/A',
|
||||
created_at: item.annotation_hit_history.created_at,
|
||||
const answerFiles = item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || []
|
||||
newChatList.push({
|
||||
id: item.id,
|
||||
content: item.answer,
|
||||
agent_thoughts: addFileInfos(item.agent_thoughts ? sortAgentSorts(item.agent_thoughts) : item.agent_thoughts, item.message_files),
|
||||
feedback: item.feedbacks.find(item => item.from_source === 'user'), // user feedback
|
||||
adminFeedback: item.feedbacks.find(item => item.from_source === 'admin'), // admin feedback
|
||||
feedbackDisabled: false,
|
||||
isAnswer: true,
|
||||
message_files: getProcessedFilesFromResponse(answerFiles.map((item: any) => ({ ...item, related_id: item.id }))),
|
||||
log: [
|
||||
...item.message,
|
||||
...(item.message[item.message.length - 1]?.role !== 'assistant'
|
||||
? [
|
||||
{
|
||||
role: 'assistant',
|
||||
text: item.answer,
|
||||
files: item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
] as IChatItem['log'],
|
||||
workflow_run_id: item.workflow_run_id,
|
||||
conversationId,
|
||||
input: {
|
||||
inputs: item.inputs,
|
||||
query: item.query,
|
||||
},
|
||||
more: {
|
||||
time: dayjs.unix(item.created_at).tz(timezone).format(format),
|
||||
tokens: item.answer_tokens + item.message_tokens,
|
||||
latency: item.provider_response_latency.toFixed(2),
|
||||
},
|
||||
citation: item.metadata?.retriever_resources,
|
||||
annotation: (() => {
|
||||
if (item.annotation_hit_history) {
|
||||
return {
|
||||
id: item.annotation_hit_history.annotation_id,
|
||||
authorName: item.annotation_hit_history.annotation_create_account?.name || 'N/A',
|
||||
created_at: item.annotation_hit_history.created_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.annotation) {
|
||||
return {
|
||||
id: item.annotation.id,
|
||||
authorName: item.annotation.account.name,
|
||||
logAnnotation: item.annotation,
|
||||
created_at: 0,
|
||||
if (item.annotation) {
|
||||
return {
|
||||
id: item.annotation.id,
|
||||
authorName: item.annotation.account.name,
|
||||
logAnnotation: item.annotation,
|
||||
created_at: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
})(),
|
||||
parentMessageId: `question-${item.id}`,
|
||||
return undefined
|
||||
})(),
|
||||
parentMessageId: `question-${item.id}`,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return newChatList
|
||||
}
|
||||
@ -503,7 +503,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
|
||||
|
||||
setThreadChatItems(getThreadMessages(tree, newAllChatItems.at(-1)?.id))
|
||||
}
|
||||
catch (error) {
|
||||
catch (error) {
|
||||
console.error(error)
|
||||
setHasMore(false)
|
||||
}
|
||||
@ -522,7 +522,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
|
||||
if (outerDiv && outerDiv.scrollHeight > outerDiv.clientHeight) {
|
||||
scrollContainer = outerDiv
|
||||
}
|
||||
else if (scrollableDiv && scrollableDiv.scrollHeight > scrollableDiv.clientHeight) {
|
||||
else if (scrollableDiv && scrollableDiv.scrollHeight > scrollableDiv.clientHeight) {
|
||||
scrollContainer = scrollableDiv
|
||||
}
|
||||
else if (chatContainer && chatContainer.scrollHeight > chatContainer.clientHeight) {
|
||||
|
||||
@ -178,7 +178,7 @@ function AppCard({
|
||||
setAppDetail(res)
|
||||
setShowAccessControl(false)
|
||||
}
|
||||
catch (error) {
|
||||
catch (error) {
|
||||
console.error('Failed to fetch app detail:', error)
|
||||
}
|
||||
}, [appDetail, setAppDetail])
|
||||
@ -242,54 +242,54 @@ function AppCard({
|
||||
</div>
|
||||
{!isMinimalState && (
|
||||
<div className='flex flex-col items-start justify-center self-stretch'>
|
||||
<div className="system-xs-medium pb-1 text-text-tertiary">
|
||||
{isApp
|
||||
? t('appOverview.overview.appInfo.accessibleAddress')
|
||||
: t('appOverview.overview.apiInfo.accessibleAddress')}
|
||||
</div>
|
||||
<div className="inline-flex h-9 w-full items-center gap-0.5 rounded-lg bg-components-input-bg-normal p-1 pl-2">
|
||||
<div className="flex h-4 min-w-0 flex-1 items-start justify-start gap-2 px-1">
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap text-xs font-medium text-text-secondary">
|
||||
{isApp ? appUrl : apiUrl}
|
||||
</div>
|
||||
<div className="system-xs-medium pb-1 text-text-tertiary">
|
||||
{isApp
|
||||
? t('appOverview.overview.appInfo.accessibleAddress')
|
||||
: t('appOverview.overview.apiInfo.accessibleAddress')}
|
||||
</div>
|
||||
<CopyFeedback
|
||||
content={isApp ? appUrl : apiUrl}
|
||||
className={'!size-6'}
|
||||
/>
|
||||
{isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} />}
|
||||
{isApp && <Divider type="vertical" className="!mx-0.5 !h-3.5 shrink-0" />}
|
||||
{/* button copy link/ button regenerate */}
|
||||
{showConfirmDelete && (
|
||||
<Confirm
|
||||
type='warning'
|
||||
title={t('appOverview.overview.appInfo.regenerate')}
|
||||
content={t('appOverview.overview.appInfo.regenerateNotice')}
|
||||
isShow={showConfirmDelete}
|
||||
onConfirm={() => {
|
||||
onGenCode()
|
||||
setShowConfirmDelete(false)
|
||||
}}
|
||||
onCancel={() => setShowConfirmDelete(false)}
|
||||
<div className="inline-flex h-9 w-full items-center gap-0.5 rounded-lg bg-components-input-bg-normal p-1 pl-2">
|
||||
<div className="flex h-4 min-w-0 flex-1 items-start justify-start gap-2 px-1">
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap text-xs font-medium text-text-secondary">
|
||||
{isApp ? appUrl : apiUrl}
|
||||
</div>
|
||||
</div>
|
||||
<CopyFeedback
|
||||
content={isApp ? appUrl : apiUrl}
|
||||
className={'!size-6'}
|
||||
/>
|
||||
)}
|
||||
{isApp && isCurrentWorkspaceManager && (
|
||||
<Tooltip
|
||||
popupContent={t('appOverview.overview.appInfo.regenerate') || ''}
|
||||
>
|
||||
<div
|
||||
className="h-6 w-6 cursor-pointer rounded-md hover:bg-state-base-hover"
|
||||
onClick={() => setShowConfirmDelete(true)}
|
||||
{isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} />}
|
||||
{isApp && <Divider type="vertical" className="!mx-0.5 !h-3.5 shrink-0" />}
|
||||
{/* button copy link/ button regenerate */}
|
||||
{showConfirmDelete && (
|
||||
<Confirm
|
||||
type='warning'
|
||||
title={t('appOverview.overview.appInfo.regenerate')}
|
||||
content={t('appOverview.overview.appInfo.regenerateNotice')}
|
||||
isShow={showConfirmDelete}
|
||||
onConfirm={() => {
|
||||
onGenCode()
|
||||
setShowConfirmDelete(false)
|
||||
}}
|
||||
onCancel={() => setShowConfirmDelete(false)}
|
||||
/>
|
||||
)}
|
||||
{isApp && isCurrentWorkspaceManager && (
|
||||
<Tooltip
|
||||
popupContent={t('appOverview.overview.appInfo.regenerate') || ''}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
`h-full w-full ${style.refreshIcon} ${genLoading ? style.generateLogo : ''}`}
|
||||
></div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
className="h-6 w-6 cursor-pointer rounded-md hover:bg-state-base-hover"
|
||||
onClick={() => setShowConfirmDelete(true)}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
`h-full w-full ${style.refreshIcon} ${genLoading ? style.generateLogo : ''}`}
|
||||
></div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isMinimalState && isApp && systemFeatures.webapp_auth.enabled && appDetail && <div className='flex flex-col items-start justify-center self-stretch'>
|
||||
<div className="system-xs-medium pb-1 text-text-tertiary">{t('app.publishApp.title')}</div>
|
||||
@ -331,33 +331,33 @@ function AppCard({
|
||||
<div className={'flex items-center gap-1 self-stretch p-3'}>
|
||||
{!isApp && <SecretKeyButton appId={appInfo.id} />}
|
||||
{OPERATIONS_MAP[cardType].map((op) => {
|
||||
const disabled
|
||||
const disabled
|
||||
= op.opName === t('appOverview.overview.appInfo.settings.entry')
|
||||
? false
|
||||
: !runningStatus
|
||||
return (
|
||||
<Button
|
||||
className="mr-1 min-w-[88px]"
|
||||
size="small"
|
||||
variant={'ghost'}
|
||||
key={op.opName}
|
||||
onClick={genClickFuncByName(op.opName)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Tooltip
|
||||
popupContent={
|
||||
t('appOverview.overview.appInfo.preUseReminder') ?? ''
|
||||
}
|
||||
popupClassName={disabled ? 'mt-[-8px]' : '!hidden'}
|
||||
return (
|
||||
<Button
|
||||
className="mr-1 min-w-[88px]"
|
||||
size="small"
|
||||
variant={'ghost'}
|
||||
key={op.opName}
|
||||
onClick={genClickFuncByName(op.opName)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-[1px]">
|
||||
<op.opIcon className="h-3.5 w-3.5" />
|
||||
<div className={`${runningStatus ? 'text-text-tertiary' : 'text-components-button-ghost-text-disabled'} system-xs-medium px-[3px]`}>{op.opName}</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</Button>
|
||||
)
|
||||
})}
|
||||
<Tooltip
|
||||
popupContent={
|
||||
t('appOverview.overview.appInfo.preUseReminder') ?? ''
|
||||
}
|
||||
popupClassName={disabled ? 'mt-[-8px]' : '!hidden'}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-[1px]">
|
||||
<op.opIcon className="h-3.5 w-3.5" />
|
||||
<div className={`${(runningStatus || !disabled) ? 'text-text-tertiary' : 'text-components-button-ghost-text-disabled'} system-xs-medium px-[3px]`}>{op.opName}</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</Button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -40,12 +40,12 @@ const OPTION_MAP = {
|
||||
`<script>
|
||||
window.difyChatbotConfig = {
|
||||
token: '${token}'${isTestEnv
|
||||
? `,
|
||||
? `,
|
||||
isDev: true`
|
||||
: ''}${IS_CE_EDITION
|
||||
? `,
|
||||
: ''}${IS_CE_EDITION
|
||||
? `,
|
||||
baseUrl: '${url}${basePath}'`
|
||||
: ''},
|
||||
: ''},
|
||||
inputs: {
|
||||
// You can define the inputs from the Start node here
|
||||
// key is the variable name
|
||||
|
||||
@ -240,7 +240,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||
<Link href={docLink('/guides/application-publishing/launch-your-webapp-quickly/README', {
|
||||
'zh-Hans': '/guides/application-publishing/launch-your-webapp-quickly/readme',
|
||||
})}
|
||||
target='_blank' rel='noopener noreferrer' className='text-text-accent'>{t('common.operation.learnMore')}</Link>
|
||||
target='_blank' rel='noopener noreferrer' className='text-text-accent'>{t('common.operation.learnMore')}</Link>
|
||||
</div>
|
||||
</div>
|
||||
{/* form body */}
|
||||
|
||||
@ -68,7 +68,7 @@ const getKey = (
|
||||
|
||||
const List = () => {
|
||||
const { t } = useTranslation()
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const router = useRouter()
|
||||
const { isCurrentWorkspaceEditor, isCurrentWorkspaceDatasetOperator } = useAppContext()
|
||||
const showTagManagementModal = useTagStore(s => s.showTagManagementModal)
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import type { ChangeEvent, FC } from 'react'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { varHighlightHTML } from '../../app/configuration/base/var-highlight'
|
||||
import VarHighlight from '../../app/configuration/base/var-highlight'
|
||||
import Toast from '../toast'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { checkKeys } from '@/utils/var'
|
||||
@ -66,11 +66,24 @@ const BlockInput: FC<IBlockInputProps> = ({
|
||||
'block-input--editing': isEditing,
|
||||
})
|
||||
|
||||
const coloredContent = (currentValue || '')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(regex, varHighlightHTML({ name: '$1' })) // `<span class="${highLightClassName}">{{$1}}</span>`
|
||||
.replace(/\n/g, '<br />')
|
||||
const renderSafeContent = (value: string) => {
|
||||
const parts = value.split(/(\{\{[^}]+\}\}|\n)/g)
|
||||
return parts.map((part, index) => {
|
||||
const variableMatch = part.match(/^\{\{([^}]+)\}\}$/)
|
||||
if (variableMatch) {
|
||||
return (
|
||||
<VarHighlight
|
||||
key={`var-${index}`}
|
||||
name={variableMatch[1]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
if (part === '\n')
|
||||
return <br key={`br-${index}`} />
|
||||
|
||||
return <span key={`text-${index}`}>{part}</span>
|
||||
})
|
||||
}
|
||||
|
||||
// Not use useCallback. That will cause out callback get old data.
|
||||
const handleSubmit = (value: string) => {
|
||||
@ -96,11 +109,11 @@ const BlockInput: FC<IBlockInputProps> = ({
|
||||
|
||||
// Prevent rerendering caused cursor to jump to the start of the contentEditable element
|
||||
const TextAreaContentView = () => {
|
||||
return <div
|
||||
className={classNames(style, className)}
|
||||
dangerouslySetInnerHTML={{ __html: coloredContent }}
|
||||
suppressContentEditableWarning={true}
|
||||
/>
|
||||
return (
|
||||
<div className={classNames(style, className)}>
|
||||
{renderSafeContent(currentValue || '')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const placeholder = ''
|
||||
|
||||
@ -407,9 +407,13 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
||||
currentChatInstanceRef.current.handleStop()
|
||||
setShowNewConversationItemInList(true)
|
||||
handleChangeConversation('')
|
||||
handleNewConversationInputsChange(await getRawInputsFromUrlParams())
|
||||
const conversationInputs: Record<string, any> = {}
|
||||
inputsForms.forEach((item: any) => {
|
||||
conversationInputs[item.variable] = item.default || null
|
||||
})
|
||||
handleNewConversationInputsChange(conversationInputs)
|
||||
setClearChatList(true)
|
||||
}, [handleChangeConversation, setShowNewConversationItemInList, handleNewConversationInputsChange, setClearChatList])
|
||||
}, [handleChangeConversation, setShowNewConversationItemInList, handleNewConversationInputsChange, setClearChatList, inputsForms])
|
||||
const handleUpdateConversationList = useCallback(() => {
|
||||
mutateAppConversationData()
|
||||
mutateAppPinnedConversationData()
|
||||
|
||||
@ -137,24 +137,14 @@ const Answer: FC<AnswerProps> = ({
|
||||
/>
|
||||
)
|
||||
}
|
||||
{/** Render the normal steps */}
|
||||
{/** Render workflow process */}
|
||||
{
|
||||
workflowProcess && !hideProcessDetail && (
|
||||
workflowProcess && (
|
||||
<WorkflowProcessItem
|
||||
data={workflowProcess}
|
||||
item={item}
|
||||
hideProcessDetail={hideProcessDetail}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{/** Hide workflow steps by it's settings in siteInfo */}
|
||||
{
|
||||
workflowProcess && hideProcessDetail && appData && (
|
||||
<WorkflowProcessItem
|
||||
data={workflowProcess}
|
||||
item={item}
|
||||
hideProcessDetail={hideProcessDetail}
|
||||
readonly={!appData.site.show_workflow_steps}
|
||||
readonly={hideProcessDetail && appData ? !appData.site.show_workflow_steps : undefined}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@ -39,6 +39,8 @@ const WorkflowProcessItem = ({
|
||||
setCollapse(!expand)
|
||||
}, [expand])
|
||||
|
||||
if (readonly) return null
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@ -51,8 +53,8 @@ const WorkflowProcessItem = ({
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn('flex cursor-pointer items-center', !collapse && 'px-1.5', readonly && 'cursor-default')}
|
||||
onClick={() => !readonly && setCollapse(!collapse)}
|
||||
className={cn('flex cursor-pointer items-center', !collapse && 'px-1.5')}
|
||||
onClick={() => setCollapse(!collapse)}
|
||||
>
|
||||
{
|
||||
running && (
|
||||
@ -72,10 +74,10 @@ const WorkflowProcessItem = ({
|
||||
<div className={cn('system-xs-medium text-text-secondary', !collapse && 'grow')}>
|
||||
{t('workflow.common.workflowProcess')}
|
||||
</div>
|
||||
{!readonly && <RiArrowRightSLine className={cn('ml-1 h-4 w-4 text-text-tertiary', !collapse && 'rotate-90')} />}
|
||||
<RiArrowRightSLine className={cn('ml-1 h-4 w-4 text-text-tertiary', !collapse && 'rotate-90')} />
|
||||
</div>
|
||||
{
|
||||
!collapse && !readonly && (
|
||||
!collapse && (
|
||||
<div className='mt-1.5'>
|
||||
{
|
||||
<TracingPanel
|
||||
|
||||
@ -46,12 +46,12 @@ const InputsFormContent = ({ showTip }: Props) => {
|
||||
{visibleInputsForms.map(form => (
|
||||
<div key={form.variable} className='space-y-1'>
|
||||
{form.type !== InputVarType.checkbox && (
|
||||
<div className='flex h-6 items-center gap-1'>
|
||||
<div className='system-md-semibold text-text-secondary'>{form.label}</div>
|
||||
{!form.required && (
|
||||
<div className='system-xs-regular text-text-tertiary'>{t('appDebug.variableTable.optional')}</div>
|
||||
)}
|
||||
</div>
|
||||
<div className='flex h-6 items-center gap-1'>
|
||||
<div className='system-md-semibold text-text-secondary'>{form.label}</div>
|
||||
{!form.required && (
|
||||
<div className='system-xs-regular text-text-tertiary'>{t('appDebug.variableTable.optional')}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{form.type === InputVarType.textInput && (
|
||||
<Input
|
||||
|
||||
@ -12,12 +12,12 @@ type CheckboxProps = {
|
||||
}
|
||||
|
||||
const Checkbox = ({
|
||||
id,
|
||||
checked,
|
||||
onCheck,
|
||||
className,
|
||||
disabled,
|
||||
indeterminate,
|
||||
id,
|
||||
checked,
|
||||
onCheck,
|
||||
className,
|
||||
disabled,
|
||||
indeterminate,
|
||||
}: CheckboxProps) => {
|
||||
const checkClassName = (checked || indeterminate)
|
||||
? 'bg-components-checkbox-bg text-components-checkbox-icon hover:bg-components-checkbox-bg-hover'
|
||||
|
||||
@ -131,7 +131,7 @@ export const formatDateForOutput = (date: Dayjs, includeTime: boolean = false, l
|
||||
// Output format with time
|
||||
return date.format('YYYY-MM-DDTHH:mm:ss.SSSZ')
|
||||
}
|
||||
else {
|
||||
else {
|
||||
// Date-only output format without timezone
|
||||
return date.format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import PureSelect from '@/app/components/base/select/pure'
|
||||
import type { FormSchema } from '@/app/components/base/form/types'
|
||||
import { FormTypeEnum } from '@/app/components/base/form/types'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
import Radio from '@/app/components/base/radio'
|
||||
import RadioE from '@/app/components/base/radio/ui'
|
||||
|
||||
export type BaseFieldProps = {
|
||||
@ -102,6 +103,12 @@ const BaseField = ({
|
||||
})
|
||||
}, [values, show_on])
|
||||
|
||||
const booleanRadioValue = useMemo(() => {
|
||||
if (value === null || value === undefined)
|
||||
return undefined
|
||||
return value ? 1 : 0
|
||||
}, [value])
|
||||
|
||||
if (!show)
|
||||
return null
|
||||
|
||||
@ -204,6 +211,18 @@ const BaseField = ({
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
formSchema.type === FormTypeEnum.boolean && (
|
||||
<Radio.Group
|
||||
className='flex w-fit items-center'
|
||||
value={booleanRadioValue}
|
||||
onChange={val => field.handleChange(val === 1)}
|
||||
>
|
||||
<Radio value={1} className='!mr-1'>True</Radio>
|
||||
<Radio value={0}>False</Radio>
|
||||
</Radio.Group>
|
||||
)
|
||||
}
|
||||
{
|
||||
formSchema.url && (
|
||||
<a
|
||||
|
||||
@ -24,7 +24,7 @@ const DemoForm = () => {
|
||||
},
|
||||
})
|
||||
|
||||
const name = useStore(form.store, state => state.values.name)
|
||||
const name = useStore(form.store, state => state.values.name)
|
||||
|
||||
return (
|
||||
<form
|
||||
|
||||
@ -32,6 +32,7 @@ export enum FormTypeEnum {
|
||||
multiToolSelector = 'array[tools]',
|
||||
appSelector = 'app-selector',
|
||||
dynamicSelect = 'dynamic-select',
|
||||
boolean = 'boolean',
|
||||
}
|
||||
|
||||
export type FormOption = {
|
||||
@ -69,10 +70,10 @@ export type GetValuesOptions = {
|
||||
needCheckValidatedValues?: boolean
|
||||
}
|
||||
export type FormRefObject = {
|
||||
getForm: () => AnyFormApi
|
||||
getFormValues: (obj: GetValuesOptions) => {
|
||||
values: Record<string, any>
|
||||
isCheckValidated: boolean
|
||||
}
|
||||
getForm: () => AnyFormApi
|
||||
getFormValues: (obj: GetValuesOptions) => {
|
||||
values: Record<string, any>
|
||||
isCheckValidated: boolean
|
||||
}
|
||||
}
|
||||
export type FormRef = ForwardedRef<FormRefObject>
|
||||
|
||||
@ -529,9 +529,9 @@ const Flowchart = React.forwardRef((props: {
|
||||
{isLoading && !svgString && (
|
||||
<div className='px-[26px] py-4'>
|
||||
<LoadingAnim type='text'/>
|
||||
<div className="mt-2 text-sm text-gray-500">
|
||||
{t('common.wait_for_completion', 'Waiting for diagram code to complete...')}
|
||||
</div>
|
||||
<div className="mt-2 text-sm text-gray-500">
|
||||
{t('common.wait_for_completion', 'Waiting for diagram code to complete...')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import clsx from 'clsx'
|
||||
import cn from 'classnames'
|
||||
import usePagination from './hook'
|
||||
import type {
|
||||
ButtonProps,
|
||||
@ -45,7 +45,7 @@ export const PrevButton = ({
|
||||
<as.type
|
||||
{...buttonProps}
|
||||
{...as.props}
|
||||
className={clsx(className, as.props.className)}
|
||||
className={cn(className, as.props.className)}
|
||||
onClick={() => previous()}
|
||||
tabIndex={disabled ? '-1' : 0}
|
||||
disabled={disabled}
|
||||
@ -80,7 +80,7 @@ export const NextButton = ({
|
||||
<as.type
|
||||
{...buttonProps}
|
||||
{...as.props}
|
||||
className={clsx(className, as.props.className)}
|
||||
className={cn(className, as.props.className)}
|
||||
onClick={() => next()}
|
||||
tabIndex={disabled ? '-1' : 0}
|
||||
disabled={disabled}
|
||||
@ -132,7 +132,7 @@ export const PageButton = ({
|
||||
<li key={page}>
|
||||
<as.type
|
||||
data-testid={
|
||||
clsx({
|
||||
cn({
|
||||
[`${dataTestIdActive}`]:
|
||||
dataTestIdActive && pagination.currentPage + 1 === page,
|
||||
[`${dataTestIdInactive}-${page}`]:
|
||||
@ -145,7 +145,7 @@ export const PageButton = ({
|
||||
pagination.setCurrentPage(page - 1)
|
||||
}}
|
||||
onClick={() => pagination.setCurrentPage(page - 1)}
|
||||
className={clsx(
|
||||
className={cn(
|
||||
className,
|
||||
pagination.currentPage + 1 === page
|
||||
? activeClassName
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
FloatingPortal,
|
||||
autoUpdate,
|
||||
flip,
|
||||
hide,
|
||||
offset,
|
||||
shift,
|
||||
size,
|
||||
@ -39,7 +40,7 @@ export function usePortalToFollowElem({
|
||||
triggerPopupSameWidth,
|
||||
}: PortalToFollowElemOptions = {}) {
|
||||
const setOpen = setControlledOpen
|
||||
|
||||
const container = document.getElementById('workflow-container') || document.body
|
||||
const data = useFloating({
|
||||
placement,
|
||||
open,
|
||||
@ -50,9 +51,17 @@ export function usePortalToFollowElem({
|
||||
flip({
|
||||
crossAxis: placement.includes('-'),
|
||||
fallbackAxisSideDirection: 'start',
|
||||
padding: 5,
|
||||
padding: 8,
|
||||
}),
|
||||
shift({
|
||||
padding: 8,
|
||||
boundary: container,
|
||||
altBoundary: true,
|
||||
}),
|
||||
hide({
|
||||
// hide when the reference element is not visible
|
||||
boundary: container,
|
||||
}),
|
||||
shift({ padding: 5 }),
|
||||
size({
|
||||
apply({ rects, elements }) {
|
||||
if (triggerPopupSameWidth)
|
||||
@ -133,9 +142,9 @@ export const PortalToFollowElemTrigger = (
|
||||
context.getReferenceProps({
|
||||
ref,
|
||||
...props,
|
||||
...children.props,
|
||||
...(children.props || {}),
|
||||
'data-state': context.open ? 'open' : 'closed',
|
||||
}),
|
||||
} as React.HTMLProps<HTMLElement>),
|
||||
)
|
||||
}
|
||||
|
||||
@ -177,6 +186,7 @@ export const PortalToFollowElemContent = (
|
||||
style={{
|
||||
...context.floatingStyles,
|
||||
...style,
|
||||
visibility: context.middlewareData.hide?.referenceHidden ? 'hidden' : 'visible',
|
||||
}}
|
||||
{...context.getFloatingProps(props)}
|
||||
/>
|
||||
|
||||
@ -20,9 +20,9 @@ const CurrentBlockComponent: FC<CurrentBlockComponentProps> = ({
|
||||
|
||||
const Icon = generatorType === GeneratorType.prompt ? MagicEdit : CodeAssistant
|
||||
useEffect(() => {
|
||||
if (!editor.hasNodes([CurrentBlockNode]))
|
||||
throw new Error('WorkflowVariableBlockPlugin: WorkflowVariableBlock not registered on editor')
|
||||
}, [editor])
|
||||
if (!editor.hasNodes([CurrentBlockNode]))
|
||||
throw new Error('WorkflowVariableBlockPlugin: WorkflowVariableBlock not registered on editor')
|
||||
}, [editor])
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@ -16,9 +16,9 @@ const ErrorMessageBlockComponent: FC<Props> = ({
|
||||
const [ref, isSelected] = useSelectOrDelete(nodeKey, DELETE_ERROR_MESSAGE_COMMAND)
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor.hasNodes([ErrorMessageBlockNode]))
|
||||
throw new Error('WorkflowVariableBlockPlugin: WorkflowVariableBlock not registered on editor')
|
||||
}, [editor])
|
||||
if (!editor.hasNodes([ErrorMessageBlockNode]))
|
||||
throw new Error('WorkflowVariableBlockPlugin: WorkflowVariableBlock not registered on editor')
|
||||
}, [editor])
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@ -16,9 +16,9 @@ const LastRunBlockComponent: FC<Props> = ({
|
||||
const [ref, isSelected] = useSelectOrDelete(nodeKey, DELETE_LAST_RUN_COMMAND)
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor.hasNodes([LastRunBlockNode]))
|
||||
throw new Error('WorkflowVariableBlockPlugin: WorkflowVariableBlock not registered on editor')
|
||||
}, [editor])
|
||||
if (!editor.hasNodes([LastRunBlockNode]))
|
||||
throw new Error('WorkflowVariableBlockPlugin: WorkflowVariableBlock not registered on editor')
|
||||
}, [editor])
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@ -221,36 +221,36 @@ const SimpleSelect: FC<ISelectProps> = ({
|
||||
{renderTrigger && <ListboxButton className='w-full'>{renderTrigger(selectedItem)}</ListboxButton>}
|
||||
{!renderTrigger && (
|
||||
<ListboxButton onClick={() => {
|
||||
onOpenChange?.(open)
|
||||
onOpenChange?.(open)
|
||||
}} className={classNames(`flex h-full w-full items-center rounded-lg border-0 bg-components-input-bg-normal pl-3 pr-10 focus-visible:bg-state-base-hover-alt focus-visible:outline-none group-hover/simple-select:bg-state-base-hover-alt sm:text-sm sm:leading-6 ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}`, className)}>
|
||||
<span className={classNames('system-sm-regular block truncate text-left text-components-input-text-filled', !selectedItem?.name && 'text-components-input-text-placeholder')}>{selectedItem?.name ?? localPlaceholder}</span>
|
||||
<span className="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
{isLoading ? <RiLoader4Line className='h-3.5 w-3.5 animate-spin text-text-secondary' />
|
||||
: (selectedItem && !notClearable)
|
||||
? (
|
||||
<XMarkIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setSelectedItem(null)
|
||||
onSelect({ name: '', value: '' })
|
||||
}}
|
||||
className="h-4 w-4 cursor-pointer text-text-quaternary"
|
||||
aria-hidden="false"
|
||||
/>
|
||||
)
|
||||
: (
|
||||
open ? (
|
||||
<ChevronUpIcon
|
||||
className="h-4 w-4 text-text-quaternary group-hover/simple-select:text-text-secondary"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : (
|
||||
<ChevronDownIcon
|
||||
className="h-4 w-4 text-text-quaternary group-hover/simple-select:text-text-secondary"
|
||||
aria-hidden="true"
|
||||
: (selectedItem && !notClearable)
|
||||
? (
|
||||
<XMarkIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setSelectedItem(null)
|
||||
onSelect({ name: '', value: '' })
|
||||
}}
|
||||
className="h-4 w-4 cursor-pointer text-text-quaternary"
|
||||
aria-hidden="false"
|
||||
/>
|
||||
)
|
||||
)}
|
||||
: (
|
||||
open ? (
|
||||
<ChevronUpIcon
|
||||
className="h-4 w-4 text-text-quaternary group-hover/simple-select:text-text-secondary"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : (
|
||||
<ChevronDownIcon
|
||||
className="h-4 w-4 text-text-quaternary group-hover/simple-select:text-text-secondary"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</span>
|
||||
</ListboxButton>
|
||||
)}
|
||||
|
||||
@ -14,6 +14,7 @@ type TagInputProps = {
|
||||
customizedConfirmKey?: 'Enter' | 'Tab'
|
||||
isInWorkflow?: boolean
|
||||
placeholder?: string
|
||||
required?: boolean
|
||||
}
|
||||
|
||||
const TagInput: FC<TagInputProps> = ({
|
||||
@ -24,6 +25,7 @@ const TagInput: FC<TagInputProps> = ({
|
||||
customizedConfirmKey = 'Enter',
|
||||
isInWorkflow,
|
||||
placeholder,
|
||||
required = false,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useToastContext()
|
||||
@ -42,7 +44,8 @@ const TagInput: FC<TagInputProps> = ({
|
||||
const handleNewTag = useCallback((value: string) => {
|
||||
const valueTrimmed = value.trim()
|
||||
if (!valueTrimmed) {
|
||||
notify({ type: 'error', message: t('datasetDocuments.segment.keywordEmpty') })
|
||||
if (required)
|
||||
notify({ type: 'error', message: t('datasetDocuments.segment.keywordEmpty') })
|
||||
return
|
||||
}
|
||||
|
||||
@ -60,7 +63,7 @@ const TagInput: FC<TagInputProps> = ({
|
||||
setTimeout(() => {
|
||||
setValue('')
|
||||
})
|
||||
}, [items, onChange, notify, t])
|
||||
}, [items, onChange, notify, t, required])
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (isSpecialMode && e.key === 'Enter')
|
||||
|
||||
@ -226,7 +226,7 @@ const TagSelector: FC<TagSelectorProps> = ({
|
||||
const res = await fetchTagList(type)
|
||||
setTagList(res)
|
||||
}
|
||||
catch (error) {
|
||||
catch (error) {
|
||||
setTagList([])
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,8 +56,7 @@ const Toast = ({
|
||||
'top-0',
|
||||
'right-0',
|
||||
)}>
|
||||
<div className={`absolute inset-0 -z-10 opacity-40 ${
|
||||
(type === 'success' && 'bg-toast-success-bg')
|
||||
<div className={`absolute inset-0 -z-10 opacity-40 ${(type === 'success' && 'bg-toast-success-bg')
|
||||
|| (type === 'warning' && 'bg-toast-warning-bg')
|
||||
|| (type === 'error' && 'bg-toast-error-bg')
|
||||
|| (type === 'info' && 'bg-toast-info-bg')
|
||||
@ -162,7 +161,9 @@ Toast.notify = ({
|
||||
</ToastContext.Provider>,
|
||||
)
|
||||
document.body.appendChild(holder)
|
||||
setTimeout(toastHandler.clear, duration || defaultDuring)
|
||||
const d = duration ?? defaultDuring
|
||||
if (d > 0)
|
||||
setTimeout(toastHandler.clear, d)
|
||||
}
|
||||
|
||||
return toastHandler
|
||||
|
||||
@ -61,7 +61,6 @@ const RetrievalParamConfig: FC<Props> = ({
|
||||
...value,
|
||||
reranking_enable: enable,
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentModel, onChange, value])
|
||||
|
||||
const rerankModel = useMemo(() => {
|
||||
|
||||
@ -29,7 +29,6 @@ const OptionsWrap: FC<Props> = ({
|
||||
useEffect(() => {
|
||||
if (controlFoldOptions)
|
||||
foldHide()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [controlFoldOptions])
|
||||
return (
|
||||
<div className={cn(className, !fold ? 'mb-0' : 'mb-3')}>
|
||||
|
||||
@ -64,7 +64,6 @@ const Website: FC<Props> = ({
|
||||
checkSetApiKey().then(() => {
|
||||
setIsLoaded(true)
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
const handleOnConfig = useCallback(() => {
|
||||
setShowAccountSettingModal({
|
||||
@ -97,7 +96,7 @@ const Website: FC<Props> = ({
|
||||
<span className={cn(s.jinaLogo, 'mr-2')}/>
|
||||
<span>Jina Reader</span>
|
||||
</button>}
|
||||
{ENABLE_WEBSITE_FIRECRAWL && <button
|
||||
{ENABLE_WEBSITE_FIRECRAWL && <button
|
||||
className={cn('rounded-lg px-4 py-2',
|
||||
selectedProvider === DataSourceProvider.fireCrawl
|
||||
? 'system-sm-medium border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary'
|
||||
|
||||
@ -29,7 +29,6 @@ const OptionsWrap: FC<Props> = ({
|
||||
useEffect(() => {
|
||||
if (controlFoldOptions)
|
||||
foldHide()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [controlFoldOptions])
|
||||
return (
|
||||
<div className={cn(className, !fold ? 'mb-0' : 'mb-3')}>
|
||||
|
||||
@ -161,7 +161,7 @@ const CSVUploader: FC<Props> = ({
|
||||
const fileChangeHandle = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const currentFile = e.target.files?.[0]
|
||||
if (!isValid(currentFile))
|
||||
return
|
||||
return
|
||||
|
||||
initialUpload(currentFile)
|
||||
}
|
||||
|
||||
@ -24,9 +24,9 @@ const InfoPanel = () => {
|
||||
{t('dataset.connectDatasetIntro.content.end')}
|
||||
</span>
|
||||
<a className='system-sm-regular self-stretch text-text-accent'
|
||||
href={docLink('/guides/knowledge-base/connect-external-knowledge-base')}
|
||||
target='_blank'
|
||||
rel="noopener noreferrer">
|
||||
href={docLink('/guides/knowledge-base/connect-external-knowledge-base')}
|
||||
target='_blank'
|
||||
rel="noopener noreferrer">
|
||||
{t('dataset.connectDatasetIntro.learnMore')}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@ -28,7 +28,7 @@ const ExternalKnowledgeBaseCreate: React.FC<ExternalKnowledgeBaseCreateProps> =
|
||||
external_knowledge_api_id: '',
|
||||
external_knowledge_id: '',
|
||||
external_retrieval_model: {
|
||||
top_k: 2,
|
||||
top_k: 4,
|
||||
score_threshold: 0.5,
|
||||
score_threshold_enabled: false,
|
||||
},
|
||||
|
||||
@ -27,10 +27,11 @@ const ChunkDetailModal: FC<Props> = ({
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { segment, score, child_chunks } = payload
|
||||
const { position, content, sign_content, keywords, document } = segment
|
||||
const { position, content, sign_content, keywords, document, answer } = segment
|
||||
const isParentChildRetrieval = !!(child_chunks && child_chunks.length > 0)
|
||||
const extension = document.name.split('.').slice(-1)[0] as FileAppearanceTypeEnum
|
||||
const heighClassName = isParentChildRetrieval ? 'h-[min(627px,_80vh)] overflow-y-auto' : 'h-[min(539px,_80vh)] overflow-y-auto'
|
||||
const labelPrefix = isParentChildRetrieval ? t('datasetDocuments.segment.parentChunk') : t('datasetDocuments.segment.chunk')
|
||||
return (
|
||||
<Modal
|
||||
title={t(`${i18nPrefix}.chunkDetail`)}
|
||||
@ -45,7 +46,7 @@ const ChunkDetailModal: FC<Props> = ({
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='flex grow items-center space-x-2'>
|
||||
<SegmentIndexTag
|
||||
labelPrefix={`${isParentChildRetrieval ? 'Parent-' : ''}Chunk`}
|
||||
labelPrefix={labelPrefix}
|
||||
positionId={position}
|
||||
className={cn('w-fit group-hover:opacity-100')}
|
||||
/>
|
||||
@ -57,11 +58,29 @@ const ChunkDetailModal: FC<Props> = ({
|
||||
</div>
|
||||
<Score value={score} />
|
||||
</div>
|
||||
<Markdown
|
||||
className={cn('!mt-2 !text-text-secondary', heighClassName)}
|
||||
content={sign_content || content}
|
||||
customDisallowedElements={['input']}
|
||||
/>
|
||||
{!answer && (
|
||||
<Markdown
|
||||
className={cn('!mt-2 !text-text-secondary', heighClassName)}
|
||||
content={sign_content || content}
|
||||
customDisallowedElements={['input']}
|
||||
/>
|
||||
)}
|
||||
{answer && (
|
||||
<div>
|
||||
<div className='flex gap-x-1'>
|
||||
<div className='w-4 shrink-0 text-[13px] font-medium leading-[20px] text-text-tertiary'>Q</div>
|
||||
<div className={cn('body-md-regular line-clamp-20 text-text-secondary')}>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex gap-x-1'>
|
||||
<div className='w-4 shrink-0 text-[13px] font-medium leading-[20px] text-text-tertiary'>A</div>
|
||||
<div className={cn('body-md-regular line-clamp-20 text-text-secondary')}>
|
||||
{answer}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isParentChildRetrieval && keywords && keywords.length > 0 && (
|
||||
<div className='mt-6'>
|
||||
<div className='text-xs font-medium uppercase text-text-tertiary'>{t(`${i18nPrefix}.keyword`)}</div>
|
||||
|
||||
@ -49,7 +49,7 @@ const TextAreaWithButton = ({
|
||||
const { t } = useTranslation()
|
||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false)
|
||||
const [externalRetrievalSettings, setExternalRetrievalSettings] = useState({
|
||||
top_k: 2,
|
||||
top_k: 4,
|
||||
score_threshold: 0.5,
|
||||
score_threshold_enabled: false,
|
||||
})
|
||||
|
||||
@ -28,7 +28,6 @@ const useEditDatasetMetadata = ({
|
||||
showEditModal()
|
||||
localStorage.removeItem(isShowManageMetadataLocalStorageKey)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const { data: datasetMetaData } = useDatasetMetaData(datasetId)
|
||||
|
||||
@ -53,11 +53,18 @@ The text generation application offers non-session support and is ideal for tran
|
||||
The rules are defined by the developer and need to ensure that the user identifier is unique within the application. The Service API does not share conversations created by the WebApp.
|
||||
</Property>
|
||||
<Property name='files' type='array[object]' key='files'>
|
||||
File list, suitable for inputting files (images) combined with text understanding and answering questions, available only when the model supports Vision capability.
|
||||
- `type` (string) Supported type: `image` (currently only supports image type)
|
||||
- `transfer_method` (string) Transfer method, `remote_url` for image URL / `local_file` for file upload
|
||||
- `url` (string) Image URL (when the transfer method is `remote_url`)
|
||||
- `upload_file_id` (string) Uploaded file ID, which must be obtained by uploading through the File Upload API in advance (when the transfer method is `local_file`)
|
||||
File list, suitable for inputting files combined with text understanding and answering questions, available only when the model supports Vision/Video capability.
|
||||
- `type` (string) Supported type:
|
||||
- `document` Supported types include: 'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` Supported types include: 'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` Supported types include: 'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` Supported types include: 'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` Supported types include: other file types
|
||||
- `transfer_method` (string) Transfer method:
|
||||
- `remote_url`: File URL.
|
||||
- `local_file`: Upload file.
|
||||
- `url` File URL. (Only when transfer method is `remote_url`).
|
||||
- `upload_file_id` Upload file ID. (Only when transfer method is `local_file`).
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
|
||||
@ -53,11 +53,18 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
アプリケーション内で開発者が一意に定義する必要があります。
|
||||
</Property>
|
||||
<Property name='files' type='array[object]' key='files'>
|
||||
ファイルリスト、モデルがVision機能をサポートしている場合のみ、テキスト理解と質問応答を組み合わせたファイル(画像)の入力に適しています。
|
||||
- `type` (string) サポートされるタイプ:`image`(現在は画像タイプのみサポート)
|
||||
- `transfer_method` (string) 転送方法、画像URLの場合は`remote_url` / ファイルアップロードの場合は`local_file`
|
||||
- `url` (string) 画像URL(転送方法が`remote_url`の場合)
|
||||
- `upload_file_id` (string) アップロードされたファイルID、事前にファイルアップロードAPIを通じてアップロードする必要があります(転送方法が`local_file`の場合)
|
||||
ファイルリスト、モデルが Vision/Video 機能をサポートしている場合に限り、ファイルをテキスト理解および質問応答に組み合わせて入力するのに適しています。
|
||||
- `type` (string) サポートされるタイプ:
|
||||
- `document` サポートされるタイプには以下が含まれます:'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` サポートされるタイプには以下が含まれます:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` サポートされるタイプには以下が含まれます:'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` サポートされるタイプには以下が含まれます:'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` サポートされるタイプには以下が含まれます:その他のファイルタイプ
|
||||
- `transfer_method` (string) 転送方法:
|
||||
- `remote_url`: ファイルのURL。
|
||||
- `local_file`: ファイルをアップロード。
|
||||
- `url` ファイルのURL。(転送方法が `remote_url` の場合のみ)。
|
||||
- `upload_file_id` アップロードされたファイルID。(転送方法が `local_file` の場合のみ)。
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
|
||||
@ -51,12 +51,17 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
由开发者定义规则,需保证用户标识在应用内唯一。
|
||||
</Property>
|
||||
<Property name='files' type='array[object]' key='files'>
|
||||
上传的文件。
|
||||
- `type` (string) 支持类型:图片 `image`(目前仅支持图片格式) 。
|
||||
- `transfer_method` (string) 传递方式:
|
||||
- `remote_url`: 图片地址。
|
||||
文件列表,适用于传入文件结合文本理解并回答问题,仅当模型支持 Vision/Video 能力时可用。
|
||||
- `type` (string) 支持类型:
|
||||
- `document` 具体类型包含:'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` 具体类型包含:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` 具体类型包含:'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` 具体类型包含:'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` 具体类型包含:其他文件类型
|
||||
- `transfer_method` (string) 传递方式:
|
||||
- `remote_url`: 文件地址。
|
||||
- `local_file`: 上传文件。
|
||||
- `url` 图片地址。(仅当传递方式为 `remote_url` 时)。
|
||||
- `url` 文件地址。(仅当传递方式为 `remote_url` 时)。
|
||||
- `upload_file_id` 上传文件 ID。(仅当传递方式为 `local_file `时)。
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
@ -57,16 +57,18 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
Conversation ID, to continue the conversation based on previous chat records, it is necessary to pass the previous message's conversation_id.
|
||||
</Property>
|
||||
<Property name='files' type='array[object]' key='files'>
|
||||
File list, suitable for inputting files combined with text understanding and answering questions, available only when the model supports Vision capability.
|
||||
File list, suitable for inputting files combined with text understanding and answering questions, available only when the model supports Vision/Video capability.
|
||||
- `type` (string) Supported type:
|
||||
- `document` ('TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB')
|
||||
- `image` ('JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG')
|
||||
- `audio` ('MP3', 'M4A', 'WAV', 'WEBM', 'AMR')
|
||||
- `video` ('MP4', 'MOV', 'MPEG', 'MPGA')
|
||||
- `custom` (Other file types)
|
||||
- `transfer_method` (string) Transfer method, `remote_url` for image URL / `local_file` for file upload
|
||||
- `url` (string) Image URL (when the transfer method is `remote_url`)
|
||||
- `upload_file_id` (string) Uploaded file ID, which must be obtained by uploading through the File Upload API in advance (when the transfer method is `local_file`)
|
||||
- `document` Supported types include: 'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` Supported types include: 'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` Supported types include: 'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` Supported types include: 'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` Supported types include: other file types
|
||||
- `transfer_method` (string) Transfer method:
|
||||
- `remote_url`: File URL.
|
||||
- `local_file`: Upload file.
|
||||
- `url` File URL. (Only when transfer method is `remote_url`).
|
||||
- `upload_file_id` Upload file ID. (Only when transfer method is `local_file`).
|
||||
</Property>
|
||||
<Property name='auto_generate_name' type='bool' key='auto_generate_name'>
|
||||
Auto-generate title, default is `true`.
|
||||
|
||||
@ -57,16 +57,18 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
会話ID、以前のチャット記録に基づいて会話を続けるには、以前のメッセージのconversation_idを渡す必要があります。
|
||||
</Property>
|
||||
<Property name='files' type='array[object]' key='files'>
|
||||
ファイルリスト、テキストの理解と質問への回答を組み合わせたファイルの入力に適しており、モデルがビジョン機能をサポートしている場合にのみ利用可能です。
|
||||
- `type` (string) サポートされているタイプ:
|
||||
- `document` ('TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB')
|
||||
- `image` ('JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG')
|
||||
- `audio` ('MP3', 'M4A', 'WAV', 'WEBM', 'AMR')
|
||||
- `video` ('MP4', 'MOV', 'MPEG', 'MPGA')
|
||||
- `custom` (他のファイルタイプ)
|
||||
- `transfer_method` (string) 転送方法、画像URLの場合は`remote_url` / ファイルアップロードの場合は`local_file`
|
||||
- `url` (string) 画像URL(転送方法が`remote_url`の場合)
|
||||
- `upload_file_id` (string) アップロードされたファイルID、事前にファイルアップロードAPIを通じて取得する必要があります(転送方法が`local_file`の場合)
|
||||
ファイルリスト、モデルが Vision/Video 機能をサポートしている場合に限り、ファイルをテキスト理解および質問応答に組み合わせて入力するのに適しています。
|
||||
- `type` (string) サポートされるタイプ:
|
||||
- `document` サポートされるタイプには以下が含まれます:'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` サポートされるタイプには以下が含まれます:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` サポートされるタイプには以下が含まれます:'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` サポートされるタイプには以下が含まれます:'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` サポートされるタイプには以下が含まれます:その他のファイルタイプ
|
||||
- `transfer_method` (string) 転送方法:
|
||||
- `remote_url`: ファイルのURL。
|
||||
- `local_file`: ファイルをアップロード。
|
||||
- `url` ファイルのURL。(転送方法が `remote_url` の場合のみ)。
|
||||
- `upload_file_id` アップロードされたファイルID。(転送方法が `local_file` の場合のみ)。
|
||||
</Property>
|
||||
<Property name='auto_generate_name' type='bool' key='auto_generate_name'>
|
||||
タイトルを自動生成、デフォルトは`true`。
|
||||
|
||||
@ -55,17 +55,17 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
(选填)会话 ID,需要基于之前的聊天记录继续对话,必须传之前消息的 conversation_id。
|
||||
</Property>
|
||||
<Property name='files' type='array[object]' key='files'>
|
||||
文件列表,适用于传入文件结合文本理解并回答问题,仅当模型支持 Vision 能力时可用。
|
||||
文件列表,适用于传入文件结合文本理解并回答问题,仅当模型支持 Vision/Video 能力时可用。
|
||||
- `type` (string) 支持类型:
|
||||
- `document` 具体类型包含:'TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `document` 具体类型包含:'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` 具体类型包含:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` 具体类型包含:'MP3', 'M4A', 'WAV', 'WEBM', 'AMR'
|
||||
- `video` 具体类型包含:'MP4', 'MOV', 'MPEG', 'MPGA'
|
||||
- `audio` 具体类型包含:'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` 具体类型包含:'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` 具体类型包含:其他文件类型
|
||||
- `transfer_method` (string) 传递方式:
|
||||
- `remote_url`: 图片地址。
|
||||
- `remote_url`: 文件地址。
|
||||
- `local_file`: 上传文件。
|
||||
- `url` 图片地址。(仅当传递方式为 `remote_url` 时)。
|
||||
- `url` 文件地址。(仅当传递方式为 `remote_url` 时)。
|
||||
- `upload_file_id` 上传文件 ID。(仅当传递方式为 `local_file `时)。
|
||||
</Property>
|
||||
<Property name='auto_generate_name' type='bool' key='auto_generate_name'>
|
||||
|
||||
@ -56,11 +56,18 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
Conversation ID, to continue the conversation based on previous chat records, it is necessary to pass the previous message's conversation_id.
|
||||
</Property>
|
||||
<Property name='files' type='array[object]' key='files'>
|
||||
File list, suitable for inputting files (images) combined with text understanding and answering questions, available only when the model supports Vision capability.
|
||||
- `type` (string) Supported type: `image` (currently only supports image type)
|
||||
- `transfer_method` (string) Transfer method, `remote_url` for image URL / `local_file` for file upload
|
||||
- `url` (string) Image URL (when the transfer method is `remote_url`)
|
||||
- `upload_file_id` (string) Uploaded file ID, which must be obtained by uploading through the File Upload API in advance (when the transfer method is `local_file`)
|
||||
File list, suitable for inputting files combined with text understanding and answering questions, available only when the model supports Vision/Video capability.
|
||||
- `type` (string) Supported type:
|
||||
- `document` Supported types include: 'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` Supported types include: 'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` Supported types include: 'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` Supported types include: 'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` Supported types include: other file types
|
||||
- `transfer_method` (string) Transfer method:
|
||||
- `remote_url`: File URL.
|
||||
- `local_file`: Upload file.
|
||||
- `url` File URL. (Only when transfer method is `remote_url`).
|
||||
- `upload_file_id` Upload file ID. (Only when transfer method is `local_file`).
|
||||
</Property>
|
||||
<Property name='auto_generate_name' type='bool' key='auto_generate_name'>
|
||||
Auto-generate title, default is `true`.
|
||||
|
||||
@ -56,11 +56,18 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
会話ID、以前のチャット記録に基づいて会話を続けるには、前のメッセージのconversation_idを渡す必要があります。
|
||||
</Property>
|
||||
<Property name='files' type='array[object]' key='files'>
|
||||
ファイルリスト、テキストの理解と質問への回答を組み合わせたファイル(画像)の入力に適しており、モデルがビジョン機能をサポートしている場合にのみ利用可能です。
|
||||
- `type` (string) サポートされているタイプ:`image`(現在は画像タイプのみサポート)
|
||||
- `transfer_method` (string) 転送方法、画像URLの場合は`remote_url` / ファイルアップロードの場合は`local_file`
|
||||
- `url` (string) 画像URL(転送方法が`remote_url`の場合)
|
||||
- `upload_file_id` (string) アップロードされたファイルID、事前にファイルアップロードAPIを通じて取得する必要があります(転送方法が`local_file`の場合)
|
||||
ファイルリスト、モデルが Vision/Video 機能をサポートしている場合に限り、ファイルをテキスト理解および質問応答に組み合わせて入力するのに適しています。
|
||||
- `type` (string) サポートされるタイプ:
|
||||
- `document` サポートされるタイプには以下が含まれます:'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` サポートされるタイプには以下が含まれます:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` サポートされるタイプには以下が含まれます:'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` サポートされるタイプには以下が含まれます:'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` サポートされるタイプには以下が含まれます:その他のファイルタイプ
|
||||
- `transfer_method` (string) 転送方法:
|
||||
- `remote_url`: ファイルのURL。
|
||||
- `local_file`: ファイルをアップロード。
|
||||
- `url` ファイルのURL。(転送方法が `remote_url` の場合のみ)。
|
||||
- `upload_file_id` アップロードされたファイルID。(転送方法が `local_file` の場合のみ)。
|
||||
</Property>
|
||||
<Property name='auto_generate_name' type='bool' key='auto_generate_name'>
|
||||
タイトルを自動生成します。デフォルトは`true`です。
|
||||
|
||||
@ -55,12 +55,17 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
(选填)会话 ID,需要基于之前的聊天记录继续对话,必须传之前消息的 conversation_id。
|
||||
</Property>
|
||||
<Property name='files' type='array[object]' key='files'>
|
||||
上传的文件。
|
||||
- `type` (string) 支持类型:图片 `image`(目前仅支持图片格式) 。
|
||||
文件列表,适用于传入文件结合文本理解并回答问题,仅当模型支持 Vision/Video 能力时可用。
|
||||
- `type` (string) 支持类型:
|
||||
- `document` 具体类型包含:'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` 具体类型包含:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` 具体类型包含:'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` 具体类型包含:'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` 具体类型包含:其他文件类型
|
||||
- `transfer_method` (string) 传递方式:
|
||||
- `remote_url`: 图片地址。
|
||||
- `remote_url`: 文件地址。
|
||||
- `local_file`: 上传文件。
|
||||
- `url` 图片地址。(仅当传递方式为 `remote_url` 时)。
|
||||
- `url` 文件地址。(仅当传递方式为 `remote_url` 时)。
|
||||
- `upload_file_id` 上传文件 ID。(仅当传递方式为 `local_file `时)。
|
||||
</Property>
|
||||
<Property name='auto_generate_name' type='bool' key='auto_generate_name'>
|
||||
|
||||
@ -39,14 +39,16 @@ Workflow applications offers non-session support and is ideal for translation, a
|
||||
File Array type variable is suitable for inputting files combined with text understanding and answering questions, available only when the model supports file parsing and understanding capability.
|
||||
If the variable is of File Array type, the corresponding value should be a list whose elements contain following attributions:
|
||||
- `type` (string) Supported type:
|
||||
- `document` ('TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB')
|
||||
- `image` ('JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG')
|
||||
- `audio` ('MP3', 'M4A', 'WAV', 'WEBM', 'AMR')
|
||||
- `video` ('MP4', 'MOV', 'MPEG', 'MPGA')
|
||||
- `custom` (Other file types)
|
||||
- `transfer_method` (string) Transfer method, `remote_url` for image URL / `local_file` for file upload
|
||||
- `url` (string) Image URL (when the transfer method is `remote_url`)
|
||||
- `upload_file_id` (string) Uploaded file ID, which must be obtained by uploading through the File Upload API in advance (when the transfer method is `local_file`)
|
||||
- `document` Supported types include: 'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` Supported types include: 'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` Supported types include: 'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` Supported types include: 'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` Supported types include: other file types
|
||||
- `transfer_method` (string) Transfer method:
|
||||
- `remote_url`: File URL.
|
||||
- `local_file`: Upload file.
|
||||
- `url` File URL. (Only when transfer method is `remote_url`).
|
||||
- `upload_file_id` Upload file ID. (Only when transfer method is `local_file`).
|
||||
|
||||
- `response_mode` (string) Required
|
||||
The mode of response return, supporting:
|
||||
|
||||
@ -39,16 +39,18 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
ファイルリストは、テキスト理解と質問への回答を組み合わせたファイルの入力に適しています。モデルがファイルの解析と理解機能をサポートしている場合にのみ使用できます。
|
||||
|
||||
変数がファイルリストの場合、リストの各要素は以下の属性を持つ必要があります。
|
||||
- `type` (string) サポートされているタイプ:
|
||||
- `document` ('TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB')
|
||||
- `image` ('JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG')
|
||||
- `audio` ('MP3', 'M4A', 'WAV', 'WEBM', 'AMR')
|
||||
- `video` ('MP4', 'MOV', 'MPEG', 'MPGA')
|
||||
- `custom` (他のファイルタイプ)
|
||||
- `transfer_method` (string) 転送方法、画像URLの場合は`remote_url` / ファイルアップロードの場合は`local_file`
|
||||
- `url` (string) 画像URL(転送方法が`remote_url`の場合)
|
||||
- `upload_file_id` (string) アップロードされたファイルID、事前にファイルアップロードAPIを通じて取得する必要があります(転送方法が`local_file`の場合)
|
||||
|
||||
- `type` (string) サポートされるタイプ:
|
||||
- `document` サポートされるタイプには以下が含まれます:'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` サポートされるタイプには以下が含まれます:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` サポートされるタイプには以下が含まれます:'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` サポートされるタイプには以下が含まれます:'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` サポートされるタイプには以下が含まれます:その他のファイルタイプ
|
||||
- `transfer_method` (string) 転送方法:
|
||||
- `remote_url`: ファイルのURL。
|
||||
- `local_file`: ファイルをアップロード。
|
||||
- `url` ファイルのURL。(転送方法が `remote_url` の場合のみ)。
|
||||
- `upload_file_id` アップロードされたファイルID。(転送方法が `local_file` の場合のみ)。
|
||||
|
||||
- `response_mode` (string) 必須
|
||||
応答の返却モードを指定します。サポートされているモード:
|
||||
- `streaming` ストリーミングモード(推奨)、SSE([Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events))を通じてタイプライターのような出力を実装します。
|
||||
|
||||
@ -342,14 +342,17 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
inputs 参数包含了多组键值对(Key/Value pairs),每组的键对应一个特定变量,每组的值则是该变量的具体值。变量可以是文件列表类型。
|
||||
文件列表类型变量适用于传入文件结合文本理解并回答问题,仅当模型支持该类型文件解析能力时可用。如果该变量是文件列表类型,该变量对应的值应是列表格式,其中每个元素应包含以下内容:
|
||||
- `type` (string) 支持类型:
|
||||
- `document` 具体类型包含:'TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `document` 具体类型包含:'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` 具体类型包含:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` 具体类型包含:'MP3', 'M4A', 'WAV', 'WEBM', 'AMR'
|
||||
- `video` 具体类型包含:'MP4', 'MOV', 'MPEG', 'MPGA'
|
||||
- `audio` 具体类型包含:'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` 具体类型包含:'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` 具体类型包含:其他文件类型
|
||||
- `transfer_method` (string) 传递方式,`remote_url` 图片地址 / `local_file` 上传文件
|
||||
- `url` (string) 图片地址(仅当传递方式为 `remote_url` 时)
|
||||
- `upload_file_id` (string) 上传文件 ID(仅当传递方式为 `local_file` 时)
|
||||
- `transfer_method` (string) 传递方式:
|
||||
- `remote_url`: 文件地址。
|
||||
- `local_file`: 上传文件。
|
||||
- `url` 文件地址。(仅当传递方式为 `remote_url` 时)。
|
||||
- `upload_file_id` 上传文件 ID。(仅当传递方式为 `local_file `时)。
|
||||
|
||||
- `response_mode` (string) Required
|
||||
返回响应模式,支持:
|
||||
- `streaming` 流式模式(推荐)。基于 SSE(**[Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)**)实现类似打字机输出方式的流式返回。
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
import type { SlashCommandHandler } from './types'
|
||||
import React from 'react'
|
||||
import { RiUser3Line } from '@remixicon/react'
|
||||
import i18n from '@/i18n-config/i18next-config'
|
||||
import { registerCommands, unregisterCommands } from './command-bus'
|
||||
|
||||
// Account command dependency types - no external dependencies needed
|
||||
type AccountDeps = Record<string, never>
|
||||
|
||||
/**
|
||||
* Account command - Navigates to account page
|
||||
*/
|
||||
export const accountCommand: SlashCommandHandler<AccountDeps> = {
|
||||
name: 'account',
|
||||
description: 'Navigate to account page',
|
||||
|
||||
async search(args: string, locale: string = 'en') {
|
||||
return [{
|
||||
id: 'account',
|
||||
title: i18n.t('common.account.account', { lng: locale }),
|
||||
description: i18n.t('app.gotoAnything.actions.accountDesc', { lng: locale }),
|
||||
type: 'command' as const,
|
||||
icon: (
|
||||
<div className='flex h-6 w-6 items-center justify-center rounded-md border-[0.5px] border-divider-regular bg-components-panel-bg'>
|
||||
<RiUser3Line className='h-4 w-4 text-text-tertiary' />
|
||||
</div>
|
||||
),
|
||||
data: { command: 'navigation.account', args: {} },
|
||||
}]
|
||||
},
|
||||
|
||||
register(_deps: AccountDeps) {
|
||||
registerCommands({
|
||||
'navigation.account': async (_args) => {
|
||||
// Navigate to account page
|
||||
window.location.href = '/account'
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
unregister() {
|
||||
unregisterCommands(['navigation.account'])
|
||||
},
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
import type { SlashCommandHandler } from './types'
|
||||
import React from 'react'
|
||||
import { RiDiscordLine } from '@remixicon/react'
|
||||
import i18n from '@/i18n-config/i18next-config'
|
||||
import { registerCommands, unregisterCommands } from './command-bus'
|
||||
|
||||
// Community command dependency types
|
||||
type CommunityDeps = Record<string, never>
|
||||
|
||||
/**
|
||||
* Community command - Opens Discord community
|
||||
*/
|
||||
export const communityCommand: SlashCommandHandler<CommunityDeps> = {
|
||||
name: 'community',
|
||||
description: 'Open community Discord',
|
||||
async search(args: string, locale: string = 'en') {
|
||||
return [{
|
||||
id: 'community',
|
||||
title: i18n.t('common.userProfile.community', { lng: locale }),
|
||||
description: i18n.t('app.gotoAnything.actions.communityDesc', { lng: locale }) || 'Open Discord community',
|
||||
type: 'command' as const,
|
||||
icon: (
|
||||
<div className='flex h-6 w-6 items-center justify-center rounded-md border-[0.5px] border-divider-regular bg-components-panel-bg'>
|
||||
<RiDiscordLine className='h-4 w-4 text-text-tertiary' />
|
||||
</div>
|
||||
),
|
||||
data: { command: 'navigation.community', args: { url: 'https://discord.gg/5AEfbxcd9k' } },
|
||||
}]
|
||||
},
|
||||
|
||||
register(_deps: CommunityDeps) {
|
||||
registerCommands({
|
||||
'navigation.community': async (args) => {
|
||||
const url = args?.url || 'https://discord.gg/5AEfbxcd9k'
|
||||
window.open(url, '_blank', 'noopener,noreferrer')
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
unregister() {
|
||||
unregisterCommands(['navigation.community'])
|
||||
},
|
||||
}
|
||||
44
web/app/components/goto-anything/actions/commands/doc.tsx
Normal file
44
web/app/components/goto-anything/actions/commands/doc.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import type { SlashCommandHandler } from './types'
|
||||
import React from 'react'
|
||||
import { RiBookOpenLine } from '@remixicon/react'
|
||||
import i18n from '@/i18n-config/i18next-config'
|
||||
import { registerCommands, unregisterCommands } from './command-bus'
|
||||
import { defaultDocBaseUrl } from '@/context/i18n'
|
||||
|
||||
// Documentation command dependency types - no external dependencies needed
|
||||
type DocDeps = Record<string, never>
|
||||
|
||||
/**
|
||||
* Documentation command - Opens help documentation
|
||||
*/
|
||||
export const docCommand: SlashCommandHandler<DocDeps> = {
|
||||
name: 'doc',
|
||||
description: 'Open documentation',
|
||||
async search(args: string, locale: string = 'en') {
|
||||
return [{
|
||||
id: 'doc',
|
||||
title: i18n.t('common.userProfile.helpCenter', { lng: locale }),
|
||||
description: i18n.t('app.gotoAnything.actions.docDesc', { lng: locale }) || 'Open help documentation',
|
||||
type: 'command' as const,
|
||||
icon: (
|
||||
<div className='flex h-6 w-6 items-center justify-center rounded-md border-[0.5px] border-divider-regular bg-components-panel-bg'>
|
||||
<RiBookOpenLine className='h-4 w-4 text-text-tertiary' />
|
||||
</div>
|
||||
),
|
||||
data: { command: 'navigation.doc', args: {} },
|
||||
}]
|
||||
},
|
||||
|
||||
register(_deps: DocDeps) {
|
||||
registerCommands({
|
||||
'navigation.doc': async (_args) => {
|
||||
const url = `${defaultDocBaseUrl}`
|
||||
window.open(url, '_blank', 'noopener,noreferrer')
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
unregister() {
|
||||
unregisterCommands(['navigation.doc'])
|
||||
},
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
import type { SlashCommandHandler } from './types'
|
||||
import React from 'react'
|
||||
import { RiFeedbackLine } from '@remixicon/react'
|
||||
import i18n from '@/i18n-config/i18next-config'
|
||||
import { registerCommands, unregisterCommands } from './command-bus'
|
||||
|
||||
// Feedback command dependency types
|
||||
type FeedbackDeps = Record<string, never>
|
||||
|
||||
/**
|
||||
* Feedback command - Opens GitHub feedback discussions
|
||||
*/
|
||||
export const feedbackCommand: SlashCommandHandler<FeedbackDeps> = {
|
||||
name: 'feedback',
|
||||
description: 'Open feedback discussions',
|
||||
async search(args: string, locale: string = 'en') {
|
||||
return [{
|
||||
id: 'feedback',
|
||||
title: i18n.t('common.userProfile.communityFeedback', { lng: locale }),
|
||||
description: i18n.t('app.gotoAnything.actions.feedbackDesc', { lng: locale }) || 'Open community feedback discussions',
|
||||
type: 'command' as const,
|
||||
icon: (
|
||||
<div className='flex h-6 w-6 items-center justify-center rounded-md border-[0.5px] border-divider-regular bg-components-panel-bg'>
|
||||
<RiFeedbackLine className='h-4 w-4 text-text-tertiary' />
|
||||
</div>
|
||||
),
|
||||
data: { command: 'navigation.feedback', args: { url: 'https://github.com/langgenius/dify/discussions/categories/feedbacks' } },
|
||||
}]
|
||||
},
|
||||
|
||||
register(_deps: FeedbackDeps) {
|
||||
registerCommands({
|
||||
'navigation.feedback': async (args) => {
|
||||
const url = args?.url || 'https://github.com/langgenius/dify/discussions/categories/feedbacks'
|
||||
window.open(url, '_blank', 'noopener,noreferrer')
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
unregister() {
|
||||
unregisterCommands(['navigation.feedback'])
|
||||
},
|
||||
}
|
||||
@ -132,7 +132,7 @@ export class SlashCommandRegistry {
|
||||
try {
|
||||
return await handler.search(args, locale)
|
||||
}
|
||||
catch (error) {
|
||||
catch (error) {
|
||||
console.warn(`Command search failed for ${commandName}:`, error)
|
||||
return []
|
||||
}
|
||||
@ -144,7 +144,7 @@ export class SlashCommandRegistry {
|
||||
try {
|
||||
return await handler.search(args, locale)
|
||||
}
|
||||
catch (error) {
|
||||
catch (error) {
|
||||
console.warn(`Command search failed for ${handler.name}:`, error)
|
||||
return []
|
||||
}
|
||||
|
||||
@ -7,6 +7,10 @@ import { useTheme } from 'next-themes'
|
||||
import { setLocaleOnClient } from '@/i18n-config'
|
||||
import { themeCommand } from './theme'
|
||||
import { languageCommand } from './language'
|
||||
import { feedbackCommand } from './feedback'
|
||||
import { docCommand } from './doc'
|
||||
import { communityCommand } from './community'
|
||||
import { accountCommand } from './account'
|
||||
import i18n from '@/i18n-config/i18next-config'
|
||||
|
||||
export const slashAction: ActionItem = {
|
||||
@ -30,12 +34,20 @@ export const registerSlashCommands = (deps: Record<string, any>) => {
|
||||
// Register command handlers to the registry system with their respective dependencies
|
||||
slashCommandRegistry.register(themeCommand, { setTheme: deps.setTheme })
|
||||
slashCommandRegistry.register(languageCommand, { setLocale: deps.setLocale })
|
||||
slashCommandRegistry.register(feedbackCommand, {})
|
||||
slashCommandRegistry.register(docCommand, {})
|
||||
slashCommandRegistry.register(communityCommand, {})
|
||||
slashCommandRegistry.register(accountCommand, {})
|
||||
}
|
||||
|
||||
export const unregisterSlashCommands = () => {
|
||||
// Remove command handlers from registry system (automatically calls each command's unregister method)
|
||||
slashCommandRegistry.unregister('theme')
|
||||
slashCommandRegistry.unregister('language')
|
||||
slashCommandRegistry.unregister('feedback')
|
||||
slashCommandRegistry.unregister('doc')
|
||||
slashCommandRegistry.unregister('community')
|
||||
slashCommandRegistry.unregister('account')
|
||||
}
|
||||
|
||||
export const SlashCommandProvider = () => {
|
||||
|
||||
@ -205,7 +205,7 @@ export const searchAnything = async (
|
||||
const results = await action.search(query, query, locale)
|
||||
return { success: true, data: results, actionType: action.key }
|
||||
}
|
||||
catch (error) {
|
||||
catch (error) {
|
||||
console.warn(`Search failed for ${action.key}:`, error)
|
||||
return { success: false, data: [], actionType: action.key, error }
|
||||
}
|
||||
@ -220,7 +220,7 @@ export const searchAnything = async (
|
||||
if (result.status === 'fulfilled' && result.value.success) {
|
||||
allResults.push(...result.value.data)
|
||||
}
|
||||
else {
|
||||
else {
|
||||
const actionKey = globalSearchActions[index]?.key || 'unknown'
|
||||
failedActions.push(actionKey)
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import type { FC } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import { Command } from 'cmdk'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { ActionItem } from './actions/types'
|
||||
import { slashCommandRegistry } from './actions/commands/registry'
|
||||
|
||||
type Props = {
|
||||
actions: Record<string, ActionItem>
|
||||
@ -10,27 +11,57 @@ type Props = {
|
||||
searchFilter?: string
|
||||
commandValue?: string
|
||||
onCommandValueChange?: (value: string) => void
|
||||
originalQuery?: string
|
||||
}
|
||||
|
||||
const CommandSelector: FC<Props> = ({ actions, onCommandSelect, searchFilter, commandValue, onCommandValueChange }) => {
|
||||
const CommandSelector: FC<Props> = ({ actions, onCommandSelect, searchFilter, commandValue, onCommandValueChange, originalQuery }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const filteredActions = Object.values(actions).filter((action) => {
|
||||
if (!searchFilter)
|
||||
return true
|
||||
const filterLower = searchFilter.toLowerCase()
|
||||
return action.shortcut.toLowerCase().includes(filterLower)
|
||||
})
|
||||
// Check if we're in slash command mode
|
||||
const isSlashMode = originalQuery?.trim().startsWith('/') || false
|
||||
|
||||
// Get slash commands from registry
|
||||
const slashCommands = useMemo(() => {
|
||||
if (!isSlashMode) return []
|
||||
|
||||
const allCommands = slashCommandRegistry.getAllCommands()
|
||||
const filter = searchFilter?.toLowerCase() || '' // searchFilter already has '/' removed
|
||||
|
||||
return allCommands.filter((cmd) => {
|
||||
if (!filter) return true
|
||||
return cmd.name.toLowerCase().includes(filter)
|
||||
}).map(cmd => ({
|
||||
key: `/${cmd.name}`,
|
||||
shortcut: `/${cmd.name}`,
|
||||
title: cmd.name,
|
||||
description: cmd.description,
|
||||
}))
|
||||
}, [isSlashMode, searchFilter])
|
||||
|
||||
const filteredActions = useMemo(() => {
|
||||
if (isSlashMode) return []
|
||||
|
||||
return Object.values(actions).filter((action) => {
|
||||
// Exclude slash action when in @ mode
|
||||
if (action.key === '/') return false
|
||||
if (!searchFilter)
|
||||
return true
|
||||
const filterLower = searchFilter.toLowerCase()
|
||||
return action.shortcut.toLowerCase().includes(filterLower)
|
||||
})
|
||||
}, [actions, searchFilter, isSlashMode])
|
||||
|
||||
const allItems = isSlashMode ? slashCommands : filteredActions
|
||||
|
||||
useEffect(() => {
|
||||
if (filteredActions.length > 0 && onCommandValueChange) {
|
||||
const currentValueExists = filteredActions.some(action => action.shortcut === commandValue)
|
||||
if (allItems.length > 0 && onCommandValueChange) {
|
||||
const currentValueExists = allItems.some(item => item.shortcut === commandValue)
|
||||
if (!currentValueExists)
|
||||
onCommandValueChange(filteredActions[0].shortcut)
|
||||
onCommandValueChange(allItems[0].shortcut)
|
||||
}
|
||||
}, [searchFilter, filteredActions.length])
|
||||
}, [searchFilter, allItems.length])
|
||||
|
||||
if (filteredActions.length === 0) {
|
||||
if (allItems.length === 0) {
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="flex items-center justify-center py-8 text-center text-text-tertiary">
|
||||
@ -50,33 +81,46 @@ const CommandSelector: FC<Props> = ({ actions, onCommandSelect, searchFilter, co
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="mb-3 text-left text-sm font-medium text-text-secondary">
|
||||
{t('app.gotoAnything.selectSearchType')}
|
||||
{isSlashMode ? t('app.gotoAnything.groups.commands') : t('app.gotoAnything.selectSearchType')}
|
||||
</div>
|
||||
<Command.Group className="space-y-1">
|
||||
{filteredActions.map(action => (
|
||||
{allItems.map(item => (
|
||||
<Command.Item
|
||||
key={action.key}
|
||||
value={action.shortcut}
|
||||
key={item.key}
|
||||
value={item.shortcut}
|
||||
className="flex cursor-pointer items-center rounded-md
|
||||
p-2.5
|
||||
transition-all
|
||||
duration-150 hover:bg-state-base-hover aria-[selected=true]:bg-state-base-hover-alt"
|
||||
onSelect={() => onCommandSelect(action.shortcut)}
|
||||
onSelect={() => onCommandSelect(item.shortcut)}
|
||||
>
|
||||
<span className="min-w-[4.5rem] text-left font-mono text-xs text-text-tertiary">
|
||||
{action.shortcut}
|
||||
{item.shortcut}
|
||||
</span>
|
||||
<span className="ml-3 text-sm text-text-secondary">
|
||||
{(() => {
|
||||
const keyMap: Record<string, string> = {
|
||||
'/': 'app.gotoAnything.actions.slashDesc',
|
||||
'@app': 'app.gotoAnything.actions.searchApplicationsDesc',
|
||||
'@plugin': 'app.gotoAnything.actions.searchPluginsDesc',
|
||||
'@knowledge': 'app.gotoAnything.actions.searchKnowledgeBasesDesc',
|
||||
'@node': 'app.gotoAnything.actions.searchWorkflowNodesDesc',
|
||||
}
|
||||
return t(keyMap[action.key])
|
||||
})()}
|
||||
{isSlashMode ? (
|
||||
(() => {
|
||||
const slashKeyMap: Record<string, string> = {
|
||||
'/theme': 'app.gotoAnything.actions.themeCategoryDesc',
|
||||
'/language': 'app.gotoAnything.actions.languageChangeDesc',
|
||||
'/account': 'app.gotoAnything.actions.accountDesc',
|
||||
'/feedback': 'app.gotoAnything.actions.feedbackDesc',
|
||||
'/doc': 'app.gotoAnything.actions.docDesc',
|
||||
'/community': 'app.gotoAnything.actions.communityDesc',
|
||||
}
|
||||
return t(slashKeyMap[item.key] || item.description)
|
||||
})()
|
||||
) : (
|
||||
(() => {
|
||||
const keyMap: Record<string, string> = {
|
||||
'@app': 'app.gotoAnything.actions.searchApplicationsDesc',
|
||||
'@plugin': 'app.gotoAnything.actions.searchPluginsDesc',
|
||||
'@knowledge': 'app.gotoAnything.actions.searchKnowledgeBasesDesc',
|
||||
'@node': 'app.gotoAnything.actions.searchWorkflowNodesDesc',
|
||||
}
|
||||
return t(keyMap[item.key])
|
||||
})()
|
||||
)}
|
||||
</span>
|
||||
</Command.Item>
|
||||
))}
|
||||
|
||||
@ -166,7 +166,7 @@ const GotoAnything: FC<Props> = ({
|
||||
acc[result.type].push(result)
|
||||
return acc
|
||||
}, {} as { [key: string]: SearchResult[] }),
|
||||
[searchResults])
|
||||
[searchResults])
|
||||
|
||||
const emptyResult = useMemo(() => {
|
||||
if (searchResults.length || !searchQuery.trim() || isLoading || isCommandsMode)
|
||||
@ -226,6 +226,7 @@ const GotoAnything: FC<Props> = ({
|
||||
<div className='mt-3 space-y-1 text-xs text-text-quaternary'>
|
||||
<div>{t('app.gotoAnything.searchHint')}</div>
|
||||
<div>{t('app.gotoAnything.commandHint')}</div>
|
||||
<div>{t('app.gotoAnything.slashHint')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>)
|
||||
@ -321,6 +322,7 @@ const GotoAnything: FC<Props> = ({
|
||||
searchFilter={searchQuery.trim().substring(1)}
|
||||
commandValue={cmdVal}
|
||||
onCommandValueChange={setCmdVal}
|
||||
originalQuery={searchQuery.trim()}
|
||||
/>
|
||||
) : (
|
||||
Object.entries(groupedResults).map(([type, results], groupIndex) => (
|
||||
|
||||
@ -32,7 +32,6 @@ const DataSourceWebsite: FC<Props> = ({ provider }) => {
|
||||
|
||||
useEffect(() => {
|
||||
checkSetApiKey()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const [configTarget, setConfigTarget] = useState<DataSourceProvider | null>(null)
|
||||
|
||||
@ -60,9 +60,9 @@ const AddCredentialInLoadBalancing = ({
|
||||
asChild
|
||||
popupContent={t('plugin.auth.credentialUnavailable')}
|
||||
>
|
||||
{Item}
|
||||
</Tooltip>
|
||||
)
|
||||
{Item}
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
return Item
|
||||
}, [notAllowCustomCredential, t, customModel])
|
||||
|
||||
@ -176,11 +176,11 @@ const Authorized = ({
|
||||
onClick={() => handleEdit(
|
||||
undefined,
|
||||
currentCustomConfigurationModelFixedFields
|
||||
? {
|
||||
model: currentCustomConfigurationModelFixedFields.__model_name,
|
||||
model_type: currentCustomConfigurationModelFixedFields.__model_type,
|
||||
}
|
||||
: undefined,
|
||||
? {
|
||||
model: currentCustomConfigurationModelFixedFields.__model_name,
|
||||
model_type: currentCustomConfigurationModelFixedFields.__model_type,
|
||||
}
|
||||
: undefined,
|
||||
)}
|
||||
className='system-xs-medium flex h-[30px] cursor-pointer items-center px-3 text-text-accent-light-mode-only'
|
||||
>
|
||||
|
||||
@ -236,6 +236,7 @@ const ParameterItem: FC<ParameterItemProps> = ({
|
||||
onChange={handleTagChange}
|
||||
customizedConfirmKey='Tab'
|
||||
isInWorkflow={isInWorkflow}
|
||||
required={parameterRule.required}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -200,8 +200,8 @@ const ModelLoadBalancingModal = ({
|
||||
<div className='grow'>
|
||||
<div className='text-sm text-text-secondary'>{
|
||||
providerFormSchemaPredefined
|
||||
? t('common.modelProvider.auth.providerManaged')
|
||||
: t('common.modelProvider.auth.specifyModelCredential')
|
||||
? t('common.modelProvider.auth.providerManaged')
|
||||
: t('common.modelProvider.auth.specifyModelCredential')
|
||||
}</div>
|
||||
<div className='text-xs text-text-tertiary'>{
|
||||
providerFormSchemaPredefined
|
||||
|
||||
@ -41,13 +41,10 @@ const Item: FC<Props> = ({
|
||||
onFetchedPayload(payload)
|
||||
setPayload({ ...payload, from: dependency.type })
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data])
|
||||
useEffect(() => {
|
||||
if (error)
|
||||
onFetchError()
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [error])
|
||||
if (!payload) return <Loading />
|
||||
return (
|
||||
|
||||
@ -131,7 +131,6 @@ const InstallByDSLList: ForwardRefRenderFunction<ExposeRefs, Props> = ({
|
||||
if (failedIndex.length > 0)
|
||||
setErrorIndexes([...errorIndexes, ...failedIndex])
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isFetchingMarketplaceDataById])
|
||||
|
||||
useEffect(() => {
|
||||
@ -156,15 +155,12 @@ const InstallByDSLList: ForwardRefRenderFunction<ExposeRefs, Props> = ({
|
||||
if (failedIndex.length > 0)
|
||||
setErrorIndexes([...errorIndexes, ...failedIndex])
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isFetchingDataByMeta])
|
||||
|
||||
useEffect(() => {
|
||||
// get info all failed
|
||||
if (infoByMetaError || infoByIdError)
|
||||
setErrorIndexes([...errorIndexes, ...marketPlaceInDSLIndex])
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [infoByMetaError, infoByIdError])
|
||||
|
||||
const isLoadedAllData = (plugins.filter(p => !!p).length + errorIndexes.length) === allPlugins.length
|
||||
@ -189,8 +185,6 @@ const InstallByDSLList: ForwardRefRenderFunction<ExposeRefs, Props> = ({
|
||||
useEffect(() => {
|
||||
if (isLoadedAllData && installedInfo)
|
||||
onLoadedAllPlugin(installedInfo!)
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isLoadedAllData, installedInfo])
|
||||
|
||||
const handleSelect = useCallback((index: number) => {
|
||||
|
||||
@ -61,7 +61,6 @@ const Loaded: React.FC<LoadedProps> = ({
|
||||
useEffect(() => {
|
||||
if (hasInstalled && uniqueIdentifier === installedInfoPayload.uniqueIdentifier)
|
||||
onInstalled()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hasInstalled])
|
||||
|
||||
const handleInstall = async () => {
|
||||
|
||||
@ -55,7 +55,6 @@ const Uploading: FC<Props> = ({
|
||||
|
||||
React.useEffect(() => {
|
||||
handleUpload()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -175,7 +175,6 @@ export const MarketplaceContextProvider = ({
|
||||
})
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [queryPlugins, queryMarketplaceCollectionsAndPlugins, isSuccess, exclude])
|
||||
|
||||
const handleQueryMarketplaceCollectionsAndPlugins = useCallback(() => {
|
||||
|
||||
@ -155,9 +155,9 @@ const Authorized = ({
|
||||
}, [setPluginDefaultCredential, onUpdate, notify, t, handleSetDoingAction])
|
||||
const { mutateAsync: updatePluginCredential } = useUpdatePluginCredentialHook(pluginPayload)
|
||||
const handleRename = useCallback(async (payload: {
|
||||
credential_id: string
|
||||
name: string
|
||||
}) => {
|
||||
credential_id: string
|
||||
name: string
|
||||
}) => {
|
||||
if (doingActionRef.current)
|
||||
return
|
||||
try {
|
||||
@ -306,17 +306,17 @@ const Authorized = ({
|
||||
!notAllowCustomCredential && (
|
||||
<>
|
||||
<div className='h-[1px] bg-divider-subtle'></div>
|
||||
<div className='p-2'>
|
||||
<Authorize
|
||||
pluginPayload={pluginPayload}
|
||||
theme='secondary'
|
||||
showDivider={false}
|
||||
canOAuth={canOAuth}
|
||||
canApiKey={canApiKey}
|
||||
disabled={disabled}
|
||||
onUpdate={onUpdate}
|
||||
/>
|
||||
</div>
|
||||
<div className='p-2'>
|
||||
<Authorize
|
||||
pluginPayload={pluginPayload}
|
||||
theme='secondary'
|
||||
showDivider={false}
|
||||
canOAuth={canOAuth}
|
||||
canApiKey={canApiKey}
|
||||
disabled={disabled}
|
||||
onUpdate={onUpdate}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -87,9 +87,9 @@ export const usePluginAuthAction = (
|
||||
}, [setPluginDefaultCredential, onUpdate, notify, t, handleSetDoingAction])
|
||||
const { mutateAsync: updatePluginCredential } = useUpdatePluginCredentialHook(pluginPayload)
|
||||
const handleRename = useCallback(async (payload: {
|
||||
credential_id: string
|
||||
name: string
|
||||
}) => {
|
||||
credential_id: string
|
||||
name: string
|
||||
}) => {
|
||||
if (doingActionRef.current)
|
||||
return
|
||||
try {
|
||||
|
||||
@ -97,7 +97,7 @@ const AppSelector: FC<Props> = ({
|
||||
try {
|
||||
await setSize((size: number) => size + 1)
|
||||
}
|
||||
finally {
|
||||
finally {
|
||||
// Add a small delay to ensure state updates are complete
|
||||
setTimeout(() => {
|
||||
setIsLoadingMore(false)
|
||||
|
||||
@ -61,7 +61,7 @@ const DetailHeader = ({
|
||||
onUpdate,
|
||||
}: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { userProfile: { timezone } } = useAppContext()
|
||||
const { userProfile: { timezone } } = useAppContext()
|
||||
|
||||
const { theme } = useTheme()
|
||||
const locale = useGetLanguage()
|
||||
|
||||
@ -55,9 +55,9 @@ const EndpointModal: FC<Props> = ({
|
||||
const value = processedCredential[field.name]
|
||||
if (typeof value === 'string')
|
||||
processedCredential[field.name] = value === 'true' || value === '1' || value === 'True'
|
||||
else if (typeof value === 'number')
|
||||
else if (typeof value === 'number')
|
||||
processedCredential[field.name] = value === 1
|
||||
else if (typeof value === 'boolean')
|
||||
else if (typeof value === 'boolean')
|
||||
processedCredential[field.name] = value
|
||||
}
|
||||
})
|
||||
|
||||
@ -45,7 +45,7 @@ const MultipleToolSelector = ({
|
||||
canChooseMCPTool,
|
||||
}: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { data: mcpTools } = useAllMCPTools()
|
||||
const { data: mcpTools } = useAllMCPTools()
|
||||
const enabledCount = value.filter((item) => {
|
||||
const isMCPTool = mcpTools?.find(tool => tool.id === item.provider_name)
|
||||
if(isMCPTool)
|
||||
|
||||
@ -222,12 +222,12 @@ const ReasoningConfigForm: React.FC<Props> = ({
|
||||
{t('workflow.nodes.agent.clickToViewParameterSchema')}
|
||||
</div>}
|
||||
asChild={false}>
|
||||
<div
|
||||
className='ml-0.5 cursor-pointer rounded-[4px] p-px text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary'
|
||||
onClick={() => showSchema(input_schema as SchemaRoot, label[language] || label.en_US)}
|
||||
>
|
||||
<RiBracesLine className='size-3.5'/>
|
||||
</div>
|
||||
<div
|
||||
className='ml-0.5 cursor-pointer rounded-[4px] p-px text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary'
|
||||
onClick={() => showSchema(input_schema as SchemaRoot, label[language] || label.en_US)}
|
||||
>
|
||||
<RiBracesLine className='size-3.5'/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
|
||||
@ -98,7 +98,6 @@ const Action: FC<Props> = ({
|
||||
hideDeleteConfirm()
|
||||
onDelete()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [installationId, onDelete])
|
||||
return (
|
||||
<div className='flex space-x-1'>
|
||||
|
||||
@ -101,19 +101,19 @@ const AutoUpdateSetting: FC<Props> = ({
|
||||
|
||||
const renderTimePickerTrigger = useCallback(({ inputElem, onClick, isOpen }: TriggerParams) => {
|
||||
return (
|
||||
<div
|
||||
className='group float-right flex h-8 w-[160px] cursor-pointer items-center justify-between rounded-lg bg-components-input-bg-normal px-2 hover:bg-state-base-hover-alt'
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className='flex w-0 grow items-center gap-x-1'>
|
||||
<RiTimeLine className={cn(
|
||||
'h-4 w-4 shrink-0 text-text-tertiary',
|
||||
isOpen ? 'text-text-secondary' : 'group-hover:text-text-secondary',
|
||||
)} />
|
||||
{inputElem}
|
||||
</div>
|
||||
<div className='system-sm-regular text-text-tertiary'>{convertTimezoneToOffsetStr(timezone)}</div>
|
||||
<div
|
||||
className='group float-right flex h-8 w-[160px] cursor-pointer items-center justify-between rounded-lg bg-components-input-bg-normal px-2 hover:bg-state-base-hover-alt'
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className='flex w-0 grow items-center gap-x-1'>
|
||||
<RiTimeLine className={cn(
|
||||
'h-4 w-4 shrink-0 text-text-tertiary',
|
||||
isOpen ? 'text-text-secondary' : 'group-hover:text-text-secondary',
|
||||
)} />
|
||||
{inputElem}
|
||||
</div>
|
||||
<div className='system-sm-regular text-text-tertiary'>{convertTimezoneToOffsetStr(timezone)}</div>
|
||||
</div>
|
||||
)
|
||||
}, [timezone])
|
||||
|
||||
|
||||
@ -28,10 +28,10 @@ export const dayjsToTimeOfDay = (date?: Dayjs): number => {
|
||||
}
|
||||
|
||||
export const convertUTCDaySecondsToLocalSeconds = (utcDaySeconds: number, localTimezone: string): number => {
|
||||
const utcDayStart = dayjs().utc().startOf('day')
|
||||
const utcTargetTime = utcDayStart.add(utcDaySeconds, 'second')
|
||||
const localTargetTime = utcTargetTime.tz(localTimezone)
|
||||
const localDayStart = localTargetTime.startOf('day')
|
||||
const secondsInLocalDay = localTargetTime.diff(localDayStart, 'second')
|
||||
return secondsInLocalDay
|
||||
const utcDayStart = dayjs().utc().startOf('day')
|
||||
const utcTargetTime = utcDayStart.add(utcDaySeconds, 'second')
|
||||
const localTargetTime = utcTargetTime.tz(localTimezone)
|
||||
const localDayStart = localTargetTime.startOf('day')
|
||||
const secondsInLocalDay = localTargetTime.diff(localDayStart, 'second')
|
||||
return secondsInLocalDay
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ const DowngradeWarningModal = ({
|
||||
<div className='flex flex-col items-start gap-2 self-stretch'>
|
||||
<div className='title-2xl-semi-bold text-text-primary'>{t(`${i18nPrefix}.title`)}</div>
|
||||
<div className='system-md-regular text-text-secondary'>
|
||||
{t(`${i18nPrefix}.description`)}
|
||||
{t(`${i18nPrefix}.description`)}
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-9 flex items-start justify-end space-x-2 self-stretch'>
|
||||
|
||||
@ -136,47 +136,47 @@ const UpdatePluginModal: FC<Props> = ({
|
||||
onExcludeAndDowngrade={handleExcludeAndDownload}
|
||||
/>
|
||||
)}
|
||||
{!doShowDowngradeWarningModal && (
|
||||
<>
|
||||
<div className='system-md-regular mb-2 mt-3 text-text-secondary'>
|
||||
{t(`${i18nPrefix}.description`)}
|
||||
</div>
|
||||
<div className='flex flex-wrap content-start items-start gap-1 self-stretch rounded-2xl bg-background-section-burn p-2'>
|
||||
<Card
|
||||
installed={uploadStep === UploadStep.installed}
|
||||
payload={pluginManifestToCardPluginProps({
|
||||
...originalPackageInfo.payload,
|
||||
icon: icon!,
|
||||
})}
|
||||
className='w-full'
|
||||
titleLeft={
|
||||
<>
|
||||
<Badge className='mx-1' size="s" state={BadgeState.Warning}>
|
||||
{`${originalPackageInfo.payload.version} -> ${targetPackageInfo.version}`}
|
||||
</Badge>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center justify-end gap-2 self-stretch pt-5'>
|
||||
{uploadStep === UploadStep.notStarted && (
|
||||
{!doShowDowngradeWarningModal && (
|
||||
<>
|
||||
<div className='system-md-regular mb-2 mt-3 text-text-secondary'>
|
||||
{t(`${i18nPrefix}.description`)}
|
||||
</div>
|
||||
<div className='flex flex-wrap content-start items-start gap-1 self-stretch rounded-2xl bg-background-section-burn p-2'>
|
||||
<Card
|
||||
installed={uploadStep === UploadStep.installed}
|
||||
payload={pluginManifestToCardPluginProps({
|
||||
...originalPackageInfo.payload,
|
||||
icon: icon!,
|
||||
})}
|
||||
className='w-full'
|
||||
titleLeft={
|
||||
<>
|
||||
<Badge className='mx-1' size="s" state={BadgeState.Warning}>
|
||||
{`${originalPackageInfo.payload.version} -> ${targetPackageInfo.version}`}
|
||||
</Badge>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center justify-end gap-2 self-stretch pt-5'>
|
||||
{uploadStep === UploadStep.notStarted && (
|
||||
<Button
|
||||
onClick={handleCancel}
|
||||
>
|
||||
{t('common.operation.cancel')}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={handleCancel}
|
||||
variant='primary'
|
||||
loading={uploadStep === UploadStep.upgrading}
|
||||
onClick={handleConfirm}
|
||||
disabled={uploadStep === UploadStep.upgrading}
|
||||
>
|
||||
{t('common.operation.cancel')}
|
||||
{configBtnText}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant='primary'
|
||||
loading={uploadStep === UploadStep.upgrading}
|
||||
onClick={handleConfirm}
|
||||
disabled={uploadStep === UploadStep.upgrading}
|
||||
>
|
||||
{configBtnText}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
</Modal>
|
||||
)
|
||||
|
||||
@ -32,6 +32,7 @@ export const checkOrSetAccessToken = async (appCode?: string | null) => {
|
||||
[userId || 'DEFAULT']: res.access_token,
|
||||
}
|
||||
localStorage.setItem('token', JSON.stringify(accessTokenJson))
|
||||
localStorage.removeItem(CONVERSATION_ID_INFO)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
EDUCATION_VERIFYING_LOCALSTORAGE_ITEM,
|
||||
EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION,
|
||||
} from '@/app/education-apply/constants'
|
||||
import { resolvePostLoginRedirect } from '../signin/utils/post-login-redirect'
|
||||
|
||||
type SwrInitializerProps = {
|
||||
children: ReactNode
|
||||
@ -63,7 +64,11 @@ const SwrInitializer = ({
|
||||
if (searchParams.has('access_token') || searchParams.has('refresh_token')) {
|
||||
consoleToken && localStorage.setItem('console_token', consoleToken)
|
||||
refreshToken && localStorage.setItem('refresh_token', refreshToken)
|
||||
router.replace(pathname)
|
||||
const redirectUrl = resolvePostLoginRedirect(searchParams)
|
||||
if (redirectUrl)
|
||||
location.replace(redirectUrl)
|
||||
else
|
||||
router.replace(pathname)
|
||||
}
|
||||
|
||||
setInit(true)
|
||||
|
||||
@ -141,7 +141,6 @@ const MCPDetailContent: FC<Props> = ({
|
||||
useEffect(() => {
|
||||
if (isTriggerAuthorize)
|
||||
handleAuthorize()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
if (!detail)
|
||||
|
||||
@ -174,54 +174,54 @@ function MCPServiceCard({
|
||||
</div>
|
||||
{!isMinimalState && (
|
||||
<div className='flex flex-col items-start justify-center self-stretch'>
|
||||
<div className="system-xs-medium pb-1 text-text-tertiary">
|
||||
{t('tools.mcp.server.url')}
|
||||
</div>
|
||||
<div className="inline-flex h-9 w-full items-center gap-0.5 rounded-lg bg-components-input-bg-normal p-1 pl-2">
|
||||
<div className="flex h-4 min-w-0 flex-1 items-start justify-start gap-2 px-1">
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap text-xs font-medium text-text-secondary">
|
||||
{serverURL}
|
||||
</div>
|
||||
<div className="system-xs-medium pb-1 text-text-tertiary">
|
||||
{t('tools.mcp.server.url')}
|
||||
</div>
|
||||
{serverPublished && (
|
||||
<>
|
||||
<CopyFeedback
|
||||
content={serverURL}
|
||||
className={'!size-6'}
|
||||
/>
|
||||
<Divider type="vertical" className="!mx-0.5 !h-3.5 shrink-0" />
|
||||
{isCurrentWorkspaceManager && (
|
||||
<Tooltip
|
||||
popupContent={t('appOverview.overview.appInfo.regenerate') || ''}
|
||||
>
|
||||
<div
|
||||
className="cursor-pointer rounded-md p-1 hover:bg-state-base-hover"
|
||||
onClick={() => setShowConfirmDelete(true)}
|
||||
<div className="inline-flex h-9 w-full items-center gap-0.5 rounded-lg bg-components-input-bg-normal p-1 pl-2">
|
||||
<div className="flex h-4 min-w-0 flex-1 items-start justify-start gap-2 px-1">
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap text-xs font-medium text-text-secondary">
|
||||
{serverURL}
|
||||
</div>
|
||||
</div>
|
||||
{serverPublished && (
|
||||
<>
|
||||
<CopyFeedback
|
||||
content={serverURL}
|
||||
className={'!size-6'}
|
||||
/>
|
||||
<Divider type="vertical" className="!mx-0.5 !h-3.5 shrink-0" />
|
||||
{isCurrentWorkspaceManager && (
|
||||
<Tooltip
|
||||
popupContent={t('appOverview.overview.appInfo.regenerate') || ''}
|
||||
>
|
||||
<RiLoopLeftLine className={cn('h-4 w-4 text-text-tertiary hover:text-text-secondary', genLoading && 'animate-spin')}/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer rounded-md p-1 hover:bg-state-base-hover"
|
||||
onClick={() => setShowConfirmDelete(true)}
|
||||
>
|
||||
<RiLoopLeftLine className={cn('h-4 w-4 text-text-tertiary hover:text-text-secondary', genLoading && 'animate-spin')}/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!isMinimalState && (
|
||||
<div className='flex items-center gap-1 self-stretch p-3'>
|
||||
<Button
|
||||
disabled={toggleDisabled}
|
||||
size='small'
|
||||
variant='ghost'
|
||||
onClick={() => setShowMCPServerModal(true)}
|
||||
>
|
||||
<Button
|
||||
disabled={toggleDisabled}
|
||||
size='small'
|
||||
variant='ghost'
|
||||
onClick={() => setShowMCPServerModal(true)}
|
||||
>
|
||||
|
||||
<div className="flex items-center justify-center gap-[1px]">
|
||||
<RiEditLine className="h-3.5 w-3.5" />
|
||||
<div className="system-xs-medium px-[3px] text-text-tertiary">{serverPublished ? t('tools.mcp.server.edit') : t('tools.mcp.server.addDescription')}</div>
|
||||
</div>
|
||||
</Button>
|
||||
<div className="flex items-center justify-center gap-[1px]">
|
||||
<RiEditLine className="h-3.5 w-3.5" />
|
||||
<div className="system-xs-medium px-[3px] text-text-tertiary">{serverPublished ? t('tools.mcp.server.edit') : t('tools.mcp.server.addDescription')}</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -70,9 +70,9 @@ export const addDefaultValue = (value: Record<string, any>, formSchemas: { varia
|
||||
if (formSchema.type === 'boolean' && itemValue !== undefined && itemValue !== null && itemValue !== '') {
|
||||
if (typeof itemValue === 'string')
|
||||
newValues[formSchema.variable] = itemValue === 'true' || itemValue === '1' || itemValue === 'True'
|
||||
else if (typeof itemValue === 'number')
|
||||
else if (typeof itemValue === 'number')
|
||||
newValues[formSchema.variable] = itemValue === 1
|
||||
else if (typeof itemValue === 'boolean')
|
||||
else if (typeof itemValue === 'boolean')
|
||||
newValues[formSchema.variable] = itemValue
|
||||
}
|
||||
})
|
||||
@ -162,13 +162,13 @@ export const getConfiguredValue = (value: Record<string, any>, formSchemas: { va
|
||||
}
|
||||
|
||||
const getVarKindType = (type: FormTypeEnum) => {
|
||||
if (type === FormTypeEnum.file || type === FormTypeEnum.files)
|
||||
return VarKindType.variable
|
||||
if (type === FormTypeEnum.select || type === FormTypeEnum.boolean || type === FormTypeEnum.textNumber)
|
||||
return VarKindType.constant
|
||||
if (type === FormTypeEnum.textInput || type === FormTypeEnum.secretInput)
|
||||
return VarKindType.mixed
|
||||
}
|
||||
if (type === FormTypeEnum.file || type === FormTypeEnum.files)
|
||||
return VarKindType.variable
|
||||
if (type === FormTypeEnum.select || type === FormTypeEnum.boolean || type === FormTypeEnum.textNumber)
|
||||
return VarKindType.constant
|
||||
if (type === FormTypeEnum.textInput || type === FormTypeEnum.secretInput)
|
||||
return VarKindType.mixed
|
||||
}
|
||||
|
||||
export const generateAgentToolValue = (value: Record<string, any>, formSchemas: { variable: string; default?: any; type: string }[], isReasoning = false) => {
|
||||
const newValues = {} as any
|
||||
|
||||
@ -101,7 +101,6 @@ const AllTools = ({
|
||||
category: PluginType.tool,
|
||||
})
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [searchText, tags, enable_marketplace])
|
||||
|
||||
const pluginRef = useRef<ListRef>(null)
|
||||
|
||||
@ -60,7 +60,6 @@ const OperationDropdown: FC<Props> = ({
|
||||
downloadFile({ data: blob, fileName })
|
||||
setNeedDownload(false)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [blob])
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
|
||||
@ -56,7 +56,6 @@ const List = forwardRef<ListRef, ListProps>(({
|
||||
|
||||
useEffect(() => {
|
||||
handleScroll()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [list])
|
||||
|
||||
const handleHeadClick = () => {
|
||||
|
||||
@ -122,7 +122,6 @@ const Tool: FC<Props> = ({
|
||||
}
|
||||
if (!hasSearchText && !isFold)
|
||||
setFold(true)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hasSearchText])
|
||||
|
||||
const FoldIcon = isFold ? RiArrowRightSLine : RiArrowDownSLine
|
||||
|
||||
@ -40,7 +40,6 @@ const DatasetsDetailProvider: FC<DatasetsDetailProviderProps> = ({
|
||||
}, [])
|
||||
if (allDatasetIds.length === 0) return
|
||||
updateDatasetsDetail(allDatasetIds)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
||||
@ -82,26 +82,26 @@ const HeaderInRestoring = ({
|
||||
<RestoringTitle />
|
||||
</div>
|
||||
<div className=' flex items-center justify-end gap-x-2'>
|
||||
<Button
|
||||
onClick={handleRestore}
|
||||
disabled={!currentVersion || currentVersion.version === WorkflowVersion.Draft}
|
||||
variant='primary'
|
||||
className={cn(
|
||||
theme === 'dark' && 'rounded-lg border border-black/5 bg-white/10 backdrop-blur-sm',
|
||||
)}
|
||||
>
|
||||
{t('workflow.common.restore')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCancelRestore}
|
||||
className={cn(
|
||||
'text-components-button-secondary-accent-text',
|
||||
theme === 'dark' && 'rounded-lg border border-black/5 bg-white/10 backdrop-blur-sm',
|
||||
)}
|
||||
>
|
||||
<div className='flex items-center gap-x-0.5'>
|
||||
<RiHistoryLine className='h-4 w-4' />
|
||||
<span className='px-0.5'>{t('workflow.common.exitVersions')}</span>
|
||||
<Button
|
||||
onClick={handleRestore}
|
||||
disabled={!currentVersion || currentVersion.version === WorkflowVersion.Draft}
|
||||
variant='primary'
|
||||
className={cn(
|
||||
theme === 'dark' && 'rounded-lg border border-black/5 bg-white/10 backdrop-blur-sm',
|
||||
)}
|
||||
>
|
||||
{t('workflow.common.restore')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCancelRestore}
|
||||
className={cn(
|
||||
'text-components-button-secondary-accent-text',
|
||||
theme === 'dark' && 'rounded-lg border border-black/5 bg-white/10 backdrop-blur-sm',
|
||||
)}
|
||||
>
|
||||
<div className='flex items-center gap-x-0.5'>
|
||||
<RiHistoryLine className='h-4 w-4' />
|
||||
<span className='px-0.5'>{t('workflow.common.exitVersions')}</span>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -49,7 +49,7 @@ const VersionHistoryButton: FC<VersionHistoryButtonProps> = ({
|
||||
e.preventDefault()
|
||||
handleViewVersionHistory()
|
||||
},
|
||||
{ exactMatch: true, useCapture: true })
|
||||
{ exactMatch: true, useCapture: true })
|
||||
|
||||
return <Tooltip
|
||||
popupContent={<PopupContent />}
|
||||
@ -57,15 +57,15 @@ const VersionHistoryButton: FC<VersionHistoryButtonProps> = ({
|
||||
popupClassName='rounded-lg border-[0.5px] border-components-panel-border bg-components-tooltip-bg
|
||||
shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px] p-1.5'
|
||||
>
|
||||
<Button
|
||||
className={cn(
|
||||
'p-2',
|
||||
theme === 'dark' && 'rounded-lg border border-black/5 bg-white/10 backdrop-blur-sm',
|
||||
)}
|
||||
onClick={handleViewVersionHistory}
|
||||
>
|
||||
<RiHistoryLine className='h-4 w-4 text-components-button-secondary-text' />
|
||||
</Button>
|
||||
<Button
|
||||
className={cn(
|
||||
'p-2',
|
||||
theme === 'dark' && 'rounded-lg border border-black/5 bg-white/10 backdrop-blur-sm',
|
||||
)}
|
||||
onClick={handleViewVersionHistory}
|
||||
>
|
||||
<RiHistoryLine className='h-4 w-4 text-components-button-secondary-text' />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +22,6 @@ export const HooksStoreContextProvider = ({ children, ...restProps }: HooksStore
|
||||
useEffect(() => {
|
||||
if (storeRef.current && d3Selection && d3Zoom)
|
||||
storeRef.current.getState().refreshAll(restProps)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [d3Selection, d3Zoom])
|
||||
|
||||
if (!storeRef.current)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user