fix prompt editor

This commit is contained in:
StyleZhang 2024-04-01 19:03:08 +08:00
parent e08d871837
commit 45d5d259a4
11 changed files with 83 additions and 15 deletions

View File

@ -166,6 +166,7 @@ const Prompt: FC<ISimplePromptInput> = ({
>
<PromptEditor
className='min-h-[210px]'
compact
value={promptTemplate}
contextBlock={{
show: false,

View File

@ -9,7 +9,10 @@ import {
$getRoot,
TextNode,
} from 'lexical'
import { CodeNode } from '@lexical/code'
import {
CodeHighlightNode,
CodeNode,
} from '@lexical/code'
import { LexicalComposer } from '@lexical/react/LexicalComposer'
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'
import { ContentEditable } from '@lexical/react/LexicalContentEditable'
@ -44,6 +47,7 @@ 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 CodeHighlightBlock from './plugins/code-highlight-block'
import { textToEditorState } from './utils'
import type {
ContextBlockType,
@ -61,6 +65,7 @@ import { useEventEmitterContextContext } from '@/context/event-emitter'
export type PromptEditorProps = {
instanceId?: string
compact?: boolean
className?: string
placeholder?: string
placeholderClassName?: string
@ -80,6 +85,7 @@ export type PromptEditorProps = {
const PromptEditor: FC<PromptEditorProps> = ({
instanceId,
compact,
className,
placeholder,
placeholderClassName,
@ -101,6 +107,7 @@ const PromptEditor: FC<PromptEditorProps> = ({
namespace: 'prompt-editor',
nodes: [
CodeNode,
CodeHighlightNode,
CustomTextNode,
{
replace: TextNode,
@ -141,8 +148,8 @@ const PromptEditor: FC<PromptEditorProps> = ({
<LexicalComposer initialConfig={{ ...initialConfig, editable }}>
<div className='relative'>
<RichTextPlugin
contentEditable={<ContentEditable className={`${className} outline-none text-sm text-gray-700 leading-6`} style={style || {}} />}
placeholder={<Placeholder value={placeholder} className={placeholderClassName} />}
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={placeholderClassName} compact={compact} />}
ErrorBoundary={LexicalErrorBoundary}
/>
<ComponentPickerBlock
@ -207,6 +214,7 @@ const PromptEditor: FC<PromptEditorProps> = ({
<OnChangePlugin onChange={handleEditorChange} />
<OnBlurBlock onBlur={onBlur} onFocus={onFocus} />
<UpdateBlock instanceId={instanceId} />
<CodeHighlightBlock />
{/* <TreeView /> */}
</div>
</LexicalComposer>

View File

@ -0,0 +1,13 @@
import { useEffect } from 'react'
import { registerCodeHighlighting } from '@lexical/code'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
export default function CodeHighlightPlugin() {
const [editor] = useLexicalComposerContext()
useEffect(() => {
return registerCodeHighlighting(editor)
}, [editor])
return null
}

View File

@ -195,7 +195,7 @@ export const useOptions = (
variableOptions,
externalToolOptions,
workflowVariableOptions,
allOptions: [...promptOptions, ...variableOptions, ...externalToolOptions, ...workflowVariableOptions],
allOptions: [...promptOptions, ...variableOptions, ...externalToolOptions],
}
}, [promptOptions, variableOptions, externalToolOptions, workflowVariableOptions])
}

View File

@ -82,11 +82,12 @@ const ComponentPicker = ({
matchingString: string,
) => {
editor.update(() => {
if (nodeToRemove)
if (nodeToRemove && selectedOption?.key)
nodeToRemove.remove()
if (selectedOption?.onSelect)
selectedOption.onSelect(matchingString)
closeMenu()
})
},
@ -104,9 +105,9 @@ const ComponentPicker = ({
anchorElementRef,
{ selectedIndex, selectOptionAndCleanUp, setHighlightedIndex },
) => {
if (anchorElementRef.current && allOptions.length) {
if (anchorElementRef.current && (allOptions.length || workflowVariableOptions.length)) {
return ReactDOM.createPortal(
<div className='mt-[25px] w-[260px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg'>
<div className='w-[260px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg'>
{
!!promptOptions.length && (
<>
@ -196,8 +197,7 @@ const ComponentPicker = ({
<VarReferenceVars
hideSearch
vars={workflowVariableOptions}
onChange={(variables: string[], item: any) => {
selectOptionAndCleanUp(item)
onChange={(variables: string[]) => {
handleSelectWorkflowVariable(variables)
}}
/>

View File

@ -1,20 +1,27 @@
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import cn from 'classnames'
const Placeholder = ({
compact,
value,
className,
}: {
compact?: boolean
value?: string
className?: string
}) => {
const { t } = useTranslation()
return (
<div className={cn(className, 'absolute top-0 left-0 h-full w-full text-sm text-gray-300 select-none pointer-events-none leading-6')}>
<div className={cn(
className,
'absolute top-0 left-0 h-full w-full text-sm text-gray-300 select-none pointer-events-none',
compact ? 'leading-5 text-[13px]' : 'leading-6 text-sm',
)}>
{value || t('common.promptEditor.placeholder')}
</div>
)
}
export default Placeholder
export default memo(Placeholder)

View File

@ -1,8 +1,11 @@
import { $insertNodes } from 'lexical'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { textToEditorState } from '../utils'
import { CustomTextNode } from './custom-text/node'
import { useEventEmitterContextContext } from '@/context/event-emitter'
export const PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER = 'PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER'
export const PROMPT_EDITOR_INSERT_QUICKLY = 'PROMPT_EDITOR_INSERT_QUICKLY'
type UpdateBlockProps = {
instanceId?: string
@ -20,6 +23,16 @@ const UpdateBlock = ({
}
})
eventEmitter?.useSubscription((v: any) => {
if (v.type === PROMPT_EDITOR_INSERT_QUICKLY && v.instanceId === instanceId) {
editor.focus()
editor.update(() => {
const textNode = new CustomTextNode('/')
$insertNodes([textNode])
})
}
})
return null
}

View File

@ -27,7 +27,7 @@ const WorkflowVariableBlockComponent: FC<WorkflowVariableBlockComponentProps> =
return (
<div
className={`
mr-[2px] relative group/wrap flex items-center h-[20px] pl-0.5 pr-[3px] rounded-[5px] border
mx-0.5 relative group/wrap flex items-center h-[18px] pl-0.5 pr-[3px] rounded-[5px] border
${isSelected ? ' border-[#84ADFF] bg-[#F5F8FF]' : ' border-black/5 bg-white'}
`}
ref={ref}

View File

@ -3,6 +3,8 @@ import {
useEffect,
} from 'react'
import {
$getNodeByKey,
$getPreviousSelection,
$insertNodes,
COMMAND_PRIORITY_EDITOR,
createCommand,
@ -39,9 +41,16 @@ const WorkflowVariableBlock = memo(({
editor.registerCommand(
INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND,
(variables: string[]) => {
const contextBlockNode = $createWorkflowVariableBlockNode(variables, getWorkflowNode)
const workflowVariableBlockNode = $createWorkflowVariableBlockNode(variables, getWorkflowNode)
const prevNodeKey = ($getPreviousSelection() as any)?.anchor?.key
$insertNodes([contextBlockNode])
if (prevNodeKey) {
const prevNode = $getNodeByKey(prevNodeKey)
prevNode?.remove()
}
$insertNodes([workflowVariableBlockNode])
if (onInsert)
onInsert()

View File

@ -15,8 +15,11 @@ import { Clipboard, ClipboardCheck } from '@/app/components/base/icons/src/vende
import s from '@/app/components/app/configuration/config-prompt/style.module.css'
import { Trash03 } from '@/app/components/base/icons/src/vender/line/general'
import TooltipPlus from '@/app/components/base/tooltip-plus'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { PROMPT_EDITOR_INSERT_QUICKLY } from '@/app/components/base/prompt-editor/plugins/update-block'
type Props = {
instanceId?: string
title: string | JSX.Element
value: string
onChange: (value: string) => void
@ -36,6 +39,7 @@ type Props = {
}
const Editor: FC<Props> = ({
instanceId,
title,
value,
onChange,
@ -51,6 +55,7 @@ const Editor: FC<Props> = ({
}) => {
const { t } = useTranslation()
const { getNode } = useWorkflow()
const { eventEmitter } = useEventEmitterContextContext()
const isShowHistory = !isChatModel && isChatApp
const isShowQuery = isShowHistory
@ -75,6 +80,11 @@ const Editor: FC<Props> = ({
setFalse: setBlur,
}] = useBoolean(false)
const handleInsertVariable = () => {
setFocus()
eventEmitter?.emit({ type: PROMPT_EDITOR_INSERT_QUICKLY, instanceId } as any)
}
return (
<div className={cn(wrapClassName)}>
<div ref={ref} className={cn(isFocus ? s.gradientBorder : 'bg-gray-100', isExpand && 'h-full', '!rounded-[9px] p-0.5')}>
@ -114,7 +124,10 @@ const Editor: FC<Props> = ({
<TooltipPlus
popupContent={`${t('workflow.common.insertVarTip')}`}
>
<div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{'{x} '}{t('workflow.nodes.common.insertVarTip')}</div>
<div
className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500"
onClick={handleInsertVariable}
>{'{x} '}{t('workflow.nodes.common.insertVarTip')}</div>
</TooltipPlus>)
: <div className='h-[18px]'></div>}
</div>
@ -123,7 +136,9 @@ const Editor: FC<Props> = ({
>
<>
<PromptEditor
instanceId={instanceId}
className={cn('min-h-[84px]')}
compact
style={isExpand ? { height: editorExpandHeight - 5 } : {}}
value={value}
contextBlock={{

View File

@ -115,6 +115,7 @@ const ConfigPrompt: FC<Props> = ({
(payload as PromptItem[]).map((item, index) => {
return (
<Editor
instanceId={`${nodeId}-chat-workflow-llm-prompt-editor-${item.role}-${index}`}
key={index}
title={
<div className='relative left-1 flex items-center'>
@ -160,6 +161,7 @@ const ConfigPrompt: FC<Props> = ({
: (
<div>
<Editor
instanceId={`${nodeId}-chat-workflow-llm-prompt-editor`}
title={<span className='capitalize'>{t(`${i18nPrefix}.prompt`)}</span>}
value={(payload as PromptItem).text}
onChange={handleCompletionPromptChange}