From a430284aa21e3ae1f0d5654e55b2ad2852519cc2 Mon Sep 17 00:00:00 2001 From: wwf <yearningwang@iqtogether.com> Date: 星期三, 04 六月 2025 15:17:49 +0800 Subject: [PATCH] 初始化 --- app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx | 155 +++++++++++++++++++-------------------------------- 1 files changed, 58 insertions(+), 97 deletions(-) diff --git a/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx b/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx index 023916e..9ac5e4a 100644 --- a/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx +++ b/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React, { useEffect, useMemo, useRef, useState } from 'react' +import React, { useEffect, useRef, useState } from 'react' import { useHover } from 'ahooks' import { useTranslation } from 'react-i18next' import cn from '@/utils/classnames' @@ -15,14 +15,7 @@ import Input from '@/app/components/base/input' import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others' import { checkKeys } from '@/utils/var' -import type { StructuredOutput } from '../../../llm/types' -import { Type } from '../../../llm/types' -import PickerStructurePanel from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker' -import { varTypeToStructType } from './utils' -import type { Field } from '@/app/components/workflow/nodes/llm/types' import { FILE_STRUCT } from '@/app/components/workflow/constants' -import { Loop } from '@/app/components/base/icons/src/vender/workflow' -import { noop } from 'lodash-es' type ObjectChildrenProps = { nodeId: string @@ -45,10 +38,7 @@ itemWidth?: number isSupportFileVar?: boolean isException?: boolean - isLoopVar?: boolean } - -const objVarTypes = [VarType.object, VarType.file] const Item: FC<ItemProps> = ({ nodeId, @@ -57,50 +47,24 @@ itemData, onChange, onHovering, + itemWidth, isSupportFileVar, isException, - isLoopVar, }) => { - const isStructureOutput = itemData.type === VarType.object && (itemData.children as StructuredOutput)?.schema?.properties - const isFile = itemData.type === VarType.file && !isStructureOutput - const isObj = ([VarType.object, VarType.file].includes(itemData.type) && itemData.children && (itemData.children as Var[]).length > 0) + const isFile = itemData.type === VarType.file + const isObj = ([VarType.object, VarType.file].includes(itemData.type) && 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 objStructuredOutput: StructuredOutput | null = useMemo(() => { - if (!isObj) return null - const properties: Record<string, Field> = {}; - (isFile ? FILE_STRUCT : (itemData.children as Var[])).forEach((c) => { - properties[c.variable] = { - type: varTypeToStructType(c.type), - } - }) - return { - schema: { - type: Type.object, - properties, - required: [], - additionalProperties: false, - }, - } - }, [isFile, isObj, itemData.children]) - - const structuredOutput = (() => { - if (isStructureOutput) - return itemData.children as StructuredOutput - return objStructuredOutput - })() - - const itemRef = useRef<HTMLDivElement>(null) + const itemRef = useRef(null) const [isItemHovering, setIsItemHovering] = useState(false) - useHover(itemRef, { + const _ = useHover(itemRef, { onChange: (hovering) => { if (hovering) { setIsItemHovering(true) } else { - if (isObj || isStructureOutput) { + if (isObj) { setTimeout(() => { setIsItemHovering(false) }, 100) @@ -113,7 +77,7 @@ }) const [isChildrenHovering, setIsChildrenHovering] = useState(false) const isHovering = isItemHovering || isChildrenHovering - const open = (isObj || isStructureOutput) && isHovering + const open = isObj && isHovering useEffect(() => { onHovering && onHovering(isHovering) // eslint-disable-next-line react-hooks/exhaustive-deps @@ -133,58 +97,70 @@ return ( <PortalToFollowElem open={open} - onOpenChange={noop} + onOpenChange={() => { }} placement='left-start' > <PortalToFollowElemTrigger className='w-full'> <div ref={itemRef} className={cn( - (isObj || isStructureOutput) ? ' pr-1' : 'pr-[18px]', - isHovering && ((isObj || isStructureOutput) ? 'bg-primary-50' : 'bg-state-base-hover'), - 'relative flex h-6 w-full cursor-pointer items-center rounded-md pl-3') + isObj ? ' pr-1' : 'pr-[18px]', + isHovering && (isObj ? 'bg-primary-50' : 'bg-state-base-hover'), + 'relative w-full flex items-center h-6 pl-3 rounded-md cursor-pointer') } onClick={handleChosen} - onMouseDown={e => e.preventDefault()} > - <div className='flex w-0 grow items-center'> - {!isEnv && !isChatVar && !isLoopVar && <Variable02 className={cn('h-3.5 w-3.5 shrink-0 text-text-accent', isException && 'text-text-warning')} />} - {isEnv && <Env className='h-3.5 w-3.5 shrink-0 text-util-colors-violet-violet-600' />} - {isChatVar && <BubbleX className='h-3.5 w-3.5 shrink-0 text-util-colors-teal-teal-700' />} - {isLoopVar && <Loop className='h-3.5 w-3.5 shrink-0 text-util-colors-cyan-cyan-500' />} + <div className='flex items-center w-0 grow'> + {!isEnv && !isChatVar && <Variable02 className={cn('shrink-0 w-3.5 h-3.5 text-text-accent', isException && 'text-text-warning')} />} + {isEnv && <Env className='shrink-0 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' />} {!isEnv && !isChatVar && ( - <div title={itemData.variable} className='system-sm-medium ml-1 w-0 grow truncate text-text-secondary'>{itemData.variable}</div> + <div title={itemData.variable} className='ml-1 w-0 grow truncate text-text-secondary system-sm-medium'>{itemData.variable}</div> )} {isEnv && ( - <div title={itemData.variable} className='system-sm-medium ml-1 w-0 grow truncate text-text-secondary'>{itemData.variable.replace('env.', '')}</div> + <div title={itemData.variable} className='ml-1 w-0 grow truncate text-text-secondary system-sm-medium'>{itemData.variable.replace('env.', '')}</div> )} {isChatVar && ( - <div title={itemData.des} className='system-sm-medium ml-1 w-0 grow truncate text-text-secondary'>{itemData.variable.replace('conversation.', '')}</div> + <div title={itemData.des} className='ml-1 w-0 grow truncate text-text-secondary system-sm-medium'>{itemData.variable.replace('conversation.', '')}</div> )} </div> - <div className='ml-1 shrink-0 text-xs font-normal capitalize text-text-tertiary'>{itemData.type}</div> - { - (isObj || isStructureOutput) && ( - <ChevronRight className={cn('ml-0.5 h-3 w-3 text-text-quaternary', isHovering && 'text-text-tertiary')} /> - ) - } - </div > - </PortalToFollowElemTrigger > + <div className='ml-1 shrink-0 text-xs font-normal text-text-tertiary capitalize'>{itemData.type}</div> + {isObj && ( + <ChevronRight className={cn('ml-0.5 w-3 h-3 text-text-quaternary', isHovering && 'text-text-tertiary')} /> + )} + </div> + </PortalToFollowElemTrigger> <PortalToFollowElemContent style={{ zIndex: 100, }}> - {(isStructureOutput || isObj) && ( - <PickerStructurePanel - root={{ nodeId, nodeName: title, attrName: itemData.variable }} - payload={structuredOutput!} + {(isObj && !isFile) && ( + // eslint-disable-next-line @typescript-eslint/no-use-before-define + <ObjectChildren + nodeId={nodeId} + title={title} + objPath={[...objPath, itemData.variable]} + data={itemData.children as Var[]} + onChange={onChange} onHovering={setIsChildrenHovering} - onSelect={(valueSelector) => { - onChange(valueSelector, itemData) - }} + itemWidth={itemWidth} + isSupportFileVar={isSupportFileVar} + /> + )} + {isFile && ( + // eslint-disable-next-line @typescript-eslint/no-use-before-define + <ObjectChildren + nodeId={nodeId} + title={title} + objPath={[...objPath, itemData.variable]} + data={FILE_STRUCT} + onChange={onChange} + onHovering={setIsChildrenHovering} + itemWidth={itemWidth} + isSupportFileVar={isSupportFileVar} /> )} </PortalToFollowElemContent> - </PortalToFollowElem > + </PortalToFollowElem> ) } @@ -199,9 +175,9 @@ isSupportFileVar, }) => { const currObjPath = objPath - const itemRef = useRef<HTMLDivElement>(null) + const itemRef = useRef(null) const [isItemHovering, setIsItemHovering] = useState(false) - useHover(itemRef, { + const _ = useHover(itemRef, { onChange: (hovering) => { if (hovering) { setIsItemHovering(true) @@ -225,11 +201,11 @@ }, [isItemHovering]) // absolute top-[-2px] return ( - <div ref={itemRef} className=' space-y-1 rounded-lg border border-gray-200 bg-white shadow-lg' style={{ + <div ref={itemRef} className=' bg-white rounded-lg border border-gray-200 shadow-lg space-y-1' style={{ right: itemWidth ? itemWidth - 10 : 215, minWidth: 252, }}> - <div className='flex h-[22px] items-center px-3 text-xs font-normal text-gray-700'><span className='text-gray-500'>{title}.</span>{currObjPath.join('.')}</div> + <div className='flex items-center h-[22px] px-3 text-xs font-normal text-gray-700'><span className='text-gray-500'>{title}.</span>{currObjPath.join('.')}</div> { (data && data.length > 0) && data.map((v, i) => ( @@ -258,8 +234,6 @@ onChange: (value: ValueSelector, item: Var) => void itemWidth?: number maxHeightClass?: string - onClose?: () => void - onBlur?: () => void } const VarReferenceVars: FC<Props> = ({ hideSearch, @@ -269,18 +243,9 @@ onChange, itemWidth, maxHeightClass, - onClose, - onBlur, }) => { const { t } = useTranslation() const [searchText, setSearchText] = useState('') - - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Escape') { - e.preventDefault() - onClose?.() - } - } const filteredVars = vars.filter((v) => { const children = v.vars.filter(v => checkKeys([v.variable], false).isValid || v.variable.startsWith('sys.') || v.variable.startsWith('env.') || v.variable.startsWith('conversation.')) @@ -312,21 +277,18 @@ { !hideSearch && ( <> - <div className={cn('var-search-input-wrapper mx-2 mb-1 mt-2', searchBoxClassName)} onClick={e => e.stopPropagation()}> + <div className={cn('mb-1 mx-2 mt-2', searchBoxClassName)} onClick={e => e.stopPropagation()}> <Input - className='var-search-input' showLeftIcon showClearIcon value={searchText} placeholder={t('workflow.common.searchVar') || ''} onChange={e => setSearchText(e.target.value)} - onKeyDown={handleKeyDown} onClear={() => setSearchText('')} - onBlur={onBlur} autoFocus /> </div> - <div className='relative left-[-4px] h-[0.5px] bg-black/5' style={{ + <div className='h-[0.5px] bg-black/5 relative left-[-4px]' style={{ width: 'calc(100% + 8px)', }}></div> </> @@ -340,7 +302,7 @@ filteredVars.map((item, i) => ( <div key={i}> <div - className='system-xs-medium-uppercase truncate px-3 leading-[22px] text-text-tertiary' + className='leading-[22px] px-3 text-text-tertiary system-xs-medium-uppercase truncate' title={item.title} >{item.title}</div> {item.vars.map((v, j) => ( @@ -354,14 +316,13 @@ itemWidth={itemWidth} isSupportFileVar={isSupportFileVar} isException={v.isException} - isLoopVar={item.isLoop} /> ))} </div>)) } </div> - : <div className='pl-3 text-xs font-medium uppercase leading-[18px] text-gray-500'>{t('workflow.common.noVar')}</div>} - </> + : <div className='pl-3 leading-[18px] text-xs font-medium text-gray-500 uppercase'>{t('workflow.common.noVar')}</div>} + </ > ) } export default React.memo(VarReferenceVars) -- Gitblit v1.8.0