|  | @@ -15,8 +15,9 @@ import { INSERT_HISTORY_BLOCK_COMMAND } from '../history-block'
 | 
	
		
			
				|  |  |  import { INSERT_QUERY_BLOCK_COMMAND } from '../query-block'
 | 
	
		
			
				|  |  |  import { INSERT_VARIABLE_VALUE_BLOCK_COMMAND } from '../variable-block'
 | 
	
		
			
				|  |  |  import { $createCustomTextNode } from '../custom-text/node'
 | 
	
		
			
				|  |  | -import { PromptOption } from './prompt-option'
 | 
	
		
			
				|  |  | -import { VariableOption } from './variable-option'
 | 
	
		
			
				|  |  | +import { PromptMenuItem } from './prompt-option'
 | 
	
		
			
				|  |  | +import { VariableMenuItem } from './variable-option'
 | 
	
		
			
				|  |  | +import { PickerBlockMenuOption } from './menu'
 | 
	
		
			
				|  |  |  import { File05 } from '@/app/components/base/icons/src/vender/solid/files'
 | 
	
		
			
				|  |  |  import {
 | 
	
		
			
				|  |  |    MessageClockCircle,
 | 
	
	
		
			
				|  | @@ -35,62 +36,111 @@ export const usePromptOptions = (
 | 
	
		
			
				|  |  |    const { t } = useTranslation()
 | 
	
		
			
				|  |  |    const [editor] = useLexicalComposerContext()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  return useMemo(() => {
 | 
	
		
			
				|  |  | -    return [
 | 
	
		
			
				|  |  | -      ...contextBlock?.show
 | 
	
		
			
				|  |  | -        ? [
 | 
	
		
			
				|  |  | -          new PromptOption(t('common.promptEditor.context.item.title'), {
 | 
	
		
			
				|  |  | -            icon: <File05 className='w-4 h-4 text-[#6938EF]' />,
 | 
	
		
			
				|  |  | -            onSelect: () => {
 | 
	
		
			
				|  |  | -              if (!contextBlock?.selectable)
 | 
	
		
			
				|  |  | -                return
 | 
	
		
			
				|  |  | -              editor.dispatchCommand(INSERT_CONTEXT_BLOCK_COMMAND, undefined)
 | 
	
		
			
				|  |  | -            },
 | 
	
		
			
				|  |  | -            disabled: !contextBlock?.selectable,
 | 
	
		
			
				|  |  | -          }),
 | 
	
		
			
				|  |  | -        ]
 | 
	
		
			
				|  |  | -        : [],
 | 
	
		
			
				|  |  | -      ...queryBlock?.show
 | 
	
		
			
				|  |  | -        ? [
 | 
	
		
			
				|  |  | -          new PromptOption(t('common.promptEditor.query.item.title'), {
 | 
	
		
			
				|  |  | -            icon: <UserEdit02 className='w-4 h-4 text-[#FD853A]' />,
 | 
	
		
			
				|  |  | -            onSelect: () => {
 | 
	
		
			
				|  |  | -              if (!queryBlock?.selectable)
 | 
	
		
			
				|  |  | -                return
 | 
	
		
			
				|  |  | -              editor.dispatchCommand(INSERT_QUERY_BLOCK_COMMAND, undefined)
 | 
	
		
			
				|  |  | -            },
 | 
	
		
			
				|  |  | -            disabled: !queryBlock?.selectable,
 | 
	
		
			
				|  |  | -          }),
 | 
	
		
			
				|  |  | -        ]
 | 
	
		
			
				|  |  | -        : [],
 | 
	
		
			
				|  |  | -      ...historyBlock?.show
 | 
	
		
			
				|  |  | -        ? [
 | 
	
		
			
				|  |  | -          new PromptOption(t('common.promptEditor.history.item.title'), {
 | 
	
		
			
				|  |  | -            icon: <MessageClockCircle className='w-4 h-4 text-[#DD2590]' />,
 | 
	
		
			
				|  |  | -            onSelect: () => {
 | 
	
		
			
				|  |  | -              if (!historyBlock?.selectable)
 | 
	
		
			
				|  |  | -                return
 | 
	
		
			
				|  |  | -              editor.dispatchCommand(INSERT_HISTORY_BLOCK_COMMAND, undefined)
 | 
	
		
			
				|  |  | -            },
 | 
	
		
			
				|  |  | -            disabled: !historyBlock?.selectable,
 | 
	
		
			
				|  |  | -          }),
 | 
	
		
			
				|  |  | -        ]
 | 
	
		
			
				|  |  | -        : [],
 | 
	
		
			
				|  |  | -    ]
 | 
	
		
			
				|  |  | -  }, [contextBlock, editor, historyBlock, queryBlock, t])
 | 
	
		
			
				|  |  | +  const promptOptions: PickerBlockMenuOption[] = []
 | 
	
		
			
				|  |  | +  if (contextBlock?.show) {
 | 
	
		
			
				|  |  | +    promptOptions.push(new PickerBlockMenuOption({
 | 
	
		
			
				|  |  | +      key: t('common.promptEditor.context.item.title'),
 | 
	
		
			
				|  |  | +      group: 'prompt context',
 | 
	
		
			
				|  |  | +      render: ({ isSelected, onSelect, onSetHighlight }) => {
 | 
	
		
			
				|  |  | +        return <PromptMenuItem
 | 
	
		
			
				|  |  | +          title={t('common.promptEditor.context.item.title')}
 | 
	
		
			
				|  |  | +          icon={<File05 className='w-4 h-4 text-[#6938EF]' />}
 | 
	
		
			
				|  |  | +          disabled={!contextBlock.selectable}
 | 
	
		
			
				|  |  | +          isSelected={isSelected}
 | 
	
		
			
				|  |  | +          onClick={onSelect}
 | 
	
		
			
				|  |  | +          onMouseEnter={onSetHighlight}
 | 
	
		
			
				|  |  | +        />
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      onSelect: () => {
 | 
	
		
			
				|  |  | +        if (!contextBlock?.selectable)
 | 
	
		
			
				|  |  | +          return
 | 
	
		
			
				|  |  | +        editor.dispatchCommand(INSERT_CONTEXT_BLOCK_COMMAND, undefined)
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +    }))
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (queryBlock?.show) {
 | 
	
		
			
				|  |  | +    promptOptions.push(
 | 
	
		
			
				|  |  | +      new PickerBlockMenuOption({
 | 
	
		
			
				|  |  | +        key: t('common.promptEditor.query.item.title'),
 | 
	
		
			
				|  |  | +        group: 'prompt query',
 | 
	
		
			
				|  |  | +        render: ({ isSelected, onSelect, onSetHighlight }) => {
 | 
	
		
			
				|  |  | +          return (
 | 
	
		
			
				|  |  | +            <PromptMenuItem
 | 
	
		
			
				|  |  | +              title={t('common.promptEditor.query.item.title')}
 | 
	
		
			
				|  |  | +              icon={<UserEdit02 className='w-4 h-4 text-[#FD853A]' />}
 | 
	
		
			
				|  |  | +              disabled={!queryBlock.selectable}
 | 
	
		
			
				|  |  | +              isSelected={isSelected}
 | 
	
		
			
				|  |  | +              onClick={onSelect}
 | 
	
		
			
				|  |  | +              onMouseEnter={onSetHighlight}
 | 
	
		
			
				|  |  | +            />
 | 
	
		
			
				|  |  | +          )
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +        onSelect: () => {
 | 
	
		
			
				|  |  | +          if (!queryBlock?.selectable)
 | 
	
		
			
				|  |  | +            return
 | 
	
		
			
				|  |  | +          editor.dispatchCommand(INSERT_QUERY_BLOCK_COMMAND, undefined)
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +      }),
 | 
	
		
			
				|  |  | +    )
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (historyBlock?.show) {
 | 
	
		
			
				|  |  | +    promptOptions.push(
 | 
	
		
			
				|  |  | +      new PickerBlockMenuOption({
 | 
	
		
			
				|  |  | +        key: t('common.promptEditor.history.item.title'),
 | 
	
		
			
				|  |  | +        group: 'prompt history',
 | 
	
		
			
				|  |  | +        render: ({ isSelected, onSelect, onSetHighlight }) => {
 | 
	
		
			
				|  |  | +          return (
 | 
	
		
			
				|  |  | +            <PromptMenuItem
 | 
	
		
			
				|  |  | +              title={t('common.promptEditor.history.item.title')}
 | 
	
		
			
				|  |  | +              icon={<MessageClockCircle className='w-4 h-4 text-[#DD2590]' />}
 | 
	
		
			
				|  |  | +              disabled={!historyBlock.selectable
 | 
	
		
			
				|  |  | +              }
 | 
	
		
			
				|  |  | +              isSelected={isSelected}
 | 
	
		
			
				|  |  | +              onClick={onSelect}
 | 
	
		
			
				|  |  | +              onMouseEnter={onSetHighlight}
 | 
	
		
			
				|  |  | +            />
 | 
	
		
			
				|  |  | +          )
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +        onSelect: () => {
 | 
	
		
			
				|  |  | +          if (!historyBlock?.selectable)
 | 
	
		
			
				|  |  | +            return
 | 
	
		
			
				|  |  | +          editor.dispatchCommand(INSERT_HISTORY_BLOCK_COMMAND, undefined)
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +      }),
 | 
	
		
			
				|  |  | +    )
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return promptOptions
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  export const useVariableOptions = (
 | 
	
		
			
				|  |  |    variableBlock?: VariableBlockType,
 | 
	
		
			
				|  |  |    queryString?: string,
 | 
	
		
			
				|  |  | -) => {
 | 
	
		
			
				|  |  | +): PickerBlockMenuOption[] => {
 | 
	
		
			
				|  |  |    const { t } = useTranslation()
 | 
	
		
			
				|  |  |    const [editor] = useLexicalComposerContext()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    const options = useMemo(() => {
 | 
	
		
			
				|  |  | -    const baseOptions = (variableBlock?.variables || []).map((item) => {
 | 
	
		
			
				|  |  | -      return new VariableOption(item.value, {
 | 
	
		
			
				|  |  | -        icon: <BracketsX className='w-[14px] h-[14px] text-[#2970FF]' />,
 | 
	
		
			
				|  |  | +    if (!variableBlock?.variables)
 | 
	
		
			
				|  |  | +      return []
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const baseOptions = (variableBlock.variables).map((item) => {
 | 
	
		
			
				|  |  | +      return new PickerBlockMenuOption({
 | 
	
		
			
				|  |  | +        key: item.value,
 | 
	
		
			
				|  |  | +        group: 'prompt variable',
 | 
	
		
			
				|  |  | +        render: ({ queryString, isSelected, onSelect, onSetHighlight }) => {
 | 
	
		
			
				|  |  | +          return (
 | 
	
		
			
				|  |  | +            <VariableMenuItem
 | 
	
		
			
				|  |  | +              title={item.value}
 | 
	
		
			
				|  |  | +              icon={<BracketsX className='w-[14px] h-[14px] text-[#2970FF]' />}
 | 
	
		
			
				|  |  | +              queryString={queryString}
 | 
	
		
			
				|  |  | +              isSelected={isSelected}
 | 
	
		
			
				|  |  | +              onClick={onSelect}
 | 
	
		
			
				|  |  | +              onMouseEnter={onSetHighlight}
 | 
	
		
			
				|  |  | +            />
 | 
	
		
			
				|  |  | +          )
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  |          onSelect: () => {
 | 
	
		
			
				|  |  |            editor.dispatchCommand(INSERT_VARIABLE_VALUE_BLOCK_COMMAND, `{{${item.value}}}`)
 | 
	
		
			
				|  |  |          },
 | 
	
	
		
			
				|  | @@ -101,12 +151,25 @@ export const useVariableOptions = (
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      const regex = new RegExp(queryString, 'i')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return baseOptions.filter(option => regex.test(option.title) || option.keywords.some(keyword => regex.test(keyword)))
 | 
	
		
			
				|  |  | +    return baseOptions.filter(option => regex.test(option.key))
 | 
	
		
			
				|  |  |    }, [editor, queryString, variableBlock])
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    const addOption = useMemo(() => {
 | 
	
		
			
				|  |  | -    return new VariableOption(t('common.promptEditor.variable.modal.add'), {
 | 
	
		
			
				|  |  | -      icon: <BracketsX className='mr-2 w-[14px] h-[14px] text-[#2970FF]' />,
 | 
	
		
			
				|  |  | +    return new PickerBlockMenuOption({
 | 
	
		
			
				|  |  | +      key: t('common.promptEditor.variable.modal.add'),
 | 
	
		
			
				|  |  | +      group: 'prompt variable',
 | 
	
		
			
				|  |  | +      render: ({ queryString, isSelected, onSelect, onSetHighlight }) => {
 | 
	
		
			
				|  |  | +        return (
 | 
	
		
			
				|  |  | +          <VariableMenuItem
 | 
	
		
			
				|  |  | +            title={t('common.promptEditor.variable.modal.add')}
 | 
	
		
			
				|  |  | +            icon={<BracketsX className='mr-2 w-[14px] h-[14px] text-[#2970FF]' />}
 | 
	
		
			
				|  |  | +            queryString={queryString}
 | 
	
		
			
				|  |  | +            isSelected={isSelected}
 | 
	
		
			
				|  |  | +            onClick={onSelect}
 | 
	
		
			
				|  |  | +            onMouseEnter={onSetHighlight}
 | 
	
		
			
				|  |  | +          />
 | 
	
		
			
				|  |  | +        )
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  |        onSelect: () => {
 | 
	
		
			
				|  |  |          editor.update(() => {
 | 
	
		
			
				|  |  |            const prefixNode = $createCustomTextNode('{{')
 | 
	
	
		
			
				|  | @@ -131,16 +194,31 @@ export const useExternalToolOptions = (
 | 
	
		
			
				|  |  |    const [editor] = useLexicalComposerContext()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    const options = useMemo(() => {
 | 
	
		
			
				|  |  | -    const baseToolOptions = (externalToolBlockType?.externalTools || []).map((item) => {
 | 
	
		
			
				|  |  | -      return new VariableOption(item.name, {
 | 
	
		
			
				|  |  | -        icon: (
 | 
	
		
			
				|  |  | -          <AppIcon
 | 
	
		
			
				|  |  | -            className='!w-[14px] !h-[14px]'
 | 
	
		
			
				|  |  | -            icon={item.icon}
 | 
	
		
			
				|  |  | -            background={item.icon_background}
 | 
	
		
			
				|  |  | -          />
 | 
	
		
			
				|  |  | -        ),
 | 
	
		
			
				|  |  | -        extraElement: <div className='text-xs text-gray-400'>{item.variableName}</div>,
 | 
	
		
			
				|  |  | +    if (!externalToolBlockType?.externalTools)
 | 
	
		
			
				|  |  | +      return []
 | 
	
		
			
				|  |  | +    const baseToolOptions = (externalToolBlockType.externalTools).map((item) => {
 | 
	
		
			
				|  |  | +      return new PickerBlockMenuOption({
 | 
	
		
			
				|  |  | +        key: item.name,
 | 
	
		
			
				|  |  | +        group: 'external tool',
 | 
	
		
			
				|  |  | +        render: ({ queryString, isSelected, onSelect, onSetHighlight }) => {
 | 
	
		
			
				|  |  | +          return (
 | 
	
		
			
				|  |  | +            <VariableMenuItem
 | 
	
		
			
				|  |  | +              title={item.name}
 | 
	
		
			
				|  |  | +              icon={
 | 
	
		
			
				|  |  | +                <AppIcon
 | 
	
		
			
				|  |  | +                  className='!w-[14px] !h-[14px]'
 | 
	
		
			
				|  |  | +                  icon={item.icon}
 | 
	
		
			
				|  |  | +                  background={item.icon_background}
 | 
	
		
			
				|  |  | +                />
 | 
	
		
			
				|  |  | +              }
 | 
	
		
			
				|  |  | +              extraElement={<div className='text-xs text-gray-400'>{item.variableName}</div>}
 | 
	
		
			
				|  |  | +              queryString={queryString}
 | 
	
		
			
				|  |  | +              isSelected={isSelected}
 | 
	
		
			
				|  |  | +              onClick={onSelect}
 | 
	
		
			
				|  |  | +              onMouseEnter={onSetHighlight}
 | 
	
		
			
				|  |  | +            />
 | 
	
		
			
				|  |  | +          )
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  |          onSelect: () => {
 | 
	
		
			
				|  |  |            editor.dispatchCommand(INSERT_VARIABLE_VALUE_BLOCK_COMMAND, `{{${item.variableName}}}`)
 | 
	
		
			
				|  |  |          },
 | 
	
	
		
			
				|  | @@ -151,16 +229,28 @@ export const useExternalToolOptions = (
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      const regex = new RegExp(queryString, 'i')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return baseToolOptions.filter(option => regex.test(option.title) || option.keywords.some(keyword => regex.test(keyword)))
 | 
	
		
			
				|  |  | +    return baseToolOptions.filter(option => regex.test(option.key))
 | 
	
		
			
				|  |  |    }, [editor, queryString, externalToolBlockType])
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    const addOption = useMemo(() => {
 | 
	
		
			
				|  |  | -    return new VariableOption(t('common.promptEditor.variable.modal.addTool'), {
 | 
	
		
			
				|  |  | -      icon: <Tool03 className='mr-2 w-[14px] h-[14px] text-[#444CE7]' />,
 | 
	
		
			
				|  |  | -      extraElement: <ArrowUpRight className='w-3 h-3 text-gray-400' />,
 | 
	
		
			
				|  |  | +    return new PickerBlockMenuOption({
 | 
	
		
			
				|  |  | +      key: t('common.promptEditor.variable.modal.addTool'),
 | 
	
		
			
				|  |  | +      group: 'external tool',
 | 
	
		
			
				|  |  | +      render: ({ queryString, isSelected, onSelect, onSetHighlight }) => {
 | 
	
		
			
				|  |  | +        return (
 | 
	
		
			
				|  |  | +          <VariableMenuItem
 | 
	
		
			
				|  |  | +            title={t('common.promptEditor.variable.modal.addTool')}
 | 
	
		
			
				|  |  | +            icon={<Tool03 className='mr-2 w-[14px] h-[14px] text-[#444CE7]' />}
 | 
	
		
			
				|  |  | +            extraElement={< ArrowUpRight className='w-3 h-3 text-gray-400' />}
 | 
	
		
			
				|  |  | +            queryString={queryString}
 | 
	
		
			
				|  |  | +            isSelected={isSelected}
 | 
	
		
			
				|  |  | +            onClick={onSelect}
 | 
	
		
			
				|  |  | +            onMouseEnter={onSetHighlight}
 | 
	
		
			
				|  |  | +          />
 | 
	
		
			
				|  |  | +        )
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  |        onSelect: () => {
 | 
	
		
			
				|  |  | -        if (externalToolBlockType?.onAddExternalTool)
 | 
	
		
			
				|  |  | -          externalToolBlockType.onAddExternalTool()
 | 
	
		
			
				|  |  | +        externalToolBlockType?.onAddExternalTool?.()
 | 
	
		
			
				|  |  |        },
 | 
	
		
			
				|  |  |      })
 | 
	
		
			
				|  |  |    }, [externalToolBlockType, t])
 | 
	
	
		
			
				|  | @@ -191,11 +281,8 @@ export const useOptions = (
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    return useMemo(() => {
 | 
	
		
			
				|  |  |      return {
 | 
	
		
			
				|  |  | -      promptOptions,
 | 
	
		
			
				|  |  | -      variableOptions,
 | 
	
		
			
				|  |  | -      externalToolOptions,
 | 
	
		
			
				|  |  |        workflowVariableOptions,
 | 
	
		
			
				|  |  | -      allOptions: [...promptOptions, ...variableOptions, ...externalToolOptions],
 | 
	
		
			
				|  |  | +      allFlattenOptions: [...promptOptions, ...variableOptions, ...externalToolOptions],
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }, [promptOptions, variableOptions, externalToolOptions, workflowVariableOptions])
 | 
	
		
			
				|  |  |  }
 |