feat(workflow): show variables in drawer

This commit is contained in:
Xin Wang
2026-08-03 09:58:01 +08:00
parent 807afb3099
commit b0f82142a5
4 changed files with 133 additions and 43 deletions

View File

@@ -151,6 +151,59 @@ export function DynamicVariablesDialog({
onOpenChange: (open: boolean) => void; onOpenChange: (open: boolean) => void;
definitions: Record<string, DynamicVariableDefinition>; definitions: Record<string, DynamicVariableDefinition>;
onChange: (definitions: Record<string, DynamicVariableDefinition>) => void; onChange: (definitions: Record<string, DynamicVariableDefinition>) => void;
}) {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-h-[78vh] overflow-hidden rounded-2xl border-hairline bg-card p-0 sm:max-w-[520px]">
<DialogHeader className="border-b border-hairline px-6 py-5 text-left">
<DialogTitle className="flex items-center gap-2 text-base font-medium">
<Braces size={17} />
</DialogTitle>
<DialogDescription className="text-xs leading-5">
</DialogDescription>
</DialogHeader>
<DynamicVariablesEditor
definitions={definitions}
onChange={onChange}
layout="dialog"
/>
</DialogContent>
</Dialog>
);
}
export function DynamicVariablesPanel({
definitions,
onChange,
}: {
definitions: Record<string, DynamicVariableDefinition>;
onChange: (definitions: Record<string, DynamicVariableDefinition>) => void;
}) {
return (
<div className="flex min-h-0 flex-1 flex-col bg-canvas-soft px-4 pb-5 pt-4">
<p className="mb-3 shrink-0 text-xs leading-5 text-muted-foreground">
</p>
<DynamicVariablesEditor
definitions={definitions}
onChange={onChange}
layout="panel"
/>
</div>
);
}
function DynamicVariablesEditor({
definitions,
onChange,
layout,
}: {
definitions: Record<string, DynamicVariableDefinition>;
onChange: (definitions: Record<string, DynamicVariableDefinition>) => void;
layout: "dialog" | "panel";
}) { }) {
const entries = Object.entries(definitions); const entries = Object.entries(definitions);
const [copiedSystemVariable, setCopiedSystemVariable] = useState<string | null>( const [copiedSystemVariable, setCopiedSystemVariable] = useState<string | null>(
@@ -168,20 +221,15 @@ export function DynamicVariablesDialog({
} }
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Tabs
<DialogContent className="max-h-[78vh] overflow-hidden rounded-2xl border-hairline bg-card p-0 sm:max-w-[520px]"> defaultValue="custom"
<DialogHeader className="border-b border-hairline px-6 py-5 text-left"> className={
<DialogTitle className="flex items-center gap-2 text-base font-medium"> layout === "panel"
<Braces size={17} /> ? "flex min-h-0 flex-1 flex-col"
: "min-h-0 px-6 pb-6"
</DialogTitle> }
<DialogDescription className="text-xs leading-5"> >
<TabsList className="grid w-full shrink-0 grid-cols-2 bg-surface-strong">
</DialogDescription>
</DialogHeader>
<Tabs defaultValue="custom" className="min-h-0 px-6 pb-6">
<TabsList className="grid w-full shrink-0 grid-cols-2 bg-surface-strong">
<TabsTrigger value="custom"> <TabsTrigger value="custom">
<span className="text-[11px] tabular-nums text-muted-foreground"> <span className="text-[11px] tabular-nums text-muted-foreground">
@@ -198,7 +246,11 @@ export function DynamicVariablesDialog({
<TabsContent <TabsContent
value="custom" value="custom"
className="max-h-[calc(78vh-168px)] overflow-y-auto pt-3" className={
layout === "panel"
? "scrollbar-subtle min-h-0 flex-1 overflow-y-auto pt-3"
: "max-h-[calc(78vh-168px)] overflow-y-auto pt-3"
}
> >
{entries.length === 0 ? ( {entries.length === 0 ? (
<div className="rounded-xl border border-dashed border-hairline-strong bg-canvas-soft px-4 py-5 text-center"> <div className="rounded-xl border border-dashed border-hairline-strong bg-canvas-soft px-4 py-5 text-center">
@@ -316,7 +368,11 @@ export function DynamicVariablesDialog({
<TabsContent <TabsContent
value="system" value="system"
className="max-h-[calc(78vh-168px)] space-y-3 overflow-y-auto pt-3" className={
layout === "panel"
? "scrollbar-subtle min-h-0 flex-1 space-y-3 overflow-y-auto pt-3"
: "max-h-[calc(78vh-168px)] space-y-3 overflow-y-auto pt-3"
}
> >
<p className="text-[11px] leading-5 text-muted-foreground"> <p className="text-[11px] leading-5 text-muted-foreground">
@@ -363,9 +419,7 @@ export function DynamicVariablesDialog({
</div> </div>
))} ))}
</div> </div>
</TabsContent> </TabsContent>
</Tabs> </Tabs>
</DialogContent>
</Dialog>
); );
} }

View File

@@ -3,7 +3,7 @@
import { Bug, Loader2, Save } from "lucide-react"; import { Bug, Loader2, Save } from "lucide-react";
import { DebugDrawer } from "@/components/assistant-editor/debug-preview"; import { DebugDrawer } from "@/components/assistant-editor/debug-preview";
import { DynamicVariablesDialog } from "@/components/assistant-editor/dynamic-variables"; import { DynamicVariablesPanel } from "@/components/assistant-editor/dynamic-variables";
import { import {
AssistantIdentity, AssistantIdentity,
EditableTitle, EditableTitle,
@@ -115,6 +115,7 @@ export function WorkflowPage({
disabled={!assistantId} disabled={!assistantId}
onClick={() => { onClick={() => {
setWorkflowSettingsOpen(false); setWorkflowSettingsOpen(false);
setDynamicVariablesOpen(false);
setWorkflowEditingNodeId(null); setWorkflowEditingNodeId(null);
setWorkflowEditingEdgeId(null); setWorkflowEditingEdgeId(null);
setWorkflowDebugOpen(true); setWorkflowDebugOpen(true);
@@ -144,7 +145,14 @@ export function WorkflowPage({
onChange={setWorkflowGraph} onChange={setWorkflowGraph}
settings={workflowSettings} settings={workflowSettings}
onSettingsChange={setWorkflowSettings} onSettingsChange={setWorkflowSettings}
onOpenDynamicVariables={() => setDynamicVariablesOpen(true)} dynamicVariablesOpen={dynamicVariablesOpen}
onDynamicVariablesOpenChange={setDynamicVariablesOpen}
dynamicVariablesPanel={
<DynamicVariablesPanel
definitions={dynamicVariableDefinitions}
onChange={setWorkflowDynamicVariableDefinitions}
/>
}
editingNodeId={workflowEditingNodeId} editingNodeId={workflowEditingNodeId}
onEditingNodeIdChange={setWorkflowEditingNodeId} onEditingNodeIdChange={setWorkflowEditingNodeId}
editingEdgeId={workflowEditingEdgeId} editingEdgeId={workflowEditingEdgeId}
@@ -179,13 +187,6 @@ export function WorkflowPage({
knowledgeOptions={kbOptions} knowledgeOptions={kbOptions}
/> />
</div> </div>
<DynamicVariablesDialog
open={dynamicVariablesOpen}
onOpenChange={setDynamicVariablesOpen}
definitions={dynamicVariableDefinitions}
onChange={setWorkflowDynamicVariableDefinitions}
/>
</div> </div>
); );
} }

View File

@@ -153,7 +153,9 @@ export function WorkflowCanvas({
onSettingsChange, onSettingsChange,
modelOptions, modelOptions,
activeNodeId, activeNodeId,
onOpenDynamicVariables, dynamicVariablesOpen,
onDynamicVariablesOpenChange,
dynamicVariablesPanel,
editingNodeId, editingNodeId,
onEditingNodeIdChange, onEditingNodeIdChange,
editingEdgeId, editingEdgeId,
@@ -288,6 +290,7 @@ export function WorkflowCanvas({
setAddSourceId(null); setAddSourceId(null);
setAddPosition(null); setAddPosition(null);
setSettingsOpen(false); setSettingsOpen(false);
onDynamicVariablesOpenChange(false);
onDebugOpenChange(false); onDebugOpenChange(false);
onEditingEdgeIdChange(null); onEditingEdgeIdChange(null);
onEditingNodeIdChange(id); onEditingNodeIdChange(id);
@@ -297,6 +300,7 @@ export function WorkflowCanvas({
addSourceId, addSourceId,
nodes, nodes,
onDebugOpenChange, onDebugOpenChange,
onDynamicVariablesOpenChange,
onEditingEdgeIdChange, onEditingEdgeIdChange,
onEditingNodeIdChange, onEditingNodeIdChange,
screenToFlowPosition, screenToFlowPosition,
@@ -456,11 +460,13 @@ export function WorkflowCanvas({
); );
const openSettings = useCallback(() => { const openSettings = useCallback(() => {
onDebugOpenChange(false); onDebugOpenChange(false);
onDynamicVariablesOpenChange(false);
onEditingNodeIdChange(null); onEditingNodeIdChange(null);
onEditingEdgeIdChange(null); onEditingEdgeIdChange(null);
setSettingsOpen(true); setSettingsOpen(true);
}, [ }, [
onDebugOpenChange, onDebugOpenChange,
onDynamicVariablesOpenChange,
onEditingEdgeIdChange, onEditingEdgeIdChange,
onEditingNodeIdChange, onEditingNodeIdChange,
setSettingsOpen, setSettingsOpen,
@@ -470,6 +476,7 @@ export function WorkflowCanvas({
() => ({ () => ({
edit: (id: string) => { edit: (id: string) => {
setSettingsOpen(false); setSettingsOpen(false);
onDynamicVariablesOpenChange(false);
onDebugOpenChange(false); onDebugOpenChange(false);
onEditingEdgeIdChange(null); onEditingEdgeIdChange(null);
onEditingNodeIdChange(id); onEditingNodeIdChange(id);
@@ -481,6 +488,7 @@ export function WorkflowCanvas({
deleteNode, deleteNode,
duplicateNode, duplicateNode,
onDebugOpenChange, onDebugOpenChange,
onDynamicVariablesOpenChange,
onEditingEdgeIdChange, onEditingEdgeIdChange,
onEditingNodeIdChange, onEditingNodeIdChange,
setSettingsOpen, setSettingsOpen,
@@ -490,6 +498,7 @@ export function WorkflowCanvas({
() => ({ () => ({
edit: (id: string) => { edit: (id: string) => {
setSettingsOpen(false); setSettingsOpen(false);
onDynamicVariablesOpenChange(false);
onDebugOpenChange(false); onDebugOpenChange(false);
onEditingNodeIdChange(null); onEditingNodeIdChange(null);
onEditingEdgeIdChange(id); onEditingEdgeIdChange(id);
@@ -499,11 +508,25 @@ export function WorkflowCanvas({
[ [
deleteEdge, deleteEdge,
onDebugOpenChange, onDebugOpenChange,
onDynamicVariablesOpenChange,
onEditingEdgeIdChange, onEditingEdgeIdChange,
onEditingNodeIdChange, onEditingNodeIdChange,
setSettingsOpen, setSettingsOpen,
], ],
); );
const openDynamicVariables = useCallback(() => {
setSettingsOpen(false);
onDebugOpenChange(false);
onEditingNodeIdChange(null);
onEditingEdgeIdChange(null);
onDynamicVariablesOpenChange(true);
}, [
onDebugOpenChange,
onDynamicVariablesOpenChange,
onEditingEdgeIdChange,
onEditingNodeIdChange,
setSettingsOpen,
]);
const editingNode = nodes.find((n) => n.id === editingNodeId); const editingNode = nodes.find((n) => n.id === editingNodeId);
const editingSpec = editingNode ? specsByType[editingNode.type as string] : null; const editingSpec = editingNode ? specsByType[editingNode.type as string] : null;
@@ -580,6 +603,7 @@ export function WorkflowCanvas({
className="h-10 w-10 rounded-full shadow-sm" className="h-10 w-10 rounded-full shadow-sm"
aria-label="添加节点" aria-label="添加节点"
onClick={() => { onClick={() => {
onDynamicVariablesOpenChange(false);
setAddSourceId(null); setAddSourceId(null);
setAddPosition(null); setAddPosition(null);
setAddOpen(true); setAddOpen(true);
@@ -613,7 +637,7 @@ export function WorkflowCanvas({
variant="outline" variant="outline"
className="h-10 w-10 rounded-full border-hairline-strong bg-card text-foreground shadow-sm hover:bg-surface-strong" className="h-10 w-10 rounded-full border-hairline-strong bg-card text-foreground shadow-sm hover:bg-surface-strong"
aria-label="动态变量" aria-label="动态变量"
onClick={onOpenDynamicVariables} onClick={openDynamicVariables}
> >
<Braces size={17} /> <Braces size={17} />
</Button> </Button>
@@ -642,6 +666,7 @@ export function WorkflowCanvas({
/> />
{(debugOpen || {(debugOpen ||
dynamicVariablesOpen ||
settingsOpen || settingsOpen ||
(editingNode && editingSpec) || (editingNode && editingSpec) ||
editingEdge) && ( editingEdge) && (
@@ -649,24 +674,30 @@ export function WorkflowCanvas({
title={ title={
debugOpen debugOpen
? undefined ? undefined
: settingsOpen : dynamicVariablesOpen
? "工作流设置" ? "动态变量"
: editingEdge : settingsOpen
? "编辑连接条件" ? "工作流设置"
: `编辑${editingSpec?.displayName ?? "节点"}` : editingEdge
? "编辑连接条件"
: `编辑${editingSpec?.displayName ?? "节点"}`
} }
closeLabel={ closeLabel={
settingsOpen dynamicVariablesOpen
? "关闭工作流设置" ? "关闭动态变量"
: editingEdge : settingsOpen
? "关闭边编辑" ? "关闭工作流设置"
: "关闭节点编辑" : editingEdge
? "关闭边编辑"
: "关闭节点编辑"
} }
onClose={ onClose={
debugOpen debugOpen
? undefined ? undefined
: () => { : () => {
if (settingsOpen) setSettingsOpen(false); if (dynamicVariablesOpen) {
onDynamicVariablesOpenChange(false);
} else if (settingsOpen) setSettingsOpen(false);
else if (editingEdge) onEditingEdgeIdChange(null); else if (editingEdge) onEditingEdgeIdChange(null);
else onEditingNodeIdChange(null); else onEditingNodeIdChange(null);
} }
@@ -674,6 +705,8 @@ export function WorkflowCanvas({
> >
{debugOpen ? ( {debugOpen ? (
debugPanel debugPanel
) : dynamicVariablesOpen ? (
dynamicVariablesPanel
) : ( ) : (
<div className="scrollbar-subtle min-h-0 flex-1 overflow-y-auto bg-canvas-soft px-4 pb-5 pt-4"> <div className="scrollbar-subtle min-h-0 flex-1 overflow-y-auto bg-canvas-soft px-4 pb-5 pt-4">
{settingsOpen ? ( {settingsOpen ? (

View File

@@ -33,7 +33,9 @@ export type WorkflowEditorProps = {
}; };
toolOptions?: ModelOption[]; toolOptions?: ModelOption[];
knowledgeOptions?: ModelOption[]; knowledgeOptions?: ModelOption[];
onOpenDynamicVariables: () => void; dynamicVariablesOpen: boolean;
onDynamicVariablesOpenChange: (open: boolean) => void;
dynamicVariablesPanel: ReactNode;
editingNodeId: string | null; editingNodeId: string | null;
onEditingNodeIdChange: (nodeId: string | null) => void; onEditingNodeIdChange: (nodeId: string | null) => void;
editingEdgeId: string | null; editingEdgeId: string | null;