hooks.tsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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 { PromptOption } from './prompt-option'
  19. import { VariableOption } from './variable-option'
  20. import { File05 } from '@/app/components/base/icons/src/vender/solid/files'
  21. import {
  22. MessageClockCircle,
  23. Tool03,
  24. } from '@/app/components/base/icons/src/vender/solid/general'
  25. import { BracketsX } from '@/app/components/base/icons/src/vender/line/development'
  26. import { UserEdit02 } from '@/app/components/base/icons/src/vender/solid/users'
  27. import { ArrowUpRight } from '@/app/components/base/icons/src/vender/line/arrows'
  28. import AppIcon from '@/app/components/base/app-icon'
  29. export const usePromptOptions = (
  30. contextBlock?: ContextBlockType,
  31. queryBlock?: QueryBlockType,
  32. historyBlock?: HistoryBlockType,
  33. ) => {
  34. const { t } = useTranslation()
  35. const [editor] = useLexicalComposerContext()
  36. return useMemo(() => {
  37. return [
  38. ...contextBlock?.show
  39. ? [
  40. new PromptOption(t('common.promptEditor.context.item.title'), {
  41. icon: <File05 className='w-4 h-4 text-[#6938EF]' />,
  42. onSelect: () => {
  43. if (!contextBlock?.selectable)
  44. return
  45. editor.dispatchCommand(INSERT_CONTEXT_BLOCK_COMMAND, undefined)
  46. },
  47. disabled: !contextBlock?.selectable,
  48. }),
  49. ]
  50. : [],
  51. ...queryBlock?.show
  52. ? [
  53. new PromptOption(t('common.promptEditor.query.item.title'), {
  54. icon: <UserEdit02 className='w-4 h-4 text-[#FD853A]' />,
  55. onSelect: () => {
  56. if (!queryBlock?.selectable)
  57. return
  58. editor.dispatchCommand(INSERT_QUERY_BLOCK_COMMAND, undefined)
  59. },
  60. disabled: !queryBlock?.selectable,
  61. }),
  62. ]
  63. : [],
  64. ...historyBlock?.show
  65. ? [
  66. new PromptOption(t('common.promptEditor.history.item.title'), {
  67. icon: <MessageClockCircle className='w-4 h-4 text-[#DD2590]' />,
  68. onSelect: () => {
  69. if (!historyBlock?.selectable)
  70. return
  71. editor.dispatchCommand(INSERT_HISTORY_BLOCK_COMMAND, undefined)
  72. },
  73. disabled: !historyBlock?.selectable,
  74. }),
  75. ]
  76. : [],
  77. ]
  78. }, [contextBlock, editor, historyBlock, queryBlock, t])
  79. }
  80. export const useVariableOptions = (
  81. variableBlock?: VariableBlockType,
  82. queryString?: string,
  83. ) => {
  84. const { t } = useTranslation()
  85. const [editor] = useLexicalComposerContext()
  86. const options = useMemo(() => {
  87. const baseOptions = (variableBlock?.variables || []).map((item) => {
  88. return new VariableOption(item.value, {
  89. icon: <BracketsX className='w-[14px] h-[14px] text-[#2970FF]' />,
  90. onSelect: () => {
  91. editor.dispatchCommand(INSERT_VARIABLE_VALUE_BLOCK_COMMAND, `{{${item.value}}}`)
  92. },
  93. })
  94. })
  95. if (!queryString)
  96. return baseOptions
  97. const regex = new RegExp(queryString, 'i')
  98. return baseOptions.filter(option => regex.test(option.title) || option.keywords.some(keyword => regex.test(keyword)))
  99. }, [editor, queryString, variableBlock])
  100. const addOption = useMemo(() => {
  101. return new VariableOption(t('common.promptEditor.variable.modal.add'), {
  102. icon: <BracketsX className='mr-2 w-[14px] h-[14px] text-[#2970FF]' />,
  103. onSelect: () => {
  104. editor.update(() => {
  105. const prefixNode = $createCustomTextNode('{{')
  106. const suffixNode = $createCustomTextNode('}}')
  107. $insertNodes([prefixNode, suffixNode])
  108. prefixNode.select()
  109. })
  110. },
  111. })
  112. }, [editor, t])
  113. return useMemo(() => {
  114. return variableBlock?.show ? [...options, addOption] : []
  115. }, [options, addOption, variableBlock?.show])
  116. }
  117. export const useExternalToolOptions = (
  118. externalToolBlockType?: ExternalToolBlockType,
  119. queryString?: string,
  120. ) => {
  121. const { t } = useTranslation()
  122. const [editor] = useLexicalComposerContext()
  123. const options = useMemo(() => {
  124. const baseToolOptions = (externalToolBlockType?.externalTools || []).map((item) => {
  125. return new VariableOption(item.name, {
  126. icon: (
  127. <AppIcon
  128. className='!w-[14px] !h-[14px]'
  129. icon={item.icon}
  130. background={item.icon_background}
  131. />
  132. ),
  133. extraElement: <div className='text-xs text-gray-400'>{item.variableName}</div>,
  134. onSelect: () => {
  135. editor.dispatchCommand(INSERT_VARIABLE_VALUE_BLOCK_COMMAND, `{{${item.variableName}}}`)
  136. },
  137. })
  138. })
  139. if (!queryString)
  140. return baseToolOptions
  141. const regex = new RegExp(queryString, 'i')
  142. return baseToolOptions.filter(option => regex.test(option.title) || option.keywords.some(keyword => regex.test(keyword)))
  143. }, [editor, queryString, externalToolBlockType])
  144. const addOption = useMemo(() => {
  145. return new VariableOption(t('common.promptEditor.variable.modal.addTool'), {
  146. icon: <Tool03 className='mr-2 w-[14px] h-[14px] text-[#444CE7]' />,
  147. extraElement: <ArrowUpRight className='w-3 h-3 text-gray-400' />,
  148. onSelect: () => {
  149. if (externalToolBlockType?.onAddExternalTool)
  150. externalToolBlockType.onAddExternalTool()
  151. },
  152. })
  153. }, [externalToolBlockType, t])
  154. return useMemo(() => {
  155. return externalToolBlockType?.show ? [...options, addOption] : []
  156. }, [options, addOption, externalToolBlockType?.show])
  157. }
  158. export const useOptions = (
  159. contextBlock?: ContextBlockType,
  160. queryBlock?: QueryBlockType,
  161. historyBlock?: HistoryBlockType,
  162. variableBlock?: VariableBlockType,
  163. externalToolBlockType?: ExternalToolBlockType,
  164. workflowVariableBlockType?: WorkflowVariableBlockType,
  165. queryString?: string,
  166. ) => {
  167. const promptOptions = usePromptOptions(contextBlock, queryBlock, historyBlock)
  168. const variableOptions = useVariableOptions(variableBlock, queryString)
  169. const externalToolOptions = useExternalToolOptions(externalToolBlockType, queryString)
  170. const workflowVariableOptions = useMemo(() => {
  171. if (!workflowVariableBlockType?.show)
  172. return []
  173. return workflowVariableBlockType.variables || []
  174. }, [workflowVariableBlockType])
  175. return useMemo(() => {
  176. return {
  177. promptOptions,
  178. variableOptions,
  179. externalToolOptions,
  180. workflowVariableOptions,
  181. allOptions: [...promptOptions, ...variableOptions, ...externalToolOptions],
  182. }
  183. }, [promptOptions, variableOptions, externalToolOptions, workflowVariableOptions])
  184. }