"use client"; import { AnimatePresence, motion } from "framer-motion"; import { BookOpen, CheckCircle2, FileText, Inbox, Search, Sparkles } from "lucide-react"; import { StepControls } from "@/components/visualizations/shared/step-controls"; import { useSteppedVisualization } from "@/hooks/useSteppedVisualization"; import { cn } from "@/lib/utils"; type MemoryType = "feedback" | "project" | "reference"; interface MemoryFile { id: string; type: MemoryType; title: string; filename: string; description: string; body: string; relevant?: boolean; } const MEMORY_FILES: MemoryFile[] = [ { id: "visual-preference", type: "feedback", title: "Beginner visual preference", filename: "lcc_visual_preference.md", description: "Use concrete mental models for LCC web pages.", body: "Prefer cards, boards, shelves, and workbenches over abstract flowcharts.", relevant: true, }, { id: "project-path", type: "project", title: "LCC web paths", filename: "lcc_web_paths.md", description: "Web app reads root lesson folders and generated JSON.", body: "Build from web/, extract content from the root lesson directories.", }, { id: "test-command", type: "reference", title: "Verification commands", filename: "lcc_test_commands.md", description: "Useful smoke checks for the course website.", body: "Run npm run build, then browser-check /zh/s09 and /zh/s17.", }, ]; const STEPS = [ { title: "A Fact Worth Keeping", desc: "The user says something that should survive future sessions.", }, { title: "Stamp It After the Turn", desc: "Memory extraction happens after useful work, so the main loop stays focused.", }, { title: "Write One Memory File", desc: "The durable detail goes into a Markdown file with a readable title and metadata.", }, { title: "Update the Catalog", desc: "MEMORY.md is the cheap catalog: short enough to keep nearby.", }, { title: "A Future Request Arrives", desc: "Later, the agent sees a new request and the catalog, not the whole library.", }, { title: "Catalog Picks One", desc: "Selection chooses the one memory file that is relevant now.", }, { title: "Build the Reading Stack", desc: "Only the selected memory joins the current request before the model call.", }, { title: "Continuity Without Clutter", desc: "The answer reflects old context while unrelated memories stay on the shelf.", }, ] as const; function typeClass(type: MemoryType): string { if (type === "feedback") return "bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-200"; if (type === "project") return "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-200"; return "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/30 dark:text-emerald-200"; } function Surface({ title, icon, active, children, className, }: { title: string; icon: React.ReactNode; active: boolean; children: React.ReactNode; className?: string; }) { return (
{icon} {title}
{children}
); } function QuoteCard({ children }: { children: React.ReactNode }) { return (
{children}
); } function CatalogRow({ file, visible, selected }: { file: MemoryFile; visible: boolean; selected: boolean }) { if (!visible) return null; return (
{file.title}
{file.type}
{file.description}
{file.filename}
); } function MemoryDetail({ file, selected }: { file: MemoryFile; selected: boolean }) { return (
{file.title}
{file.filename}
{selected && ( selected )}
{file.body}
); } function EmptyState({ label }: { label: string }) { return (
{label}
); } export default function MemoryVisualization({ title }: { title?: string }) { const vis = useSteppedVisualization({ totalSteps: STEPS.length, autoPlayInterval: 2500 }); const step = vis.currentStep; const current = STEPS[step]; const selectedFile = MEMORY_FILES[0]; const catalogVisible = step >= 3; const futureVisible = step >= 4; const selected = step >= 5; const injected = step >= 6; return (

{title || "Memory Library"}

{["learn", "catalog", "recall"].map((label, index) => { const active = (index === 0 && step <= 2) || (index === 1 && (step === 3 || selected)) || (index === 2 && futureVisible); return (
{index + 1}. {label}
); })}
} active={step <= 2}>
"Please keep LCC pages concrete for beginners." {step >= 1 && (
Memory extractor stamp
Save a durable preference after the useful work is done.
)}
{step >= 2 && }
: } active={futureVisible}>
{!futureVisible && } {futureVisible && "Continue improving the web lesson visuals."} {selected && ( Catalog search selects lcc_visual_preference.md )} {injected && (
Reading stack before LLM
current request
selected memory detail
{step >= 7 && (
answer keeps the user's preference
)}
)}
} active={catalogVisible || selected} className="mt-3" >
MEMORY.md catalog
{MEMORY_FILES.map((file, index) => ( = 4)} selected={selected && file.relevant === true} /> ))} {!catalogVisible && }
Memory file preview
{step >= 2 ? (
{MEMORY_FILES.slice(1).map((file) => (
{file.title}
not loaded
{file.description}
))}
) : ( )}
Beginner rule: the catalog stays cheap and readable; full memory files are borrowed only when the current request needs them.
); }