feat: major UI/UX overhaul, snippets system, and performance fixes
Some checks failed
Dev Deployment (Zero Downtime) / deploy-dev (push) Failing after 9m26s

This commit is contained in:
2026-02-16 12:31:40 +01:00
parent 6f62b37c3a
commit a5dba298f3
41 changed files with 1610 additions and 499 deletions

View File

@@ -0,0 +1,109 @@
"use client";
import React, { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Snippet } from "@/lib/directus";
import { X, Copy, Check, Hash } from "lucide-react";
export default function SnippetsClient({ initialSnippets }: { initialSnippets: Snippet[] }) {
const [selectedSnippet, setSelectedSnippet] = useState<Snippet | null>(null);
const [copied, setCopied] = useState(false);
const copyToClipboard = (code: string) => {
navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-8">
{initialSnippets.map((s, i) => (
<motion.button
key={s.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: i * 0.05 }}
onClick={() => setSelectedSnippet(s)}
className="text-left bg-white dark:bg-stone-900 rounded-[2.5rem] p-10 border border-stone-200/60 dark:border-stone-800/60 shadow-sm hover:shadow-xl hover:border-liquid-purple/40 transition-all group"
>
<div className="flex items-center gap-2 mb-6">
<div className="w-8 h-8 rounded-xl bg-stone-50 dark:bg-stone-800 flex items-center justify-center text-stone-400 group-hover:text-liquid-purple transition-colors">
<Hash size={16} />
</div>
<span className="text-[10px] font-black uppercase tracking-widest text-stone-400">{s.category}</span>
</div>
<h3 className="text-2xl font-black text-stone-900 dark:text-white uppercase tracking-tighter mb-4 group-hover:text-liquid-purple transition-colors">{s.title}</h3>
<p className="text-stone-500 dark:text-stone-400 text-sm line-clamp-2 leading-relaxed">
{s.description}
</p>
</motion.button>
))}
</div>
{/* Snippet Modal */}
<AnimatePresence>
{selectedSnippet && (
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 md:p-8">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setSelectedSnippet(null)}
className="absolute inset-0 bg-stone-950/60 backdrop-blur-md"
/>
<motion.div
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
className="relative w-full max-w-3xl bg-white dark:bg-stone-900 rounded-[2.5rem] shadow-2xl border border-stone-200 dark:border-stone-800 overflow-hidden flex flex-col max-h-[90vh]"
>
<div className="p-8 md:p-10 overflow-y-auto">
<div className="flex justify-between items-start mb-8">
<div>
<p className="text-[10px] font-black uppercase tracking-[0.2em] text-liquid-purple mb-2">{selectedSnippet.category}</p>
<h3 className="text-3xl font-black text-stone-900 dark:text-white uppercase tracking-tighter">{selectedSnippet.title}</h3>
</div>
<button
onClick={() => setSelectedSnippet(null)}
className="p-3 bg-stone-50 dark:bg-stone-800 rounded-full hover:bg-stone-100 dark:hover:bg-stone-700 transition-colors"
>
<X size={20} />
</button>
</div>
<p className="text-stone-600 dark:text-stone-400 mb-8 leading-relaxed">
{selectedSnippet.description}
</p>
<div className="relative group/code">
<div className="absolute top-4 right-4 flex gap-2">
<button
onClick={() => copyToClipboard(selectedSnippet.code)}
className="p-2.5 bg-white/10 backdrop-blur-md hover:bg-white/20 rounded-lg border border-white/10 transition-all text-white"
title="Copy Code"
>
{copied ? <Check size={16} className="text-emerald-400" /> : <Copy size={16} />}
</button>
</div>
<pre className="bg-stone-950 p-6 rounded-2xl overflow-x-auto text-sm font-mono text-stone-300 border border-stone-800 leading-relaxed">
<code>{selectedSnippet.code}</code>
</pre>
</div>
</div>
<div className="p-6 bg-stone-50 dark:bg-stone-800/50 border-t border-stone-100 dark:border-stone-800 text-center">
<button
onClick={() => setSelectedSnippet(null)}
className="text-[10px] font-black uppercase tracking-[0.2em] text-stone-400 hover:text-stone-900 dark:hover:text-white transition-colors"
>
Close Laboratory
</button>
</div>
</motion.div>
</div>
)}
</AnimatePresence>
</>
);
}

View File

@@ -0,0 +1,41 @@
import React from "react";
import { getSnippets } from "@/lib/directus";
import { Terminal, ArrowLeft, Code } from "lucide-react";
import Link from "next/link";
import SnippetsClient from "./SnippetsClient";
export default async function SnippetsPage({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
const snippets = await getSnippets(100) || [];
return (
<main className="min-h-screen bg-[#fdfcf8] dark:bg-stone-950 py-24 px-6 transition-colors duration-500">
<div className="max-w-7xl mx-auto">
<Link
href={`/${locale}`}
className="inline-flex items-center gap-2 text-[10px] font-black uppercase tracking-[0.3em] text-stone-400 hover:text-stone-900 dark:hover:text-white transition-all mb-12 group"
>
<ArrowLeft size={14} className="group-hover:-translate-x-1 transition-transform" />
Back to Portfolio
</Link>
<header className="mb-20">
<div className="flex items-center gap-4 mb-6">
<div className="w-12 h-12 rounded-2xl bg-stone-900 dark:bg-stone-50 flex items-center justify-center text-white dark:text-stone-900">
<Terminal size={24} />
</div>
<h1 className="text-5xl md:text-8xl font-black tracking-tighter uppercase text-stone-900 dark:text-stone-50">
The Lab<span className="text-liquid-purple">.</span>
</h1>
</div>
<p className="text-xl md:text-2xl font-light text-stone-500 max-w-2xl leading-relaxed">
A collection of technical snippets, configurations, and mental notes from my daily building process.
</p>
</header>
<SnippetsClient initialSnippets={snippets} />
</div>
</main>
);
}