mirror of https://github.com/langgenius/dify.git
editor
This commit is contained in:
parent
82a82fff35
commit
4235baf493
|
|
@ -26,6 +26,8 @@ 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 WorkflowVariableBlock from './plugins/workflow-variable-block'
|
||||
import { WorkflowVariableBlockNode } from './plugins/workflow-variable-block/node'
|
||||
import QueryBlockReplacementBlock from './plugins/query-block-replacement-block'
|
||||
import VariableBlock from './plugins/variable-block'
|
||||
import VariableValueBlock from './plugins/variable-value-block'
|
||||
|
|
@ -42,6 +44,10 @@ import {
|
|||
UPDATE_HISTORY_EVENT_EMITTER,
|
||||
} from './constants'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import type {
|
||||
Node,
|
||||
NodeOutPutVar,
|
||||
} from '@/app/components/workflow/types'
|
||||
|
||||
export type PromptEditorProps = {
|
||||
className?: string
|
||||
|
|
@ -81,6 +87,14 @@ export type PromptEditorProps = {
|
|||
onInsert?: () => void
|
||||
onDelete?: () => void
|
||||
}
|
||||
workflowVariableBlock?: {
|
||||
show?: boolean
|
||||
selectable?: boolean
|
||||
variables: NodeOutPutVar[]
|
||||
getWorkflowNode: (nodeId: string) => Node | undefined
|
||||
onInsert?: () => void
|
||||
onDelete?: () => void
|
||||
}
|
||||
}
|
||||
|
||||
const PromptEditor: FC<PromptEditorProps> = ({
|
||||
|
|
@ -121,6 +135,14 @@ const PromptEditor: FC<PromptEditorProps> = ({
|
|||
onInsert: () => { },
|
||||
onDelete: () => { },
|
||||
},
|
||||
workflowVariableBlock = {
|
||||
show: true,
|
||||
selectable: true,
|
||||
variables: [],
|
||||
getWorkflowNode: () => {},
|
||||
onInsert: () => { },
|
||||
onDelete: () => { },
|
||||
},
|
||||
}) => {
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const initialConfig = {
|
||||
|
|
@ -134,6 +156,7 @@ const PromptEditor: FC<PromptEditorProps> = ({
|
|||
ContextBlockNode,
|
||||
HistoryBlockNode,
|
||||
QueryBlockNode,
|
||||
WorkflowVariableBlockNode,
|
||||
VariableValueBlockNode,
|
||||
],
|
||||
editorState: value ? textToEditorState(value as string) : null,
|
||||
|
|
@ -177,6 +200,8 @@ const PromptEditor: FC<PromptEditorProps> = ({
|
|||
queryDisabled={!queryBlock.selectable}
|
||||
queryShow={queryBlock.show}
|
||||
outToolDisabled={outToolDisabled}
|
||||
workflowVariableShow={workflowVariableBlock.show}
|
||||
workflowVariables={workflowVariableBlock.variables}
|
||||
/>
|
||||
<VariablePicker
|
||||
items={variableBlock.variables}
|
||||
|
|
@ -232,6 +257,17 @@ const PromptEditor: FC<PromptEditorProps> = ({
|
|||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
workflowVariableBlock.show && (
|
||||
<>
|
||||
<WorkflowVariableBlock
|
||||
getWorkflowNode={workflowVariableBlock.getWorkflowNode as any}
|
||||
onInsert={workflowVariableBlock.onInsert}
|
||||
onDelete={workflowVariableBlock.onDelete}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
<VariableValueBlock />
|
||||
<OnChangePlugin onChange={handleEditorChange} />
|
||||
<OnBlurBlock onBlur={onBlur} onFocus={onFocus} />
|
||||
|
|
|
|||
|
|
@ -13,10 +13,13 @@ import { INSERT_CONTEXT_BLOCK_COMMAND } from './context-block'
|
|||
import { INSERT_VARIABLE_BLOCK_COMMAND } from './variable-block'
|
||||
import { INSERT_HISTORY_BLOCK_COMMAND } from './history-block'
|
||||
import { INSERT_QUERY_BLOCK_COMMAND } from './query-block'
|
||||
import { INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND } from './workflow-variable-block'
|
||||
import { File05 } from '@/app/components/base/icons/src/vender/solid/files'
|
||||
import { Variable } from '@/app/components/base/icons/src/vender/line/development'
|
||||
import { MessageClockCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import { UserEdit02 } from '@/app/components/base/icons/src/vender/solid/users'
|
||||
import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars'
|
||||
import type { NodeOutPutVar } from '@/app/components/workflow/types'
|
||||
|
||||
class ComponentPickerOption extends MenuOption {
|
||||
title: string
|
||||
|
|
@ -97,6 +100,8 @@ type ComponentPickerProps = {
|
|||
historyShow?: boolean
|
||||
queryShow?: boolean
|
||||
outToolDisabled?: boolean
|
||||
workflowVariableShow?: boolean
|
||||
workflowVariables?: NodeOutPutVar[]
|
||||
}
|
||||
const ComponentPicker: FC<ComponentPickerProps> = ({
|
||||
contextDisabled,
|
||||
|
|
@ -106,6 +111,8 @@ const ComponentPicker: FC<ComponentPickerProps> = ({
|
|||
historyShow,
|
||||
queryShow,
|
||||
outToolDisabled,
|
||||
workflowVariableShow,
|
||||
workflowVariables,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [editor] = useLexicalComposerContext()
|
||||
|
|
@ -184,6 +191,10 @@ const ComponentPicker: FC<ComponentPickerProps> = ({
|
|||
[editor],
|
||||
)
|
||||
|
||||
const handleSelectWorkflowVariable = useCallback((variables: string[]) => {
|
||||
editor.dispatchCommand(INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND, variables)
|
||||
}, [editor])
|
||||
|
||||
return (
|
||||
<LexicalTypeaheadMenuPlugin
|
||||
options={options}
|
||||
|
|
@ -215,6 +226,14 @@ const ComponentPicker: FC<ComponentPickerProps> = ({
|
|||
option={option}
|
||||
/>
|
||||
))}
|
||||
{
|
||||
workflowVariableShow && !!workflowVariables?.length && (
|
||||
<VarReferenceVars
|
||||
vars={workflowVariables}
|
||||
onChange={handleSelectWorkflowVariable}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>,
|
||||
anchorElementRef.current,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import type { FC } from 'react'
|
||||
import { useSelectOrDelete } from '../../hooks'
|
||||
import { DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND } from './index'
|
||||
import type { Node } from '@/app/components/workflow/types'
|
||||
|
||||
type WorkflowVariableBlockComponentProps = {
|
||||
nodeKey: string
|
||||
variables: string[]
|
||||
getWorkflowNode: (nodeId: string) => Node
|
||||
}
|
||||
|
||||
const WorkflowVariableBlockComponent: FC<WorkflowVariableBlockComponentProps> = ({
|
||||
nodeKey,
|
||||
variables,
|
||||
getWorkflowNode,
|
||||
}) => {
|
||||
const [ref, isSelected] = useSelectOrDelete(nodeKey, DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND)
|
||||
const node = getWorkflowNode(variables[0])
|
||||
const variablesLength = variables.length
|
||||
const lastVariable = variables[variablesLength - 1]
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
inline-flex items-center pl-1 pr-0.5 h-6 bg-white border-[0.5px] border-black/5 rounded-[5px]
|
||||
hover:border hover:border-primary-300 hover:bg-primary-25
|
||||
${isSelected && '!border !border-primary-500 !bg-primary-50'}
|
||||
`}
|
||||
ref={ref}
|
||||
>
|
||||
<div>{node.data.title}</div>
|
||||
/
|
||||
<div>{lastVariable}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkflowVariableBlockComponent
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
import type { FC } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import {
|
||||
$insertNodes,
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
createCommand,
|
||||
} from 'lexical'
|
||||
import { mergeRegister } from '@lexical/utils'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
import {
|
||||
$createWorkflowVariableBlockNode,
|
||||
WorkflowVariableBlockNode,
|
||||
} from './node'
|
||||
import type { Node } from '@/app/components/workflow/types'
|
||||
|
||||
export const INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND = createCommand('INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND')
|
||||
export const DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND = createCommand('DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND')
|
||||
|
||||
export type WorkflowVariableBlockProps = {
|
||||
getWorkflowNode: (nodeId: string) => Node
|
||||
onInsert?: () => void
|
||||
onDelete?: () => void
|
||||
}
|
||||
const WorkflowVariableBlock: FC<WorkflowVariableBlockProps> = ({
|
||||
getWorkflowNode,
|
||||
onInsert,
|
||||
onDelete,
|
||||
}) => {
|
||||
const [editor] = useLexicalComposerContext()
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor.hasNodes([WorkflowVariableBlockNode]))
|
||||
throw new Error('WorkflowVariableBlockPlugin: WorkflowVariableBlock not registered on editor')
|
||||
|
||||
return mergeRegister(
|
||||
editor.registerCommand(
|
||||
INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND,
|
||||
(variables: string[]) => {
|
||||
const contextBlockNode = $createWorkflowVariableBlockNode(variables, getWorkflowNode)
|
||||
|
||||
$insertNodes([contextBlockNode])
|
||||
if (onInsert)
|
||||
onInsert()
|
||||
|
||||
return true
|
||||
},
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
),
|
||||
editor.registerCommand(
|
||||
DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND,
|
||||
() => {
|
||||
if (onDelete)
|
||||
onDelete()
|
||||
|
||||
return true
|
||||
},
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
),
|
||||
)
|
||||
}, [editor, onInsert, onDelete, getWorkflowNode])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export default WorkflowVariableBlock
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
import type { LexicalNode, NodeKey, SerializedLexicalNode } from 'lexical'
|
||||
import { DecoratorNode } from 'lexical'
|
||||
import WorkflowVariableBlockComponent from './component'
|
||||
import type { Node } from '@/app/components/workflow/types'
|
||||
|
||||
export type SerializedNode = SerializedLexicalNode & {
|
||||
variables: string[]
|
||||
getWorkflowNode: (nodeId: string) => Node
|
||||
}
|
||||
|
||||
export class WorkflowVariableBlockNode extends DecoratorNode<JSX.Element> {
|
||||
__variables: string[]
|
||||
__getWorkflowNode: (nodeId: string) => Node
|
||||
|
||||
static getType(): string {
|
||||
return 'workflow-variable-block'
|
||||
}
|
||||
|
||||
static clone(node: WorkflowVariableBlockNode): WorkflowVariableBlockNode {
|
||||
return new WorkflowVariableBlockNode(node.__variables, node.__getWorkflowNode)
|
||||
}
|
||||
|
||||
isInline(): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
constructor(variables: string[], getWorkflowNode: (nodeId: string) => Node, key?: NodeKey) {
|
||||
super(key)
|
||||
|
||||
this.__variables = variables
|
||||
this.__getWorkflowNode = getWorkflowNode
|
||||
}
|
||||
|
||||
createDOM(): HTMLElement {
|
||||
const div = document.createElement('div')
|
||||
div.classList.add('inline-flex', 'items-center', 'align-middle')
|
||||
return div
|
||||
}
|
||||
|
||||
updateDOM(): false {
|
||||
return false
|
||||
}
|
||||
|
||||
decorate(): JSX.Element {
|
||||
return (
|
||||
<WorkflowVariableBlockComponent
|
||||
nodeKey={this.getKey()}
|
||||
variables={this.__variables}
|
||||
getWorkflowNode={this.__getWorkflowNode}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
static importJSON(serializedNode: SerializedNode): WorkflowVariableBlockNode {
|
||||
const node = $createWorkflowVariableBlockNode(serializedNode.variables, serializedNode.getWorkflowNode)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
exportJSON(): SerializedNode {
|
||||
return {
|
||||
type: 'workflow-variable-block',
|
||||
version: 1,
|
||||
variables: this.__variables,
|
||||
getWorkflowNode: this.__getWorkflowNode,
|
||||
}
|
||||
}
|
||||
|
||||
getTextContent(): string {
|
||||
return `{{#${this.__variables.join('.')}#}}`
|
||||
}
|
||||
}
|
||||
export function $createWorkflowVariableBlockNode(variables: string[], getWorkflowNodeName: (nodeId: string) => Node): WorkflowVariableBlockNode {
|
||||
return new WorkflowVariableBlockNode(variables, getWorkflowNodeName)
|
||||
}
|
||||
|
||||
export function $isWorkflowVariableBlockNode(
|
||||
node: WorkflowVariableBlockNode | LexicalNode | null | undefined,
|
||||
): node is WorkflowVariableBlockNode {
|
||||
return node instanceof WorkflowVariableBlockNode
|
||||
}
|
||||
|
|
@ -342,6 +342,12 @@ export const useWorkflow = () => {
|
|||
setViewport(viewport)
|
||||
}, [store, reactflow])
|
||||
|
||||
const getNode = useCallback((nodeId: string) => {
|
||||
const { getNodes } = store.getState()
|
||||
|
||||
return getNodes().find(node => node.id === nodeId)
|
||||
}, [store])
|
||||
|
||||
return {
|
||||
handleLayout,
|
||||
getTreeLeafNodes,
|
||||
|
|
@ -355,6 +361,7 @@ export const useWorkflow = () => {
|
|||
formatTimeFromNow,
|
||||
getValidTreeNodes,
|
||||
renderTreeFromRecord,
|
||||
getNode,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import cn from 'classnames'
|
|||
import copy from 'copy-to-clipboard'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import { useWorkflow } from '../../../../hooks'
|
||||
import type { NodeOutPutVar } from '../../../../types'
|
||||
import ToggleExpandBtn from '@/app/components/workflow/nodes/_base/components/toggle-expand-btn'
|
||||
import useToggleExpend from '@/app/components/workflow/nodes/_base/hooks/use-toggle-expend'
|
||||
import PromptEditorHeightResizeWrap from '@/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap'
|
||||
|
|
@ -30,6 +32,7 @@ type Props = {
|
|||
history: boolean
|
||||
query: boolean
|
||||
}
|
||||
nodesOutputVars?: NodeOutPutVar[]
|
||||
}
|
||||
|
||||
const Editor: FC<Props> = ({
|
||||
|
|
@ -45,8 +48,12 @@ const Editor: FC<Props> = ({
|
|||
isChatApp,
|
||||
isShowContext,
|
||||
hasSetBlockStatus,
|
||||
nodesOutputVars,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { getNode } = useWorkflow()
|
||||
|
||||
console.log(nodesOutputVars, '2')
|
||||
|
||||
const isShowHistory = !isChatModel && isChatApp
|
||||
const isShowQuery = isShowHistory
|
||||
|
|
@ -146,6 +153,12 @@ const Editor: FC<Props> = ({
|
|||
show: justVar ? false : isShowQuery,
|
||||
selectable: !hasSetBlockStatus?.query,
|
||||
}}
|
||||
workflowVariableBlock={{
|
||||
show: true,
|
||||
selectable: true,
|
||||
variables: nodesOutputVars || [],
|
||||
getWorkflowNode: getNode,
|
||||
}}
|
||||
onChange={onChange}
|
||||
onBlur={setBlur}
|
||||
onFocus={setFocus}
|
||||
|
|
|
|||
|
|
@ -146,6 +146,7 @@ const ConfigPrompt: FC<Props> = ({
|
|||
isChatApp={isChatApp}
|
||||
isShowContext={isShowContext}
|
||||
hasSetBlockStatus={hasSetBlockStatus}
|
||||
nodesOutputVars={availableVarList}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue