index.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  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. className?: string
  66. placeholder?: string
  67. placeholderClassName?: string
  68. style?: React.CSSProperties
  69. value?: string
  70. editable?: boolean
  71. onChange?: (text: string) => void
  72. onBlur?: () => void
  73. onFocus?: () => void
  74. contextBlock?: ContextBlockType
  75. queryBlock?: QueryBlockType
  76. historyBlock?: HistoryBlockType
  77. variableBlock?: VariableBlockType
  78. externalToolBlock?: ExternalToolBlockType
  79. workflowVariableBlock?: WorkflowVariableBlockType
  80. isSupportFileVar?: boolean
  81. }
  82. const PromptEditor: FC<PromptEditorProps> = ({
  83. instanceId,
  84. compact,
  85. className,
  86. placeholder,
  87. placeholderClassName,
  88. style,
  89. value,
  90. editable = true,
  91. onChange,
  92. onBlur,
  93. onFocus,
  94. contextBlock,
  95. queryBlock,
  96. historyBlock,
  97. variableBlock,
  98. externalToolBlock,
  99. workflowVariableBlock,
  100. isSupportFileVar,
  101. }) => {
  102. const { eventEmitter } = useEventEmitterContextContext()
  103. const initialConfig = {
  104. namespace: 'prompt-editor',
  105. nodes: [
  106. CodeNode,
  107. CustomTextNode,
  108. {
  109. replace: TextNode,
  110. with: (node: TextNode) => new CustomTextNode(node.__text),
  111. },
  112. ContextBlockNode,
  113. HistoryBlockNode,
  114. QueryBlockNode,
  115. WorkflowVariableBlockNode,
  116. VariableValueBlockNode,
  117. ],
  118. editorState: textToEditorState(value || ''),
  119. onError: (error: Error) => {
  120. throw error
  121. },
  122. }
  123. const handleEditorChange = (editorState: EditorState) => {
  124. const text = editorState.read(() => {
  125. return $getRoot().getChildren().map(p => p.getTextContent()).join('\n')
  126. })
  127. if (onChange)
  128. onChange(text)
  129. }
  130. useEffect(() => {
  131. eventEmitter?.emit({
  132. type: UPDATE_DATASETS_EVENT_EMITTER,
  133. payload: contextBlock?.datasets,
  134. } as any)
  135. }, [eventEmitter, contextBlock?.datasets])
  136. useEffect(() => {
  137. eventEmitter?.emit({
  138. type: UPDATE_HISTORY_EVENT_EMITTER,
  139. payload: historyBlock?.history,
  140. } as any)
  141. }, [eventEmitter, historyBlock?.history])
  142. return (
  143. <LexicalComposer initialConfig={{ ...initialConfig, editable }}>
  144. <div className='relative min-h-5'>
  145. <RichTextPlugin
  146. contentEditable={<ContentEditable className={`${className} outline-none ${compact ? 'leading-5 text-[13px]' : 'leading-6 text-sm'} text-gray-700`} style={style || {}} />}
  147. placeholder={<Placeholder value={placeholder} className={cn('truncate', placeholderClassName)} compact={compact} />}
  148. ErrorBoundary={LexicalErrorBoundary}
  149. />
  150. <ComponentPickerBlock
  151. triggerString='/'
  152. contextBlock={contextBlock}
  153. historyBlock={historyBlock}
  154. queryBlock={queryBlock}
  155. variableBlock={variableBlock}
  156. externalToolBlock={externalToolBlock}
  157. workflowVariableBlock={workflowVariableBlock}
  158. isSupportFileVar={isSupportFileVar}
  159. />
  160. <ComponentPickerBlock
  161. triggerString='{'
  162. contextBlock={contextBlock}
  163. historyBlock={historyBlock}
  164. queryBlock={queryBlock}
  165. variableBlock={variableBlock}
  166. externalToolBlock={externalToolBlock}
  167. workflowVariableBlock={workflowVariableBlock}
  168. isSupportFileVar={isSupportFileVar}
  169. />
  170. {
  171. contextBlock?.show && (
  172. <>
  173. <ContextBlock {...contextBlock} />
  174. <ContextBlockReplacementBlock {...contextBlock} />
  175. </>
  176. )
  177. }
  178. {
  179. queryBlock?.show && (
  180. <>
  181. <QueryBlock {...queryBlock} />
  182. <QueryBlockReplacementBlock />
  183. </>
  184. )
  185. }
  186. {
  187. historyBlock?.show && (
  188. <>
  189. <HistoryBlock {...historyBlock} />
  190. <HistoryBlockReplacementBlock {...historyBlock} />
  191. </>
  192. )
  193. }
  194. {
  195. (variableBlock?.show || externalToolBlock?.show) && (
  196. <>
  197. <VariableBlock />
  198. <VariableValueBlock />
  199. </>
  200. )
  201. }
  202. {
  203. workflowVariableBlock?.show && (
  204. <>
  205. <WorkflowVariableBlock {...workflowVariableBlock} />
  206. <WorkflowVariableBlockReplacementBlock {...workflowVariableBlock} />
  207. </>
  208. )
  209. }
  210. <OnChangePlugin onChange={handleEditorChange} />
  211. <OnBlurBlock onBlur={onBlur} onFocus={onFocus} />
  212. <UpdateBlock instanceId={instanceId} />
  213. <HistoryPlugin />
  214. {/* <TreeView /> */}
  215. </div>
  216. </LexicalComposer>
  217. )
  218. }
  219. export default PromptEditor