feat(viz): backtracking, greedy, intervals, matrix

This commit is contained in:
2025-09-03 21:47:33 +01:00
parent a9d71de4b1
commit 6c4b827c36
15 changed files with 3063 additions and 1 deletions

View File

@@ -0,0 +1,58 @@
'use client';
import { motion } from 'framer-motion';
import { cn } from '@/lib/utils';
import type { IntervalState } from '@/lib/visualizations/types';
interface IntervalBarProps {
interval: IntervalState;
pixelsPerUnit: number;
minValue: number;
height?: number;
className?: string;
}
const STATE_CLASSES = {
normal: 'bg-[var(--muted)] border-[var(--border)] text-[var(--foreground)]',
highlighted: 'bg-[var(--primary)]/30 border-[var(--primary)] text-[var(--primary)]',
merging: 'bg-amber-500/30 border-amber-500 text-amber-500',
merged: 'bg-green-500/30 border-green-500 text-green-500',
dimmed: 'bg-[var(--muted)]/30 border-[var(--border)]/50 text-[var(--muted-foreground)] opacity-40',
} as const;
export function IntervalBar({
interval,
pixelsPerUnit,
minValue,
height = 32,
className,
}: IntervalBarProps) {
const width = (interval.end - interval.start) * pixelsPerUnit;
const left = (interval.start - minValue) * pixelsPerUnit;
const label = interval.label ?? `[${interval.start},${interval.end}]`;
return (
<motion.div
layout
initial={{ opacity: 0, scale: 0.9 }}
animate={{
opacity: 1,
scale: interval.state === 'highlighted' || interval.state === 'merging' ? 1.02 : 1,
}}
exit={{ opacity: 0, scale: 0.9 }}
transition={{ duration: 0.2 }}
className={cn(
'absolute flex items-center justify-center rounded border-2 font-mono text-xs font-medium transition-colors duration-200',
STATE_CLASSES[interval.state],
className
)}
style={{
left: `${left}px`,
width: `${Math.max(width, 40)}px`,
height: `${height}px`,
}}
>
{label}
</motion.div>
);
}