"use client"; import React, { useState, useEffect, useCallback, useMemo } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { Snippet } from "@/lib/directus"; import { X, Copy, Check, ChevronLeft, ChevronRight, Search } from "lucide-react"; // Color-coded language badges using the liquid design palette const LANG_STYLES: Record = { typescript: { bg: "bg-liquid-lavender/40", text: "text-purple-700 dark:text-purple-300", label: "TS" }, ts: { bg: "bg-liquid-lavender/40", text: "text-purple-700 dark:text-purple-300", label: "TS" }, javascript: { bg: "bg-liquid-amber/40", text: "text-amber-700 dark:text-amber-300", label: "JS" }, js: { bg: "bg-liquid-amber/40", text: "text-amber-700 dark:text-amber-300", label: "JS" }, python: { bg: "bg-liquid-sky/40", text: "text-sky-700 dark:text-sky-300", label: "PY" }, bash: { bg: "bg-liquid-mint/40", text: "text-emerald-700 dark:text-emerald-300", label: "SH" }, shell: { bg: "bg-liquid-mint/40", text: "text-emerald-700 dark:text-emerald-300", label: "SH" }, sh: { bg: "bg-liquid-mint/40", text: "text-emerald-700 dark:text-emerald-300", label: "SH" }, dockerfile: { bg: "bg-liquid-blue/40", text: "text-blue-700 dark:text-blue-300", label: "🐳" }, docker: { bg: "bg-liquid-blue/40", text: "text-blue-700 dark:text-blue-300", label: "🐳" }, css: { bg: "bg-liquid-pink/40", text: "text-pink-700 dark:text-pink-300", label: "CSS" }, scss: { bg: "bg-liquid-pink/40", text: "text-pink-700 dark:text-pink-300", label: "SCSS" }, go: { bg: "bg-liquid-teal/40", text: "text-teal-700 dark:text-teal-300", label: "GO" }, rust: { bg: "bg-liquid-peach/40", text: "text-orange-700 dark:text-orange-300", label: "RS" }, yaml: { bg: "bg-liquid-lime/40", text: "text-lime-700 dark:text-lime-300", label: "YAML" }, json: { bg: "bg-liquid-lime/40", text: "text-lime-700 dark:text-lime-300", label: "JSON" }, sql: { bg: "bg-liquid-coral/40", text: "text-red-700 dark:text-red-300", label: "SQL" }, nginx: { bg: "bg-liquid-teal/40", text: "text-teal-700 dark:text-teal-300", label: "NGINX" }, }; function getLangStyle(language: string) { return LANG_STYLES[language?.toLowerCase()] ?? { bg: "bg-liquid-purple/30", text: "text-purple-700 dark:text-purple-300", label: language?.toUpperCase() || "CODE", }; } function CodePreview({ code }: { code: string }) { const lines = code.split("\n").slice(0, 4); return (
      {lines.map((line, i) => (
        
{line || " "}
))} {code.split("\n").length > 4 && (
)}
); } export default function SnippetsClient({ initialSnippets }: { initialSnippets: Snippet[] }) { const [selectedSnippet, setSelectedSnippet] = useState(null); const [copied, setCopied] = useState(false); const [activeCategory, setActiveCategory] = useState("All"); const [search, setSearch] = useState(""); // Derived data const categories = useMemo(() => { const cats = Array.from(new Set(initialSnippets.map((s) => s.category))).sort(); return ["All", ...cats]; }, [initialSnippets]); const filtered = useMemo(() => { const q = search.toLowerCase(); return initialSnippets.filter((s) => { const matchCat = activeCategory === "All" || s.category === activeCategory; const matchSearch = !q || s.title.toLowerCase().includes(q) || s.description.toLowerCase().includes(q) || s.category.toLowerCase().includes(q) || s.language.toLowerCase().includes(q); return matchCat && matchSearch; }); }, [initialSnippets, activeCategory, search]); // Language badge for the currently open modal const modalLang = useMemo( () => (selectedSnippet ? getLangStyle(selectedSnippet.language) : null), [selectedSnippet] ); // Keyboard nav: ESC + arrows const handleKeyDown = useCallback( (e: KeyboardEvent) => { if (!selectedSnippet) return; if (e.key === "Escape") { setSelectedSnippet(null); } else if (e.key === "ArrowRight" || e.key === "ArrowDown") { const idx = filtered.findIndex((s) => s.id === selectedSnippet.id); if (idx < filtered.length - 1) setSelectedSnippet(filtered[idx + 1]); } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") { const idx = filtered.findIndex((s) => s.id === selectedSnippet.id); if (idx > 0) setSelectedSnippet(filtered[idx - 1]); } }, [selectedSnippet, filtered] ); useEffect(() => { window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [handleKeyDown]); const copyToClipboard = useCallback((code: string) => { navigator.clipboard.writeText(code); setCopied(true); setTimeout(() => setCopied(false), 2000); }, []); const currentIndex = selectedSnippet ? filtered.findIndex((s) => s.id === selectedSnippet.id) : -1; return ( <> {/* ── Filter & Search bar ── */}
{/* Search */}
setSearch(e.target.value)} className="w-full pl-10 pr-4 py-2.5 bg-white dark:bg-stone-900 border border-stone-200 dark:border-stone-800 rounded-2xl text-sm text-stone-900 dark:text-stone-100 placeholder:text-stone-400 focus:outline-none focus:border-liquid-purple transition-colors" />
{/* Category chips */}
{categories.map((cat) => ( ))}
{/* ── Empty state ── */} {filtered.length === 0 && (

No snippets found{search ? ` for "${search}"` : ""}.

)} {/* ── Snippet Grid ── */}
{filtered.map((s, i) => { const lang = getLangStyle(s.language); return ( setSelectedSnippet(s)} className="text-left bg-white dark:bg-stone-900 rounded-[2.5rem] p-8 border border-stone-200/60 dark:border-stone-800/60 shadow-sm hover:shadow-xl hover:border-liquid-purple/40 transition-all group flex flex-col" > {/* Header row: category + language badge */}
{s.category} {s.language && ( {lang.label} )}
{/* Title */}

{s.title}

{/* Description */}

{s.description}

{/* Mini code preview */}
); })}
{/* ── Snippet Modal ── */} {selectedSnippet && modalLang && (
setSelectedSnippet(null)} className="absolute inset-0 bg-stone-950/60 backdrop-blur-md" />
{/* Modal header */}

{selectedSnippet.category}

{selectedSnippet.language && ( {modalLang.label} )}

{selectedSnippet.title}

{selectedSnippet.description}

{/* Code block */}
                    {selectedSnippet.code}
                  
{/* Modal footer: navigation */}
{currentIndex + 1} / {filtered.length}
)}
); }