Merge branch 'main' into feat/trigger

This commit is contained in:
Yeuoly
2025-09-01 18:05:31 +08:00
480 changed files with 10562 additions and 4040 deletions

View File

@ -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' />,
}

View File

@ -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

View File

@ -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 = () => {

View File

@ -84,7 +84,7 @@ const Annotation: FC<Props> = (props) => {
setList(data as AnnotationItem[])
setTotal(total)
}
finally {
finally {
setIsLoading(false)
}
}

View File

@ -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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
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

View File

@ -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>

View File

@ -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,

View File

@ -175,7 +175,6 @@ const ConfigContent: FC<Props> = ({
...datasetConfigs,
reranking_enable: enable,
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentRerankModel, datasetConfigs, onChange])
return (

View File

@ -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' && (

View File

@ -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) {

View File

@ -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>

View File

@ -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

View File

@ -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 */}

View File

@ -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)

View File

@ -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, '&lt;')
.replace(/>/g, '&gt;')
.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 = ''

View File

@ -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()

View File

@ -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}
/>
)
}

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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')
}

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>
)}

View File

@ -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

View File

@ -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)}
/>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>
)}

View File

@ -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')

View File

@ -226,7 +226,7 @@ const TagSelector: FC<TagSelectorProps> = ({
const res = await fetchTagList(type)
setTagList(res)
}
catch (error) {
catch (error) {
setTagList([])
}
}

View File

@ -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

View File

@ -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(() => {

View File

@ -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')}>

View File

@ -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'

View File

@ -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')}>

View File

@ -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)
}

View File

@ -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>

View File

@ -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,
},

View File

@ -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>

View File

@ -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,
})

View File

@ -28,7 +28,6 @@ const useEditDatasetMetadata = ({
showEditModal()
localStorage.removeItem(isShowManageMetadataLocalStorageKey)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const { data: datasetMetaData } = useDatasetMetaData(datasetId)

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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`.

View File

@ -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`。

View File

@ -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'>

View File

@ -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`.

View File

@ -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`です。

View File

@ -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'>

View File

@ -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:

View File

@ -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))を通じてタイプライターのような出力を実装します。

View File

@ -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)**)实现类似打字机输出方式的流式返回。

View File

@ -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'])
},
}

View File

@ -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'])
},
}

View 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'])
},
}

View File

@ -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'])
},
}

View File

@ -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 []
}

View File

@ -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 = () => {

View File

@ -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)
}

View File

@ -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>
))}

View File

@ -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) => (

View File

@ -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)

View File

@ -60,9 +60,9 @@ const AddCredentialInLoadBalancing = ({
asChild
popupContent={t('plugin.auth.credentialUnavailable')}
>
{Item}
</Tooltip>
)
{Item}
</Tooltip>
)
}
return Item
}, [notAllowCustomCredential, t, customModel])

View File

@ -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'
>

View File

@ -236,6 +236,7 @@ const ParameterItem: FC<ParameterItemProps> = ({
onChange={handleTagChange}
customizedConfirmKey='Tab'
isInWorkflow={isInWorkflow}
required={parameterRule.required}
/>
</div>
)

View File

@ -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

View File

@ -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 (

View File

@ -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) => {

View File

@ -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 () => {

View File

@ -55,7 +55,6 @@ const Uploading: FC<Props> = ({
React.useEffect(() => {
handleUpload()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
<>

View File

@ -175,7 +175,6 @@ export const MarketplaceContextProvider = ({
})
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [queryPlugins, queryMarketplaceCollectionsAndPlugins, isSuccess, exclude])
const handleQueryMarketplaceCollectionsAndPlugins = useCallback(() => {

View File

@ -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>
</>
)
}

View File

@ -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 {

View File

@ -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)

View File

@ -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()

View File

@ -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
}
})

View File

@ -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)

View File

@ -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>
)}

View File

@ -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'>

View File

@ -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])

View File

@ -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
}

View File

@ -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'>

View File

@ -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>
)

View File

@ -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)
}
}

View File

@ -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)

View File

@ -141,7 +141,6 @@ const MCPDetailContent: FC<Props> = ({
useEffect(() => {
if (isTriggerAuthorize)
handleAuthorize()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
if (!detail)

View File

@ -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>

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -56,7 +56,6 @@ const List = forwardRef<ListRef, ListProps>(({
useEffect(() => {
handleScroll()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [list])
const handleHeadClick = () => {

View File

@ -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

View File

@ -40,7 +40,6 @@ const DatasetsDetailProvider: FC<DatasetsDetailProviderProps> = ({
}, [])
if (allDatasetIds.length === 0) return
updateDatasetsDetail(allDatasetIds)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (

View File

@ -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>

View File

@ -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>
}

View File

@ -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