Feat: conversation variable & variable assigner node (#7222)

Signed-off-by: -LAN- <laipz8200@outlook.com>
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: -LAN- <laipz8200@outlook.com>
This commit is contained in:
KVOJJJin
2024-08-13 14:44:10 +08:00
committed by GitHub
parent 8b55bd5828
commit 935e72d449
128 changed files with 3354 additions and 683 deletions

View File

@@ -64,6 +64,7 @@ const AddVariablePopupWithPosition = ({
} as any,
],
hideEnv: true,
hideChatVar: true,
isChatMode,
filterVar: filterVar(outputType as VarType),
})

View File

@@ -18,6 +18,8 @@ import { useFeatures } from '@/app/components/base/features/hooks'
import { VarBlockIcon } from '@/app/components/workflow/block-icon'
import { Line3 } from '@/app/components/base/icons/src/public/common'
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
import { BubbleX } from '@/app/components/base/icons/src/vender/line/others'
import cn from '@/utils/classnames'
type Props = {
payload: InputVar
@@ -56,22 +58,24 @@ const FormItem: FC<Props> = ({
}, [value, onChange])
const nodeKey = (() => {
if (typeof payload.label === 'object') {
const { nodeType, nodeName, variable } = payload.label
const { nodeType, nodeName, variable, isChatVar } = payload.label
return (
<div className='h-full flex items-center'>
<div className='flex items-center'>
<div className='p-[1px]'>
<VarBlockIcon type={nodeType || BlockEnum.Start} />
{!isChatVar && (
<div className='flex items-center'>
<div className='p-[1px]'>
<VarBlockIcon type={nodeType || BlockEnum.Start} />
</div>
<div className='mx-0.5 text-xs font-medium text-gray-700 max-w-[150px] truncate' title={nodeName}>
{nodeName}
</div>
<Line3 className='mr-0.5'></Line3>
</div>
<div className='mx-0.5 text-xs font-medium text-gray-700 max-w-[150px] truncate' title={nodeName}>
{nodeName}
</div>
<Line3 className='mr-0.5'></Line3>
</div>
)}
<div className='flex items-center text-primary-600'>
<Variable02 className='w-3.5 h-3.5' />
<div className='ml-0.5 text-xs font-medium max-w-[150px] truncate' title={variable} >
{!isChatVar && <Variable02 className='w-3.5 h-3.5' />}
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
<div className={cn('ml-0.5 text-xs font-medium max-w-[150px] truncate', isChatVar && 'text-text-secondary')} title={variable} >
{variable}
</div>
</div>
@@ -86,7 +90,12 @@ const FormItem: FC<Props> = ({
const isIterator = type === InputVarType.iterator
return (
<div className={`${className}`}>
{!isArrayLikeType && <div className='h-8 leading-8 text-[13px] font-medium text-gray-700 truncate'>{typeof payload.label === 'object' ? nodeKey : payload.label}</div>}
{!isArrayLikeType && (
<div className='h-6 mb-1 flex items-center gap-1 text-text-secondary system-sm-semibold'>
<div className='truncate'>{typeof payload.label === 'object' ? nodeKey : payload.label}</div>
{!payload.required && <span className='text-text-tertiary system-xs-regular'>{t('workflow.panel.optional')}</span>}
</div>
)}
<div className='grow'>
{
type === InputVarType.textInput && (

View File

@@ -15,7 +15,7 @@ const CODE_EDITOR_LINE_HEIGHT = 18
export type Props = {
value?: string | object
placeholder?: string
placeholder?: JSX.Element | string
onChange?: (value: string) => void
title?: JSX.Element
language: CodeLanguage
@@ -167,7 +167,7 @@ const CodeEditor: FC<Props> = ({
}}
onMount={handleEditorDidMount}
/>
{!outPutValue && <div className='pointer-events-none absolute left-[36px] top-0 leading-[18px] text-[13px] font-normal text-gray-300'>{placeholder}</div>}
{!outPutValue && !isFocus && <div className='pointer-events-none absolute left-[36px] top-0 leading-[18px] text-[13px] font-normal text-gray-300'>{placeholder}</div>}
</>
)

View File

@@ -26,6 +26,7 @@ type Props = {
justVar?: boolean
nodesOutputVars?: NodeOutPutVar[]
availableNodes?: Node[]
insertVarTipToLeft?: boolean
}
const Editor: FC<Props> = ({
@@ -40,6 +41,7 @@ const Editor: FC<Props> = ({
readOnly,
nodesOutputVars,
availableNodes = [],
insertVarTipToLeft,
}) => {
const { t } = useTranslation()
@@ -106,12 +108,12 @@ const Editor: FC<Props> = ({
{/* to patch Editor not support dynamic change editable status */}
{readOnly && <div className='absolute inset-0 z-10'></div>}
{isFocus && (
<div className='absolute z-10 top-[-9px] right-1'>
<div className={cn('absolute z-10', insertVarTipToLeft ? 'top-1.5 left-[-12px]' : ' top-[-9px] right-1')}>
<TooltipPlus
popupContent={`${t('workflow.common.insertVarTip')}`}
>
<div className='p-0.5 rounded-[5px] shadow-lg cursor-pointer bg-white hover:bg-gray-100 border-[0.5px] border-black/5'>
<Variable02 className='w-3.5 h-3.5 text-gray-500' />
<Variable02 className='w-3.5 h-3.5 text-components-button-secondary-accent-text' />
</div>
</TooltipPlus>
</div>

View File

@@ -45,7 +45,7 @@ const OptionCard: FC<Props> = ({
return (
<div
className={cn(
'flex items-center px-2 h-8 rounded-md system-sm-regular bg-components-option-card-option-bg border border-components-option-card-option-bg text-text-secondary cursor-default',
'flex items-center px-2 h-8 rounded-md system-sm-regular bg-components-option-card-option-bg border border-components-option-card-option-border text-text-secondary cursor-default',
(!selected && !disabled) && 'hover:bg-components-option-card-option-bg-hover hover:border-components-option-card-option-border-hover hover:shadow-xs cursor-pointer',
selected && 'bg-components-option-card-option-selected-bg border-[1.5px] border-components-option-card-option-selected-border system-sm-medium shadow-xs',
disabled && 'text-text-disabled',

View File

@@ -5,10 +5,10 @@ import cn from 'classnames'
import { useWorkflow } from '../../../hooks'
import { BlockEnum } from '../../../types'
import { VarBlockIcon } from '../../../block-icon'
import { getNodeInfoById, isENV, isSystemVar } from './variable/utils'
import { getNodeInfoById, isConversationVar, isENV, isSystemVar } from './variable/utils'
import { Line3 } from '@/app/components/base/icons/src/public/common'
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
import { Env } from '@/app/components/base/icons/src/vender/line/others'
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
type Props = {
nodeId: string
value: string
@@ -42,13 +42,14 @@ const ReadonlyInputWithSelectVar: FC<Props> = ({
const value = vars[index].split('.')
const isSystem = isSystemVar(value)
const isEnv = isENV(value)
const isChatVar = isConversationVar(value)
const node = (isSystem ? startNode : getNodeInfoById(availableNodes, value[0]))?.data
const varName = `${isSystem ? 'sys.' : ''}${value[value.length - 1]}`
return (<span key={index}>
<span className='relative top-[-3px] leading-[16px]'>{str}</span>
<div className=' inline-flex h-[16px] items-center px-1.5 rounded-[5px] bg-white'>
{!isEnv && (
{!isEnv && !isChatVar && (
<div className='flex items-center'>
<div className='p-[1px]'>
<VarBlockIcon
@@ -61,9 +62,10 @@ const ReadonlyInputWithSelectVar: FC<Props> = ({
</div>
)}
<div className='flex items-center text-primary-600'>
{!isEnv && <Variable02 className='shrink-0 w-3.5 h-3.5' />}
{!isEnv && !isChatVar && <Variable02 className='shrink-0 w-3.5 h-3.5' />}
{isEnv && <Env className='shrink-0 w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
<div className={cn('max-w-[50px] ml-0.5 text-xs font-medium truncate', isEnv && 'text-gray-900')} title={varName}>{varName}</div>
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
<div className={cn('max-w-[50px] ml-0.5 text-xs font-medium truncate', (isEnv || isChatVar) && 'text-gray-900')} title={varName}>{varName}</div>
</div>
</div>
</span>)

View File

@@ -10,6 +10,7 @@ type Item = {
label: string
}
type Props = {
className?: string
trigger?: JSX.Element
DropDownIcon?: any
noLeft?: boolean
@@ -27,6 +28,7 @@ type Props = {
}
const TypeSelector: FC<Props> = ({
className,
trigger,
DropDownIcon = ChevronSelectorVertical,
noLeft,
@@ -50,11 +52,12 @@ const TypeSelector: FC<Props> = ({
setHide()
}, ref)
return (
<div className={cn(!trigger && !noLeft && 'left-[-8px]', 'relative')} ref={ref}>
<div className={cn(!trigger && !noLeft && 'left-[-8px]', 'relative select-none', className)} ref={ref}>
{trigger
? (
<div
onClick={toggleShow}
className={cn(!readonly && 'cursor-pointer')}
>
{trigger}
</div>
@@ -63,13 +66,13 @@ const TypeSelector: FC<Props> = ({
<div
onClick={toggleShow}
className={cn(showOption && 'bg-black/5', 'flex items-center h-5 pl-1 pr-0.5 rounded-md text-xs font-semibold text-gray-700 cursor-pointer hover:bg-black/5')}>
<div className={cn(triggerClassName, 'text-xs font-semibold', uppercase && 'uppercase', noValue && 'text-gray-400')}>{!noValue ? item?.label : placeholder}</div>
<div className={cn('text-sm font-semibold', uppercase && 'uppercase', noValue && 'text-gray-400', triggerClassName)}>{!noValue ? item?.label : placeholder}</div>
{!readonly && <DropDownIcon className='w-3 h-3 ' />}
</div>
)}
{(showOption && !readonly) && (
<div className={cn(popupClassName, 'absolute z-10 top-[24px] w-[120px] p-1 border border-gray-200 shadow-lg rounded-lg bg-white')}>
<div className={cn('absolute z-10 top-[24px] w-[120px] p-1 border border-gray-200 shadow-lg rounded-lg bg-white select-none', popupClassName)}>
{list.map(item => (
<div
key={item.value}

View File

@@ -10,8 +10,8 @@ import type {
import { BlockEnum } from '@/app/components/workflow/types'
import { Line3 } from '@/app/components/base/icons/src/public/common'
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
import { Env } from '@/app/components/base/icons/src/vender/line/others'
import { isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
import cn from '@/utils/classnames'
type VariableTagProps = {
@@ -30,12 +30,13 @@ const VariableTag = ({
return nodes.find(node => node.id === valueSelector[0])
}, [nodes, valueSelector])
const isEnv = isENV(valueSelector)
const isChatVar = isConversationVar(valueSelector)
const variableName = isSystemVar(valueSelector) ? valueSelector.slice(0).join('.') : valueSelector.slice(1).join('.')
return (
<div className='inline-flex items-center px-1.5 max-w-full h-6 text-xs rounded-md border-[0.5px] border-[rgba(16, 2440,0.08)] bg-white shadow-xs'>
{!isEnv && (
{!isEnv && !isChatVar && (
<>
{node && (
<VarBlockIcon
@@ -54,8 +55,9 @@ const VariableTag = ({
</>
)}
{isEnv && <Env className='shrink-0 mr-0.5 w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
<div
className={cn('truncate text-text-accent font-medium', isEnv && 'text-text-secondary')}
className={cn('truncate text-text-accent font-medium', (isEnv || isChatVar) && 'text-text-secondary')}
title={variableName}
>
{variableName}

View File

@@ -9,14 +9,14 @@ import type { Var } from '@/app/components/workflow/types'
import { SimpleSelect } from '@/app/components/base/select'
type Props = {
schema: CredentialFormSchema
schema: Partial<CredentialFormSchema>
readonly: boolean
value: string
onChange: (value: string | number, varKindType: VarKindType, varInfo?: Var) => void
}
const ConstantField: FC<Props> = ({
schema,
schema = {} as CredentialFormSchema,
readonly,
value,
onChange,
@@ -47,7 +47,7 @@ const ConstantField: FC<Props> = ({
{schema.type === FormTypeEnum.textNumber && (
<input
type='number'
className='w-full h-8 leading-8 pl-0.5 bg-transparent text-[13px] font-normal text-gray-900 placeholder:text-gray-400 focus:outline-none overflow-hidden'
className='w-full h-8 leading-8 p-2 rounded-lg bg-gray-100 text-[13px] font-normal text-gray-900 placeholder:text-gray-400 focus:outline-none overflow-hidden'
value={value}
onChange={handleStaticChange}
readOnly={readonly}

View File

@@ -15,7 +15,7 @@ import type { ParameterExtractorNodeType } from '../../../parameter-extractor/ty
import type { IterationNodeType } from '../../../iteration/types'
import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types'
import type { StartNodeType } from '@/app/components/workflow/nodes/start/types'
import type { EnvironmentVariable, Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
import type { ConversationVariable, EnvironmentVariable, Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
import type { VariableAssignerNodeType } from '@/app/components/workflow/nodes/variable-assigner/types'
import {
HTTP_REQUEST_OUTPUT_STRUCT,
@@ -38,6 +38,10 @@ export const isENV = (valueSelector: ValueSelector) => {
return valueSelector[0] === 'env'
}
export const isConversationVar = (valueSelector: ValueSelector) => {
return valueSelector[0] === 'conversation'
}
const inputVarTypeToVarType = (type: InputVarType): VarType => {
if (type === InputVarType.number)
return VarType.number
@@ -246,13 +250,32 @@ const formatItem = (
}) as Var[]
break
}
case 'conversation': {
res.vars = data.chatVarList.map((chatVar: ConversationVariable) => {
return {
variable: `conversation.${chatVar.name}`,
type: chatVar.value_type,
des: chatVar.description,
}
}) as Var[]
break
}
}
const selector = [id]
res.vars = res.vars.filter((v) => {
const { children } = v
if (!children)
return filterVar(v, selector)
if (!children) {
return filterVar(v, (() => {
const variableArr = v.variable.split('.')
const [first, ..._other] = variableArr
if (first === 'sys' || first === 'env' || first === 'conversation')
return variableArr
return [...selector, ...variableArr]
})())
}
const obj = findExceptVarInObject(v, filterVar, selector)
return obj?.children && obj?.children.length > 0
@@ -271,6 +294,7 @@ export const toNodeOutputVars = (
isChatMode: boolean,
filterVar = (_payload: Var, _selector: ValueSelector) => true,
environmentVariables: EnvironmentVariable[] = [],
conversationVariables: ConversationVariable[] = [],
): NodeOutPutVar[] => {
// ENV_NODE data format
const ENV_NODE = {
@@ -281,9 +305,19 @@ export const toNodeOutputVars = (
envList: environmentVariables,
},
}
// CHAT_VAR_NODE data format
const CHAT_VAR_NODE = {
id: 'conversation',
data: {
title: 'CONVERSATION',
type: 'conversation',
chatVarList: conversationVariables,
},
}
const res = [
...nodes.filter(node => SUPPORT_OUTPUT_VARS_NODE.includes(node.data.type)),
...(environmentVariables.length > 0 ? [ENV_NODE] : []),
...((isChatMode && conversationVariables.length) > 0 ? [CHAT_VAR_NODE] : []),
].map((node) => {
return {
...formatItem(node, isChatMode, filterVar),
@@ -348,6 +382,7 @@ export const getVarType = ({
isChatMode,
isConstant,
environmentVariables = [],
conversationVariables = [],
}:
{
valueSelector: ValueSelector
@@ -357,6 +392,7 @@ export const getVarType = ({
isChatMode: boolean
isConstant?: boolean
environmentVariables?: EnvironmentVariable[]
conversationVariables?: ConversationVariable[]
}): VarType => {
if (isConstant)
return VarType.string
@@ -366,6 +402,7 @@ export const getVarType = ({
isChatMode,
undefined,
environmentVariables,
conversationVariables,
)
const isIterationInnerVar = parentNode?.data.type === BlockEnum.Iteration
@@ -388,6 +425,7 @@ export const getVarType = ({
}
const isSystem = isSystemVar(valueSelector)
const isEnv = isENV(valueSelector)
const isChatVar = isConversationVar(valueSelector)
const startNode = availableNodes.find((node: any) => {
return node.data.type === BlockEnum.Start
})
@@ -400,7 +438,7 @@ export const getVarType = ({
let type: VarType = VarType.string
let curr: any = targetVar.vars
if (isSystem || isEnv) {
if (isSystem || isEnv || isChatVar) {
return curr.find((v: any) => v.variable === (valueSelector as ValueSelector).join('.'))?.type
}
else {
@@ -426,6 +464,7 @@ export const toNodeAvailableVars = ({
beforeNodes,
isChatMode,
environmentVariables,
conversationVariables,
filterVar,
}: {
parentNode?: Node | null
@@ -435,6 +474,8 @@ export const toNodeAvailableVars = ({
isChatMode: boolean
// env
environmentVariables?: EnvironmentVariable[]
// chat var
conversationVariables?: ConversationVariable[]
filterVar: (payload: Var, selector: ValueSelector) => boolean
}): NodeOutPutVar[] => {
const beforeNodesOutputVars = toNodeOutputVars(
@@ -442,6 +483,7 @@ export const toNodeAvailableVars = ({
isChatMode,
filterVar,
environmentVariables,
conversationVariables,
)
const isInIteration = parentNode?.data.type === BlockEnum.Iteration
if (isInIteration) {
@@ -453,6 +495,7 @@ export const toNodeAvailableVars = ({
availableNodes: beforeNodes,
isChatMode,
environmentVariables,
conversationVariables,
})
const iterationVar = {
nodeId: iterationNode?.id,

View File

@@ -9,7 +9,7 @@ import {
import produce from 'immer'
import { useStoreApi } from 'reactflow'
import VarReferencePopup from './var-reference-popup'
import { getNodeInfoById, isENV, isSystemVar } from './utils'
import { getNodeInfoById, isConversationVar, isENV, isSystemVar } from './utils'
import ConstantField from './constant-field'
import cn from '@/utils/classnames'
import type { Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
@@ -17,7 +17,7 @@ import type { CredentialFormSchema } from '@/app/components/header/account-setti
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'
import { Env } from '@/app/components/base/icons/src/vender/line/others'
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
import {
PortalToFollowElem,
@@ -32,6 +32,7 @@ import {
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
import TypeSelector from '@/app/components/workflow/nodes/_base/components/selector'
import AddButton from '@/app/components/base/button/add-button'
import Badge from '@/app/components/base/badge'
const TRIGGER_DEFAULT_WIDTH = 227
type Props = {
@@ -49,7 +50,8 @@ type Props = {
availableNodes?: Node[]
availableVars?: NodeOutPutVar[]
isAddBtnTrigger?: boolean
schema?: CredentialFormSchema
schema?: Partial<CredentialFormSchema>
valueTypePlaceHolder?: string
}
const VarReferencePicker: FC<Props> = ({
@@ -57,7 +59,7 @@ const VarReferencePicker: FC<Props> = ({
readonly,
className,
isShowNodeName,
value,
value = [],
onOpen = () => { },
onChange,
isSupportConstantValue,
@@ -68,6 +70,7 @@ const VarReferencePicker: FC<Props> = ({
availableVars,
isAddBtnTrigger,
schema,
valueTypePlaceHolder,
}) => {
const { t } = useTranslation()
const store = useStoreApi()
@@ -99,7 +102,6 @@ const VarReferencePicker: FC<Props> = ({
const [varKindType, setVarKindType] = useState<VarKindType>(defaultVarKindType)
const isConstant = isSupportConstantValue && varKindType === VarKindType.constant
const outputVars = useMemo(() => {
if (availableVars)
return availableVars
@@ -215,6 +217,7 @@ const VarReferencePicker: FC<Props> = ({
})
const isEnv = isENV(value as ValueSelector)
const isChatVar = isConversationVar(value as ValueSelector)
// 8(left/right-padding) + 14(icon) + 4 + 14 + 2 = 42 + 17 buff
const availableWidth = triggerWidth - 56
@@ -227,6 +230,8 @@ const VarReferencePicker: FC<Props> = ({
return [maxNodeNameWidth, maxVarNameWidth, maxTypeWidth]
})()
const WrapElem = isSupportConstantValue ? 'div' : PortalToFollowElemTrigger
const VarPickerWrap = !isSupportConstantValue ? 'div' : PortalToFollowElemTrigger
return (
<div className={cn(className, !readonly && 'cursor-pointer')}>
<PortalToFollowElem
@@ -234,7 +239,7 @@ const VarReferencePicker: FC<Props> = ({
onOpenChange={setOpen}
placement={isAddBtnTrigger ? 'bottom-end' : 'bottom-start'}
>
<PortalToFollowElemTrigger onClick={() => {
<WrapElem onClick={() => {
if (readonly)
return
!isConstant ? setOpen(!open) : setControlFocus(Date.now())
@@ -245,23 +250,28 @@ const VarReferencePicker: FC<Props> = ({
<AddButton onClick={() => { }}></AddButton>
</div>
)
: (<div ref={triggerRef} className={cn((open || isFocus) ? 'border-gray-300' : 'border-gray-100', 'relative group/wrap flex items-center w-full h-8 p-1 rounded-lg bg-gray-100 border')}>
: (<div ref={!isSupportConstantValue ? triggerRef : null} className={cn((open || isFocus) ? 'border-gray-300' : 'border-gray-100', 'relative group/wrap flex items-center w-full h-8', !isSupportConstantValue && 'p-1 rounded-lg bg-gray-100 border')}>
{isSupportConstantValue
? <div onClick={(e) => {
e.stopPropagation()
setOpen(false)
setControlFocus(Date.now())
}} className='mr-1 flex items-center space-x-1'>
}} className='h-full mr-1 flex items-center space-x-1'>
<TypeSelector
noLeft
triggerClassName='!text-xs'
trigger={
<div className='flex items-center h-8 px-2 radius-md bg-components-input-bg-normal'>
<div className='mr-1 system-sm-regular text-components-input-text-filled'>{varKindTypes.find(item => item.value === varKindType)?.label}</div>
<RiArrowDownSLine className='w-4 h-4 text-text-quaternary' />
</div>
}
popupClassName='top-8'
readonly={readonly}
DropDownIcon={RiArrowDownSLine}
value={varKindType}
options={varKindTypes}
onChange={handleVarKindTypeChange}
showChecked
/>
<div className='h-4 w-px bg-black/5'></div>
</div>
: (!hasValue && <div className='ml-1.5 mr-1'>
<Variable02 className='w-3.5 h-3.5 text-gray-400' />
@@ -276,38 +286,51 @@ const VarReferencePicker: FC<Props> = ({
/>
)
: (
<div className={cn('inline-flex h-full items-center px-1.5 rounded-[5px]', hasValue && 'bg-white')}>
{hasValue
? (
<>
{isShowNodeName && !isEnv && (
<div className='flex items-center'>
<div className='p-[1px]'>
<VarBlockIcon
className='!text-gray-900'
type={outputVarNode?.type || BlockEnum.Start}
/>
<VarPickerWrap
onClick={() => {
if (readonly)
return
!isConstant ? setOpen(!open) : setControlFocus(Date.now())
}}
className='grow h-full'
>
<div ref={isSupportConstantValue ? triggerRef : null} className={cn('h-full', isSupportConstantValue && 'flex items-center pl-1 py-1 rounded-lg bg-gray-100')}>
<div className={cn('h-full items-center px-1.5 rounded-[5px]', hasValue ? 'bg-white inline-flex' : 'flex')}>
{hasValue
? (
<>
{isShowNodeName && !isEnv && !isChatVar && (
<div className='flex items-center'>
<div className='p-[1px]'>
<VarBlockIcon
className='!text-gray-900'
type={outputVarNode?.type || BlockEnum.Start}
/>
</div>
<div className='mx-0.5 text-xs font-medium text-gray-700 truncate' title={outputVarNode?.title} style={{
maxWidth: maxNodeNameWidth,
}}>{outputVarNode?.title}</div>
<Line3 className='mr-0.5'></Line3>
</div>
)}
<div className='flex items-center text-primary-600'>
{!hasValue && <Variable02 className='w-3.5 h-3.5' />}
{isEnv && <Env className='w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
<div className={cn('ml-0.5 text-xs font-medium truncate', (isEnv || isChatVar) && '!text-text-secondary')} title={varName} style={{
maxWidth: maxVarNameWidth,
}}>{varName}</div>
</div>
<div className='mx-0.5 text-xs font-medium text-gray-700 truncate' title={outputVarNode?.title} style={{
maxWidth: maxNodeNameWidth,
}}>{outputVarNode?.title}</div>
<Line3 className='mr-0.5'></Line3>
</div>
)}
<div className='flex items-center text-primary-600'>
{!hasValue && <Variable02 className='w-3.5 h-3.5' />}
{isEnv && <Env className='w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
<div className={cn('ml-0.5 text-xs font-medium truncate', isEnv && '!text-gray-900')} title={varName} style={{
maxWidth: maxVarNameWidth,
}}>{varName}</div>
</div>
<div className='ml-0.5 text-xs font-normal text-gray-500 capitalize truncate' title={type} style={{
maxWidth: maxTypeWidth,
}}>{type}</div>
</>
)
: <div className='text-[13px] font-normal text-gray-400'>{t('workflow.common.setVarValuePlaceholder')}</div>}
</div>
<div className='ml-0.5 text-xs font-normal text-gray-500 capitalize truncate' title={type} style={{
maxWidth: maxTypeWidth,
}}>{type}</div>
</>
)
: <div className='text-[13px] font-normal text-gray-400'>{t('workflow.common.setVarValuePlaceholder')}</div>}
</div>
</div>
</VarPickerWrap>
)}
{(hasValue && !readonly) && (<div
className='invisible group-hover/wrap:visible absolute h-5 right-1 top-[50%] translate-y-[-50%] group p-1 rounded-md hover:bg-black/5 cursor-pointer'
@@ -315,8 +338,15 @@ const VarReferencePicker: FC<Props> = ({
>
<RiCloseLine className='w-3.5 h-3.5 text-gray-500 group-hover:text-gray-800' />
</div>)}
{!hasValue && valueTypePlaceHolder && (
<Badge
className=' absolute right-1 top-[50%] translate-y-[-50%] capitalize'
text={valueTypePlaceHolder}
uppercase={false}
/>
)}
</div>)}
</PortalToFollowElemTrigger>
</WrapElem>
<PortalToFollowElemContent style={{
zIndex: 100,
}}>

View File

@@ -16,7 +16,7 @@ import {
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
import { Env } from '@/app/components/base/icons/src/vender/line/others'
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
import { checkKeys } from '@/utils/var'
type ObjectChildrenProps = {
@@ -51,6 +51,7 @@ const Item: FC<ItemProps> = ({
const isObj = itemData.type === VarType.object && itemData.children && itemData.children.length > 0
const isSys = itemData.variable.startsWith('sys.')
const isEnv = itemData.variable.startsWith('env.')
const isChatVar = itemData.variable.startsWith('conversation.')
const itemRef = useRef(null)
const [isItemHovering, setIsItemHovering] = useState(false)
const _ = useHover(itemRef, {
@@ -79,7 +80,7 @@ const Item: FC<ItemProps> = ({
}, [isHovering])
const handleChosen = (e: React.MouseEvent) => {
e.stopPropagation()
if (isSys || isEnv) { // system variable or environment variable
if (isSys || isEnv || isChatVar) { // system variable | environment variable | conversation variable
onChange([...objPath, ...itemData.variable.split('.')], itemData)
}
else {
@@ -100,13 +101,21 @@ const Item: FC<ItemProps> = ({
isHovering && (isObj ? 'bg-primary-50' : 'bg-gray-50'),
'relative w-full flex items-center h-6 pl-3 rounded-md cursor-pointer')
}
// style={{ width: itemWidth || 252 }}
onClick={handleChosen}
>
<div className='flex items-center w-0 grow'>
{!isEnv && <Variable02 className='shrink-0 w-3.5 h-3.5 text-primary-500' />}
{!isEnv && !isChatVar && <Variable02 className='shrink-0 w-3.5 h-3.5 text-primary-500' />}
{isEnv && <Env className='shrink-0 w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
<div title={itemData.variable} className='ml-1 w-0 grow truncate text-[13px] font-normal text-gray-900'>{!isEnv ? itemData.variable : itemData.variable.replace('env.', '')}</div>
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
{!isEnv && !isChatVar && (
<div title={itemData.variable} className='ml-1 w-0 grow truncate text-[13px] font-normal text-gray-900'>{itemData.variable}</div>
)}
{isEnv && (
<div title={itemData.variable} className='ml-1 w-0 grow truncate text-[13px] font-normal text-gray-900'>{itemData.variable.replace('env.', '')}</div>
)}
{isChatVar && (
<div title={itemData.des} className='ml-1 w-0 grow truncate text-[13px] font-normal text-gray-900'>{itemData.variable.replace('conversation.', '')}</div>
)}
</div>
<div className='ml-1 shrink-0 text-xs font-normal text-gray-500 capitalize'>{itemData.type}</div>
{isObj && (
@@ -211,7 +220,7 @@ const VarReferenceVars: FC<Props> = ({
const [searchText, setSearchText] = useState('')
const filteredVars = vars.filter((v) => {
const children = v.vars.filter(v => checkKeys([v.variable], false).isValid || v.variable.startsWith('sys.') || v.variable.startsWith('env.'))
const children = v.vars.filter(v => checkKeys([v.variable], false).isValid || v.variable.startsWith('sys.') || v.variable.startsWith('env.') || v.variable.startsWith('conversation.'))
return children.length > 0
}).filter((node) => {
if (!searchText)
@@ -222,7 +231,7 @@ const VarReferenceVars: FC<Props> = ({
})
return children.length > 0
}).map((node) => {
let vars = node.vars.filter(v => checkKeys([v.variable], false).isValid || v.variable.startsWith('sys.') || v.variable.startsWith('env.'))
let vars = node.vars.filter(v => checkKeys([v.variable], false).isValid || v.variable.startsWith('sys.') || v.variable.startsWith('env.') || v.variable.startsWith('conversation.'))
if (searchText) {
const searchTextLower = searchText.toLowerCase()
if (!node.title.toLowerCase().includes(searchTextLower))