"use client"; import { motion } from "framer-motion"; import type { Activity } from "@/lib/schemas"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui"; export type ActivityComponentProps = { activity: A; /** True when this slide is the one currently filling the screen. */ active: boolean; /** Language of the deck, so AI grading answers in the same language. */ language?: string; /** Report the result to the feed (once per attempt). */ onAnswered: (correct: boolean, meta?: { userAnswer?: string }) => void; /** Scroll the feed to the next slide. */ onNext: () => void; isLast: boolean; }; export const TYPE_META: Record = { flashcard: { label: "Flashcard", icon: "🃏", tint: "tint-sky" }, multiple_choice: { label: "Multiple choice", icon: "🔘", tint: "tint-lilac" }, true_false: { label: "True or false", icon: "⚖️", tint: "tint-mint" }, fill_blank: { label: "Fill the blank", icon: "✏️", tint: "tint-peach" }, matching: { label: "Match up", icon: "🧲", tint: "tint-sky" }, ordering: { label: "Put in order", icon: "🔢", tint: "tint-lilac" }, short_answer: { label: "Short answer", icon: "💬", tint: "tint-rose" }, }; export function ActivityHeader({ type, topic, extra, }: { type: Activity["type"]; topic?: string; extra?: React.ReactNode; }) { const meta = TYPE_META[type]; return (
{meta.icon} {meta.label} {topic ? ( {topic} ) : null} {extra}
); } export function PromptCard({ children, className, }: { children: React.ReactNode; className?: string; }) { return (
{children}
); } export function QuestionText({ children, className }: { children: React.ReactNode; className?: string }) { return (

{children}

); } /** Standard tile used for answer options. */ export function OptionTile({ children, state = "idle", disabled, onClick, className, shortcut, }: { children: React.ReactNode; state?: "idle" | "selected" | "correct" | "wrong" | "dim"; disabled?: boolean; onClick?: () => void; className?: string; shortcut?: string; }) { const states: Record = { idle: "border-white/60 bg-white/55 hover:bg-white/80 dark:border-white/12 dark:bg-white/[0.07] dark:hover:bg-white/[0.12]", selected: "border-accent-400 bg-accent-50/80 dark:bg-accent-500/20", correct: "border-emerald-300 bg-emerald-50/90 text-emerald-950 dark:border-emerald-400/50 dark:bg-emerald-400/15 dark:text-emerald-50", wrong: "border-rose-300 bg-rose-50/90 text-rose-950 dark:border-rose-400/50 dark:bg-rose-400/15 dark:text-rose-50", dim: "border-white/40 bg-white/35 text-frost-400 dark:border-white/10 dark:bg-white/[0.04] dark:text-frost-500", }; return ( ); } export function FeedbackPanel({ correct, explanation, xp, onNext, isLast, headline, detail, children, }: { correct: boolean; explanation: string; xp?: number; onNext: () => void; isLast?: boolean; headline?: string; detail?: React.ReactNode; children?: React.ReactNode; }) { return (

{correct ? "✅" : "💧"} {headline ?? (correct ? "Nice one!" : "Not quite")}

{correct && xp ? ( +{xp} XP ) : null}
{detail ?
{detail}
: null}

Why: {explanation}

{children}
); } /** Small hint row under a question, e.g. "Tap a term, then its definition". */ export function Hint({ children }: { children: React.ReactNode }) { return

{children}

; }