This commit is contained in:
StyleZhang 2024-03-28 17:11:13 +08:00
parent 82a82fff35
commit 4235baf493
8 changed files with 260 additions and 0 deletions

View File

@ -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} />

View File

@ -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,
)

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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}

View File

@ -146,6 +146,7 @@ const ConfigPrompt: FC<Props> = ({
isChatApp={isChatApp}
isShowContext={isShowContext}
hasSetBlockStatus={hasSetBlockStatus}
nodesOutputVars={availableVarList}
/>
)
})