Files
ai-video-fullstack/frontend/src/components/workflow/ConditionEdge.tsx
T
Xin Wang 162a3d8bec Refactor workflow routing and greeting management in Brain classes
- Update WorkflowBrain to handle greeting playback more effectively, ensuring that the initial greeting completes before transitioning to the first node.
- Introduce new methods for managing greeting states and conditions, enhancing the interaction flow for user turns.
- Refactor WorkflowLLMRouter to improve routing logic and ensure proper handling of conditional paths.
- Enhance tests to verify the correct behavior of greeting management and routing under various scenarios, including waiting for audio playback to finish.
- Update frontend components to reflect changes in edge handling and improve user experience in workflow configurations.
2026-07-17 22:01:42 +08:00

122 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
/**
* Workflow v3 edge: LLM judgement, variable expression, or deterministic default.
*/
import {
BaseEdge,
EdgeLabelRenderer,
getBezierPath,
type EdgeProps,
useReactFlow,
} from "@xyflow/react";
import { Pencil, Trash2 } from "lucide-react";
import { useContext, useState } from "react";
import { cn } from "@/lib/utils";
import { EdgeActionContext } from "./context";
export function ConditionEdge({
id,
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
data,
selected,
}: EdgeProps) {
const actions = useContext(EdgeActionContext);
const { setEdges } = useReactFlow();
const [hovered, setHovered] = useState(false);
// 点击标签:只选中这条边(露出编辑/删除按钮),不直接进入编辑。
const selectThisEdge = () =>
setEdges((eds) => eds.map((e) => ({ ...e, selected: e.id === id })));
const [path, labelX, labelY] = getBezierPath({
sourceX,
sourceY,
sourcePosition,
targetX,
targetY,
targetPosition,
curvature: 0.28,
});
const mode = (data?.mode as string) || "always";
const label = (
(data?.label as string) ||
(mode === "llm" ? (data?.condition as string) : "") ||
(mode === "llm"
? "大模型判断"
: mode === "expression"
? "表达式"
: "默认路径")
).toString().trim();
const expanded = hovered || selected;
return (
<>
<BaseEdge
id={id}
path={path}
style={{
stroke: selected ? "var(--primary)" : "var(--muted-soft)",
strokeWidth: selected ? 2.5 : 1.5,
transition: "stroke 0.15s ease, stroke-width 0.15s ease",
}}
/>
<EdgeLabelRenderer>
<div
className="nodrag nopan pointer-events-auto absolute flex items-center gap-1"
style={{
transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
}}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<button
type="button"
title="条件"
className={cn(
"max-w-[160px] truncate rounded-full border px-2.5 py-1 text-xs shadow-sm transition-colors",
label
? "border-hairline bg-card text-foreground hover:border-primary"
: "border-dashed border-hairline-strong bg-card/80 text-muted-soft hover:text-foreground",
selected && "border-primary ring-2 ring-primary/30",
)}
onClick={selectThisEdge}
>
{label || "+ 条件"}
</button>
{expanded && (
<button
type="button"
title="编辑条件"
className="flex h-6 w-6 items-center justify-center rounded-full border border-hairline bg-card text-muted-foreground shadow-sm transition-colors hover:text-foreground"
onClick={() => actions.edit(id)}
>
<Pencil size={12} />
</button>
)}
{expanded && (
<button
type="button"
title="删除连线"
className="flex h-6 w-6 items-center justify-center rounded-full border border-hairline bg-card text-muted-foreground shadow-sm transition-colors hover:text-destructive"
onClick={() => actions.remove(id)}
>
<Trash2 size={12} />
</button>
)}
</div>
</EdgeLabelRenderer>
</>
);
}
export const edgeTypes = { condition: ConditionEdge };