mirror of
https://github.com/langgenius/dify.git
synced 2026-10-04 13:47:55 +08:00
Add a copilot toggle button next to Test Run in the workflow header and a left-mounted panel that runs the copilot Fix flow (start fix, live SSE progress log, approve_repair/run_verify/provide_testdata/publish/ undo/re_fix actions, and a message box), auto-resolving the app id (workflow store `appId`), workspace id (`currentWorkspaceAtom`), and the most recent failed run (`useWorkflowRunHistory`) instead of requiring manual entry. Extracts the session/SSE request logic from the standalone `/workflow-copilot-debug` page into a shared `app/components/workflow-copilot` module (`use-copilot-session`) so both the debug page and the new panel drive the same implementation. Adds a `showWorkflowCopilotPanel` store flag (defaults false) mirroring the existing `showEnvPanel` pattern, so the feature is fully additive and does not affect rag-pipeline/snippet editors that reuse the same header/panel shell. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
112 lines
4.7 KiB
TypeScript
112 lines
4.7 KiB
TypeScript
import type { StartNodeType } from '../nodes/start/types'
|
|
import type { RunAndHistoryProps } from './run-and-history'
|
|
import { useCallback } from 'react'
|
|
import { useNodes } from 'reactflow'
|
|
import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks/use-input-field-panel'
|
|
import Divider from '../../base/divider'
|
|
import { useHooksStore } from '../hooks-store'
|
|
import { useNodesInteractions } from '../hooks/use-nodes-interactions'
|
|
import { useNodesReadOnly } from '../hooks/use-workflow'
|
|
import { useWorkflowRun } from '../hooks/use-workflow-run'
|
|
import { useStore, useWorkflowStore } from '../store'
|
|
import EditingTitle from './editing-title'
|
|
import EnvButton from './env-button'
|
|
import GlobalVariableButton from './global-variable-button'
|
|
import OnlineUsers from './online-users'
|
|
import RunAndHistory from './run-and-history'
|
|
import ScrollToSelectedNodeButton from './scroll-to-selected-node-button'
|
|
import { VersionHistoryButton } from './version-history-button'
|
|
import WorkflowCopilotButton from './workflow-copilot-button'
|
|
|
|
export type HeaderInNormalProps = {
|
|
components?: {
|
|
title?: React.ReactNode
|
|
left?: React.ReactNode
|
|
middle?: React.ReactNode
|
|
chatVariableTrigger?: React.ReactNode
|
|
}
|
|
controls?: {
|
|
showEnvButton?: boolean
|
|
showGlobalVariableButton?: boolean
|
|
showWorkflowCopilotButton?: boolean
|
|
}
|
|
runAndHistoryProps?: RunAndHistoryProps
|
|
}
|
|
const HeaderInNormal = ({ components, controls, runAndHistoryProps }: HeaderInNormalProps) => {
|
|
const workflowStore = useWorkflowStore()
|
|
const { nodesReadOnly } = useNodesReadOnly()
|
|
const canReleaseAndVersion = useHooksStore((s) => s.accessControl.canReleaseAndVersion)
|
|
const { handleNodeSelect } = useNodesInteractions()
|
|
const setShowWorkflowVersionHistoryPanel = useStore((s) => s.setShowWorkflowVersionHistoryPanel)
|
|
const setShowEnvPanel = useStore((s) => s.setShowEnvPanel)
|
|
const setShowDebugAndPreviewPanel = useStore((s) => s.setShowDebugAndPreviewPanel)
|
|
const setShowVariableInspectPanel = useStore((s) => s.setShowVariableInspectPanel)
|
|
const setShowChatVariablePanel = useStore((s) => s.setShowChatVariablePanel)
|
|
const setShowGlobalVariablePanel = useStore((s) => s.setShowGlobalVariablePanel)
|
|
const setShowWorkflowCopilotPanel = useStore((s) => s.setShowWorkflowCopilotPanel)
|
|
const nodes = useNodes<StartNodeType>()
|
|
const selectedNode = nodes.find((node) => node.data.selected)
|
|
const { handleBackupDraft } = useWorkflowRun()
|
|
const { closeAllInputFieldPanels } = useInputFieldPanel()
|
|
const showEnvButton = controls?.showEnvButton !== false
|
|
const showGlobalVariableButton = controls?.showGlobalVariableButton !== false
|
|
const showWorkflowCopilotButton = controls?.showWorkflowCopilotButton === true
|
|
const showContextButtons =
|
|
!!components?.chatVariableTrigger || showEnvButton || showGlobalVariableButton
|
|
|
|
const onStartRestoring = useCallback(() => {
|
|
workflowStore.setState({ isRestoring: true })
|
|
handleBackupDraft()
|
|
// clear right panel
|
|
if (selectedNode) handleNodeSelect(selectedNode.id, true)
|
|
setShowWorkflowVersionHistoryPanel(true)
|
|
setShowEnvPanel(false)
|
|
setShowDebugAndPreviewPanel(false)
|
|
setShowVariableInspectPanel(false)
|
|
setShowChatVariablePanel(false)
|
|
setShowGlobalVariablePanel(false)
|
|
setShowWorkflowCopilotPanel(false)
|
|
closeAllInputFieldPanels()
|
|
}, [
|
|
workflowStore,
|
|
handleBackupDraft,
|
|
selectedNode,
|
|
handleNodeSelect,
|
|
setShowWorkflowVersionHistoryPanel,
|
|
setShowEnvPanel,
|
|
setShowDebugAndPreviewPanel,
|
|
setShowVariableInspectPanel,
|
|
setShowChatVariablePanel,
|
|
setShowGlobalVariablePanel,
|
|
setShowWorkflowCopilotPanel,
|
|
closeAllInputFieldPanels,
|
|
])
|
|
|
|
return (
|
|
<div className="flex w-full items-center justify-between">
|
|
<div>{components?.title ?? <EditingTitle />}</div>
|
|
<div>
|
|
<ScrollToSelectedNodeButton />
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<OnlineUsers />
|
|
{components?.left}
|
|
<Divider type="vertical" className="mx-auto h-3.5" />
|
|
<RunAndHistory {...runAndHistoryProps} />
|
|
{showWorkflowCopilotButton && <WorkflowCopilotButton disabled={nodesReadOnly} />}
|
|
{showContextButtons && (
|
|
<div className="shrink-0 cursor-pointer rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg shadow-xs backdrop-blur-[10px]">
|
|
{components?.chatVariableTrigger}
|
|
{showEnvButton && <EnvButton disabled={nodesReadOnly} />}
|
|
{showGlobalVariableButton && <GlobalVariableButton disabled={nodesReadOnly} />}
|
|
</div>
|
|
)}
|
|
{components?.middle}
|
|
{canReleaseAndVersion && <VersionHistoryButton onClick={onStartRestoring} />}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default HeaderInNormal
|