import type { StudioFileAttachment, StudioPermissionDecision, StudioPermissionRequest, StudioRun, StudioSession, StudioTask, StudioWork, StudioWorkResult, } from '../protocol/studio-agent-types' import { truncateStudioText } from '../theme' interface PlotWorkListItem { work: StudioWork latestTask: StudioTask | null result: StudioWorkResult | null } interface PlotPreviewPanelProps { session: StudioSession | null works: PlotWorkListItem[] selectedWorkId: string | null work: StudioWork | null result: StudioWorkResult | null latestRun: StudioRun | null tasks: StudioTask[] requests: StudioPermissionRequest[] replyingPermissionIds: Record latestAssistantText: string errorMessage?: string | null onSelectWork: (workId: string) => void onReply: (requestId: string, reply: StudioPermissionDecision) => Promise | void } export function PlotPreviewPanel({ session, works, selectedWorkId, result, onSelectWork, }: PlotPreviewPanelProps) { const previewAttachment = result?.attachments?.find(isPreviewAttachment) ?? result?.attachments?.[0] ?? null const outputPath = formatOutputPath(previewAttachment, session) const stripItems = works.slice(0, 12) return (
{/* 顶部路径栏 - 采用极致消隐设计 */}
{outputPath}
READY _
{/* 画布主区域 - 对齐项目的大圆角与高级阴影 */}
{/* 这里就是你提到的右上角,现在我们改为半透明消隐设计 */}
Canvas Engine: Matplotlib
{/* 底部条状预览 - 增加呼吸感 */}
历史产出
{works.length.toString().padStart(2, '0')}
{stripItems.map((entry, index) => { const selected = entry.work.id === selectedWorkId const thumbnail = entry.result?.attachments?.find(isImageAttachment) ?? null return ( ) })}
) } function PlotPreviewSurface({ attachment, result, }: { attachment: StudioFileAttachment | null | undefined result: StudioWorkResult | null }) { if (attachment?.mimeType?.startsWith('image/') || isImagePath(attachment?.path)) { return (
{attachment?.name
) } if (result?.kind === 'failure-report') { return (
Render Failed
) } // 没有任何产出时直接返回空,保持界面洁净 return null } function isPreviewAttachment(attachment: { path: string; mimeType?: string } | undefined) { return isImageAttachment(attachment) } function formatOutputPath( attachment: StudioFileAttachment | null | undefined, session: StudioSession | null, ) { if (attachment?.name) { return attachment.name } if (attachment?.path) { if (attachment.path.startsWith('data:')) { return 'inline-preview.png' } return truncateStudioText(attachment.path, 88) } return session?.directory ?? '等待输出文件' } function isImageAttachment(attachment: { path: string; mimeType?: string } | undefined) { if (!attachment) { return false } return attachment.mimeType?.startsWith('image/') || isImagePath(attachment.path) } function isImagePath(path?: string) { return Boolean(path && /\.(png|jpg|jpeg|gif|webp|svg)$/i.test(path)) }