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 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { 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 {
  42. CurrentBlock,
  43. CurrentBlockNode,
  44. CurrentBlockReplacementBlock,
  45. } from './plugins/current-block'
  46. import {
  47. ErrorMessageBlock,
  48. ErrorMessageBlockNode,
  49. ErrorMessageBlockReplacementBlock,
  50. } from './plugins/error-message-block'
  51. import {
  52. LastRunBlock,
  53. LastRunBlockNode,
  54. LastRunReplacementBlock,
  55. } from './plugins/last-run-block'
  56. import VariableBlock from './plugins/variable-block'
  57. import VariableValueBlock from './plugins/variable-value-block'
  58. import { VariableValueBlockNode } from './plugins/variable-value-block/node'
  59. import { CustomTextNode } from './plugins/custom-text/node'
  60. import OnBlurBlock from './plugins/on-blur-or-focus-block'
  61. import UpdateBlock from './plugins/update-block'
  62. import { textToEditorState } from './utils'
  63. import type {
  64. ContextBlockType,
  65. CurrentBlockType,
  66. ErrorMessageBlockType,
  67. ExternalToolBlockType,
  68. HistoryBlockType,
  69. LastRunBlockType,
  70. QueryBlockType,
  71. VariableBlockType,
  72. WorkflowVariableBlockType,
  73. } from './types'
  74. import {
  75. UPDATE_DATASETS_EVENT_EMITTER,
  76. UPDATE_HISTORY_EVENT_EMITTER,
  77. } from './constants'
  78. import { useEventEmitterContextContext } from '@/context/event-emitter'
  79. import cn from '@/utils/classnames'
  80. export type PromptEditorProps = {
  81. instanceId?: string
  82. compact?: boolean
  83. wrapperClassName?: string
  84. className?: string
  85. placeholder?: string | React.JSX.Element
  86. placeholderClassName?: string
  87. style?: React.CSSProperties
  88. value?: string
  89. editable?: boolean
  90. onChange?: (text: string) => void
  91. onBlur?: () => void
  92. onFocus?: () => void
  93. contextBlock?: ContextBlockType
  94. queryBlock?: QueryBlockType
  95. historyBlock?: HistoryBlockType
  96. variableBlock?: VariableBlockType
  97. externalToolBlock?: ExternalToolBlockType
  98. workflowVariableBlock?: WorkflowVariableBlockType
  99. currentBlock?: CurrentBlockType
  100. errorMessageBlock?: ErrorMessageBlockType
  101. lastRunBlock?: LastRunBlockType
  102. isSupportFileVar?: boolean
  103. }
  104. const PromptEditor: FC<PromptEditorProps> = ({
  105. instanceId,
  106. compact,
  107. wrapperClassName,
  108. className,
  109. placeholder,
  110. placeholderClassName,
  111. style,
  112. value,
  113. editable = true,
  114. onChange,
  115. onBlur,
  116. onFocus,
  117. contextBlock,
  118. queryBlock,
  119. historyBlock,
  120. variableBlock,
  121. externalToolBlock,
  122. workflowVariableBlock,
  123. currentBlock,
  124. errorMessageBlock,
  125. lastRunBlock,
  126. isSupportFileVar,
  127. }) => {
  128. const { eventEmitter } = useEventEmitterContextContext()
  129. const initialConfig = {
  130. namespace: 'prompt-editor',
  131. nodes: [
  132. CodeNode,
  133. CustomTextNode,
  134. {
  135. replace: TextNode,
  136. with: (node: TextNode) => new CustomTextNode(node.__text),
  137. },
  138. ContextBlockNode,
  139. HistoryBlockNode,
  140. QueryBlockNode,
  141. WorkflowVariableBlockNode,
  142. VariableValueBlockNode,
  143. CurrentBlockNode,
  144. ErrorMessageBlockNode,
  145. LastRunBlockNode, // LastRunBlockNode is used for error message block replacement
  146. ],
  147. editorState: textToEditorState(value || ''),
  148. onError: (error: Error) => {
  149. throw error
  150. },
  151. }
  152. const handleEditorChange = (editorState: EditorState) => {
  153. const text = editorState.read(() => {
  154. return $getRoot().getChildren().map(p => p.getTextContent()).join('\n')
  155. })
  156. if (onChange)
  157. onChange(text)
  158. }
  159. useEffect(() => {
  160. eventEmitter?.emit({
  161. type: UPDATE_DATASETS_EVENT_EMITTER,
  162. payload: contextBlock?.datasets,
  163. } as any)
  164. }, [eventEmitter, contextBlock?.datasets])
  165. useEffect(() => {
  166. eventEmitter?.emit({
  167. type: UPDATE_HISTORY_EVENT_EMITTER,
  168. payload: historyBlock?.history,
  169. } as any)
  170. }, [eventEmitter, historyBlock?.history])
  171. return (
  172. <LexicalComposer initialConfig={{ ...initialConfig, editable }}>
  173. <div className={cn('relative', wrapperClassName)}>
  174. <RichTextPlugin
  175. contentEditable={
  176. <ContentEditable
  177. className={cn(
  178. 'text-text-secondary outline-none',
  179. compact ? 'text-[13px] leading-5' : 'text-sm leading-6',
  180. className,
  181. )}
  182. style={style || {}}
  183. />
  184. }
  185. placeholder={
  186. <Placeholder
  187. value={placeholder}
  188. className={cn('truncate', placeholderClassName)}
  189. compact={compact}
  190. />
  191. }
  192. ErrorBoundary={LexicalErrorBoundary}
  193. />
  194. <ComponentPickerBlock
  195. triggerString='/'
  196. contextBlock={contextBlock}
  197. historyBlock={historyBlock}
  198. queryBlock={queryBlock}
  199. variableBlock={variableBlock}
  200. externalToolBlock={externalToolBlock}
  201. workflowVariableBlock={workflowVariableBlock}
  202. currentBlock={currentBlock}
  203. errorMessageBlock={errorMessageBlock}
  204. lastRunBlock={lastRunBlock}
  205. isSupportFileVar={isSupportFileVar}
  206. />
  207. <ComponentPickerBlock
  208. triggerString='{'
  209. contextBlock={contextBlock}
  210. historyBlock={historyBlock}
  211. queryBlock={queryBlock}
  212. variableBlock={variableBlock}
  213. externalToolBlock={externalToolBlock}
  214. workflowVariableBlock={workflowVariableBlock}
  215. currentBlock={currentBlock}
  216. errorMessageBlock={errorMessageBlock}
  217. lastRunBlock={lastRunBlock}
  218. isSupportFileVar={isSupportFileVar}
  219. />
  220. {
  221. contextBlock?.show && (
  222. <>
  223. <ContextBlock {...contextBlock} />
  224. <ContextBlockReplacementBlock {...contextBlock} />
  225. </>
  226. )
  227. }
  228. {
  229. queryBlock?.show && (
  230. <>
  231. <QueryBlock {...queryBlock} />
  232. <QueryBlockReplacementBlock />
  233. </>
  234. )
  235. }
  236. {
  237. historyBlock?.show && (
  238. <>
  239. <HistoryBlock {...historyBlock} />
  240. <HistoryBlockReplacementBlock {...historyBlock} />
  241. </>
  242. )
  243. }
  244. {
  245. (variableBlock?.show || externalToolBlock?.show) && (
  246. <>
  247. <VariableBlock />
  248. <VariableValueBlock />
  249. </>
  250. )
  251. }
  252. {
  253. workflowVariableBlock?.show && (
  254. <>
  255. <WorkflowVariableBlock {...workflowVariableBlock} />
  256. <WorkflowVariableBlockReplacementBlock {...workflowVariableBlock} />
  257. </>
  258. )
  259. }
  260. {
  261. currentBlock?.show && (
  262. <>
  263. <CurrentBlock {...currentBlock} />
  264. <CurrentBlockReplacementBlock {...currentBlock} />
  265. </>
  266. )
  267. }
  268. {
  269. errorMessageBlock?.show && (
  270. <>
  271. <ErrorMessageBlock {...errorMessageBlock} />
  272. <ErrorMessageBlockReplacementBlock {...errorMessageBlock} />
  273. </>
  274. )
  275. }
  276. {
  277. lastRunBlock?.show && (
  278. <>
  279. <LastRunBlock {...lastRunBlock} />
  280. <LastRunReplacementBlock {...lastRunBlock} />
  281. </>
  282. )
  283. }
  284. {
  285. isSupportFileVar && (
  286. <VariableValueBlock />
  287. )
  288. }
  289. <OnChangePlugin onChange={handleEditorChange} />
  290. <OnBlurBlock onBlur={onBlur} onFocus={onFocus} />
  291. <UpdateBlock instanceId={instanceId} />
  292. <HistoryPlugin />
  293. {/* <TreeView /> */}
  294. </div>
  295. </LexicalComposer>
  296. )
  297. }
  298. export default PromptEditor