|
'use client' |
|
|
|
import type { FC } from 'react' |
|
import { useEffect } from 'react' |
|
import type { |
|
EditorState, |
|
} from 'lexical' |
|
import { |
|
$getRoot, |
|
TextNode, |
|
} from 'lexical' |
|
import { CodeNode } from '@lexical/code' |
|
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 { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin' |
|
|
|
import Placeholder from './plugins/placeholder' |
|
import ComponentPickerBlock from './plugins/component-picker-block' |
|
import { |
|
ContextBlock, |
|
ContextBlockNode, |
|
ContextBlockReplacementBlock, |
|
} from './plugins/context-block' |
|
import { |
|
QueryBlock, |
|
QueryBlockNode, |
|
QueryBlockReplacementBlock, |
|
} from './plugins/query-block' |
|
import { |
|
HistoryBlock, |
|
HistoryBlockNode, |
|
HistoryBlockReplacementBlock, |
|
} from './plugins/history-block' |
|
import { |
|
WorkflowVariableBlock, |
|
WorkflowVariableBlockNode, |
|
WorkflowVariableBlockReplacementBlock, |
|
} from './plugins/workflow-variable-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-or-focus-block' |
|
import UpdateBlock from './plugins/update-block' |
|
import { textToEditorState } from './utils' |
|
import type { |
|
ContextBlockType, |
|
ExternalToolBlockType, |
|
HistoryBlockType, |
|
QueryBlockType, |
|
VariableBlockType, |
|
WorkflowVariableBlockType, |
|
} from './types' |
|
import { |
|
UPDATE_DATASETS_EVENT_EMITTER, |
|
UPDATE_HISTORY_EVENT_EMITTER, |
|
} from './constants' |
|
import { useEventEmitterContextContext } from '@/context/event-emitter' |
|
import cn from '@/utils/classnames' |
|
|
|
export type PromptEditorProps = { |
|
instanceId?: string |
|
compact?: boolean |
|
className?: string |
|
placeholder?: string |
|
placeholderClassName?: string |
|
style?: React.CSSProperties |
|
value?: string |
|
editable?: boolean |
|
onChange?: (text: string) => void |
|
onBlur?: () => void |
|
onFocus?: () => void |
|
contextBlock?: ContextBlockType |
|
queryBlock?: QueryBlockType |
|
historyBlock?: HistoryBlockType |
|
variableBlock?: VariableBlockType |
|
externalToolBlock?: ExternalToolBlockType |
|
workflowVariableBlock?: WorkflowVariableBlockType |
|
isSupportFileVar?: boolean |
|
} |
|
|
|
const PromptEditor: FC<PromptEditorProps> = ({ |
|
instanceId, |
|
compact, |
|
className, |
|
placeholder, |
|
placeholderClassName, |
|
style, |
|
value, |
|
editable = true, |
|
onChange, |
|
onBlur, |
|
onFocus, |
|
contextBlock, |
|
queryBlock, |
|
historyBlock, |
|
variableBlock, |
|
externalToolBlock, |
|
workflowVariableBlock, |
|
isSupportFileVar, |
|
}) => { |
|
const { eventEmitter } = useEventEmitterContextContext() |
|
const initialConfig = { |
|
namespace: 'prompt-editor', |
|
nodes: [ |
|
CodeNode, |
|
CustomTextNode, |
|
{ |
|
replace: TextNode, |
|
with: (node: TextNode) => new CustomTextNode(node.__text), |
|
}, |
|
ContextBlockNode, |
|
HistoryBlockNode, |
|
QueryBlockNode, |
|
WorkflowVariableBlockNode, |
|
VariableValueBlockNode, |
|
], |
|
editorState: textToEditorState(value || ''), |
|
onError: (error: Error) => { |
|
throw error |
|
}, |
|
} |
|
|
|
const handleEditorChange = (editorState: EditorState) => { |
|
const text = editorState.read(() => { |
|
return $getRoot().getChildren().map(p => p.getTextContent()).join('\n') |
|
}) |
|
if (onChange) |
|
onChange(text) |
|
} |
|
|
|
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 min-h-5'> |
|
<RichTextPlugin |
|
contentEditable={<ContentEditable className={`${className} outline-none ${compact ? 'leading-5 text-[13px]' : 'leading-6 text-sm'} text-gray-700`} style={style || {}} />} |
|
placeholder={<Placeholder value={placeholder} className={cn('truncate', placeholderClassName)} compact={compact} />} |
|
ErrorBoundary={LexicalErrorBoundary} |
|
/> |
|
<ComponentPickerBlock |
|
triggerString='/' |
|
contextBlock={contextBlock} |
|
historyBlock={historyBlock} |
|
queryBlock={queryBlock} |
|
variableBlock={variableBlock} |
|
externalToolBlock={externalToolBlock} |
|
workflowVariableBlock={workflowVariableBlock} |
|
isSupportFileVar={isSupportFileVar} |
|
/> |
|
<ComponentPickerBlock |
|
triggerString='{' |
|
contextBlock={contextBlock} |
|
historyBlock={historyBlock} |
|
queryBlock={queryBlock} |
|
variableBlock={variableBlock} |
|
externalToolBlock={externalToolBlock} |
|
workflowVariableBlock={workflowVariableBlock} |
|
isSupportFileVar={isSupportFileVar} |
|
/> |
|
{ |
|
contextBlock?.show && ( |
|
<> |
|
<ContextBlock {...contextBlock} /> |
|
<ContextBlockReplacementBlock {...contextBlock} /> |
|
</> |
|
) |
|
} |
|
{ |
|
queryBlock?.show && ( |
|
<> |
|
<QueryBlock {...queryBlock} /> |
|
<QueryBlockReplacementBlock /> |
|
</> |
|
) |
|
} |
|
{ |
|
historyBlock?.show && ( |
|
<> |
|
<HistoryBlock {...historyBlock} /> |
|
<HistoryBlockReplacementBlock {...historyBlock} /> |
|
</> |
|
) |
|
} |
|
{ |
|
(variableBlock?.show || externalToolBlock?.show) && ( |
|
<> |
|
<VariableBlock /> |
|
<VariableValueBlock /> |
|
</> |
|
) |
|
} |
|
{ |
|
workflowVariableBlock?.show && ( |
|
<> |
|
<WorkflowVariableBlock {...workflowVariableBlock} /> |
|
<WorkflowVariableBlockReplacementBlock {...workflowVariableBlock} /> |
|
</> |
|
) |
|
} |
|
<OnChangePlugin onChange={handleEditorChange} /> |
|
<OnBlurBlock onBlur={onBlur} onFocus={onFocus} /> |
|
<UpdateBlock instanceId={instanceId} /> |
|
<HistoryPlugin /> |
|
{} |
|
</div> |
|
</LexicalComposer> |
|
) |
|
} |
|
|
|
export default PromptEditor |
|
|