feat(web): live step list, option choice card and inline approval card; drop run mode switch

- session page shows the agent.plan TodoList (drawer: one-line progress); options and approval
  cards sit at their step; composer has a "先看方案再执行" toggle instead of the mode switch
- settings expose option timeout, timeout action and approval validity; quick goals follow glossary

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-28 21:07:01 +08:00
1 parent cf2c4ff0e6
commit 9649854ab3
15 files changed
+731 -205

No files matched your search

@@ -2,13 +2,14 @@
// Copyright (c) 2026 Saurabh Chauhan. MIT License, see ./LICENSE-agentui.md
// 改造:SVG 状态图标保留几何(虚线圈=待办、弧线=进行中、勾=完成、叉=取消),pathLength 弹簧动画换成
// CSS 过渡/animate-spin(motion-reduce 关闭);完成色 emerald → success,取消 rose → destructive,且都配 sr-only 文字;
// 表头完成数去掉滚字;折叠用 Disclosure;maxHeight 改成 Tailwind 类;新增 `error` 状态(工具失败)与 `detail` 槽。
// 表头完成数去掉滚字;折叠用 Disclosure;maxHeight 改成 Tailwind 类;新增 `error` 状态(工具失败)、
// `skipped` 状态(Agent 步骤清单里跳过的步骤)与 `detail` 槽。
import { ChevronDown, ListTodo } from "lucide-react";
import { type ReactNode, useEffect, useId, useRef, useState } from "react";
import { cn } from "~/lib/utils.ts";
import { CHEVRON_CLASS, Disclosure } from "./disclosure.tsx";
export type TodoItemStatus = "pending" | "in-progress" | "completed" | "cancelled" | "error";
export type TodoItemStatus = "pending" | "in-progress" | "completed" | "cancelled" | "error" | "skipped";
export interface TodoItem {
id: string;
@@ -23,6 +24,7 @@ const LABEL: Record<TodoItemStatus, string> = {
completed: "已完成",
cancelled: "被拒绝",
error: "失败",
skipped: "已跳过",
};
function StatusIcon({ status }: { status: TodoItemStatus }) {
@@ -45,7 +47,7 @@ function StatusIcon({ status }: { status: TodoItemStatus }) {
fillOpacity={status === "completed" ? 0.1 : 0}
stroke="currentColor"
strokeWidth="1.5"
strokeDasharray={status === "pending" ? "2 3" : undefined}
strokeDasharray={status === "pending" || status === "skipped" ? "2 3" : undefined}
className={cn(status === "in-progress" && "opacity-20")}
/>
{status === "in-progress" ? (
@@ -72,6 +74,9 @@ function StatusIcon({ status }: { status: TodoItemStatus }) {
strokeLinejoin="round"
/>
) : null}
{status === "skipped" ? (
<path d="M8 12h8" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
) : null}
{status === "cancelled" || status === "error" ? (
<path
d="M8.5 8.5 15.5 15.5M15.5 8.5 8.5 15.5"
@@ -162,6 +167,7 @@ export function TodoList({
s === "pending" && "text-muted-foreground",
s === "in-progress" && "text-foreground",
s === "completed" && "text-muted-foreground line-through decoration-foreground/30",
s === "skipped" && "text-muted-foreground",
(s === "cancelled" || s === "error") && "text-destructive",
)}
>