Spaces:
Sleeping
Sleeping
File size: 4,604 Bytes
98b952a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | "use client";
import { AnimatePresence, motion } from "framer-motion";
import {
Line,
LineChart,
ReferenceLine,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { RewardDeltaBadge } from "@/components/RewardDeltaBadge";
import { RewardBars } from "@/components/RewardBars";
import { ScriptPanel } from "@/components/ScriptPanel";
import { ArbitratorReasoning } from "@/components/ArbitratorReasoning";
export interface EpisodeSnapshot {
episode: number;
script: string;
reasoning: string[];
rewards: {
r1: number; r2: number; r3: number; r4: number; r5: number;
r6: number; r7: number; r8: number; r9: number; r10: number;
process: number; total: number;
};
}
interface Props {
episodes: EpisodeSnapshot[];
current: number;
historySeries: { episode: number; total: number }[];
}
export function LearningTimeline({ episodes, current, historySeries }: Props) {
const snap = episodes[current];
const prev = current > 0 ? episodes[current - 1] : null;
const delta = prev ? snap.rewards.total - prev.rewards.total : 0;
const improved = delta > 0.01;
return (
<div className="grid gap-5 lg:grid-cols-3">
{/* LEFT — Script */}
<div>
<AnimatePresence mode="wait">
<motion.div
key={current}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 10 }}
transition={{ duration: 0.35, ease: "easeInOut" }}
>
<ScriptPanel
script={snap.script}
meta={{ platform: "Reels", region: "India", niche: "Creator productivity" }}
/>
</motion.div>
</AnimatePresence>
</div>
{/* CENTER — Reasoning */}
<div>
<AnimatePresence mode="wait">
<motion.div
key={current}
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -8 }}
transition={{ duration: 0.4, ease: "easeInOut" }}
className={improved ? "ring-2 ring-emerald-500/50 rounded-2xl" : ""}
>
<ArbitratorReasoning before={snap.reasoning} after={snap.reasoning} />
</motion.div>
</AnimatePresence>
</div>
{/* RIGHT — Rewards */}
<div className="space-y-3">
<AnimatePresence mode="wait">
<motion.div
key={current}
initial={{ opacity: 0, x: 10 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -10 }}
transition={{ duration: 0.35, ease: "easeInOut" }}
>
<RewardBars data={snap.rewards} title="Reward Components (R1–R10)" />
</motion.div>
</AnimatePresence>
<Card>
<CardContent className="p-4 flex items-center justify-between">
<div>
<p className="text-xs text-purple-400/70">Total Reward</p>
<p className="text-2xl font-bold text-violet-400 tabular-nums">
{snap.rewards.total.toFixed(3)}
</p>
</div>
{prev && (
<RewardDeltaBadge
delta={(snap.rewards.total - prev.rewards.total) / prev.rewards.total}
/>
)}
</CardContent>
</Card>
{/* Progress chart */}
<Card>
<CardHeader className="pb-1">
<CardTitle className="text-sm text-white">Reward Over Episodes</CardTitle>
</CardHeader>
<CardContent className="h-36 px-2 pb-3">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={historySeries.slice(0, current + 1)}>
<XAxis dataKey="episode" tick={{ fontSize: 10, fill: "#a78bfa" }} />
<YAxis domain={[0, 1]} tick={{ fontSize: 10, fill: "#a78bfa" }} />
<Tooltip contentStyle={{ background: "#120f1e", border: "1px solid #2e2255", borderRadius: "0.5rem", color: "#ede9f8" }} />
<ReferenceLine y={historySeries[0]?.total} stroke="#4b3a7a" strokeDasharray="4 2" />
<Line
type="monotone"
dataKey="total"
stroke="#8b5cf6"
strokeWidth={2}
dot={false}
isAnimationActive
/>
</LineChart>
</ResponsiveContainer>
</CardContent>
</Card>
</div>
</div>
);
}
|