初始化模版工程
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user