hooks.tsx 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. import { useMemo } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import { $insertNodes } from 'lexical'
  4. import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
  5. import type {
  6. ContextBlockType,
  7. ExternalToolBlockType,
  8. HistoryBlockType,
  9. QueryBlockType,
  10. VariableBlockType,
  11. WorkflowVariableBlockType,
  12. } from '../../types'
  13. import { INSERT_CONTEXT_BLOCK_COMMAND } from '../context-block'
  14. import { INSERT_HISTORY_BLOCK_COMMAND } from '../history-block'
  15. import { INSERT_QUERY_BLOCK_COMMAND } from '../query-block'
  16. import { INSERT_VARIABLE_VALUE_BLOCK_COMMAND } from '../variable-block'
  17. import { $createCustomTextNode } from '../custom-text/node'
  18. import { PromptMenuItem } from './prompt-option'
  19. import { VariableMenuItem } from './variable-option'
  20. import { PickerBlockMenuOption } from './menu'
  21. import { File05 } from '@/app/components/base/icons/src/vender/solid/files'
  22. import {
  23. MessageClockCircle,
  24. Tool03,
  25. } from '@/app/components/base/icons/src/vender/solid/general'
  26. import { BracketsX } from '@/app/components/base/icons/src/vender/line/development'
  27. import { UserEdit02 } from '@/app/components/base/icons/src/vender/solid/users'
  28. import { ArrowUpRight } from '@/app/components/base/icons/src/vender/line/arrows'
  29. import AppIcon from '@/app/components/base/app-icon'
  30. export const usePromptOptions = (
  31. contextBlock?: ContextBlockType,
  32. queryBlock?: QueryBlockType,
  33. historyBlock?: HistoryBlockType,
  34. ) => {
  35. const { t } = useTranslation()
  36. const [editor] = useLexicalComposerContext()
  37. const promptOptions: PickerBlockMenuOption[] = []
  38. if (contextBlock?.show) {
  39. promptOptions.push(new PickerBlockMenuOption({
  40. key: t('common.promptEditor.context.item.title'),
  41. group: 'prompt context',
  42. render: ({ isSelected, onSelect, onSetHighlight }) => {
  43. return <PromptMenuItem
  44. title={t('common.promptEditor.context.item.title')}
  45. icon={<File05 className='w-4 h-4 text-[#6938EF]' />}
  46. disabled={!contextBlock.selectable}
  47. isSelected={isSelected}
  48. onClick={onSelect}
  49. onMouseEnter={onSetHighlight}
  50. />
  51. },
  52. onSelect: () => {
  53. if (!contextBlock?.selectable)
  54. return
  55. editor.dispatchCommand(INSERT_CONTEXT_BLOCK_COMMAND, undefined)
  56. },
  57. }))
  58. }
  59. if (queryBlock?.show) {
  60. promptOptions.push(
  61. new PickerBlockMenuOption({
  62. key: t('common.promptEditor.query.item.title'),
  63. group: 'prompt query',
  64. render: ({ isSelected, onSelect, onSetHighlight }) => {
  65. return (
  66. <PromptMenuItem
  67. title={t('common.promptEditor.query.item.title')}
  68. icon={<UserEdit02 className='w-4 h-4 text-[#FD853A]' />}
  69. disabled={!queryBlock.selectable}
  70. isSelected={isSelected}
  71. onClick={onSelect}
  72. onMouseEnter={onSetHighlight}
  73. />
  74. )
  75. },
  76. onSelect: () => {
  77. if (!queryBlock?.selectable)
  78. return
  79. editor.dispatchCommand(INSERT_QUERY_BLOCK_COMMAND, undefined)
  80. },
  81. }),
  82. )
  83. }
  84. if (historyBlock?.show) {
  85. promptOptions.push(
  86. new PickerBlockMenuOption({
  87. key: t('common.promptEditor.history.item.title'),
  88. group: 'prompt history',
  89. render: ({ isSelected, onSelect, onSetHighlight }) => {
  90. return (
  91. <PromptMenuItem
  92. title={t('common.promptEditor.history.item.title')}
  93. icon={<MessageClockCircle className='w-4 h-4 text-[#DD2590]' />}
  94. disabled={!historyBlock.selectable
  95. }
  96. isSelected={isSelected}
  97. onClick={onSelect}
  98. onMouseEnter={onSetHighlight}
  99. />
  100. )
  101. },
  102. onSelect: () => {
  103. if (!historyBlock?.selectable)
  104. return
  105. editor.dispatchCommand(INSERT_HISTORY_BLOCK_COMMAND, undefined)
  106. },
  107. }),
  108. )
  109. }
  110. return promptOptions
  111. }
  112. export const useVariableOptions = (
  113. variableBlock?: VariableBlockType,
  114. queryString?: string,
  115. ): PickerBlockMenuOption[] => {
  116. const { t } = useTranslation()
  117. const [editor] = useLexicalComposerContext()
  118. const options = useMemo(() => {
  119. if (!variableBlock?.variables)
  120. return []
  121. const baseOptions = (variableBlock.variables).map((item) => {
  122. return new PickerBlockMenuOption({
  123. key: item.value,
  124. group: 'prompt variable',
  125. render: ({ queryString, isSelected, onSelect, onSetHighlight }) => {
  126. return (
  127. <VariableMenuItem
  128. title={item.value}
  129. icon={<BracketsX className='w-[14px] h-[14px] text-[#2970FF]' />}
  130. queryString={queryString}
  131. isSelected={isSelected}
  132. onClick={onSelect}
  133. onMouseEnter={onSetHighlight}
  134. />
  135. )
  136. },
  137. onSelect: () => {
  138. editor.dispatchCommand(INSERT_VARIABLE_VALUE_BLOCK_COMMAND, `{{${item.value}}}`)
  139. },
  140. })
  141. })
  142. if (!queryString)
  143. return baseOptions
  144. const regex = new RegExp(queryString, 'i')
  145. return baseOptions.filter(option => regex.test(option.key))
  146. }, [editor, queryString, variableBlock])
  147. const addOption = useMemo(() => {
  148. return new PickerBlockMenuOption({
  149. key: t('common.promptEditor.variable.modal.add'),
  150. group: 'prompt variable',
  151. render: ({ queryString, isSelected, onSelect, onSetHighlight }) => {
  152. return (
  153. <VariableMenuItem
  154. title={t('common.promptEditor.variable.modal.add')}
  155. icon={<BracketsX className='mr-2 w-[14px] h-[14px] text-[#2970FF]' />}
  156. queryString={queryString}
  157. isSelected={isSelected}
  158. onClick={onSelect}
  159. onMouseEnter={onSetHighlight}
  160. />
  161. )
  162. },
  163. onSelect: () => {
  164. editor.update(() => {
  165. const prefixNode = $createCustomTextNode('{{')
  166. const suffixNode = $createCustomTextNode('}}')
  167. $insertNodes([prefixNode, suffixNode])
  168. prefixNode.select()
  169. })
  170. },
  171. })
  172. }, [editor, t])
  173. return useMemo(() => {
  174. return variableBlock?.show ? [...options, addOption] : []
  175. }, [options, addOption, variableBlock?.show])
  176. }
  177. export const useExternalToolOptions = (
  178. externalToolBlockType?: ExternalToolBlockType,
  179. queryString?: string,
  180. ) => {
  181. const { t } = useTranslation()
  182. const [editor] = useLexicalComposerContext()
  183. const options = useMemo(() => {
  184. if (!externalToolBlockType?.externalTools)
  185. return []
  186. const baseToolOptions = (externalToolBlockType.externalTools).map((item) => {
  187. return new PickerBlockMenuOption({
  188. key: item.name,
  189. group: 'external tool',
  190. render: ({ queryString, isSelected, onSelect, onSetHighlight }) => {
  191. return (
  192. <VariableMenuItem
  193. title={item.name}
  194. icon={
  195. <AppIcon
  196. className='!w-[14px] !h-[14px]'
  197. icon={item.icon}
  198. background={item.icon_background}
  199. />
  200. }
  201. extraElement={<div className='text-xs text-gray-400'>{item.variableName}</div>}
  202. queryString={queryString}
  203. isSelected={isSelected}
  204. onClick={onSelect}
  205. onMouseEnter={onSetHighlight}
  206. />
  207. )
  208. },
  209. onSelect: () => {
  210. editor.dispatchCommand(INSERT_VARIABLE_VALUE_BLOCK_COMMAND, `{{${item.variableName}}}`)
  211. },
  212. })
  213. })
  214. if (!queryString)
  215. return baseToolOptions
  216. const regex = new RegExp(queryString, 'i')
  217. return baseToolOptions.filter(option => regex.test(option.key))
  218. }, [editor, queryString, externalToolBlockType])
  219. const addOption = useMemo(() => {
  220. return new PickerBlockMenuOption({
  221. key: t('common.promptEditor.variable.modal.addTool'),
  222. group: 'external tool',
  223. render: ({ queryString, isSelected, onSelect, onSetHighlight }) => {
  224. return (
  225. <VariableMenuItem
  226. title={t('common.promptEditor.variable.modal.addTool')}
  227. icon={<Tool03 className='mr-2 w-[14px] h-[14px] text-[#444CE7]' />}
  228. extraElement={< ArrowUpRight className='w-3 h-3 text-gray-400' />}
  229. queryString={queryString}
  230. isSelected={isSelected}
  231. onClick={onSelect}
  232. onMouseEnter={onSetHighlight}
  233. />
  234. )
  235. },
  236. onSelect: () => {
  237. externalToolBlockType?.onAddExternalTool?.()
  238. },
  239. })
  240. }, [externalToolBlockType, t])
  241. return useMemo(() => {
  242. return externalToolBlockType?.show ? [...options, addOption] : []
  243. }, [options, addOption, externalToolBlockType?.show])
  244. }
  245. export const useOptions = (
  246. contextBlock?: ContextBlockType,
  247. queryBlock?: QueryBlockType,
  248. historyBlock?: HistoryBlockType,
  249. variableBlock?: VariableBlockType,
  250. externalToolBlockType?: ExternalToolBlockType,
  251. workflowVariableBlockType?: WorkflowVariableBlockType,
  252. queryString?: string,
  253. ) => {
  254. const promptOptions = usePromptOptions(contextBlock, queryBlock, historyBlock)
  255. const variableOptions = useVariableOptions(variableBlock, queryString)
  256. const externalToolOptions = useExternalToolOptions(externalToolBlockType, queryString)
  257. const workflowVariableOptions = useMemo(() => {
  258. if (!workflowVariableBlockType?.show)
  259. return []
  260. return workflowVariableBlockType.variables || []
  261. }, [workflowVariableBlockType])
  262. return useMemo(() => {
  263. return {
  264. workflowVariableOptions,
  265. allFlattenOptions: [...promptOptions, ...variableOptions, ...externalToolOptions],
  266. }
  267. }, [promptOptions, variableOptions, externalToolOptions, workflowVariableOptions])
  268. }