fix: refine agent progress feedback
This commit is contained in:
@@ -37,6 +37,8 @@ type TodoStatus = AgentTodoUpdate["todos"][number]["status"];
|
||||
type PermissionStatus = AgentPermissionRequest["status"];
|
||||
type QuestionStatus = AgentQuestionRequest["status"];
|
||||
|
||||
const STREAMING_PROGRESS_LIMIT = 3;
|
||||
|
||||
const progressStatusMeta: Record<ProgressStatus, { label: string; className: string; dotClassName: string }> = {
|
||||
running: {
|
||||
label: "进行中",
|
||||
@@ -155,7 +157,7 @@ export function AgentMessageProgress({
|
||||
return null;
|
||||
}
|
||||
|
||||
return <ProgressList progress={progress} running={running} />;
|
||||
return <ProgressList key={running ? "running" : "settled"} progress={progress} running={running} />;
|
||||
}
|
||||
|
||||
function AgentNestedBlock({
|
||||
@@ -198,30 +200,35 @@ function AnimatedDetailItem({ children }: { children: ReactNode }) {
|
||||
|
||||
function ProgressList({ progress, running }: { progress: AgentChatProgress[]; running: boolean }) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const visibleProgress = expanded ? progress : progress.slice(-1);
|
||||
const listMode = expanded ? "expanded" : "summary";
|
||||
const visibleProgress = running
|
||||
? progress.slice(-STREAMING_PROGRESS_LIMIT)
|
||||
: expanded
|
||||
? progress
|
||||
: [];
|
||||
const listMode = running ? "streaming" : expanded ? "expanded" : "collapsed";
|
||||
|
||||
return (
|
||||
<div className="agent-panel-nested rounded-xl p-2.5">
|
||||
<button
|
||||
type="button"
|
||||
className="flex w-full items-center gap-2 text-left text-xs font-semibold text-slate-600"
|
||||
aria-expanded={expanded}
|
||||
className="flex w-full items-center gap-2 text-left text-xs font-semibold text-slate-600 disabled:cursor-default"
|
||||
aria-expanded={running || expanded}
|
||||
disabled={running}
|
||||
onClick={() => setExpanded((current) => !current)}
|
||||
>
|
||||
<ListTree size={16} className="shrink-0 text-blue-700" aria-hidden="true" />
|
||||
<span>执行进度</span>
|
||||
<span className="flex-1" />
|
||||
<span className="shrink-0 font-normal text-slate-400">
|
||||
{expanded
|
||||
? `全部 ${progress.length} 条`
|
||||
: running
|
||||
? "当前步骤"
|
||||
{running
|
||||
? `最近 ${Math.min(progress.length, STREAMING_PROGRESS_LIMIT)} 条`
|
||||
: expanded
|
||||
? `全部 ${progress.length} 条`
|
||||
: `共 ${progress.length} 条`}
|
||||
</span>
|
||||
<ChevronDown
|
||||
size={14}
|
||||
className={cn("shrink-0 text-slate-400 transition-transform", expanded && "rotate-180")}
|
||||
className={cn("shrink-0 text-slate-400 transition-transform", !running && expanded && "rotate-180")}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
@@ -229,7 +236,7 @@ function ProgressList({ progress, running }: { progress: AgentChatProgress[]; ru
|
||||
{visibleProgress.length ? (
|
||||
<motion.ol
|
||||
key={listMode}
|
||||
aria-label={expanded ? "全部执行进度" : "当前执行进度"}
|
||||
aria-label={!running && expanded ? "全部执行进度" : "最近执行进度"}
|
||||
className="mt-2 space-y-1.5 overflow-hidden"
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
@@ -240,7 +247,7 @@ function ProgressList({ progress, running }: { progress: AgentChatProgress[]; ru
|
||||
{visibleProgress.map((item) => (
|
||||
<motion.li
|
||||
key={item.id}
|
||||
className="grid min-h-7 grid-cols-[16px_1fr_auto] items-start gap-2 text-xs"
|
||||
className="grid h-12 grid-cols-[16px_minmax(0,1fr)_auto] items-start gap-2 overflow-hidden text-xs"
|
||||
initial={{ opacity: 0, y: 4 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -2 }}
|
||||
@@ -252,10 +259,18 @@ function ProgressList({ progress, running }: { progress: AgentChatProgress[]; ru
|
||||
"mt-0.5 h-3.5 w-3.5 rounded-full border transition-colors duration-150",
|
||||
progressStatusMeta[item.status].dotClassName
|
||||
)}
|
||||
animate={item.status === "running" ? { opacity: [0.45, 1, 0.45] } : { opacity: 1 }}
|
||||
animate={
|
||||
item.status === "running"
|
||||
? { opacity: [0.45, 1, 0.45], scale: 1 }
|
||||
: item.status === "error"
|
||||
? { opacity: [0.55, 1, 1], scale: [1, 1.28, 1] }
|
||||
: { opacity: 1, scale: 1 }
|
||||
}
|
||||
transition={
|
||||
item.status === "running"
|
||||
? { duration: 1.2, ease: "easeInOut", repeat: Infinity }
|
||||
: item.status === "error"
|
||||
? { duration: 0.28, ease: agentEase }
|
||||
: { duration: 0.14 }
|
||||
}
|
||||
/>
|
||||
@@ -264,7 +279,7 @@ function ProgressList({ progress, running }: { progress: AgentChatProgress[]; ru
|
||||
{item.title}
|
||||
</span>
|
||||
{item.detail ? (
|
||||
<span className="block line-clamp-2 whitespace-pre-wrap leading-5 text-slate-500">
|
||||
<span className="block truncate leading-5 text-slate-500" title={item.detail}>
|
||||
{item.detail}
|
||||
</span>
|
||||
) : null}
|
||||
@@ -272,14 +287,23 @@ function ProgressList({ progress, running }: { progress: AgentChatProgress[]; ru
|
||||
<AnimatePresence initial={false} mode="wait">
|
||||
<motion.span
|
||||
key={item.status}
|
||||
data-progress-status={item.status}
|
||||
className={cn(
|
||||
"min-w-12 rounded-full px-2 py-0.5 text-center font-semibold",
|
||||
progressStatusMeta[item.status].className
|
||||
)}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
animate={
|
||||
item.status === "error"
|
||||
? { opacity: [0, 1, 1], x: [0, -2, 2, -1, 0] }
|
||||
: { opacity: 1, x: 0 }
|
||||
}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.14 }}
|
||||
transition={
|
||||
item.status === "error"
|
||||
? { duration: 0.24, ease: agentEase }
|
||||
: { duration: 0.14 }
|
||||
}
|
||||
>
|
||||
{progressStatusMeta[item.status].label}
|
||||
</motion.span>
|
||||
|
||||
Reference in New Issue
Block a user