Spaces:
Sleeping
Sleeping
| import React from "react"; | |
| import { TbExternalLink } from "react-icons/tb"; | |
| import { motion } from "framer-motion"; | |
| const projects = [ | |
| { | |
| id: 1, | |
| title: "Multi-Platform Document Intelligence App", | |
| status: "In progress", | |
| description: ( | |
| <> | |
| Cross-platform client that uploads documents and returns{" "} | |
| <span className="font-semibold">entity extraction</span>,{" "} | |
| <span className="font-semibold">concise summaries</span>, and{" "} | |
| <span className="font-semibold">semantic search</span> using an LLM-backed | |
| extraction & embedding service. Design includes client-side indexing, | |
| secure uploads, vector search, and cloud sync for offline-first usage. | |
| </> | |
| ), | |
| tech: [ | |
| "Kotlin Multiplatform", | |
| "Compose Multiplatform", | |
| "Ktor", | |
| "Supabase", | |
| "Docker", | |
| "Embeddings / LLM", | |
| ], | |
| image: "/assets/project-doc.png", | |
| link: "#" | |
| }, | |
| { | |
| id: 2, | |
| title: "End-to-End Media Processing Pipeline", | |
| status: "Prototype", | |
| description: ( | |
| <> | |
| Asynchronous processing pipeline for uploaded media: resilient{" "} | |
| <span className="font-semibold">transcoding</span>,{" "} | |
| <span className="font-semibold">speech-to-text</span> (Whisper / ASR), | |
| AI-powered <span className="font-semibold">moderation & summarization</span>, | |
| and webhook/push notifications. Built for horizontal scale and fault | |
| tolerance with signed artifact URLs for clients. | |
| </> | |
| ), | |
| tech: [ | |
| "Ktor", | |
| "FFmpeg", | |
| "Whisper / ASR", | |
| "Redis / RabbitMQ", | |
| "S3 / Supabase", | |
| "Docker workers", | |
| ], | |
| image: "/assets/project-media.png", | |
| link: "#" | |
| }, | |
| { | |
| id: 3, | |
| title: "Hermetic Reproducible ML Pipelines (MLOps PoC)", | |
| status: "Prototype", | |
| description: ( | |
| <> | |
| Hermetic, reproducible training + serving setup: Dockerized training | |
| environments, deterministic seeds, dataset versioning, experiment | |
| tracking with <span className="font-semibold">MLflow</span>, and REST | |
| inference endpoints for stable model deployment and CI-driven retraining. | |
| </> | |
| ), | |
| tech: [ | |
| "Docker", | |
| "Python", | |
| "PyTorch / scikit-learn", | |
| "MLflow", | |
| "GitHub Actions", | |
| "Ktor (inference)", | |
| ], | |
| image: "/assets/project-mlops.png", | |
| link: "#" | |
| } | |
| ]; | |
| export default function Projects() { | |
| return ( | |
| <section | |
| id="projects" | |
| className="bg-black px-5 lg:px-28 py-10 lg:py-16 text-white" | |
| aria-label="Projects" | |
| > | |
| <h2 className="text-2xl lg:text-4xl text-center"> | |
| My <span className="font-extrabold">Projects</span> | |
| </h2> | |
| <div className="mt-10 lg:mt-16 space-y-12"> | |
| {projects.map((project, index) => ( | |
| <motion.article | |
| key={project.id} | |
| className={`flex flex-col items-center gap-8 lg:gap-12 ${ | |
| index % 2 === 0 ? "lg:flex-row" : "lg:flex-row-reverse" | |
| }`} | |
| initial={{ opacity: 0, y: 40 }} | |
| whileInView={{ opacity: 1, y: 0 }} | |
| transition={{ type: "spring", stiffness: 80, damping: 12, delay: index * 0.15 }} | |
| viewport={{ once: true }} | |
| > | |
| {/* Image */} | |
| <div className="lg:w-1/2 w-full rounded-2xl overflow-hidden shadow-lg"> | |
| <img | |
| src={project.image} | |
| alt={`${project.title} screenshot`} | |
| className="w-full h-64 lg:h-80 object-cover transform hover:scale-105 transition-transform duration-500" | |
| loading="lazy" | |
| /> | |
| </div> | |
| {/* Content */} | |
| <div className="lg:w-1/2 w-full"> | |
| <div className="flex items-start justify-between"> | |
| <div> | |
| <h3 className="text-3xl lg:text-4xl font-extrabold"> | |
| {String(project.id).padStart(2, "0")} | |
| </h3> | |
| <p className="mt-1 text-xl lg:text-2xl font-bold">{project.title}</p> | |
| </div> | |
| <div className="text-sm lg:text-base"> | |
| <span className="inline-block bg-white/10 px-3 py-1 rounded-full text-white/90 text-xs lg:text-sm"> | |
| {project.status} | |
| </span> | |
| </div> | |
| </div> | |
| <p className="mt-4 text-sm lg:text-base text-gray-300 leading-relaxed"> | |
| {project.description} | |
| </p> | |
| {/* Tech badges */} | |
| <div className="mt-4 flex flex-wrap gap-2"> | |
| {project.tech.map((t) => ( | |
| <span | |
| key={t} | |
| className="text-xs lg:text-sm bg-white/6 border border-white/8 px-2 py-1 rounded-md text-gray-200" | |
| > | |
| {t} | |
| </span> | |
| ))} | |
| </div> | |
| {/* Links */} | |
| <div className="mt-4 flex items-center gap-4"> | |
| <a | |
| href={project.link} | |
| className="inline-flex items-center gap-2 text-white hover:underline" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| aria-label={`Open ${project.title} link`} | |
| > | |
| <TbExternalLink size={20} /> | |
| <span className="text-sm lg:text-base">View Repo / Demo</span> | |
| </a> | |
| </div> | |
| </div> | |
| </motion.article> | |
| ))} | |
| </div> | |
| </section> | |
| ); | |
| } | |