index.tsx 6.3 KB

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