初始化模版工程

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,122 @@
import React from 'react'
import { GalleryHorizontal } from 'lucide-react'
import { cn } from '@/utils/cn'
interface SlideFile {
id: string
title?: string
summary?: string
path?: string
filename?: string
}
interface SlideOutlineOutput {
slide_files?: SlideFile[]
project_path?: string
}
export interface SlideOutlineActionProps {
/** action 名称(如"正在初始化PPT大纲" */
name?: string
/** 参数(如主标题) */
arguments?: string[]
/** tool_output包含 slide_files */
toolOutput?: unknown
/** 点击整体卡片的回调(对应 BlockAction 的 onClick */
onClick?: () => void
className?: string
}
/**
* slide_init 专属渲染组件
* 对应 Remix: BlockAction + SlideOutline (compact)
*/
function InnerSlideOutlineAction({
name,
arguments: args,
toolOutput,
onClick,
className,
}: SlideOutlineActionProps) {
const output = toolOutput as SlideOutlineOutput | undefined
const slideFiles = output?.slide_files || []
return (
<div
className={cn(
'flex flex-col my-2 rounded-lg border border-border/60 bg-background overflow-hidden',
'w-full max-w-[480px]',
onClick && 'cursor-pointer hover:border-border transition-colors',
className,
)}
onClick={onClick}
role={onClick ? 'button' : undefined}
tabIndex={onClick ? 0 : undefined}
onKeyDown={onClick ? e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick() } } : undefined}
>
{/* 标题栏 */}
<div className="shrink-0 h-9 px-3 flex items-center gap-1.5 border-b border-border/50 bg-muted/40">
<GalleryHorizontal className="w-4 h-4 text-muted-foreground shrink-0" />
<span className="text-sm font-medium text-foreground shrink-0">
{name || '初始化幻灯片'}
</span>
{args && args.length > 0 && (
<span className="text-sm text-muted-foreground truncate">
{args.join(' ')}
</span>
)}
</div>
{/* 幻灯片大纲列表 */}
<div className="flex-1 overflow-auto max-h-52">
{slideFiles.length === 0 ? (
<div className="flex flex-col gap-2 p-3">
{[1, 2, 3].map(i => (
<div key={i} className="flex gap-3 animate-pulse">
<div className="w-7 h-4 bg-muted rounded mt-1 shrink-0" />
<div className="flex-1 space-y-1.5">
<div className="h-4 bg-muted rounded w-3/4" />
<div className="h-3 bg-muted rounded w-full" />
</div>
</div>
))}
</div>
) : (
<div className="divide-y divide-border/40">
{slideFiles.map((slide, index) => (
<div key={slide.id || index} className="flex bg-background hover:bg-muted/20 transition-colors">
<span className="shrink-0 w-8 py-3 pl-3 text-sm text-muted-foreground italic">
P{index + 1}
</span>
<div className="flex-1 px-3 py-3 flex flex-col gap-1 min-w-0">
{slide.title ? (
<h3 className="text-sm font-medium text-foreground leading-tight">
{slide.title}
</h3>
) : (
<div className="h-4 bg-muted rounded animate-pulse w-1/2" />
)}
{slide.summary ? (
<p className="text-xs text-muted-foreground leading-relaxed line-clamp-2">
{slide.summary}
</p>
) : (
<div className="h-3 bg-muted rounded animate-pulse w-3/4" />
)}
</div>
</div>
))}
</div>
)}
</div>
{/* 底部渐变遮罩(对应 Remix BlockAction 的渐变效果) */}
{slideFiles.length > 3 && (
<div className="h-8 absolute left-0 bottom-0 bg-gradient-to-b from-transparent to-background pointer-events-none" />
)}
</div>
)
}
export const SlideOutlineAction = React.memo(InnerSlideOutlineAction)
export default SlideOutlineAction