ManimCat / frontend /src /studio /plot /preview /PlotHistoryStrip.tsx
Bin29's picture
Sync from main: e764154 feat(plot-skill): add math-exam-diagram SKILL.md for exam-style math figures
abcf568
import { useI18n } from '../../../i18n'
interface PlotHistoryEntry {
workId: string
title: string
imageIndex: number
attachment: {
path: string
name?: string
}
}
interface PlotHistoryStripProps {
entries: PlotHistoryEntry[]
selectedWorkId: string | null
selectedImageIndex: number
draggingWorkId: string | null
onSelect: (workId: string, imageIndex: number) => void
onDragStart: (workId: string) => void
onDrop: (workId: string) => void
onDragEnd: () => void
}
export function PlotHistoryStrip({
entries,
selectedWorkId,
selectedImageIndex,
draggingWorkId,
onSelect,
onDragStart,
onDrop,
onDragEnd,
}: PlotHistoryStripProps) {
const { t } = useI18n()
return (
<div className="mt-8">
<div className="flex items-center justify-between px-2">
<div className="flex items-center gap-3">
<div className="text-[10px] font-bold uppercase tracking-[0.4em] text-text-secondary/35">{t('studio.plot.history')}</div>
<div className="h-px w-8 bg-border/10" />
<span className="font-mono text-[10px] text-text-secondary/40">
{entries.length.toString().padStart(2, '0')}
</span>
</div>
</div>
<div className="mt-4 flex min-w-0 gap-4 overflow-x-auto pb-4 pt-1">
{entries.map((entry, index) => {
const selected = entry.workId === selectedWorkId && entry.imageIndex === selectedImageIndex
return (
<button
key={`${entry.workId}-${entry.imageIndex}-${entry.attachment.path}`}
type="button"
draggable
onClick={() => onSelect(entry.workId, entry.imageIndex)}
onDragStart={() => onDragStart(entry.workId)}
onDragOver={(event) => event.preventDefault()}
onDrop={() => onDrop(entry.workId)}
onDragEnd={onDragEnd}
className={`group relative flex h-20 w-32 shrink-0 items-center justify-center overflow-hidden rounded-2xl transition-all duration-500 ${
selected
? 'scale-[0.96] border border-accent/25 bg-bg-secondary/60 shadow-inner'
: 'border border-transparent bg-bg-secondary/30 hover:scale-[0.98] hover:bg-bg-secondary/50'
} ${draggingWorkId === entry.workId ? 'opacity-50' : ''}`}
>
<img
src={entry.attachment.path}
alt={entry.attachment.name ?? entry.title}
className={`h-full w-full object-cover transition-transform duration-700 ${selected ? 'scale-100' : 'scale-110 opacity-60 group-hover:scale-100 group-hover:opacity-100'}`}
/>
<div className="pointer-events-none absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/45 to-transparent px-2 py-1 text-left">
<div className="font-mono text-[9px] uppercase tracking-[0.18em] text-white/80">
{String(index + 1).padStart(2, '0')}
</div>
</div>
{selected && <div className="pointer-events-none absolute inset-0 bg-accent/5" />}
</button>
)
})}
</div>
</div>
)
}