初始化模版工程

This commit is contained in:
Cloud Bot
2026-03-20 07:33:46 +00:00
commit 23717e0ecd
386 changed files with 51675 additions and 0 deletions

View File

@@ -0,0 +1,156 @@
import { memo } from 'react'
import { ArrowRight, Check, ClipboardList, Circle } from 'lucide-react'
import { cn } from '@/utils/cn'
import { useNovaKit } from '../../context/useNovaKit'
export interface TodoItem {
id?: string | number
title?: string
status?: 'pending' | 'completed' | 'in_progress' | string
[key: string]: unknown
}
export interface TodoListProps {
items: TodoItem[]
className?: string
}
function TodoListComponent({ items = [], className }: TodoListProps) {
const { agentName } = useNovaKit()
const todoItems = items
const totalCount = todoItems.length
const completedCount = todoItems.filter(item => item.status === 'completed').length
const isAllCompleted = totalCount > 0 && completedCount === totalCount
const displayCompletedCount =
totalCount === 0
? 0
: Math.min(totalCount, isAllCompleted ? completedCount : completedCount + 1)
const progressRatio =
totalCount === 0
? 0
: displayCompletedCount / totalCount
const progressPercent = Math.max(0, Math.min(100, progressRatio * 100))
return (
<div
className={cn(
'relative my-1 mb-4 max-w-3xl overflow-hidden rounded-xl border border-gray-200 bg-white',
className,
)}
>
{/* Header任务执行流水线 */}
<div className="flex items-center justify-between bg-gray-50 border-b border-gray-100 px-3 py-[5px]">
<div className="flex min-w-0 flex-1 items-center gap-2">
<div className="flex h-4 w-4 flex-none items-center justify-center text-muted-foreground">
<ClipboardList className="h-4 w-4" />
</div>
<div className="min-w-0">
<h2 className="truncate text-[14px] leading-[22px] text-foreground">
线
</h2>
<p className="truncate text-[12px] text-muted-foreground">{agentName}</p>
</div>
</div>
<div className="flex items-center gap-1 pl-3">
<span className="text-[12px] font-medium text-muted-foreground">Progress</span>
<span className="text-[12px] font-medium text-muted-foreground">
{displayCompletedCount} / {totalCount || 0}
</span>
</div>
</div>
{/* 列表内容:卡片列表布局 */}
<div className="relative overflow-hidden transition-all duration-200 ease-in-out">
<div className="relative overflow-visible bg-white px-3 py-2.5">
<div className="absolute left-0 top-0 h-[2px] w-full bg-border/70">
<div
className="h-full bg-primary transition-all duration-700"
style={{ width: `${progressPercent}%` }}
/>
</div>
<div className="flex flex-col pt-1">
{todoItems.map((item, index) => {
const status = item.status as string | undefined
const isCompleted = status === 'completed'
const isActive = status === 'in_progress'
const isPending = !isCompleted && !isActive
const stepLabel = `STEP ${index + 1} OF ${totalCount || 0}`
return (
<div
key={(item.id as string) ?? index}
className={cn(
'flex items-center justify-between gap-3 py-1',
isCompleted && 'opacity-70',
isPending && 'opacity-60',
)}
>
{/* 左侧:状态圆点 + 文案 */}
<div className="flex min-w-0 flex-1 items-center gap-2">
{/* 状态圆点 */}
<div className="relative flex flex-none items-center justify-center">
{isCompleted && (
<div className="flex h-4 w-4 items-center justify-center rounded-full bg-primary">
<Check className="h-2.5 w-2.5 text-white" />
</div>
)}
{isActive && (
<div className="flex h-4 w-4 items-center justify-center rounded-full bg-primary/12 text-primary">
<ArrowRight className="h-2.5 w-2.5" />
</div>
)}
{isPending && !isCompleted && !isActive && (
<div className="flex h-4 w-4 items-center justify-center rounded-full border border-gray-200 bg-white">
<Circle className="h-2.5 w-2.5 text-muted-foreground/55" />
</div>
)}
</div>
{/* 文案 */}
<div className="min-w-0 space-y-0.5">
<p
className={cn(
'truncate text-[14px] leading-[22px] font-normal',
isActive
? 'text-foreground'
: 'text-muted-foreground',
isCompleted && 'line-through decoration-border',
)}
>
{item.title as string}
</p>
{typeof (item as any).description === 'string' && (
<p className="truncate text-[12px] text-muted-foreground">
{(item as any).description as string}
</p>
)}
</div>
{isActive && (
<span className="h-1 w-1 flex-none rounded-full bg-primary animate-pulse" />
)}
</div>
{/* 右侧:步骤标签 */}
<span
className={cn(
'flex-none text-[10px] font-mono font-medium tracking-tight',
isActive
? 'text-primary'
: 'text-muted-foreground',
)}
>
{stepLabel}
</span>
</div>
)
})}
</div>
</div>
</div>
</div>
)
}
export const TodoList = memo(TodoListComponent)
export default TodoList