You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.tsx 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. 'use client'
  2. import type { FC } from 'react'
  3. import { useEffect } from 'react'
  4. import type {
  5. EditorState,
  6. } from 'lexical'
  7. import {
  8. $getRoot,
  9. TextNode,
  10. } from 'lexical'
  11. import { CodeNode } from '@lexical/code'
  12. import { LexicalComposer } from '@lexical/react/LexicalComposer'
  13. import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'
  14. import { ContentEditable } from '@lexical/react/LexicalContentEditable'
  15. import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary'
  16. import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'
  17. import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin'
  18. // import TreeView from './plugins/tree-view'
  19. import Placeholder from './plugins/placeholder'
  20. import ComponentPickerBlock from './plugins/component-picker-block'
  21. import {
  22. ContextBlock,
  23. ContextBlockNode,
  24. ContextBlockReplacementBlock,
  25. } from './plugins/context-block'
  26. import {
  27. QueryBlock,
  28. QueryBlockNode,
  29. QueryBlockReplacementBlock,
  30. } from './plugins/query-block'
  31. import {
  32. HistoryBlock,
  33. HistoryBlockNode,
  34. HistoryBlockReplacementBlock,
  35. } from './plugins/history-block'
  36. import {
  37. WorkflowVariableBlock,
  38. WorkflowVariableBlockNode,
  39. WorkflowVariableBlockReplacementBlock,
  40. } from './plugins/workflow-variable-block'
  41. import VariableBlock from './plugins/variable-block'
  42. import VariableValueBlock from './plugins/variable-value-block'
  43. import { VariableValueBlockNode } from './plugins/variable-value-block/node'
  44. import { CustomTextNode } from './plugins/custom-text/node'
  45. import OnBlurBlock from './plugins/on-blur-or-focus-block'
  46. import UpdateBlock from './plugins/update-block'
  47. import { textToEditorState } from './utils'
  48. import type {
  49. ContextBlockType,
  50. ExternalToolBlockType,
  51. HistoryBlockType,
  52. QueryBlockType,
  53. VariableBlockType,
  54. WorkflowVariableBlockType,
  55. } from './types'
  56. import {
  57. UPDATE_DATASETS_EVENT_EMITTER,
  58. UPDATE_HISTORY_EVENT_EMITTER,
  59. } from './constants'
  60. import { useEventEmitterContextContext } from '@/context/event-emitter'
  61. import cn from '@/utils/classnames'
  62. export type PromptEditorProps = {
  63. instanceId?: string
  64. compact?: boolean
  65. wrapperClassName?: string
  66. className?: string
  67. placeholder?: string | JSX.Element
  68. placeholderClassName?: string
  69. style?: React.CSSProperties
  70. value?: string
  71. editable?: boolean
  72. onChange?: (text: string) => void
  73. onBlur?: () => void
  74. onFocus?: () => void
  75. contextBlock?: ContextBlockType
  76. queryBlock?: QueryBlockType
  77. historyBlock?: HistoryBlockType
  78. variableBlock?: VariableBlockType
  79. externalToolBlock?: ExternalToolBlockType
  80. workflowVariableBlock?: WorkflowVariableBlockType
  81. isSupportFileVar?: boolean
  82. }
  83. const PromptEditor: FC<PromptEditorProps> = ({
  84. instanceId,
  85. compact,
  86. wrapperClassName,
  87. className,
  88. placeholder,
  89. placeholderClassName,
  90. style,
  91. value,
  92. editable = true,
  93. onChange,
  94. onBlur,
  95. onFocus,
  96. contextBlock,
  97. queryBlock,
  98. historyBlock,
  99. variableBlock,
  100. externalToolBlock,
  101. workflowVariableBlock,
  102. isSupportFileVar,
  103. }) => {
  104. const { eventEmitter } = useEventEmitterContextContext()
  105. const initialConfig = {
  106. namespace: 'prompt-editor',
  107. nodes: [
  108. CodeNode,
  109. CustomTextNode,
  110. {
  111. replace: TextNode,
  112. with: (node: TextNode) => new CustomTextNode(node.__text),
  113. },
  114. ContextBlockNode,
  115. HistoryBlockNode,
  116. QueryBlockNode,
  117. WorkflowVariableBlockNode,
  118. VariableValueBlockNode,
  119. ],
  120. editorState: textToEditorState(value || ''),
  121. onError: (error: Error) => {
  122. throw error
  123. },
  124. }
  125. const handleEditorChange = (editorState: EditorState) => {
  126. const text = editorState.read(() => {
  127. return $getRoot().getChildren().map(p => p.getTextContent()).join('\n')
  128. })
  129. if (onChange)
  130. onChange(text)
  131. }
  132. useEffect(() => {
  133. eventEmitter?.emit({
  134. type: UPDATE_DATASETS_EVENT_EMITTER,
  135. payload: contextBlock?.datasets,
  136. } as any)
  137. }, [eventEmitter, contextBlock?.datasets])
  138. useEffect(() => {
  139. eventEmitter?.emit({
  140. type: UPDATE_HISTORY_EVENT_EMITTER,
  141. payload: historyBlock?.history,
  142. } as any)
  143. }, [eventEmitter, historyBlock?.history])
  144. return (
  145. <LexicalComposer initialConfig={{ ...initialConfig, editable }}>
  146. <div className={cn('relative', wrapperClassName)}>
  147. <RichTextPlugin
  148. contentEditable={
  149. <ContentEditable
  150. className={cn(
  151. 'text-text-secondary outline-none',
  152. compact ? 'text-[13px] leading-5' : 'text-sm leading-6',
  153. className,
  154. )}
  155. style={style || {}}
  156. />
  157. }
  158. placeholder={
  159. <Placeholder
  160. value={placeholder}
  161. className={cn('truncate', placeholderClassName)}
  162. compact={compact}
  163. />
  164. }
  165. ErrorBoundary={LexicalErrorBoundary}
  166. />
  167. <ComponentPickerBlock
  168. triggerString='/'
  169. contextBlock={contextBlock}
  170. historyBlock={historyBlock}
  171. queryBlock={queryBlock}
  172. variableBlock={variableBlock}
  173. externalToolBlock={externalToolBlock}
  174. workflowVariableBlock={workflowVariableBlock}
  175. isSupportFileVar={isSupportFileVar}
  176. />
  177. <ComponentPickerBlock
  178. triggerString='{'
  179. contextBlock={contextBlock}
  180. historyBlock={historyBlock}
  181. queryBlock={queryBlock}
  182. variableBlock={variableBlock}
  183. externalToolBlock={externalToolBlock}
  184. workflowVariableBlock={workflowVariableBlock}
  185. isSupportFileVar={isSupportFileVar}
  186. />
  187. {
  188. contextBlock?.show && (
  189. <>
  190. <ContextBlock {...contextBlock} />
  191. <ContextBlockReplacementBlock {...contextBlock} />
  192. </>
  193. )
  194. }
  195. {
  196. queryBlock?.show && (
  197. <>
  198. <QueryBlock {...queryBlock} />
  199. <QueryBlockReplacementBlock />
  200. </>
  201. )
  202. }
  203. {
  204. historyBlock?.show && (
  205. <>
  206. <HistoryBlock {...historyBlock} />
  207. <HistoryBlockReplacementBlock {...historyBlock} />
  208. </>
  209. )
  210. }
  211. {
  212. (variableBlock?.show || externalToolBlock?.show) && (
  213. <>
  214. <VariableBlock />
  215. <VariableValueBlock />
  216. </>
  217. )
  218. }
  219. {
  220. workflowVariableBlock?.show && (
  221. <>
  222. <WorkflowVariableBlock {...workflowVariableBlock} />
  223. <WorkflowVariableBlockReplacementBlock {...workflowVariableBlock} />
  224. </>
  225. )
  226. }
  227. <OnChangePlugin onChange={handleEditorChange} />
  228. <OnBlurBlock onBlur={onBlur} onFocus={onFocus} />
  229. <UpdateBlock instanceId={instanceId} />
  230. <HistoryPlugin />
  231. {/* <TreeView /> */}
  232. </div>
  233. </LexicalComposer>
  234. )
  235. }
  236. export default PromptEditor