mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
Merge branch 'main' into feat/plugin-auto-upgrade-fe
This commit is contained in:
@ -191,6 +191,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
|
||||
const { userProfile: { timezone } } = useAppContext()
|
||||
const { formatTime } = useTimestamp()
|
||||
const { onClose, appDetail } = useContext(DrawerContext)
|
||||
const { notify } = useContext(ToastContext)
|
||||
const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal, showPromptLogModal, setShowPromptLogModal, currentLogModalActiveTab } = useAppStore(useShallow(state => ({
|
||||
currentLogItem: state.currentLogItem,
|
||||
setCurrentLogItem: state.setCurrentLogItem,
|
||||
@ -312,18 +313,34 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
|
||||
return item
|
||||
}))
|
||||
}, [allChatItems])
|
||||
const handleAnnotationRemoved = useCallback((index: number) => {
|
||||
setAllChatItems(allChatItems.map((item, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...item,
|
||||
content: item.content,
|
||||
annotation: undefined,
|
||||
}
|
||||
const handleAnnotationRemoved = useCallback(async (index: number): Promise<boolean> => {
|
||||
const annotation = allChatItems[index]?.annotation
|
||||
|
||||
try {
|
||||
if (annotation?.id) {
|
||||
const { delAnnotation } = await import('@/service/annotation')
|
||||
await delAnnotation(appDetail?.id || '', annotation.id)
|
||||
}
|
||||
return item
|
||||
}))
|
||||
}, [allChatItems])
|
||||
|
||||
setAllChatItems(allChatItems.map((item, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...item,
|
||||
content: item.content,
|
||||
annotation: undefined,
|
||||
}
|
||||
}
|
||||
return item
|
||||
}))
|
||||
|
||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||
return true
|
||||
}
|
||||
catch {
|
||||
notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
|
||||
return false
|
||||
}
|
||||
}, [allChatItems, appDetail?.id, t])
|
||||
|
||||
const fetchInitiated = useRef(false)
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 6.9 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 6.9 KiB |
@ -18,7 +18,7 @@ describe('InputNumber Component', () => {
|
||||
|
||||
it('renders input with default values', () => {
|
||||
render(<InputNumber {...defaultProps} />)
|
||||
const input = screen.getByRole('textbox')
|
||||
const input = screen.getByRole('spinbutton')
|
||||
expect(input).toBeInTheDocument()
|
||||
})
|
||||
|
||||
@ -56,7 +56,7 @@ describe('InputNumber Component', () => {
|
||||
|
||||
it('handles direct input changes', () => {
|
||||
render(<InputNumber {...defaultProps} />)
|
||||
const input = screen.getByRole('textbox')
|
||||
const input = screen.getByRole('spinbutton')
|
||||
|
||||
fireEvent.change(input, { target: { value: '42' } })
|
||||
expect(defaultProps.onChange).toHaveBeenCalledWith(42)
|
||||
@ -64,7 +64,7 @@ describe('InputNumber Component', () => {
|
||||
|
||||
it('handles empty input', () => {
|
||||
render(<InputNumber {...defaultProps} value={0} />)
|
||||
const input = screen.getByRole('textbox')
|
||||
const input = screen.getByRole('spinbutton')
|
||||
|
||||
fireEvent.change(input, { target: { value: '' } })
|
||||
expect(defaultProps.onChange).toHaveBeenCalledWith(undefined)
|
||||
@ -72,7 +72,7 @@ describe('InputNumber Component', () => {
|
||||
|
||||
it('handles invalid input', () => {
|
||||
render(<InputNumber {...defaultProps} />)
|
||||
const input = screen.getByRole('textbox')
|
||||
const input = screen.getByRole('spinbutton')
|
||||
|
||||
fireEvent.change(input, { target: { value: 'abc' } })
|
||||
expect(defaultProps.onChange).not.toHaveBeenCalled()
|
||||
@ -86,7 +86,7 @@ describe('InputNumber Component', () => {
|
||||
|
||||
it('disables controls when disabled prop is true', () => {
|
||||
render(<InputNumber {...defaultProps} disabled />)
|
||||
const input = screen.getByRole('textbox')
|
||||
const input = screen.getByRole('spinbutton')
|
||||
const incrementBtn = screen.getByRole('button', { name: /increment/i })
|
||||
const decrementBtn = screen.getByRole('button', { name: /decrement/i })
|
||||
|
||||
|
||||
@ -55,8 +55,8 @@ export const InputNumber: FC<InputNumberProps> = (props) => {
|
||||
return <div className={classNames('flex', wrapClassName)}>
|
||||
<Input {...rest}
|
||||
// disable default controller
|
||||
type='text'
|
||||
className={classNames('rounded-r-none', className)}
|
||||
type='number'
|
||||
className={classNames('no-spinner rounded-r-none', className)}
|
||||
value={value}
|
||||
max={max}
|
||||
min={min}
|
||||
@ -77,8 +77,8 @@ export const InputNumber: FC<InputNumberProps> = (props) => {
|
||||
size={size}
|
||||
/>
|
||||
<div className={classNames(
|
||||
'flex flex-col bg-components-input-bg-normal rounded-r-md border-l border-divider-subtle text-text-tertiary focus:shadow-xs',
|
||||
disabled && 'opacity-50 cursor-not-allowed',
|
||||
'flex flex-col rounded-r-md border-l border-divider-subtle bg-components-input-bg-normal text-text-tertiary focus:shadow-xs',
|
||||
disabled && 'cursor-not-allowed opacity-50',
|
||||
controlWrapClassName)}
|
||||
>
|
||||
<button
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { Combobox, ComboboxButton, ComboboxInput, ComboboxOption, ComboboxOptions, Listbox, ListboxButton, ListboxOption, ListboxOptions } from '@headlessui/react'
|
||||
import { ChevronDownIcon, ChevronUpIcon, XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import Badge from '../badge/index'
|
||||
import { RiCheckLine } from '@remixicon/react'
|
||||
import { RiCheckLine, RiLoader4Line } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import classNames from '@/utils/classnames'
|
||||
import {
|
||||
@ -51,6 +51,8 @@ export type ISelectProps = {
|
||||
item: Item
|
||||
selected: boolean
|
||||
}) => React.ReactNode
|
||||
isLoading?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
}
|
||||
const Select: FC<ISelectProps> = ({
|
||||
className,
|
||||
@ -178,17 +180,20 @@ const SimpleSelect: FC<ISelectProps> = ({
|
||||
defaultValue = 1,
|
||||
disabled = false,
|
||||
onSelect,
|
||||
onOpenChange,
|
||||
placeholder,
|
||||
optionWrapClassName,
|
||||
optionClassName,
|
||||
hideChecked,
|
||||
notClearable,
|
||||
renderOption,
|
||||
isLoading = false,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const localPlaceholder = placeholder || t('common.placeholder.select')
|
||||
|
||||
const [selectedItem, setSelectedItem] = useState<Item | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let defaultSelect = null
|
||||
const existed = items.find((item: Item) => item.value === defaultValue)
|
||||
@ -199,8 +204,10 @@ const SimpleSelect: FC<ISelectProps> = ({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [defaultValue])
|
||||
|
||||
const listboxRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
return (
|
||||
<Listbox
|
||||
<Listbox ref={listboxRef}
|
||||
value={selectedItem}
|
||||
onChange={(value: Item) => {
|
||||
if (!disabled) {
|
||||
@ -212,10 +219,17 @@ const SimpleSelect: FC<ISelectProps> = ({
|
||||
<div className={classNames('group/simple-select relative h-9', wrapperClassName)}>
|
||||
{renderTrigger && <ListboxButton className='w-full'>{renderTrigger(selectedItem)}</ListboxButton>}
|
||||
{!renderTrigger && (
|
||||
<ListboxButton className={classNames(`flex items-center w-full h-full rounded-lg border-0 bg-components-input-bg-normal pl-3 pr-10 sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-state-base-hover-alt group-hover/simple-select:bg-state-base-hover-alt ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}`, className)}>
|
||||
<ListboxButton onClick={() => {
|
||||
// get data-open, use setTimeout to ensure the attribute is set
|
||||
setTimeout(() => {
|
||||
if (listboxRef.current)
|
||||
onOpenChange?.(listboxRef.current.getAttribute('data-open') !== null)
|
||||
})
|
||||
}} className={classNames(`flex items-center w-full h-full rounded-lg border-0 bg-components-input-bg-normal pl-3 pr-10 sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-state-base-hover-alt group-hover/simple-select:bg-state-base-hover-alt ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}`, className)}>
|
||||
<span className={classNames('block truncate text-left system-sm-regular 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">
|
||||
{(selectedItem && !notClearable)
|
||||
{isLoading ? <RiLoader4Line className='h-3.5 w-3.5 animate-spin text-text-secondary' />
|
||||
: (selectedItem && !notClearable)
|
||||
? (
|
||||
<XMarkIcon
|
||||
onClick={(e) => {
|
||||
@ -237,7 +251,7 @@ const SimpleSelect: FC<ISelectProps> = ({
|
||||
</ListboxButton>
|
||||
)}
|
||||
|
||||
{!disabled && (
|
||||
{(!disabled) && (
|
||||
<ListboxOptions className={classNames('absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-xl bg-components-panel-bg-blur backdrop-blur-sm py-1 text-base shadow-lg border-components-panel-border border-[0.5px] focus:outline-none sm:text-sm', optionWrapClassName)}>
|
||||
{items.map((item: Item) => (
|
||||
<ListboxOption
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { useState } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import type { ChangeEvent, FC, KeyboardEvent } from 'react'
|
||||
import { } from 'use-context-selector'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AutosizeInput from 'react-18-input-autosize'
|
||||
import { RiAddLine, RiCloseLine } from '@remixicon/react'
|
||||
@ -40,6 +39,29 @@ const TagInput: FC<TagInputProps> = ({
|
||||
onChange(copyItems)
|
||||
}
|
||||
|
||||
const handleNewTag = useCallback((value: string) => {
|
||||
const valueTrimmed = value.trim()
|
||||
if (!valueTrimmed) {
|
||||
notify({ type: 'error', message: t('datasetDocuments.segment.keywordEmpty') })
|
||||
return
|
||||
}
|
||||
|
||||
if ((items.find(item => item === valueTrimmed))) {
|
||||
notify({ type: 'error', message: t('datasetDocuments.segment.keywordDuplicate') })
|
||||
return
|
||||
}
|
||||
|
||||
if (valueTrimmed.length > 20) {
|
||||
notify({ type: 'error', message: t('datasetDocuments.segment.keywordError') })
|
||||
return
|
||||
}
|
||||
|
||||
onChange([...items, valueTrimmed])
|
||||
setTimeout(() => {
|
||||
setValue('')
|
||||
})
|
||||
}, [items, onChange, notify, t])
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (isSpecialMode && e.key === 'Enter')
|
||||
setValue(`${value}↵`)
|
||||
@ -48,24 +70,12 @@ const TagInput: FC<TagInputProps> = ({
|
||||
if (isSpecialMode)
|
||||
e.preventDefault()
|
||||
|
||||
const valueTrimmed = value.trim()
|
||||
if (!valueTrimmed || (items.find(item => item === valueTrimmed)))
|
||||
return
|
||||
|
||||
if (valueTrimmed.length > 20) {
|
||||
notify({ type: 'error', message: t('datasetDocuments.segment.keywordError') })
|
||||
return
|
||||
}
|
||||
|
||||
onChange([...items, valueTrimmed])
|
||||
setTimeout(() => {
|
||||
setValue('')
|
||||
})
|
||||
handleNewTag(value)
|
||||
}
|
||||
}
|
||||
|
||||
const handleBlur = () => {
|
||||
setValue('')
|
||||
handleNewTag(value)
|
||||
setFocused(false)
|
||||
}
|
||||
|
||||
|
||||
@ -52,8 +52,8 @@ const StepThree = ({ datasetId, datasetName, indexingType, creationCache, retrie
|
||||
datasetId={datasetId || creationCache?.dataset?.id || ''}
|
||||
batchId={creationCache?.batch || ''}
|
||||
documents={creationCache?.documents as FullDocumentDetail[]}
|
||||
indexingType={indexingType || creationCache?.dataset?.indexing_technique}
|
||||
retrievalMethod={retrievalMethod || creationCache?.dataset?.retrieval_model?.search_method}
|
||||
indexingType={creationCache?.dataset?.indexing_technique || indexingType}
|
||||
retrievalMethod={creationCache?.dataset?.retrieval_model_dict?.search_method || retrievalMethod}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -507,13 +507,15 @@ const StepTwo = ({
|
||||
const separator = rules.segmentation.separator
|
||||
const max = rules.segmentation.max_tokens
|
||||
const overlap = rules.segmentation.chunk_overlap
|
||||
const isHierarchicalDocument = documentDetail.doc_form === ChunkingMode.parentChild
|
||||
|| (rules.parent_mode && rules.subchunk_segmentation)
|
||||
setSegmentIdentifier(separator)
|
||||
setMaxChunkLength(max)
|
||||
setOverlap(overlap!)
|
||||
setRules(rules.pre_processing_rules)
|
||||
setDefaultConfig(rules)
|
||||
|
||||
if (documentDetail.dataset_process_rule.mode === 'hierarchical') {
|
||||
if (isHierarchicalDocument) {
|
||||
setParentChildConfig({
|
||||
chunkForContext: rules.parent_mode || 'paragraph',
|
||||
parent: {
|
||||
@ -575,6 +577,7 @@ const StepTwo = ({
|
||||
onSuccess(data) {
|
||||
updateIndexingTypeCache && updateIndexingTypeCache(indexType as string)
|
||||
updateResultCache && updateResultCache(data)
|
||||
updateRetrievalMethodCache && updateRetrievalMethodCache(retrievalConfig.search_method as string)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@ -19,12 +19,14 @@ export enum FormTypeEnum {
|
||||
toolSelector = 'tool-selector',
|
||||
multiToolSelector = 'array[tools]',
|
||||
appSelector = 'app-selector',
|
||||
dynamicSelect = 'dynamic-select',
|
||||
}
|
||||
|
||||
export type FormOption = {
|
||||
label: TypeWithI18N
|
||||
value: string
|
||||
show_on: FormShowOnObject[]
|
||||
icon?: string
|
||||
}
|
||||
|
||||
export enum ModelTypeEnum {
|
||||
|
||||
@ -30,7 +30,7 @@ const HeaderWrapper = ({
|
||||
|
||||
return (
|
||||
<div className={classNames(
|
||||
'sticky top-0 left-0 right-0 z-30 flex flex-col grow-0 shrink-0 basis-auto min-h-[56px]',
|
||||
'sticky left-0 right-0 top-0 z-[15] flex min-h-[56px] shrink-0 grow-0 basis-auto flex-col',
|
||||
s.header,
|
||||
isBordered ? 'border-b border-divider-regular' : '',
|
||||
)}
|
||||
|
||||
@ -18,6 +18,15 @@ type Props = {
|
||||
onSaved: (value: Record<string, any>) => void
|
||||
}
|
||||
|
||||
const extractDefaultValues = (schemas: any[]) => {
|
||||
const result: Record<string, any> = {}
|
||||
for (const field of schemas) {
|
||||
if (field.default !== undefined)
|
||||
result[field.name] = field.default
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const EndpointModal: FC<Props> = ({
|
||||
formSchemas,
|
||||
defaultValues = {},
|
||||
@ -26,7 +35,10 @@ const EndpointModal: FC<Props> = ({
|
||||
}) => {
|
||||
const getValueFromI18nObject = useRenderI18nObject()
|
||||
const { t } = useTranslation()
|
||||
const [tempCredential, setTempCredential] = React.useState<any>(defaultValues)
|
||||
const initialValues = Object.keys(defaultValues).length > 0
|
||||
? defaultValues
|
||||
: extractDefaultValues(formSchemas)
|
||||
const [tempCredential, setTempCredential] = React.useState<any>(initialValues)
|
||||
|
||||
const handleSave = () => {
|
||||
for (const field of formSchemas) {
|
||||
|
||||
@ -117,6 +117,7 @@ const MultipleToolSelector = ({
|
||||
)}
|
||||
{!disabled && (
|
||||
<ActionButton className='mx-1' onClick={() => {
|
||||
setCollapse(false)
|
||||
setOpen(!open)
|
||||
setPanelShowState(true)
|
||||
}}>
|
||||
@ -126,23 +127,6 @@ const MultipleToolSelector = ({
|
||||
</div>
|
||||
{!collapse && (
|
||||
<>
|
||||
<ToolSelector
|
||||
nodeId={nodeId}
|
||||
nodeOutputVars={nodeOutputVars}
|
||||
availableNodes={availableNodes}
|
||||
scope={scope}
|
||||
value={undefined}
|
||||
selectedTools={value}
|
||||
onSelect={handleAdd}
|
||||
controlledState={open}
|
||||
onControlledStateChange={setOpen}
|
||||
trigger={
|
||||
<div className=''></div>
|
||||
}
|
||||
panelShowState={panelShowState}
|
||||
onPanelShowStateChange={setPanelShowState}
|
||||
isEdit={false}
|
||||
/>
|
||||
{value.length === 0 && (
|
||||
<div className='system-xs-regular flex justify-center rounded-[10px] bg-background-section p-3 text-text-tertiary'>{t('plugin.detailPanel.toolSelector.empty')}</div>
|
||||
)}
|
||||
@ -164,6 +148,23 @@ const MultipleToolSelector = ({
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
<ToolSelector
|
||||
nodeId={nodeId}
|
||||
nodeOutputVars={nodeOutputVars}
|
||||
availableNodes={availableNodes}
|
||||
scope={scope}
|
||||
value={undefined}
|
||||
selectedTools={value}
|
||||
onSelect={handleAdd}
|
||||
controlledState={open}
|
||||
onControlledStateChange={setOpen}
|
||||
trigger={
|
||||
<div className=''></div>
|
||||
}
|
||||
panelShowState={panelShowState}
|
||||
onPanelShowStateChange={setPanelShowState}
|
||||
isEdit={false}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -275,7 +275,7 @@ const ToolSelector: FC<Props> = ({
|
||||
/>
|
||||
)}
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[1000]'>
|
||||
<PortalToFollowElemContent>
|
||||
<div className={cn('relative max-h-[642px] min-h-20 w-[361px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur pb-4 shadow-lg backdrop-blur-sm', !isShowSettingAuth && 'overflow-y-auto pb-2')}>
|
||||
{!isShowSettingAuth && (
|
||||
<>
|
||||
|
||||
@ -36,7 +36,7 @@ export type ToolValue = {
|
||||
provider_name: string
|
||||
tool_name: string
|
||||
tool_label: string
|
||||
tool_description: string
|
||||
tool_description?: string
|
||||
settings?: Record<string, any>
|
||||
parameters?: Record<string, any>
|
||||
enabled?: boolean
|
||||
|
||||
@ -33,6 +33,8 @@ const HeaderInNormal = ({
|
||||
const setShowWorkflowVersionHistoryPanel = useStore(s => s.setShowWorkflowVersionHistoryPanel)
|
||||
const setShowEnvPanel = useStore(s => s.setShowEnvPanel)
|
||||
const setShowDebugAndPreviewPanel = useStore(s => s.setShowDebugAndPreviewPanel)
|
||||
const setShowVariableInspectPanel = useStore(s => s.setShowVariableInspectPanel)
|
||||
const setShowChatVariablePanel = useStore(s => s.setShowChatVariablePanel)
|
||||
const nodes = useNodes<StartNodeType>()
|
||||
const selectedNode = nodes.find(node => node.data.selected)
|
||||
const { handleBackupDraft } = useWorkflowRun()
|
||||
@ -46,8 +48,10 @@ const HeaderInNormal = ({
|
||||
setShowWorkflowVersionHistoryPanel(true)
|
||||
setShowEnvPanel(false)
|
||||
setShowDebugAndPreviewPanel(false)
|
||||
setShowVariableInspectPanel(false)
|
||||
setShowChatVariablePanel(false)
|
||||
}, [handleBackupDraft, workflowStore, handleNodeSelect, selectedNode,
|
||||
setShowWorkflowVersionHistoryPanel, setShowEnvPanel, setShowDebugAndPreviewPanel])
|
||||
setShowWorkflowVersionHistoryPanel, setShowEnvPanel, setShowDebugAndPreviewPanel, setShowVariableInspectPanel])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -15,6 +15,7 @@ import {
|
||||
import useToggleExpend from '@/app/components/workflow/nodes/_base/hooks/use-toggle-expend'
|
||||
import type { FileEntity } from '@/app/components/base/file-uploader/types'
|
||||
import FileListInLog from '@/app/components/base/file-uploader/file-list-in-log'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
@ -88,15 +89,16 @@ const Base: FC<Props> = ({
|
||||
<CodeGeneratorButton onGenerated={onGenerated} codeLanguages={codeLanguages} />
|
||||
</div>
|
||||
)}
|
||||
{!isCopied
|
||||
? (
|
||||
<Clipboard className='mx-1 h-3.5 w-3.5 cursor-pointer text-text-tertiary' onClick={handleCopy} />
|
||||
)
|
||||
: (
|
||||
<ClipboardCheck className='mx-1 h-3.5 w-3.5 text-text-tertiary' />
|
||||
)
|
||||
}
|
||||
|
||||
<ActionButton className='ml-1' onClick={handleCopy}>
|
||||
{!isCopied
|
||||
? (
|
||||
<Clipboard className='h-4 w-4 cursor-pointer' />
|
||||
)
|
||||
: (
|
||||
<ClipboardCheck className='h-4 w-4' />
|
||||
)
|
||||
}
|
||||
</ActionButton>
|
||||
<div className='ml-1'>
|
||||
<ToggleExpandBtn isExpand={isExpand} onExpandChange={setIsExpand} />
|
||||
</div>
|
||||
|
||||
@ -13,6 +13,8 @@ type Props = {
|
||||
readonly: boolean
|
||||
value: string
|
||||
onChange: (value: string | number, varKindType: VarKindType, varInfo?: Var) => void
|
||||
onOpenChange?: (open: boolean) => void
|
||||
isLoading?: boolean
|
||||
}
|
||||
|
||||
const DEFAULT_SCHEMA = {} as CredentialFormSchema
|
||||
@ -22,6 +24,8 @@ const ConstantField: FC<Props> = ({
|
||||
readonly,
|
||||
value,
|
||||
onChange,
|
||||
onOpenChange,
|
||||
isLoading,
|
||||
}) => {
|
||||
const language = useLanguage()
|
||||
const placeholder = (schema as CredentialFormSchemaSelect).placeholder
|
||||
@ -36,7 +40,7 @@ const ConstantField: FC<Props> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{schema.type === FormTypeEnum.select && (
|
||||
{(schema.type === FormTypeEnum.select || schema.type === FormTypeEnum.dynamicSelect) && (
|
||||
<SimpleSelect
|
||||
wrapperClassName='w-full !h-8'
|
||||
className='flex items-center'
|
||||
@ -45,6 +49,8 @@ const ConstantField: FC<Props> = ({
|
||||
items={(schema as CredentialFormSchemaSelect).options.map(option => ({ value: option.value, name: option.label[language] || option.label.en_US }))}
|
||||
onSelect={item => handleSelectChange(item.value)}
|
||||
placeholder={placeholder?.[language] || placeholder?.en_US}
|
||||
onOpenChange={onOpenChange}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
)}
|
||||
{schema.type === FormTypeEnum.textNumber && (
|
||||
|
||||
@ -1090,13 +1090,13 @@ export const getNodeUsedVarPassToServerKey = (node: Node, valueSelector: ValueSe
|
||||
break
|
||||
}
|
||||
case BlockEnum.Code: {
|
||||
const targetVar = (data as CodeNodeType).variables?.find(v => v.value_selector.join('.') === valueSelector.join('.'))
|
||||
const targetVar = (data as CodeNodeType).variables?.find(v => Array.isArray(v.value_selector) && v.value_selector && v.value_selector.join('.') === valueSelector.join('.'))
|
||||
if (targetVar)
|
||||
res = targetVar.variable
|
||||
break
|
||||
}
|
||||
case BlockEnum.TemplateTransform: {
|
||||
const targetVar = (data as TemplateTransformNodeType).variables?.find(v => v.value_selector.join('.') === valueSelector.join('.'))
|
||||
const targetVar = (data as TemplateTransformNodeType).variables?.find(v => Array.isArray(v.value_selector) && v.value_selector && v.value_selector.join('.') === valueSelector.join('.'))
|
||||
if (targetVar)
|
||||
res = targetVar.variable
|
||||
break
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
RiArrowDownSLine,
|
||||
RiCloseLine,
|
||||
RiErrorWarningFill,
|
||||
RiLoader4Line,
|
||||
RiMoreLine,
|
||||
} from '@remixicon/react'
|
||||
import produce from 'immer'
|
||||
@ -16,8 +17,9 @@ import VarReferencePopup from './var-reference-popup'
|
||||
import { getNodeInfoById, isConversationVar, isENV, isSystemVar, varTypeToStructType } from './utils'
|
||||
import ConstantField from './constant-field'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { Node, NodeOutPutVar, ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import type { CredentialFormSchemaSelect } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { type CredentialFormSchema, type FormOption, FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { VarBlockIcon } from '@/app/components/workflow/block-icon'
|
||||
import { Line3 } from '@/app/components/base/icons/src/public/common'
|
||||
@ -40,6 +42,8 @@ import Tooltip from '@/app/components/base/tooltip'
|
||||
import { isExceptionVariable } from '@/app/components/workflow/utils'
|
||||
import VarFullPathPanel from './var-full-path-panel'
|
||||
import { noop } from 'lodash-es'
|
||||
import { useFetchDynamicOptions } from '@/service/use-plugins'
|
||||
import type { Tool } from '@/app/components/tools/types'
|
||||
|
||||
const TRIGGER_DEFAULT_WIDTH = 227
|
||||
|
||||
@ -68,6 +72,8 @@ type Props = {
|
||||
minWidth?: number
|
||||
popupFor?: 'assigned' | 'toAssigned'
|
||||
zIndex?: number
|
||||
currentTool?: Tool
|
||||
currentProvider?: ToolWithProvider
|
||||
}
|
||||
|
||||
const DEFAULT_VALUE_SELECTOR: Props['value'] = []
|
||||
@ -97,6 +103,8 @@ const VarReferencePicker: FC<Props> = ({
|
||||
minWidth,
|
||||
popupFor,
|
||||
zIndex,
|
||||
currentTool,
|
||||
currentProvider,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const store = useStoreApi()
|
||||
@ -316,6 +324,42 @@ const VarReferencePicker: FC<Props> = ({
|
||||
|
||||
return null
|
||||
}, [isValidVar, isShowAPart, hasValue, t, outputVarNode?.title, outputVarNode?.type, value, type])
|
||||
|
||||
const [dynamicOptions, setDynamicOptions] = useState<FormOption[] | null>(null)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const { mutateAsync: fetchDynamicOptions } = useFetchDynamicOptions(
|
||||
currentProvider?.plugin_id || '', currentProvider?.name || '', currentTool?.name || '', (schema as CredentialFormSchemaSelect)?.variable || '',
|
||||
'tool',
|
||||
)
|
||||
const handleFetchDynamicOptions = async () => {
|
||||
if (schema?.type !== FormTypeEnum.dynamicSelect || !currentTool || !currentProvider)
|
||||
return
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const data = await fetchDynamicOptions()
|
||||
setDynamicOptions(data?.options || [])
|
||||
}
|
||||
finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
handleFetchDynamicOptions()
|
||||
}, [currentTool, currentProvider, schema])
|
||||
|
||||
const schemaWithDynamicSelect = useMemo(() => {
|
||||
if (schema?.type !== FormTypeEnum.dynamicSelect)
|
||||
return schema
|
||||
// rewrite schema.options with dynamicOptions
|
||||
if (dynamicOptions) {
|
||||
return {
|
||||
...schema,
|
||||
options: dynamicOptions,
|
||||
}
|
||||
}
|
||||
return schema
|
||||
}, [dynamicOptions])
|
||||
|
||||
return (
|
||||
<div className={cn(className, !readonly && 'cursor-pointer')}>
|
||||
<PortalToFollowElem
|
||||
@ -366,8 +410,9 @@ const VarReferencePicker: FC<Props> = ({
|
||||
<ConstantField
|
||||
value={value as string}
|
||||
onChange={onChange as ((value: string | number, varKindType: VarKindType, varInfo?: Var) => void)}
|
||||
schema={schema as CredentialFormSchema}
|
||||
schema={schemaWithDynamicSelect as CredentialFormSchema}
|
||||
readonly={readonly}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
@ -412,6 +457,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
)}
|
||||
<div className='flex items-center text-text-accent'>
|
||||
{!hasValue && <Variable02 className='h-3.5 w-3.5' />}
|
||||
{isLoading && <RiLoader4Line className='h-3.5 w-3.5 animate-spin text-text-secondary' />}
|
||||
{isEnv && <Env className='h-3.5 w-3.5 text-util-colors-violet-violet-600' />}
|
||||
{isChatVar && <BubbleX className='h-3.5 w-3.5 text-util-colors-teal-teal-700' />}
|
||||
<div className={cn('ml-0.5 truncate text-xs font-medium', isEnv && '!text-text-secondary', isChatVar && 'text-util-colors-teal-teal-700', isException && 'text-text-warning')} title={varName} style={{
|
||||
@ -424,7 +470,16 @@ const VarReferencePicker: FC<Props> = ({
|
||||
{!isValidVar && <RiErrorWarningFill className='ml-0.5 h-3 w-3 text-text-destructive' />}
|
||||
</>
|
||||
)
|
||||
: <div className={`overflow-hidden ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'} system-sm-regular text-ellipsis`}>{placeholder ?? t('workflow.common.setVarValuePlaceholder')}</div>}
|
||||
: <div className={`overflow-hidden ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'} system-sm-regular text-ellipsis`}>
|
||||
{isLoading ? (
|
||||
<div className='flex items-center'>
|
||||
<RiLoader4Line className='mr-1 h-3.5 w-3.5 animate-spin text-text-secondary' />
|
||||
<span>{placeholder ?? t('workflow.common.setVarValuePlaceholder')}</span>
|
||||
</div>
|
||||
) : (
|
||||
placeholder ?? t('workflow.common.setVarValuePlaceholder')
|
||||
)}
|
||||
</div>}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import type { ToolVarInputs } from '../types'
|
||||
import { VarType as VarKindType } from '../types'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import type { ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
@ -17,6 +17,7 @@ import { VarType } from '@/app/components/workflow/types'
|
||||
import AppSelector from '@/app/components/plugins/plugin-detail-panel/app-selector'
|
||||
import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector'
|
||||
import { noop } from 'lodash-es'
|
||||
import type { Tool } from '@/app/components/tools/types'
|
||||
|
||||
type Props = {
|
||||
readOnly: boolean
|
||||
@ -27,6 +28,8 @@ type Props = {
|
||||
onOpen?: (index: number) => void
|
||||
isSupportConstantValue?: boolean
|
||||
filterVar?: (payload: Var, valueSelector: ValueSelector) => boolean
|
||||
currentTool?: Tool
|
||||
currentProvider?: ToolWithProvider
|
||||
}
|
||||
|
||||
const InputVarList: FC<Props> = ({
|
||||
@ -38,6 +41,8 @@ const InputVarList: FC<Props> = ({
|
||||
onOpen = noop,
|
||||
isSupportConstantValue,
|
||||
filterVar,
|
||||
currentTool,
|
||||
currentProvider,
|
||||
}) => {
|
||||
const language = useLanguage()
|
||||
const { t } = useTranslation()
|
||||
@ -58,6 +63,8 @@ const InputVarList: FC<Props> = ({
|
||||
return 'ModelSelector'
|
||||
else if (type === FormTypeEnum.toolSelector)
|
||||
return 'ToolSelector'
|
||||
else if (type === FormTypeEnum.dynamicSelect || type === FormTypeEnum.select)
|
||||
return 'Select'
|
||||
else
|
||||
return 'String'
|
||||
}
|
||||
@ -149,6 +156,7 @@ const InputVarList: FC<Props> = ({
|
||||
const handleOpen = useCallback((index: number) => {
|
||||
return () => onOpen(index)
|
||||
}, [onOpen])
|
||||
|
||||
return (
|
||||
<div className='space-y-3'>
|
||||
{
|
||||
@ -163,7 +171,8 @@ const InputVarList: FC<Props> = ({
|
||||
} = schema
|
||||
const varInput = value[variable]
|
||||
const isNumber = type === FormTypeEnum.textNumber
|
||||
const isSelect = type === FormTypeEnum.select
|
||||
const isDynamicSelect = type === FormTypeEnum.dynamicSelect
|
||||
const isSelect = type === FormTypeEnum.select || type === FormTypeEnum.dynamicSelect
|
||||
const isFile = type === FormTypeEnum.file || type === FormTypeEnum.files
|
||||
const isAppSelector = type === FormTypeEnum.appSelector
|
||||
const isModelSelector = type === FormTypeEnum.modelSelector
|
||||
@ -198,11 +207,13 @@ const InputVarList: FC<Props> = ({
|
||||
value={varInput?.type === VarKindType.constant ? (varInput?.value ?? '') : (varInput?.value ?? [])}
|
||||
onChange={handleNotMixedTypeChange(variable)}
|
||||
onOpen={handleOpen(index)}
|
||||
defaultVarKindType={varInput?.type || (isNumber ? VarKindType.constant : VarKindType.variable)}
|
||||
defaultVarKindType={varInput?.type || ((isNumber || isDynamicSelect) ? VarKindType.constant : VarKindType.variable)}
|
||||
isSupportConstantValue={isSupportConstantValue}
|
||||
filterVar={isNumber ? filterVar : undefined}
|
||||
availableVars={isSelect ? availableVars : undefined}
|
||||
schema={schema}
|
||||
currentTool={currentTool}
|
||||
currentProvider={currentProvider}
|
||||
/>
|
||||
)}
|
||||
{isFile && (
|
||||
|
||||
@ -42,6 +42,7 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({
|
||||
isLoading,
|
||||
outputSchema,
|
||||
hasObjectOutput,
|
||||
currTool,
|
||||
} = useConfig(id, data)
|
||||
|
||||
if (isLoading) {
|
||||
@ -80,6 +81,8 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({
|
||||
filterVar={filterVar}
|
||||
isSupportConstantValue
|
||||
onOpen={handleOnVarOpen}
|
||||
currentProvider={currCollection}
|
||||
currentTool={currTool}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { FC } from 'react'
|
||||
import { memo, useEffect, useRef } from 'react'
|
||||
import { memo, useCallback, useEffect, useRef } from 'react'
|
||||
import { useNodes } from 'reactflow'
|
||||
import type { CommonNodeType } from '../types'
|
||||
import { Panel as NodePanel } from '../nodes'
|
||||
@ -13,6 +13,51 @@ export type PanelProps = {
|
||||
right?: React.ReactNode
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reference MDN standard implementation:https://developer.mozilla.org/zh-CN/docs/Web/API/ResizeObserverEntry/borderBoxSize
|
||||
*/
|
||||
const getEntryWidth = (entry: ResizeObserverEntry, element: HTMLElement): number => {
|
||||
if (entry.borderBoxSize?.length > 0)
|
||||
return entry.borderBoxSize[0].inlineSize
|
||||
|
||||
if (entry.contentRect.width > 0)
|
||||
return entry.contentRect.width
|
||||
|
||||
return element.getBoundingClientRect().width
|
||||
}
|
||||
|
||||
const useResizeObserver = (
|
||||
callback: (width: number) => void,
|
||||
dependencies: React.DependencyList = [],
|
||||
) => {
|
||||
const elementRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const stableCallback = useCallback(callback, [callback])
|
||||
|
||||
useEffect(() => {
|
||||
const element = elementRef.current
|
||||
if (!element) return
|
||||
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const width = getEntryWidth(entry, element)
|
||||
stableCallback(width)
|
||||
}
|
||||
})
|
||||
|
||||
resizeObserver.observe(element)
|
||||
|
||||
const initialWidth = element.getBoundingClientRect().width
|
||||
stableCallback(initialWidth)
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect()
|
||||
}
|
||||
}, [stableCallback, ...dependencies])
|
||||
return elementRef
|
||||
}
|
||||
|
||||
const Panel: FC<PanelProps> = ({
|
||||
components,
|
||||
}) => {
|
||||
@ -20,44 +65,21 @@ const Panel: FC<PanelProps> = ({
|
||||
const selectedNode = nodes.find(node => node.data.selected)
|
||||
const showEnvPanel = useStore(s => s.showEnvPanel)
|
||||
const isRestoring = useStore(s => s.isRestoring)
|
||||
const showWorkflowVersionHistoryPanel = useStore(s => s.showWorkflowVersionHistoryPanel)
|
||||
|
||||
const rightPanelRef = useRef<HTMLDivElement>(null)
|
||||
const setRightPanelWidth = useStore(s => s.setRightPanelWidth)
|
||||
|
||||
// get right panel width
|
||||
useEffect(() => {
|
||||
if (rightPanelRef.current) {
|
||||
const resizeRightPanelObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const { inlineSize } = entry.borderBoxSize[0]
|
||||
setRightPanelWidth(inlineSize)
|
||||
}
|
||||
})
|
||||
resizeRightPanelObserver.observe(rightPanelRef.current)
|
||||
return () => {
|
||||
resizeRightPanelObserver.disconnect()
|
||||
}
|
||||
}
|
||||
}, [setRightPanelWidth])
|
||||
|
||||
const otherPanelRef = useRef<HTMLDivElement>(null)
|
||||
const setOtherPanelWidth = useStore(s => s.setOtherPanelWidth)
|
||||
|
||||
// get other panel width
|
||||
useEffect(() => {
|
||||
if (otherPanelRef.current) {
|
||||
const resizeOtherPanelObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const { inlineSize } = entry.borderBoxSize[0]
|
||||
setOtherPanelWidth(inlineSize)
|
||||
}
|
||||
})
|
||||
resizeOtherPanelObserver.observe(otherPanelRef.current)
|
||||
return () => {
|
||||
resizeOtherPanelObserver.disconnect()
|
||||
}
|
||||
}
|
||||
}, [setOtherPanelWidth])
|
||||
const rightPanelRef = useResizeObserver(
|
||||
setRightPanelWidth,
|
||||
[setRightPanelWidth, selectedNode, showEnvPanel, showWorkflowVersionHistoryPanel],
|
||||
)
|
||||
|
||||
const otherPanelRef = useResizeObserver(
|
||||
setOtherPanelWidth,
|
||||
[setOtherPanelWidth, showEnvPanel, showWorkflowVersionHistoryPanel],
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={rightPanelRef}
|
||||
@ -65,26 +87,14 @@ const Panel: FC<PanelProps> = ({
|
||||
className={cn('absolute bottom-1 right-0 top-14 z-10 flex outline-none')}
|
||||
key={`${isRestoring}`}
|
||||
>
|
||||
{
|
||||
components?.left
|
||||
}
|
||||
{
|
||||
!!selectedNode && (
|
||||
<NodePanel {...selectedNode!} />
|
||||
)
|
||||
}
|
||||
{components?.left}
|
||||
{!!selectedNode && <NodePanel {...selectedNode} />}
|
||||
<div
|
||||
className='relative'
|
||||
className="relative"
|
||||
ref={otherPanelRef}
|
||||
>
|
||||
{
|
||||
components?.right
|
||||
}
|
||||
{
|
||||
showEnvPanel && (
|
||||
<EnvPanel />
|
||||
)
|
||||
}
|
||||
{components?.right}
|
||||
{showEnvPanel && <EnvPanel />}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -29,6 +29,7 @@ import type {
|
||||
import ErrorHandleTip from '@/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip'
|
||||
import { hasRetryNode } from '@/app/components/workflow/utils'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
@ -129,10 +130,16 @@ const NodePanel: FC<Props> = ({
|
||||
/>
|
||||
)}
|
||||
<BlockIcon size={inMessage ? 'xs' : 'sm'} className={cn('mr-2 shrink-0', inMessage && '!mr-1')} type={nodeInfo.node_type} toolIcon={nodeInfo.extras?.icon || nodeInfo.extras} />
|
||||
<div className={cn(
|
||||
'system-xs-semibold-uppercase grow truncate text-text-secondary',
|
||||
hideInfo && '!text-xs',
|
||||
)} title={nodeInfo.title}>{nodeInfo.title}</div>
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<div className='max-w-xs'>{nodeInfo.title}</div>
|
||||
}
|
||||
>
|
||||
<div className={cn(
|
||||
'system-xs-semibold-uppercase grow truncate text-text-secondary',
|
||||
hideInfo && '!text-xs',
|
||||
)}>{nodeInfo.title}</div>
|
||||
</Tooltip>
|
||||
{nodeInfo.status !== 'running' && !hideInfo && (
|
||||
<div className='system-xs-regular shrink-0 text-text-tertiary'>{nodeInfo.execution_metadata?.total_tokens ? `${getTokenCount(nodeInfo.execution_metadata?.total_tokens || 0)} tokens · ` : ''}{`${getTime(nodeInfo.elapsed_time || 0)}`}</div>
|
||||
)}
|
||||
|
||||
@ -12,6 +12,7 @@ import type { CommonNodeType } from '@/app/components/workflow/types'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useNodesReadOnly } from '../hooks/use-workflow'
|
||||
|
||||
const VariableInspectTrigger: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
@ -32,7 +33,10 @@ const VariableInspectTrigger: FC = () => {
|
||||
const allVars = [...environmentVariables, ...conversationVars, ...systemVars, ...nodesWithInspectVars]
|
||||
return allVars
|
||||
}, [environmentVariables, conversationVars, systemVars, nodesWithInspectVars])
|
||||
|
||||
const {
|
||||
nodesReadOnly,
|
||||
getNodesReadOnly,
|
||||
} = useNodesReadOnly()
|
||||
const workflowRunningData = useStore(s => s.workflowRunningData)
|
||||
const nodes = useNodes<CommonNodeType>()
|
||||
const isStepRunning = useMemo(() => nodes.some(node => node.data._singleRunningStatus === NodeRunningStatus.Running), [nodes])
|
||||
@ -61,8 +65,14 @@ const VariableInspectTrigger: FC = () => {
|
||||
<div className={cn('flex items-center gap-1')}>
|
||||
{!isRunning && !currentVars.length && (
|
||||
<div
|
||||
className='system-2xs-semibold-uppercase flex h-5 cursor-pointer items-center gap-1 rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-2 text-text-tertiary shadow-lg backdrop-blur-sm hover:bg-background-default-hover'
|
||||
onClick={() => setShowVariableInspectPanel(true)}
|
||||
className={cn('system-2xs-semibold-uppercase flex h-5 cursor-pointer items-center gap-1 rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-2 text-text-tertiary shadow-lg backdrop-blur-sm hover:bg-background-default-hover',
|
||||
nodesReadOnly && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled',
|
||||
)}
|
||||
onClick={() => {
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
setShowVariableInspectPanel(true)
|
||||
}}
|
||||
>
|
||||
{t('workflow.debug.variableInspect.trigger.normal')}
|
||||
</div>
|
||||
@ -70,13 +80,21 @@ const VariableInspectTrigger: FC = () => {
|
||||
{!isRunning && currentVars.length > 0 && (
|
||||
<>
|
||||
<div
|
||||
className='system-xs-medium flex h-6 cursor-pointer items-center gap-1 rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-2 text-text-accent shadow-lg backdrop-blur-sm hover:bg-components-actionbar-bg-accent'
|
||||
onClick={() => setShowVariableInspectPanel(true)}
|
||||
className={cn('system-xs-medium flex h-6 cursor-pointer items-center gap-1 rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-2 text-text-accent shadow-lg backdrop-blur-sm hover:bg-components-actionbar-bg-accent',
|
||||
nodesReadOnly && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled',
|
||||
)}
|
||||
onClick={() => {
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
setShowVariableInspectPanel(true)
|
||||
}}
|
||||
>
|
||||
{t('workflow.debug.variableInspect.trigger.cached')}
|
||||
</div>
|
||||
<div
|
||||
className='system-xs-medium flex h-6 cursor-pointer items-center rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-1 text-text-tertiary shadow-lg backdrop-blur-sm hover:bg-components-actionbar-bg-accent hover:text-text-accent'
|
||||
className={cn('system-xs-medium flex h-6 cursor-pointer items-center rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-1 text-text-tertiary shadow-lg backdrop-blur-sm hover:bg-components-actionbar-bg-accent hover:text-text-accent',
|
||||
nodesReadOnly && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled',
|
||||
)}
|
||||
onClick={handleClearAll}
|
||||
>
|
||||
{t('workflow.debug.variableInspect.trigger.clear')}
|
||||
|
||||
Reference in New Issue
Block a user