feat: refactor workflow definition handling and integrate undo/redo functionality in WorkflowEditor
This commit is contained in:
@@ -70,7 +70,6 @@ import {
|
|||||||
IMConversationServiceMode,
|
IMConversationServiceMode,
|
||||||
Status,
|
Status,
|
||||||
} from "@/lib/generated/enums"
|
} from "@/lib/generated/enums"
|
||||||
import { useWorkflowDefinitionHistory } from "../../ai-workflows/_components/use-workflow-definition-history"
|
|
||||||
import { WorkflowEditor } from "../../ai-workflows/_components/workflow-editor"
|
import { WorkflowEditor } from "../../ai-workflows/_components/workflow-editor"
|
||||||
|
|
||||||
type DirectToolItem = CreateAIAgentPayload["directTools"][number]
|
type DirectToolItem = CreateAIAgentPayload["directTools"][number]
|
||||||
@@ -154,16 +153,8 @@ export function AIAgentConfigWorkbench({
|
|||||||
const [selectedSkillIds, setSelectedSkillIds] = useState<number[]>([])
|
const [selectedSkillIds, setSelectedSkillIds] = useState<number[]>([])
|
||||||
const [directTools, setDirectTools] = useState<DirectToolItem[]>([])
|
const [directTools, setDirectTools] = useState<DirectToolItem[]>([])
|
||||||
|
|
||||||
const {
|
const [definition, setDefinition] = useState<AIWorkflowDefinition>(fallbackDefinition)
|
||||||
definition,
|
const [workflowRevision, setWorkflowRevision] = useState(0)
|
||||||
revision: workflowRevision,
|
|
||||||
canUndo: canUndoWorkflow,
|
|
||||||
canRedo: canRedoWorkflow,
|
|
||||||
replace: replaceWorkflowHistory,
|
|
||||||
update: updateWorkflowDefinition,
|
|
||||||
undo: undoWorkflowDefinition,
|
|
||||||
redo: redoWorkflowDefinition,
|
|
||||||
} = useWorkflowDefinitionHistory(fallbackDefinition)
|
|
||||||
|
|
||||||
const [aiConfigs, setAIConfigs] = useState<AIConfig[]>([])
|
const [aiConfigs, setAIConfigs] = useState<AIConfig[]>([])
|
||||||
const [knowledgeBases, setKnowledgeBases] = useState<KnowledgeBase[]>([])
|
const [knowledgeBases, setKnowledgeBases] = useState<KnowledgeBase[]>([])
|
||||||
@@ -181,8 +172,9 @@ export function AIAgentConfigWorkbench({
|
|||||||
}, [agentId])
|
}, [agentId])
|
||||||
|
|
||||||
const replaceWorkflowDefinition = useCallback((nextDefinition: AIWorkflowDefinition) => {
|
const replaceWorkflowDefinition = useCallback((nextDefinition: AIWorkflowDefinition) => {
|
||||||
replaceWorkflowHistory(nextDefinition)
|
setDefinition(nextDefinition)
|
||||||
}, [replaceWorkflowHistory])
|
setWorkflowRevision((current) => current + 1)
|
||||||
|
}, [])
|
||||||
|
|
||||||
const loadData = useCallback(async () => {
|
const loadData = useCallback(async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
@@ -823,11 +815,8 @@ export function AIAgentConfigWorkbench({
|
|||||||
key={workflowRevision}
|
key={workflowRevision}
|
||||||
definition={definition}
|
definition={definition}
|
||||||
nodeSpecs={nodeSpecs}
|
nodeSpecs={nodeSpecs}
|
||||||
onDefinitionChange={updateWorkflowDefinition}
|
onDefinitionChange={setDefinition}
|
||||||
onUndo={undoWorkflowDefinition}
|
historyDisabled={savingWorkflow || loading}
|
||||||
undoDisabled={!canUndoWorkflow || savingWorkflow || loading}
|
|
||||||
onRedo={redoWorkflowDefinition}
|
|
||||||
redoDisabled={!canRedoWorkflow || savingWorkflow || loading}
|
|
||||||
onRestoreDefault={restoreDefaultWorkflow}
|
onRestoreDefault={restoreDefaultWorkflow}
|
||||||
restoreDefaultDisabled={savingWorkflow || loading}
|
restoreDefaultDisabled={savingWorkflow || loading}
|
||||||
onValidate={validateWorkflowDraft}
|
onValidate={validateWorkflowDraft}
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { useCallback, useState } from "react"
|
|
||||||
|
|
||||||
import type { AIWorkflowDefinition } from "@/lib/api/admin"
|
|
||||||
|
|
||||||
type WorkflowDefinitionHistoryState = {
|
|
||||||
present: AIWorkflowDefinition
|
|
||||||
past: AIWorkflowDefinition[]
|
|
||||||
future: AIWorkflowDefinition[]
|
|
||||||
revision: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useWorkflowDefinitionHistory(initialDefinition: AIWorkflowDefinition) {
|
|
||||||
const [state, setState] = useState<WorkflowDefinitionHistoryState>({
|
|
||||||
present: initialDefinition,
|
|
||||||
past: [],
|
|
||||||
future: [],
|
|
||||||
revision: 0,
|
|
||||||
})
|
|
||||||
|
|
||||||
const replace = useCallback((definition: AIWorkflowDefinition) => {
|
|
||||||
setState((current) => ({
|
|
||||||
present: definition,
|
|
||||||
past: [],
|
|
||||||
future: [],
|
|
||||||
revision: current.revision + 1,
|
|
||||||
}))
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const update = useCallback((definition: AIWorkflowDefinition) => {
|
|
||||||
setState((current) => {
|
|
||||||
if (sameWorkflowDefinition(current.present, definition)) {
|
|
||||||
return current
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
present: definition,
|
|
||||||
past: [...current.past.slice(-49), current.present],
|
|
||||||
future: [],
|
|
||||||
revision: current.revision,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const undo = useCallback(() => {
|
|
||||||
setState((current) => {
|
|
||||||
const previous = current.past[current.past.length - 1]
|
|
||||||
if (!previous) {
|
|
||||||
return current
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
present: previous,
|
|
||||||
past: current.past.slice(0, -1),
|
|
||||||
future: [current.present, ...current.future.slice(0, 49)],
|
|
||||||
revision: current.revision + 1,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const redo = useCallback(() => {
|
|
||||||
setState((current) => {
|
|
||||||
const next = current.future[0]
|
|
||||||
if (!next) {
|
|
||||||
return current
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
present: next,
|
|
||||||
past: [...current.past.slice(-49), current.present],
|
|
||||||
future: current.future.slice(1),
|
|
||||||
revision: current.revision + 1,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return {
|
|
||||||
definition: state.present,
|
|
||||||
revision: state.revision,
|
|
||||||
canUndo: state.past.length > 0,
|
|
||||||
canRedo: state.future.length > 0,
|
|
||||||
replace,
|
|
||||||
update,
|
|
||||||
undo,
|
|
||||||
redo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function sameWorkflowDefinition(left: AIWorkflowDefinition, right: AIWorkflowDefinition) {
|
|
||||||
return JSON.stringify(left) === JSON.stringify(right)
|
|
||||||
}
|
|
||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
type WorkflowNodeJSON,
|
type WorkflowNodeJSON,
|
||||||
type WorkflowPortEntity,
|
type WorkflowPortEntity,
|
||||||
useClientContext,
|
useClientContext,
|
||||||
|
useUndoRedo,
|
||||||
usePlaygroundTools,
|
usePlaygroundTools,
|
||||||
useService,
|
useService,
|
||||||
} from "@flowgram.ai/free-layout-editor"
|
} from "@flowgram.ai/free-layout-editor"
|
||||||
@@ -51,10 +52,7 @@ export function WorkflowEditor({
|
|||||||
onDefinitionChange,
|
onDefinitionChange,
|
||||||
onRestoreDefault,
|
onRestoreDefault,
|
||||||
restoreDefaultDisabled = false,
|
restoreDefaultDisabled = false,
|
||||||
onUndo,
|
historyDisabled = false,
|
||||||
undoDisabled = false,
|
|
||||||
onRedo,
|
|
||||||
redoDisabled = false,
|
|
||||||
onValidate,
|
onValidate,
|
||||||
validateDisabled = false,
|
validateDisabled = false,
|
||||||
onSaveDraft,
|
onSaveDraft,
|
||||||
@@ -68,10 +66,7 @@ export function WorkflowEditor({
|
|||||||
onDefinitionChange: (definition: AIWorkflowDefinition) => void
|
onDefinitionChange: (definition: AIWorkflowDefinition) => void
|
||||||
onRestoreDefault?: () => void
|
onRestoreDefault?: () => void
|
||||||
restoreDefaultDisabled?: boolean
|
restoreDefaultDisabled?: boolean
|
||||||
onUndo?: () => void
|
historyDisabled?: boolean
|
||||||
undoDisabled?: boolean
|
|
||||||
onRedo?: () => void
|
|
||||||
redoDisabled?: boolean
|
|
||||||
onValidate?: () => void
|
onValidate?: () => void
|
||||||
validateDisabled?: boolean
|
validateDisabled?: boolean
|
||||||
onSaveDraft?: () => void
|
onSaveDraft?: () => void
|
||||||
@@ -136,10 +131,7 @@ export function WorkflowEditor({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onSelectBranch={handleSelectBranch}
|
onSelectBranch={handleSelectBranch}
|
||||||
onUndo={onUndo}
|
historyDisabled={historyDisabled}
|
||||||
undoDisabled={undoDisabled}
|
|
||||||
onRedo={onRedo}
|
|
||||||
redoDisabled={redoDisabled}
|
|
||||||
onRestoreDefault={onRestoreDefault}
|
onRestoreDefault={onRestoreDefault}
|
||||||
restoreDefaultDisabled={restoreDefaultDisabled}
|
restoreDefaultDisabled={restoreDefaultDisabled}
|
||||||
onValidate={onValidate}
|
onValidate={onValidate}
|
||||||
@@ -164,10 +156,7 @@ function WorkflowEditorInner({
|
|||||||
onDefinitionChange,
|
onDefinitionChange,
|
||||||
onSelectNode,
|
onSelectNode,
|
||||||
onSelectBranch,
|
onSelectBranch,
|
||||||
onUndo,
|
historyDisabled,
|
||||||
undoDisabled,
|
|
||||||
onRedo,
|
|
||||||
redoDisabled,
|
|
||||||
onRestoreDefault,
|
onRestoreDefault,
|
||||||
restoreDefaultDisabled,
|
restoreDefaultDisabled,
|
||||||
onValidate,
|
onValidate,
|
||||||
@@ -186,10 +175,7 @@ function WorkflowEditorInner({
|
|||||||
onDefinitionChange: (definition: AIWorkflowDefinition) => void
|
onDefinitionChange: (definition: AIWorkflowDefinition) => void
|
||||||
onSelectNode: (nodeId: string) => void
|
onSelectNode: (nodeId: string) => void
|
||||||
onSelectBranch: (branch: SelectedWorkflowBranch | null) => void
|
onSelectBranch: (branch: SelectedWorkflowBranch | null) => void
|
||||||
onUndo?: () => void
|
historyDisabled?: boolean
|
||||||
undoDisabled?: boolean
|
|
||||||
onRedo?: () => void
|
|
||||||
redoDisabled?: boolean
|
|
||||||
onRestoreDefault?: () => void
|
onRestoreDefault?: () => void
|
||||||
restoreDefaultDisabled?: boolean
|
restoreDefaultDisabled?: boolean
|
||||||
onValidate?: () => void
|
onValidate?: () => void
|
||||||
@@ -201,6 +187,7 @@ function WorkflowEditorInner({
|
|||||||
}) {
|
}) {
|
||||||
const context = useClientContext()
|
const context = useClientContext()
|
||||||
const playgroundTools = usePlaygroundTools()
|
const playgroundTools = usePlaygroundTools()
|
||||||
|
const undoRedo = useUndoRedo()
|
||||||
const workflowDocument = useService(WorkflowDocument)
|
const workflowDocument = useService(WorkflowDocument)
|
||||||
const linesManager = useService(WorkflowLinesManager)
|
const linesManager = useService(WorkflowLinesManager)
|
||||||
const selectService = useService(WorkflowSelectService)
|
const selectService = useService(WorkflowSelectService)
|
||||||
@@ -229,6 +216,16 @@ function WorkflowEditorInner({
|
|||||||
onDefinitionChange(context.document.toJSON() as AIWorkflowDefinition)
|
onDefinitionChange(context.document.toJSON() as AIWorkflowDefinition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const undo = async () => {
|
||||||
|
await undoRedo.undo()
|
||||||
|
emitCurrentDefinition()
|
||||||
|
}
|
||||||
|
|
||||||
|
const redo = async () => {
|
||||||
|
await undoRedo.redo()
|
||||||
|
emitCurrentDefinition()
|
||||||
|
}
|
||||||
|
|
||||||
const openNodeMenuFromPort = useCallback((request: WorkflowPortAddRequest) => {
|
const openNodeMenuFromPort = useCallback((request: WorkflowPortAddRequest) => {
|
||||||
const rootRect = editorRootRef.current?.getBoundingClientRect()
|
const rootRect = editorRootRef.current?.getBoundingClientRect()
|
||||||
setNodeMenu({
|
setNodeMenu({
|
||||||
@@ -336,10 +333,10 @@ function WorkflowEditorInner({
|
|||||||
>
|
>
|
||||||
<WorkflowEditorToolbar
|
<WorkflowEditorToolbar
|
||||||
toolbarExtra={toolbarExtra}
|
toolbarExtra={toolbarExtra}
|
||||||
onUndo={onUndo}
|
onUndo={() => void undo()}
|
||||||
undoDisabled={undoDisabled}
|
undoDisabled={historyDisabled || !undoRedo.canUndo}
|
||||||
onRedo={onRedo}
|
onRedo={() => void redo()}
|
||||||
redoDisabled={redoDisabled}
|
redoDisabled={historyDisabled || !undoRedo.canRedo}
|
||||||
onRestoreDefault={onRestoreDefault}
|
onRestoreDefault={onRestoreDefault}
|
||||||
restoreDefaultDisabled={restoreDefaultDisabled}
|
restoreDefaultDisabled={restoreDefaultDisabled}
|
||||||
onValidate={onValidate}
|
onValidate={onValidate}
|
||||||
|
|||||||
Reference in New Issue
Block a user