Files
ai-video-fullstack/frontend/src/components/assistant-editor/workflow-page.tsx
T

203 lines
6.7 KiB
TypeScript

"use client";
import { Bug, Loader2, Save } from "lucide-react";
import { DebugDrawer } from "@/components/assistant-editor/debug-preview";
import { DynamicVariablesPanel } from "@/components/assistant-editor/dynamic-variables";
import {
AssistantIdentity,
EditableTitle,
EditorBackButton,
} from "@/components/assistant-editor/editor-controls";
import { Button } from "@/components/ui/button";
import { TopbarPortal } from "@/components/layout/topbar-portal";
import { WorkflowEditor } from "@/components/workflow/WorkflowEditor";
import type { WorkflowSettings } from "@/components/workflow/types";
import {
workflowUsesVision,
type WorkflowGraph,
} from "@/components/workflow/specs";
import type { DynamicVariableDefinition } from "@/lib/api";
type ResourceOption = { value: string; label: string };
type WorkflowPageProps = {
assistantId: string | null;
workflowName: string;
workflowGraph: WorkflowGraph;
workflowSettings: WorkflowSettings;
dynamicVariableDefinitions: Record<string, DynamicVariableDefinition>;
workflowDebugOpen: boolean;
workflowSettingsOpen: boolean;
workflowEditingNodeId: string | null;
workflowEditingEdgeId: string | null;
activeNodeId: string | null;
dynamicVariablesOpen: boolean;
saving: boolean;
dirty: boolean;
saveError: string | null;
modelOptions: {
realtime: ResourceOption[];
llm: ResourceOption[];
asr: ResourceOption[];
tts: ResourceOption[];
vision: ResourceOption[];
};
toolOptions: ResourceOption[];
knowledgeOptions: ResourceOption[];
onBack: () => void;
onSave: () => void;
setWorkflowName: (name: string) => void;
setWorkflowGraph: (graph: WorkflowGraph) => void;
setWorkflowSettings: (settings: WorkflowSettings) => void;
setWorkflowDynamicVariableDefinitions: (
definitions: Record<string, DynamicVariableDefinition>,
) => void;
setWorkflowDebugOpen: (open: boolean) => void;
setWorkflowSettingsOpen: (open: boolean) => void;
setWorkflowEditingNodeId: (nodeId: string | null) => void;
setWorkflowEditingEdgeId: (edgeId: string | null) => void;
setActiveNodeId: (nodeId: string | null) => void;
setDynamicVariablesOpen: (open: boolean) => void;
};
export function WorkflowPage({
assistantId,
workflowName,
workflowGraph,
workflowSettings,
dynamicVariableDefinitions,
workflowDebugOpen,
workflowSettingsOpen,
workflowEditingNodeId,
workflowEditingEdgeId,
activeNodeId,
dynamicVariablesOpen,
saving,
dirty,
saveError,
modelOptions,
toolOptions,
knowledgeOptions: kbOptions,
onBack,
onSave,
setWorkflowName,
setWorkflowGraph,
setWorkflowSettings,
setWorkflowDynamicVariableDefinitions,
setWorkflowDebugOpen,
setWorkflowSettingsOpen,
setWorkflowEditingNodeId,
setWorkflowEditingEdgeId,
setActiveNodeId,
setDynamicVariablesOpen,
}: WorkflowPageProps) {
return (
<>
<TopbarPortal>
<div className="flex h-full min-w-0 flex-1 items-center justify-between gap-4 sm:-ml-2 lg:-ml-4">
<div className="flex min-w-0 items-center gap-2">
<EditorBackButton onClick={onBack} />
<EditableTitle value={workflowName} onChange={setWorkflowName} />
<AssistantIdentity assistantId={assistantId} />
</div>
<div className="flex shrink-0 items-center gap-2">
{saveError && (
<span
role="alert"
title={saveError}
className="line-clamp-1 max-w-[min(32vw,420px)] text-right text-xs text-destructive"
>
{saveError}
</span>
)}
<Button
variant="outline"
className="gap-2 border-hairline-strong text-foreground hover:bg-surface-strong"
disabled={!assistantId}
onClick={() => {
setWorkflowSettingsOpen(false);
setDynamicVariablesOpen(false);
setWorkflowEditingNodeId(null);
setWorkflowEditingEdgeId(null);
setWorkflowDebugOpen(true);
}}
>
<Bug size={16} />
调试
</Button>
<Button
className="gap-2"
disabled={saving || !dirty || !workflowName.trim()}
onClick={onSave}
>
{saving ? (
<Loader2 size={16} className="animate-spin" />
) : (
<Save size={16} />
)}
保存
</Button>
</div>
</div>
</TopbarPortal>
<div data-app-content="full-bleed" className="h-full min-h-0">
<WorkflowEditor
value={workflowGraph}
onChange={setWorkflowGraph}
settings={workflowSettings}
onSettingsChange={setWorkflowSettings}
dynamicVariablesOpen={dynamicVariablesOpen}
onDynamicVariablesOpenChange={setDynamicVariablesOpen}
dynamicVariablesPanel={
<DynamicVariablesPanel
definitions={dynamicVariableDefinitions}
onChange={setWorkflowDynamicVariableDefinitions}
/>
}
editingNodeId={workflowEditingNodeId}
onEditingNodeIdChange={setWorkflowEditingNodeId}
editingEdgeId={workflowEditingEdgeId}
onEditingEdgeIdChange={setWorkflowEditingEdgeId}
settingsOpen={workflowSettingsOpen}
onSettingsOpenChange={setWorkflowSettingsOpen}
debugOpen={workflowDebugOpen}
onDebugOpenChange={(open) => {
setWorkflowDebugOpen(open);
if (!open) setActiveNodeId(null);
}}
debugPanel={
<DebugDrawer
overlay
assistantId={assistantId}
vision={workflowUsesVision(workflowGraph)}
onClose={() => {
setWorkflowDebugOpen(false);
setActiveNodeId(null);
}}
hasUnsavedChanges={dirty}
onNodeActive={setActiveNodeId}
dynamicVariablesEnabled
dynamicVariableDefinitions={
dynamicVariableDefinitions
}
/>
}
activeNodeId={activeNodeId}
modelOptions={modelOptions}
toolOptions={toolOptions}
knowledgeOptions={kbOptions}
dynamicVariableOptions={Object.entries(
dynamicVariableDefinitions,
).map(([value, definition]) => ({
value,
label: `${value} · ${definition.type}`,
}))}
/>
</div>
</>
);
}