| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 | 'use client'import type { FC } from 'react'import { useEffect } from 'react'import type {  EditorState,} from 'lexical'import {  $getRoot,  TextNode,} from 'lexical'import { LexicalComposer } from '@lexical/react/LexicalComposer'import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'import { ContentEditable } from '@lexical/react/LexicalContentEditable'import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary'import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'// import TreeView from './plugins/tree-view'import Placeholder from './plugins/placeholder'import ComponentPicker from './plugins/component-picker'import VariablePicker from './plugins/variable-picker'import ContextBlock from './plugins/context-block'import { ContextBlockNode } from './plugins/context-block/node'import ContextBlockReplacementBlock from './plugins/context-block-replacement-block'import HistoryBlock from './plugins/history-block'import { HistoryBlockNode } from './plugins/history-block/node'import HistoryBlockReplacementBlock from './plugins/history-block-replacement-block'import QueryBlock from './plugins/query-block'import { QueryBlockNode } from './plugins/query-block/node'import QueryBlockReplacementBlock from './plugins/query-block-replacement-block'import VariableBlock from './plugins/variable-block'import VariableValueBlock from './plugins/variable-value-block'import { VariableValueBlockNode } from './plugins/variable-value-block/node'import { CustomTextNode } from './plugins/custom-text/node'import OnBlurBlock from './plugins/on-blur-block'import { textToEditorState } from './utils'import type { Dataset } from './plugins/context-block'import type { RoleName } from './plugins/history-block'import type { ExternalToolOption, Option } from './plugins/variable-picker'import {  UPDATE_DATASETS_EVENT_EMITTER,  UPDATE_HISTORY_EVENT_EMITTER,} from './constants'import { useEventEmitterContextContext } from '@/context/event-emitter'export type PromptEditorProps = {  className?: string  value?: string  editable?: boolean  onChange?: (text: string) => void  onBlur?: () => void  contextBlock?: {    show?: boolean    selectable?: boolean    datasets: Dataset[]    onInsert?: () => void    onDelete?: () => void    onAddContext: () => void  }  variableBlock?: {    selectable?: boolean    variables: Option[]    externalTools?: ExternalToolOption[]    onAddExternalTool?: () => void  }  historyBlock?: {    show?: boolean    selectable?: boolean    history: RoleName    onInsert?: () => void    onDelete?: () => void    onEditRole: () => void  }  queryBlock?: {    show?: boolean    selectable?: boolean    onInsert?: () => void    onDelete?: () => void  }}const PromptEditor: FC<PromptEditorProps> = ({  className,  value,  editable = true,  onChange,  onBlur,  contextBlock = {    show: true,    selectable: true,    datasets: [],    onAddContext: () => {},    onInsert: () => {},    onDelete: () => {},  },  historyBlock = {    show: true,    selectable: true,    history: {      user: '',      assistant: '',    },    onEditRole: () => {},    onInsert: () => {},    onDelete: () => {},  },  variableBlock = {    variables: [],  },  queryBlock = {    show: true,    selectable: true,    onInsert: () => {},    onDelete: () => {},  },}) => {  const { eventEmitter } = useEventEmitterContextContext()  const initialConfig = {    namespace: 'prompt-editor',    nodes: [      CustomTextNode,      {        replace: TextNode,        with: (node: TextNode) => new CustomTextNode(node.__text),      },      ContextBlockNode,      HistoryBlockNode,      QueryBlockNode,      VariableValueBlockNode,    ],    editorState: value ? textToEditorState(value as string) : null,    onError: (error: Error) => {      throw error    },  }  const handleEditorChange = (editorState: EditorState) => {    const text = editorState.read(() => $getRoot().getTextContent())    if (onChange)      onChange(text.replaceAll('\n\n', '\n'))  }  useEffect(() => {    eventEmitter?.emit({      type: UPDATE_DATASETS_EVENT_EMITTER,      payload: contextBlock.datasets,    } as any)  }, [eventEmitter, contextBlock.datasets])  useEffect(() => {    eventEmitter?.emit({      type: UPDATE_HISTORY_EVENT_EMITTER,      payload: historyBlock.history,    } as any)  }, [eventEmitter, historyBlock.history])  return (    <LexicalComposer initialConfig={{ ...initialConfig, editable }}>      <div className='relative'>        <RichTextPlugin          contentEditable={<ContentEditable className={`${className} outline-none text-sm text-gray-700 leading-6`} />}          placeholder={<Placeholder />}          ErrorBoundary={LexicalErrorBoundary}        />        <ComponentPicker          contextDisabled={!contextBlock.selectable}          contextShow={contextBlock.show}          historyDisabled={!historyBlock.selectable}          historyShow={historyBlock.show}          queryDisabled={!queryBlock.selectable}          queryShow={queryBlock.show}        />        <VariablePicker          items={variableBlock.variables}          externalTools={variableBlock.externalTools}          onAddExternalTool={variableBlock.onAddExternalTool}        />        {          contextBlock.show && (            <>              <ContextBlock                datasets={contextBlock.datasets}                onAddContext={contextBlock.onAddContext}                onInsert={contextBlock.onInsert}                onDelete={contextBlock.onDelete}              />              <ContextBlockReplacementBlock                datasets={contextBlock.datasets}                onAddContext={contextBlock.onAddContext}                onInsert={contextBlock.onInsert}              />            </>          )        }        <VariableBlock />        {          historyBlock.show && (            <>              <HistoryBlock                roleName={historyBlock.history}                onEditRole={historyBlock.onEditRole}                onInsert={historyBlock.onInsert}                onDelete={historyBlock.onDelete}              />              <HistoryBlockReplacementBlock                roleName={historyBlock.history}                onEditRole={historyBlock.onEditRole}                onInsert={historyBlock.onInsert}              />            </>          )        }        {          queryBlock.show && (            <>              <QueryBlock                onInsert={queryBlock.onInsert}                onDelete={queryBlock.onDelete}              />              <QueryBlockReplacementBlock />            </>          )        }        <VariableValueBlock />        <OnChangePlugin onChange={handleEditorChange} />        <OnBlurBlock onBlur={onBlur} />        {/* <TreeView /> */}      </div>    </LexicalComposer>  )}export default PromptEditor
 |