feat: add AboutSummary component with personal introduction and game preview
feat: create Chip component for displaying technology tags feat: implement ExperienceCard component to showcase job experiences feat: add Experience component to list multiple job experiences feat: create Footer component with social links and copyright information feat: implement Hero component for the landing section with social links feat: add ImageCarousel component for displaying project images feat: create Navbar component with scroll effect and navigation links feat: implement ProjectItem component to display individual project details feat: add Skills component to showcase technical skills feat: create data module with skills, jobs, and projects information feat: define types for Skill, Job, and Project in types module chore: update package.json with new dependencies for Tailwind CSS and Lucide icons chore: add CV PDF file to public directory chore: remove unused SVG files from public directory chore: add new images for background and hero sections feat: implement formatDate utility function for date formatting
206
app/about/page.tsx
Normal file
@@ -0,0 +1,206 @@
|
||||
import Image from "next/image";
|
||||
import Chip from "@/components/chip";
|
||||
import { Metadata } from "next";
|
||||
import { Check } from "lucide-react";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "About Me | Gabriel Kaszewski",
|
||||
description:
|
||||
"Learn more about Gabriel Kaszewski, his skills, and his journey as a developer.",
|
||||
};
|
||||
|
||||
const hobbies = [
|
||||
"Programming 💻",
|
||||
"Filmmaking 🎥",
|
||||
"Gaming 🕹️",
|
||||
"Playing guitar 🎸",
|
||||
];
|
||||
const interests = [
|
||||
"Computer Science 💾",
|
||||
"Sci-Fi Books📚",
|
||||
"Astronomy 🔭",
|
||||
"Sports 🏅",
|
||||
"History 🏰",
|
||||
];
|
||||
const futureGoals = [
|
||||
"Deepen my expertise in Rust for high-performance applications.",
|
||||
"Contribute to impactful open-source projects.",
|
||||
"Develop and release my first full-fledged indie game.",
|
||||
];
|
||||
const faqItems = [
|
||||
{
|
||||
q: "How old were you when you started programming?",
|
||||
a: "I was 11 years old 🧑💻.",
|
||||
},
|
||||
{
|
||||
q: "How did you learn programming?",
|
||||
a: "I read books 📖 and practiced a lot.",
|
||||
},
|
||||
{
|
||||
q: "Are you studying Computer Science?",
|
||||
a: "No, I have a degree in Bioinformatics, which is a closely related field.",
|
||||
},
|
||||
{
|
||||
q: "Which programming language do you recommend for a beginner?",
|
||||
a: "The language doesn't really matter, just choose one you like and dive in 🌊.",
|
||||
},
|
||||
{
|
||||
q: "What was your first programming language?",
|
||||
a: "My journey began with C++ 🖥️.",
|
||||
},
|
||||
{
|
||||
q: "What is your favorite programming language?",
|
||||
a: "I enjoy writing in C, Rust, and Python 🐍. But Rust is the best 💖🦀",
|
||||
},
|
||||
];
|
||||
|
||||
const calculateAge = (birthDate: string): number => {
|
||||
const today = new Date();
|
||||
const birth = new Date(birthDate);
|
||||
let age = today.getFullYear() - birth.getFullYear();
|
||||
const monthDifference = today.getMonth() - birth.getMonth();
|
||||
if (
|
||||
monthDifference < 0 ||
|
||||
(monthDifference === 0 && today.getDate() < birth.getDate())
|
||||
) {
|
||||
age--;
|
||||
}
|
||||
return age;
|
||||
};
|
||||
|
||||
const AboutPage = () => {
|
||||
const age = calculateAge("2002-02-27");
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col items-center gap-8 p-4 pt-24 text-white">
|
||||
<div className="flex flex-col items-center justify-center gap-2 p-4 backdrop-blur-sm glass-effect glossy-effect bottom gloss-highlight rounded-lg shadow-lg">
|
||||
<Image
|
||||
src="/images/ja.avif"
|
||||
alt="A photo of Gabriel Kaszewski"
|
||||
width={300}
|
||||
height={300}
|
||||
className="object-cover rounded-md shadow-lg"
|
||||
priority
|
||||
/>
|
||||
<h2 className="mt-4 text-2xl font-bold">Hello, I'm Gabriel! 👋</h2>
|
||||
</div>
|
||||
|
||||
<div className="prose prose-invert lg:prose-lg xl:prose-xl max-w-3xl mx-auto w-full">
|
||||
<section id="more-info">
|
||||
<h1 className="text-center">More info about me! 💡</h1>
|
||||
<p>
|
||||
Hi! I am Gabriel and I am {age} years old. I graduated in
|
||||
Bioinformatics from the University of Gdańsk 🏫. I'm fluent in
|
||||
Polish and English and currently work as a Python Developer at
|
||||
digimonkeys.com 🐒💻.
|
||||
</p>
|
||||
<p>
|
||||
I have co-authored one scientific article, which you can read{" "}
|
||||
<a
|
||||
target="_blank"
|
||||
href="http://dx.doi.org/10.1038/s41598-023-44488-7"
|
||||
className="text-yellow-400 underline"
|
||||
>
|
||||
here
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section
|
||||
id="hobbies&interests"
|
||||
className="not-prose flex flex-col sm:flex-row gap-8 mt-12"
|
||||
>
|
||||
<div className="flex-1">
|
||||
<h2 className="text-center">Hobbies 🎮🎸</h2>
|
||||
<div className="flex flex-wrap items-center justify-center gap-4 mt-4">
|
||||
{hobbies.map((hobby) => (
|
||||
<Chip key={hobby} text={hobby} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h2 className="text-center">Interests 🌌📚</h2>
|
||||
<div className="flex flex-wrap items-center justify-center gap-4 mt-4">
|
||||
{interests.map((interest) => (
|
||||
<Chip key={interest} text={interest} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="philosophy" className="mt-12">
|
||||
<h1>My Philosophy 🧠</h1>
|
||||
<p>
|
||||
I believe much of today's software is bloated, inefficient, and
|
||||
disrespectful of the user's resources. My passion, which
|
||||
started with a simple curiosity at age 11, is to build a better
|
||||
alternative. I focus on creating software that is{" "}
|
||||
<strong className="text-yellow-400">
|
||||
fast, reliable, and genuinely intuitive
|
||||
</strong>
|
||||
, guided by the principles of clean and efficient code.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="toolkit">
|
||||
<h1>My Toolkit 🛠️</h1>
|
||||
<div className="not-prose bg-black/20 backdrop-blur-sm glass-effect rounded-2xl p-4 text-white text-shadow-sm">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<h3 className="text-xl font-bold">OS & Hardware</h3>
|
||||
<p>
|
||||
Arch Linux, Custom-built PC (Ryzen 7 5800X3D, RTX 4070 Ti,
|
||||
48GB RAM)
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold">Editor</h3>
|
||||
<p>Jetbrains IDEs (Pycharm, Rider) & VS Code</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold">Primary Languages</h3>
|
||||
<p>Rust, Python, C#, TypeScript</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold">Favorite Tech</h3>
|
||||
<p>Axum, Godot, React, TailwindCSS</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="future-goals" className="mt-12">
|
||||
<h1>Future Goals 🚀</h1>
|
||||
<p>
|
||||
I'm always eager to learn and grow. My goal is to continue
|
||||
honing my skills in backend development and system architecture.
|
||||
Here's what's on my radar:
|
||||
</p>
|
||||
<ul className="list-none p-0">
|
||||
{futureGoals.map((goal) => (
|
||||
<li key={goal} className="flex items-center gap-2 not-prose mb-2">
|
||||
<Check className="text-yellow-400 flex-shrink-0" size={20} />
|
||||
<span>{goal}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="faq" className="mt-12">
|
||||
<h1>FAQ ❓</h1>
|
||||
<div className="not-prose flex flex-col gap-4">
|
||||
{faqItems.map((item) => (
|
||||
<div key={item.q}>
|
||||
<h3 className="text-xl font-bold">{item.q}</h3>
|
||||
<p>{item.a}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AboutPage;
|
BIN
app/favicon.ico
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 4.2 KiB |
166
app/globals.css
@@ -1,4 +1,6 @@
|
||||
@import "tailwindcss";
|
||||
@plugin "@tailwindcss/typography";
|
||||
@plugin "tailwindcss-motion";
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
@@ -6,10 +8,30 @@
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--card: hsl(0 0% 100%);
|
||||
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
|
||||
--gradient-fa-blue: 135deg, hsl(217 91% 60%) 0%, hsl(200 90% 70%) 100%;
|
||||
--gradient-fa-green: 135deg, hsl(155 70% 55%) 0%, hsl(170 80% 65%) 100%;
|
||||
--gradient-fa-card: 180deg, hsl(var(--card)) 0%, hsl(var(--card)) 90%,
|
||||
hsl(var(--card)) 100%;
|
||||
--gradient-fa-gloss: 135deg, rgba(255, 255, 255, 0.2) 0%,
|
||||
rgba(255, 255, 255, 0) 100%;
|
||||
|
||||
--shadow-fa-sm: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||
--shadow-fa-md: 0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06);
|
||||
--shadow-fa-lg: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||
--fa-inner: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
|
||||
--text-shadow-default: 0 1px 1px rgba(0, 0, 0, 0.2);
|
||||
--text-shadow-sm: 0 1px 0px rgba(255, 255, 255, 0.4);
|
||||
--text-shadow-md: 0 2px 2px rgba(0, 0, 0, 0.2);
|
||||
--text-shadow-lg: 0 4px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@@ -20,7 +42,149 @@
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
/* background: var(--background); */
|
||||
background-image: url("/images/background.avif");
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
background-position: center;
|
||||
color: var(--foreground);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
glossy-effect::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50%;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0.4) 0%,
|
||||
rgba(255, 255, 255, 0.1) 100%
|
||||
);
|
||||
opacity: 0.8;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.glossy-effect.bottom::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 30%;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.1) 0%,
|
||||
rgba(0, 0, 0, 0) 100%
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.fa-gradient-blue {
|
||||
background: linear-gradient(var(--gradient-fa-blue));
|
||||
}
|
||||
.fa-gradient-green {
|
||||
background: linear-gradient(var(--gradient-fa-green));
|
||||
}
|
||||
.fa-gradient-card {
|
||||
background: linear-gradient(var(--gradient-fa-card));
|
||||
}
|
||||
.fa-gloss {
|
||||
position: relative;
|
||||
}
|
||||
.fa-gloss::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50%;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(var(--gradient-fa-gloss));
|
||||
opacity: 0.8;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
.fa-gloss.bottom::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 30%;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.1) 0%,
|
||||
rgba(0, 0, 0, 0) 100%
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.shadow-fa-sm {
|
||||
box-shadow: var(--shadow-fa-sm), var(--fa-inner);
|
||||
}
|
||||
.shadow-fa-md {
|
||||
box-shadow: var(--shadow-fa-md), var(--fa-inner);
|
||||
}
|
||||
.shadow-fa-lg {
|
||||
box-shadow: var(--shadow-fa-lg), var(--fa-inner);
|
||||
}
|
||||
.text-shadow-default {
|
||||
text-shadow: var(--text-shadow-default);
|
||||
}
|
||||
.text-shadow-sm {
|
||||
text-shadow: var(--text-shadow-sm);
|
||||
}
|
||||
.text-shadow-md {
|
||||
text-shadow: var(--text-shadow-md);
|
||||
}
|
||||
.text-shadow-lg {
|
||||
text-shadow: var(--text-shadow-lg);
|
||||
}
|
||||
|
||||
.glass-effect {
|
||||
@apply backdrop-blur-lg border border-white/20 shadow-fa-lg;
|
||||
}
|
||||
|
||||
.gloss-highlight::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 60%;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0.5) 0%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.my-animate {
|
||||
opacity: 1 !important;
|
||||
transform: translateY(0) !important;
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.hidden-for-animation {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,9 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import Navbar from "@/components/navbar";
|
||||
import Footer from "@/components/footer";
|
||||
import Image from "next/image";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
@@ -13,8 +16,8 @@ const geistMono = Geist_Mono({
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
title: "Gabriel Kaszewski",
|
||||
description: "Welcome to my portfolio",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
@@ -25,9 +28,11 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased relative`}
|
||||
>
|
||||
<Navbar />
|
||||
{children}
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
107
app/page.tsx
@@ -1,103 +1,18 @@
|
||||
import Image from "next/image";
|
||||
import AboutSummary from "@/components/about-summary";
|
||||
import Experience from "@/components/experience";
|
||||
import Hero from "@/components/hero";
|
||||
import Skills from "@/components/skills";
|
||||
import { skills, jobs } from "@/lib/data";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="font-sans grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
|
||||
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol className="font-mono list-inside list-decimal text-sm/6 text-center sm:text-left">
|
||||
<li className="mb-2 tracking-[-.01em]">
|
||||
Get started by editing{" "}
|
||||
<code className="bg-black/[.05] dark:bg-white/[.06] font-mono font-semibold px-1 py-0.5 rounded">
|
||||
app/page.tsx
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li className="tracking-[-.01em]">
|
||||
Save and see your changes instantly.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
</a>
|
||||
<a
|
||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
<div className="flex flex-col items-center w-full">
|
||||
<Hero />
|
||||
<div className="container mx-auto px-4">
|
||||
<AboutSummary />
|
||||
<Skills skills={skills} />
|
||||
<Experience jobs={jobs} />
|
||||
</div>
|
||||
</main>
|
||||
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
139
app/projects/[projectName]/page.tsx
Normal file
@@ -0,0 +1,139 @@
|
||||
import { projects } from "@/lib/data";
|
||||
import { Project } from "@/lib/types";
|
||||
import Chip from "@/components/chip";
|
||||
import { Metadata } from "next";
|
||||
import Image from "next/image";
|
||||
import { Github, Eye, CloudDownload } from "lucide-react";
|
||||
import { notFound } from "next/navigation";
|
||||
import { remark } from "remark";
|
||||
import html from "remark-html";
|
||||
|
||||
function getProjectByName(name: string): Project | undefined {
|
||||
const decodedName = decodeURIComponent(name.replace(/\+/g, " "));
|
||||
return projects.find(
|
||||
(p) => p.name.toLowerCase() === decodedName.toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return projects.map((project) => ({
|
||||
projectName: encodeURIComponent(project.name.replace(/\s/g, "+")),
|
||||
}));
|
||||
}
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: { projectName: string };
|
||||
}): Promise<Metadata> {
|
||||
const { projectName } = await params;
|
||||
const project = getProjectByName(projectName);
|
||||
|
||||
if (!project) {
|
||||
return { title: "Project Not Found" };
|
||||
}
|
||||
|
||||
return {
|
||||
title: `${project.name} | Gabriel Kaszewski`,
|
||||
description: project.short_description,
|
||||
};
|
||||
}
|
||||
|
||||
async function getProjectData(project: Project) {
|
||||
const processedContent = await remark()
|
||||
.use(html)
|
||||
.process(project.description);
|
||||
return processedContent.toString();
|
||||
}
|
||||
|
||||
export default async function ProjectDetailPage({
|
||||
params,
|
||||
}: {
|
||||
params: { projectName: string };
|
||||
}) {
|
||||
const { projectName } = await params;
|
||||
const project = getProjectByName(projectName);
|
||||
const descriptionHtml = project ? await getProjectData(project) : "";
|
||||
|
||||
if (!project) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const hasLinks =
|
||||
project.github_url || project.visit_url || project.download_url;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-full h-full min-h-screen gap-4 p-4 pt-24">
|
||||
<div className="prose prose-invert lg:prose-lg xl:prose-xl max-w-4xl mx-auto">
|
||||
<h1>{project.name}</h1>
|
||||
|
||||
<section dangerouslySetInnerHTML={{ __html: descriptionHtml }} />
|
||||
|
||||
<section className="not-prose mt-12 flex flex-col items-center">
|
||||
<h2>Technologies</h2>
|
||||
<div className="flex flex-wrap justify-center gap-2 mt-4">
|
||||
{project.technologies.map((tech) => (
|
||||
<Chip key={tech} text={tech} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{project.thumbnails && project.thumbnails.length > 0 && (
|
||||
<section className="not-prose mt-12 flex flex-col items-center">
|
||||
<h2>Gallery</h2>
|
||||
<div className="flex flex-col gap-4 mt-4">
|
||||
{project.thumbnails.map((thumb, index) => (
|
||||
<Image
|
||||
key={index}
|
||||
src={thumb}
|
||||
alt={`${project.name} thumbnail ${index + 1}`}
|
||||
width={1024}
|
||||
height={768}
|
||||
className="rounded-lg shadow-lg"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{hasLinks && (
|
||||
<section className="not-prose mt-12 flex flex-col items-center">
|
||||
<h2>Links</h2>
|
||||
<div className="flex flex-col sm:flex-row gap-4 mt-4">
|
||||
{project.github_url && (
|
||||
<a
|
||||
href={project.github_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 p-2 px-4 text-center glass-effect glossy-effect bottom gloss-highlight rounded-2xl bg-yellow-400 hover:bg-yellow-500 text-black font-bold"
|
||||
>
|
||||
<Github size={20} /> CODE
|
||||
</a>
|
||||
)}
|
||||
{project.visit_url && (
|
||||
<a
|
||||
href={project.visit_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 p-2 px-4 text-center glass-effect glossy-effect bottom gloss-highlight rounded-2xl bg-yellow-400 hover:bg-yellow-500 text-black font-bold"
|
||||
>
|
||||
<Eye size={20} /> LIVE
|
||||
</a>
|
||||
)}
|
||||
{project.download_url && (
|
||||
<a
|
||||
href={project.download_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 p-2 px-4 text-center glass-effect glossy-effect bottom gloss-highlight rounded-2xl bg-yellow-400 hover:bg-yellow-500 text-black font-bold"
|
||||
>
|
||||
<CloudDownload size={20} /> DOWNLOAD
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
29
app/projects/page.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import ProjectItem from "@/components/project-item";
|
||||
import { projects } from "@/lib/data";
|
||||
import { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "My Projects | Gabriel Kaszewski",
|
||||
description: "A showcase of projects by Gabriel Kaszewski.",
|
||||
};
|
||||
|
||||
const ProjectsPage = () => {
|
||||
return (
|
||||
<div className="flex w-full h-full min-h-screen flex-col items-center gap-4 pt-24">
|
||||
<h1 className="text-5xl font-bold text-center text-white">My Projects</h1>
|
||||
|
||||
<div className="w-full flex flex-col items-center gap-16 mt-8">
|
||||
{projects.map((project) => (
|
||||
<ProjectItem key={project.id} project={project} />
|
||||
))}
|
||||
{projects.length === 0 && (
|
||||
<p className="text-white text-center">
|
||||
No projects available. Working on it!
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProjectsPage;
|
153
bun.lock
@@ -4,9 +4,14 @@
|
||||
"": {
|
||||
"name": "gabrielkaszewski-next",
|
||||
"dependencies": {
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"lucide-react": "^0.542.0",
|
||||
"next": "15.5.2",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"remark": "^15.0.1",
|
||||
"remark-html": "^16.0.1",
|
||||
"tailwindcss-motion": "^1.1.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
@@ -178,20 +183,32 @@
|
||||
|
||||
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.13", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.13", "@tailwindcss/oxide": "4.1.13", "postcss": "^8.4.41", "tailwindcss": "4.1.13" } }, "sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ=="],
|
||||
|
||||
"@tailwindcss/typography": ["@tailwindcss/typography@0.5.16", "", { "dependencies": { "lodash.castarray": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA=="],
|
||||
|
||||
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="],
|
||||
|
||||
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
|
||||
"@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
|
||||
|
||||
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
|
||||
|
||||
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
|
||||
|
||||
"@types/node": ["@types/node@20.19.13", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g=="],
|
||||
|
||||
"@types/react": ["@types/react@19.1.12", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w=="],
|
||||
|
||||
"@types/react-dom": ["@types/react-dom@19.1.9", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ=="],
|
||||
|
||||
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.42.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.42.0", "@typescript-eslint/type-utils": "8.42.0", "@typescript-eslint/utils": "8.42.0", "@typescript-eslint/visitor-keys": "8.42.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.42.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ=="],
|
||||
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.42.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.42.0", "@typescript-eslint/types": "8.42.0", "@typescript-eslint/typescript-estree": "8.42.0", "@typescript-eslint/visitor-keys": "8.42.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg=="],
|
||||
@@ -212,6 +229,8 @@
|
||||
|
||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.42.0", "", { "dependencies": { "@typescript-eslint/types": "8.42.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ=="],
|
||||
|
||||
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
|
||||
|
||||
"@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="],
|
||||
|
||||
"@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.11.1", "", { "os": "android", "cpu": "arm64" }, "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g=="],
|
||||
@@ -288,6 +307,8 @@
|
||||
|
||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||
|
||||
"bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
|
||||
|
||||
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
||||
@@ -304,8 +325,16 @@
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001741", "", {}, "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw=="],
|
||||
|
||||
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
||||
|
||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
"character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
|
||||
|
||||
"character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
|
||||
|
||||
"character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
|
||||
|
||||
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
||||
|
||||
"client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="],
|
||||
@@ -318,10 +347,14 @@
|
||||
|
||||
"color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
|
||||
|
||||
"comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
|
||||
|
||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="],
|
||||
@@ -334,14 +367,20 @@
|
||||
|
||||
"debug": ["debug@4.4.2", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-IQeXCZhGRpFiLI3MYlCGLjNssUBiE8G21RMyNH35KFsxIvhrMeh5jXuG82woDZrYX9pgqHs+GF5js2Ducn4y4A=="],
|
||||
|
||||
"decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="],
|
||||
|
||||
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
||||
|
||||
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
|
||||
|
||||
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
||||
|
||||
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
|
||||
|
||||
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||
|
||||
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
@@ -400,6 +439,8 @@
|
||||
|
||||
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
||||
|
||||
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="],
|
||||
@@ -464,6 +505,14 @@
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"hast-util-sanitize": ["hast-util-sanitize@5.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "unist-util-position": "^5.0.0" } }, "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg=="],
|
||||
|
||||
"hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
|
||||
|
||||
"hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
|
||||
|
||||
"html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
|
||||
|
||||
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||
|
||||
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
||||
@@ -508,6 +557,8 @@
|
||||
|
||||
"is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
|
||||
|
||||
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
||||
|
||||
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
|
||||
|
||||
"is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="],
|
||||
@@ -580,16 +631,76 @@
|
||||
|
||||
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||
|
||||
"lodash.castarray": ["lodash.castarray@4.4.0", "", {}, "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q=="],
|
||||
|
||||
"lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="],
|
||||
|
||||
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
||||
|
||||
"longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
|
||||
|
||||
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
||||
|
||||
"lucide-react": ["lucide-react@0.542.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="],
|
||||
|
||||
"mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="],
|
||||
|
||||
"mdast-util-to-hast": ["mdast-util-to-hast@13.2.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA=="],
|
||||
|
||||
"mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="],
|
||||
|
||||
"mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
|
||||
|
||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||
|
||||
"micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
|
||||
|
||||
"micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
|
||||
|
||||
"micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="],
|
||||
|
||||
"micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="],
|
||||
|
||||
"micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="],
|
||||
|
||||
"micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="],
|
||||
|
||||
"micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="],
|
||||
|
||||
"micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
|
||||
|
||||
"micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="],
|
||||
|
||||
"micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="],
|
||||
|
||||
"micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="],
|
||||
|
||||
"micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="],
|
||||
|
||||
"micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="],
|
||||
|
||||
"micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
|
||||
|
||||
"micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="],
|
||||
|
||||
"micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="],
|
||||
|
||||
"micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="],
|
||||
|
||||
"micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
|
||||
|
||||
"micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="],
|
||||
|
||||
"micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
|
||||
|
||||
"micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
|
||||
|
||||
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||
|
||||
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||
@@ -652,10 +763,14 @@
|
||||
|
||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||
|
||||
"postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="],
|
||||
|
||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||
|
||||
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||
|
||||
"property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||
@@ -670,6 +785,14 @@
|
||||
|
||||
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
|
||||
|
||||
"remark": ["remark@15.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A=="],
|
||||
|
||||
"remark-html": ["remark-html@16.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "hast-util-sanitize": "^5.0.0", "hast-util-to-html": "^9.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0" } }, "sha512-B9JqA5i0qZe0Nsf49q3OXyGvyXuZFDzAP2iOFLEumymuYJITVpiH1IgsTEwTpdptDmZlMDMWeDmSawdaJIGCXQ=="],
|
||||
|
||||
"remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="],
|
||||
|
||||
"remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="],
|
||||
|
||||
"resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
|
||||
|
||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||
@@ -714,6 +837,8 @@
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
|
||||
|
||||
"stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="],
|
||||
|
||||
"stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
|
||||
@@ -730,6 +855,8 @@
|
||||
|
||||
"string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="],
|
||||
|
||||
"stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
|
||||
|
||||
"strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
||||
@@ -742,6 +869,8 @@
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.13", "", {}, "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w=="],
|
||||
|
||||
"tailwindcss-motion": ["tailwindcss-motion@1.1.1", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-CeeQAc5o31BuEPMyWdq/786X7QWNeifa+8khfu74Fs8lGkgEwjNYv6dGv+lRFS8FWXV5dp7F3AU9JjBXjiaQfw=="],
|
||||
|
||||
"tapable": ["tapable@2.2.3", "", {}, "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg=="],
|
||||
|
||||
"tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="],
|
||||
@@ -750,6 +879,10 @@
|
||||
|
||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||
|
||||
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
|
||||
|
||||
"trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
|
||||
|
||||
"ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
|
||||
|
||||
"tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
|
||||
@@ -772,10 +905,28 @@
|
||||
|
||||
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||
|
||||
"unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
|
||||
|
||||
"unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="],
|
||||
|
||||
"unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="],
|
||||
|
||||
"unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
|
||||
|
||||
"unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||
|
||||
"unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="],
|
||||
|
||||
"unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="],
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
|
||||
|
||||
"vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
|
||||
@@ -792,6 +943,8 @@
|
||||
|
||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
|
||||
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||
|
||||
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="],
|
||||
|
40
components/about-summary.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
const AboutSummary = () => {
|
||||
return (
|
||||
<div
|
||||
id="who-am-i"
|
||||
className="flex flex-col items-center justify-center gap-4 p-4 rounded w-full"
|
||||
>
|
||||
<h3 className="mt-4 mb-2 text-5xl font-bold tracking-tight text-white">
|
||||
Who am I? 🤔
|
||||
</h3>
|
||||
<section className="prose text-white md:prose-lg lg:prose-xl max-w-3xl mx-auto">
|
||||
<p>
|
||||
Hi, my name is Gabriel Kaszewski - I'm a Bioinformatics graduate
|
||||
🧬 and a self-taught full-stack developer 💻. I love creating software
|
||||
that resolves complex problems, and my most ambitious project yet is
|
||||
my first game. Take a look at the Steam preview below and consider
|
||||
adding it to your wishlist!
|
||||
</p>
|
||||
|
||||
<div className="w-full max-w-full aspect-[646/190] mx-auto not-prose">
|
||||
<iframe
|
||||
className="w-full h-full"
|
||||
src="https://store.steampowered.com/widget/3575090/"
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
My journey with programming started when I was 11 🚀. I love solving
|
||||
problems and creating software that resolves them 👨💻.
|
||||
</p>
|
||||
<p>
|
||||
Currently, I am working as a Python Developer at digimonkeys.com 🐒.
|
||||
In my free time I like to read about new technologies and work on my
|
||||
projects 📚.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AboutSummary;
|
13
components/chip.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
interface ChipProps {
|
||||
text: string;
|
||||
}
|
||||
|
||||
const Chip = ({ text }: ChipProps) => {
|
||||
return (
|
||||
<p className="text-base text-center rounded-2xl font-semibold tracking-tight text-black bg-yellow-400 p-2 w-32 h-12 flex items-center justify-center glass-effect glossy-effect bottom gloss-highlight">
|
||||
{text}
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
export default Chip;
|
30
components/experience-card.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Job } from "@/lib/types";
|
||||
import { CircleUserRound, Building, Clock, Microchip } from "lucide-react";
|
||||
import Chip from "./chip";
|
||||
import formatDate from "@/utils/format-date";
|
||||
|
||||
const ExperienceCard = ({ job }: { job: Job }) => (
|
||||
<div className="bg-transparent backdrop-blur-sm glass-effect flex flex-col gap-2 p-4 text-white w-[20rem] max-w-[20rem] rounded-lg shadow-lg">
|
||||
<h4 className="flex items-center gap-1 text-2xl">
|
||||
<CircleUserRound /> {job.position}
|
||||
</h4>
|
||||
<h5 className="flex items-center gap-1 text-xl font-light">
|
||||
<Building /> {job.company}
|
||||
</h5>
|
||||
<h6 className="flex items-center gap-1">
|
||||
<Clock />
|
||||
{formatDate(job.start_date)} -{" "}
|
||||
{job.still_working ? "Present" : formatDate(job.end_date!)}
|
||||
</h6>
|
||||
<p className="flex items-center gap-1 font-bold">
|
||||
<Microchip /> Technologies
|
||||
</p>
|
||||
<div className="flex flex-wrap items-center w-full gap-2">
|
||||
{job.technologies.map((tech) => (
|
||||
<Chip key={tech} text={tech} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default ExperienceCard;
|
22
components/experience.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Job } from "@/lib/types";
|
||||
import ExperienceCard from "@/components/experience-card";
|
||||
|
||||
const Experience = ({ jobs }: { jobs: Job[] }) => {
|
||||
return (
|
||||
<div
|
||||
id="experience"
|
||||
className="flex flex-col items-center justify-center gap-4 p-4 rounded w-full"
|
||||
>
|
||||
<h3 className="mt-4 mb-2 text-5xl font-bold tracking-tight text-white">
|
||||
Experience 📈
|
||||
</h3>
|
||||
<div className="flex flex-wrap justify-center gap-4">
|
||||
{jobs.map((job) => (
|
||||
<ExperienceCard key={job.id} job={job} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Experience;
|
88
components/footer.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import Link from "next/link";
|
||||
import { Github, Linkedin, Gamepad2 } from "lucide-react";
|
||||
|
||||
const Footer = () => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
const socialLinks = [
|
||||
{
|
||||
title: "github",
|
||||
href: "https://github.com/GKaszewski",
|
||||
icon: <Github />,
|
||||
},
|
||||
{
|
||||
title: "linkedin",
|
||||
href: "https://www.linkedin.com/in/gabriel-kaszewski-5344b3183",
|
||||
icon: <Linkedin />,
|
||||
},
|
||||
{
|
||||
title: "itch.io",
|
||||
href: "https://gabrielkaszewski.itch.io/",
|
||||
icon: <Gamepad2 />,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<footer className="flex w-full flex-col gap-4 bg-gray-900 p-4 text-white">
|
||||
<div className="flex items-center gap-2">
|
||||
<h1 className="text-xl">Gabriel Kaszewski</h1>
|
||||
<span className="flex-1"></span>
|
||||
<div className="flex gap-2">
|
||||
{socialLinks.map((link) => (
|
||||
<a
|
||||
key={link.title}
|
||||
title={link.title}
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-yellow-400 transition-colors"
|
||||
>
|
||||
{link.icon}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-sm">
|
||||
<p>
|
||||
Background photo by{" "}
|
||||
<a className="underline" href="https://duckwithsunglasses.com">
|
||||
Liam
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-x-4 gap-y-2 text-sm">
|
||||
<p className="font-semibold">
|
||||
© Gabriel Kaszewski, {currentYear}. All rights reserved.
|
||||
</p>
|
||||
<p>Made with 💗 in Poland</p>
|
||||
<span className="flex-1"></span>
|
||||
<div className="flex gap-4">
|
||||
<Link
|
||||
href="/projects"
|
||||
className="hover:text-yellow-400 transition-colors"
|
||||
>
|
||||
Projects
|
||||
</Link>
|
||||
<a
|
||||
href="https://blog.gabrielkaszewski.dev/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-yellow-400 transition-colors"
|
||||
>
|
||||
Blog
|
||||
</a>
|
||||
<Link
|
||||
href="/about"
|
||||
className="hover:text-yellow-400 transition-colors"
|
||||
>
|
||||
About
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
66
components/hero.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import Image from "next/image";
|
||||
import { FileText, Github, Mail, Linkedin } from "lucide-react";
|
||||
|
||||
const Hero = () => {
|
||||
const socialLinks = [
|
||||
{ title: "My CV", href: "/cv.pdf", icon: <FileText /> },
|
||||
{
|
||||
title: "GitHub",
|
||||
href: "https://github.com/GKaszewski",
|
||||
icon: <Github />,
|
||||
},
|
||||
{
|
||||
title: "My email",
|
||||
href: "mailto:gabrielkaszewski@gmail.com",
|
||||
icon: <Mail />,
|
||||
},
|
||||
{
|
||||
title: "LinkedIn",
|
||||
href: "https://www.linkedin.com/in/gabriel-kaszewski-5344b3183",
|
||||
icon: <Linkedin />,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="relative w-full h-screen">
|
||||
<Image
|
||||
src="/images/hero.avif"
|
||||
alt="Background"
|
||||
fill
|
||||
priority
|
||||
className="hidden object-cover pointer-events-none md:block"
|
||||
/>
|
||||
<div className="flex flex-col items-center justify-center md:justify-start w-full h-full md:inset-0 md:absolute md:items-start md:p-16 lg:p-20">
|
||||
<div className="text-center md:text-left">
|
||||
<h1 className="mb-4 text-4xl font-bold tracking-tight text-white md:text-6xl">
|
||||
Gabriel Kaszewski
|
||||
</h1>
|
||||
<h2 className="text-xl font-light tracking-tight text-white md:text-2xl">
|
||||
Full-Stack Developer
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 mt-4">
|
||||
{socialLinks.map((link) => (
|
||||
<a
|
||||
key={link.title}
|
||||
href={link.href}
|
||||
title={link.title}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-white hover:text-yellow-400"
|
||||
>
|
||||
{link.icon}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<div className="absolute bottom-0 hidden py-2 text-sm md:block">
|
||||
<p>Photo by me Łazy, 2018 (compressed)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Hero;
|
60
components/image-carousel.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
|
||||
interface ImageCarouselProps {
|
||||
id: string;
|
||||
thumbnails: string[];
|
||||
}
|
||||
|
||||
const ImageCarousel = ({ id, thumbnails }: ImageCarouselProps) => {
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
|
||||
const goToSlide = (slideIndex: number) => {
|
||||
setCurrentIndex(slideIndex);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="carousel relative shadow-lg w-full max-w-full md:max-w-[50hw] h-[40rem]"
|
||||
>
|
||||
<div className="relative w-full h-full overflow-hidden carousel-inner">
|
||||
{thumbnails.map((thumbnail, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`absolute inset-0 w-full h-full transition-opacity duration-500 ease-in-out delay-250 ${
|
||||
currentIndex === index ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
>
|
||||
<Image
|
||||
alt={`Slide ${index + 1}`}
|
||||
src={thumbnail}
|
||||
fill
|
||||
style={{ objectFit: "scale-down" }}
|
||||
priority={index === 0}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-0 z-10 flex justify-center w-full gap-2 py-2">
|
||||
{thumbnails.map((_, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => goToSlide(index)}
|
||||
className={`text-2xl leading-none ${
|
||||
currentIndex === index ? "text-white" : "text-white/50"
|
||||
}`}
|
||||
aria-label={`Go to slide ${index + 1}`}
|
||||
>
|
||||
•
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImageCarousel;
|
78
components/navbar.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
const Navbar = () => {
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const pathname = usePathname();
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setIsScrolled(window.scrollY > 10);
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const navLinks = [
|
||||
{ href: "/", label: "Home" },
|
||||
{ href: "/projects", label: "Projects" },
|
||||
{
|
||||
href: "https://blog.gabrielkaszewski.dev/",
|
||||
label: "Blog",
|
||||
external: true,
|
||||
},
|
||||
{ href: "/about", label: "About" },
|
||||
];
|
||||
|
||||
const baseClasses =
|
||||
"fixed z-20 flex w-full items-center justify-center p-4 transition-all duration-300";
|
||||
const scrolledClasses = "bg-gray-900/80 backdrop-blur-md";
|
||||
const transparentClasses = "bg-transparent";
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={`${baseClasses} ${
|
||||
isScrolled ? scrolledClasses : transparentClasses
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-1"></div>
|
||||
<div className="flex gap-4">
|
||||
{navLinks.map((link) => {
|
||||
const isActive = pathname === link.href;
|
||||
const linkClasses = `text-lg hover:text-yellow-400 transition-colors ${
|
||||
isActive ? "text-yellow-400 font-semibold" : "text-white"
|
||||
}`;
|
||||
|
||||
if (link.external) {
|
||||
return (
|
||||
<a
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className={linkClasses}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Link key={link.href} href={link.href} className={linkClasses}>
|
||||
{link.label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navbar;
|
114
components/project-item.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import Link from "next/link";
|
||||
import { Project } from "@/lib/types";
|
||||
import Chip from "@/components/chip";
|
||||
import ImageCarousel from "@/components/image-carousel";
|
||||
import {
|
||||
Eye,
|
||||
CloudDownload,
|
||||
AppWindow,
|
||||
Smartphone,
|
||||
Globe,
|
||||
MonitorCog,
|
||||
Gamepad2,
|
||||
Github,
|
||||
} from "lucide-react";
|
||||
|
||||
const PlaceholderIcon = ({ category }: { category: Project["category"] }) => {
|
||||
const iconProps = { size: 128, className: "text-white/80" };
|
||||
switch (category) {
|
||||
case "Desktop":
|
||||
return <AppWindow {...iconProps} />;
|
||||
case "Mobile":
|
||||
return <Smartphone {...iconProps} />;
|
||||
case "Web":
|
||||
return <Globe {...iconProps} />;
|
||||
case "Api":
|
||||
return <MonitorCog {...iconProps} />;
|
||||
case "Game":
|
||||
return <Gamepad2 {...iconProps} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
interface ProjectItemProps {
|
||||
project: Project;
|
||||
}
|
||||
|
||||
const ProjectItem = ({ project }: ProjectItemProps) => {
|
||||
const ImageDisplay = (
|
||||
<div className="bg-gradient-to-r from-violet-600 to-indigo-600 shadow-lg w-full h-[40rem] flex items-center justify-center rounded-lg">
|
||||
{project.thumbnails && project.thumbnails.length > 0 ? (
|
||||
<ImageCarousel
|
||||
id={`carousel-${project.id}`}
|
||||
thumbnails={project.thumbnails}
|
||||
/>
|
||||
) : (
|
||||
<PlaceholderIcon category={project.category} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col md:flex-row items-center justify-between w-full h-full gap-8 text-white p-4">
|
||||
<div className="flex flex-col w-full gap-4 md:w-1/3 backdrop-blur-2xl glass-effect glossy-effect bottom p-4 rounded-md h-full">
|
||||
<div className="prose prose-invert">
|
||||
<h1>{project.name}</h1>
|
||||
<p className="whitespace-pre-wrap">{project.short_description}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{project.technologies.map((tech) => (
|
||||
<Chip key={tech} text={tech} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href={`/projects/${project.name}`}
|
||||
className="text-center glass-effect glossy-effect bottom gloss-highlight p-2 rounded-2xl bg-yellow-400 hover:bg-yellow-500 text-black font-bold"
|
||||
>
|
||||
Read more
|
||||
</Link>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-2 w-full">
|
||||
{project.github_url && (
|
||||
<a
|
||||
href={project.github_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex-1 flex items-center justify-center gap-2 text-center glass-effect glossy-effect bottom gloss-highlight p-2 rounded-2xl bg-yellow-400 hover:bg-yellow-500 text-black font-bold"
|
||||
>
|
||||
<Github size={20} /> CODE
|
||||
</a>
|
||||
)}
|
||||
{project.visit_url && (
|
||||
<a
|
||||
href={project.visit_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex-1 flex items-center justify-center gap-2 text-center glass-effect glossy-effect bottom gloss-highlight p-2 rounded-2xl bg-yellow-400 hover:bg-yellow-500 text-black font-bold"
|
||||
>
|
||||
<Eye size={20} /> LIVE
|
||||
</a>
|
||||
)}
|
||||
{project.download_url && (
|
||||
<a
|
||||
href={project.download_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex-1 flex items-center justify-center gap-2 text-center glass-effect glossy-effect bottom gloss-highlight p-2 rounded-2xl bg-yellow-400 hover:bg-yellow-500 text-black font-bold"
|
||||
>
|
||||
<CloudDownload size={20} /> DOWNLOAD
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="w-full mt-4 md:hidden">{ImageDisplay}</div>
|
||||
</div>
|
||||
|
||||
<div className="hidden md:flex md:w-1/2">{ImageDisplay}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProjectItem;
|
31
components/skills.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import Chip from "@/components/chip";
|
||||
import { Skill } from "@/lib/types";
|
||||
|
||||
interface SkillsProps {
|
||||
skills: Skill[];
|
||||
}
|
||||
|
||||
const Skills = ({ skills }: SkillsProps) => {
|
||||
return (
|
||||
<div
|
||||
id="skills"
|
||||
className="flex flex-col items-center justify-center gap-4 p-4 rounded w-full"
|
||||
>
|
||||
<h3 className="mt-4 mb-2 text-5xl font-bold tracking-tight text-white">
|
||||
Skills 🛠️
|
||||
</h3>
|
||||
<div className="flex flex-wrap justify-center w-full max-w-lg gap-4">
|
||||
{skills.map((skill, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="odd:motion-preset-slide-left even:motion-preset-slide-right odd:motion-delay-100"
|
||||
>
|
||||
<Chip text={skill.name} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Skills;
|
230
lib/data.ts
Normal file
@@ -0,0 +1,230 @@
|
||||
import { Skill, Job, Project } from "@/lib/types";
|
||||
|
||||
export const skills: Skill[] = [
|
||||
{
|
||||
"name": "Azure Pipelines"
|
||||
},
|
||||
{
|
||||
"name": "C#"
|
||||
},
|
||||
{
|
||||
"name": "C++"
|
||||
},
|
||||
{
|
||||
"name": "Django"
|
||||
},
|
||||
{
|
||||
"name": "Docker"
|
||||
},
|
||||
{
|
||||
"name": "FastAPI"
|
||||
},
|
||||
{
|
||||
"name": "Git"
|
||||
},
|
||||
{
|
||||
"name": "Java"
|
||||
},
|
||||
{
|
||||
"name": "Javascript"
|
||||
},
|
||||
{
|
||||
"name": "Linux"
|
||||
},
|
||||
{
|
||||
"name": "PostGIS"
|
||||
},
|
||||
{
|
||||
"name": "PostgreSQL"
|
||||
},
|
||||
{
|
||||
"name": "Python"
|
||||
},
|
||||
{
|
||||
"name": "Qt"
|
||||
},
|
||||
{
|
||||
"name": "React"
|
||||
},
|
||||
{
|
||||
"name": "Rust"
|
||||
},
|
||||
{
|
||||
"name": "TailwindCSS"
|
||||
},
|
||||
{
|
||||
"name": "Typescript"
|
||||
},
|
||||
{
|
||||
"name": "Unity"
|
||||
}
|
||||
];
|
||||
|
||||
export const jobs: Job[] = [
|
||||
{
|
||||
"id": 2,
|
||||
"position": "Python Developer",
|
||||
"company": "GIAP",
|
||||
"still_working": false,
|
||||
"start_date": "2021-05-19",
|
||||
"end_date": "2023-02-03",
|
||||
"technologies": [
|
||||
"Python",
|
||||
"Qt",
|
||||
"PostgreSQL",
|
||||
"PostGIS",
|
||||
"Git",
|
||||
"QGIS"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"position": "Frontend Developer",
|
||||
"company": "GIAP",
|
||||
"still_working": false,
|
||||
"start_date": "2022-02-01",
|
||||
"end_date": "2023-02-03",
|
||||
"technologies": [
|
||||
"React",
|
||||
"Typescript",
|
||||
"Redux",
|
||||
"ArcGIS JS API"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"position": "Frontend Developer",
|
||||
"company": "Wavemaker",
|
||||
"still_working": false,
|
||||
"start_date": "2023-09-13",
|
||||
"end_date": "2024-01-01",
|
||||
"technologies": [
|
||||
"React",
|
||||
"Typescript",
|
||||
"Angular",
|
||||
"TailwindCSS",
|
||||
"SCSS",
|
||||
"Azure Pipelines",
|
||||
" Gitlab CI",
|
||||
"Kubernetes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"position": "Frontend Developer",
|
||||
"company": "Choreograph",
|
||||
"still_working": false,
|
||||
"start_date": "2024-01-01",
|
||||
"end_date": "2025-03-01",
|
||||
"technologies": [
|
||||
"React",
|
||||
"Typescript",
|
||||
"Angular",
|
||||
"TailwindCSS",
|
||||
"SCSS",
|
||||
"Azure Pipelines",
|
||||
" Gitlab CI",
|
||||
"Kubernetes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"position": "Python Developer",
|
||||
"company": "Choreograph",
|
||||
"still_working": true,
|
||||
"start_date": "2025-03-01",
|
||||
"end_date": null,
|
||||
"technologies": [
|
||||
"Python",
|
||||
"FastAPI",
|
||||
"Django",
|
||||
"PostgreSQL",
|
||||
"GCP",
|
||||
"Azure",
|
||||
"Docker",
|
||||
"Kubernetes",
|
||||
"Gitlab Pipelines"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"position": "Full Stack Developer",
|
||||
"company": "digimonkeys.com",
|
||||
"still_working": true,
|
||||
"start_date": "2021-05-19",
|
||||
"end_date": null,
|
||||
"technologies": [
|
||||
"Python",
|
||||
"FastAPI",
|
||||
"Django",
|
||||
"PostgreSQL",
|
||||
"React",
|
||||
"TailwindCSS",
|
||||
"Nginx"
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export const projects: Project[] = [
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Spanish Inquisition",
|
||||
"short_description": "Educational game made in 24 hours in Unity.",
|
||||
"description": "I made this game because I have Spanish as one of the subjects in school. I wanted to improve my vocabulary by having fun and a game is a perfect solution for this problem. I made this in 24 hours. It is not perfect gameplay-wise but I learned some Spanish from it. So it kind of works.",
|
||||
"category": "Game",
|
||||
"github_url": "https://github.com/GKaszewski/Spanish-Learning-Game",
|
||||
"visit_url": "https://gamejolt.com/games/spanish-inquisition/425125",
|
||||
"download_url": null,
|
||||
"technologies": [
|
||||
"C#",
|
||||
" Unity"
|
||||
],
|
||||
"thumbnails": []
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Everyday quotes",
|
||||
"short_description": "Simple app for reading various quotations",
|
||||
"description": "I made this app as my first Flutter app because I wanted to learn how to write mobile apps and how to use public APIs.",
|
||||
"category": "Mobile",
|
||||
"github_url": "https://github.com/GKaszewski/Everyday_quotes",
|
||||
"visit_url": "https://play.google.com/store/apps/details?id=com.GabrielKaszewski.everydayquotes",
|
||||
"download_url": null,
|
||||
"technologies": [
|
||||
"Dart",
|
||||
" Flutter"
|
||||
],
|
||||
"thumbnails": []
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Raytracer",
|
||||
"short_description": "CPU based raytracer",
|
||||
"description": "My attempt at making CPU raytracer in Rust.\r\n\r\nFeatures:\r\n- rendering spheres, cubes, triangles and meshes (gltf only)\r\n- multithreading\r\nResources:\r\n[Raytracing in One weekend](https://raytracing.github.io/books/RayTracingInOneWeekend.html)",
|
||||
"category": "Desktop",
|
||||
"github_url": "https://github.com/GKaszewski/raytracer-rs",
|
||||
"visit_url": null,
|
||||
"download_url": null,
|
||||
"technologies": [
|
||||
"Rust"
|
||||
],
|
||||
"thumbnails": []
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Tiny packer",
|
||||
"short_description": "Small utility to combine images into one atlas.",
|
||||
"description": "**Overview:**\r\nTiny Packer is a command-line utility designed to combine multiple images into a single texture atlas. It provides options for manual and automatic sizing of the atlas, including adjustable padding between images.\r\n\r\n# Features\r\n- CLI \r\n- GUI \r\n- Padding support\r\n- Auto size\r\n\r\n# How to use? \r\n## GUI\r\n**Hotkeys**\r\n- `Ctrl+I` - Import images\r\n- `Ctrl+Shift+I` - Add images\r\n- `Ctrl+Shift+C` - Clear images\r\n- `Ctrl+S` - Save generated atlas\r\n## CLI\r\n**Usage:**\r\n```bash\r\ntiny_packer --input <input_files> --output <output_file> [OPTIONS]\r\n```\r\n\r\n**Required Arguments:**\r\n- `--input`, `-i`: Specify the input image files. Multiple files can be specified by repeating the argument for each file.\r\n- `--output`, `-o`: Specify the path where the output atlas image will be saved.\r\n\r\n**Options:**\r\n- `--width`: Specify the width of the atlas. Defaults to `512` pixels. This option is ignored if auto sizing is enabled.\r\n- `--height`: Specify the height of the atlas. Defaults to `512` pixels. This option is ignored if auto sizing is enabled.\r\n- `--padding`, `-p`: Set the padding between images in the atlas. Defaults to `0` pixels.\r\n- `--auto_size`, `-a`: Enable or disable automatic sizing of the atlas dimensions. Defaults to `false`. When enabled, the atlas dimensions are calculated based on the input images.\r\n- `--unified`: Each cell has the same size (based on largest dimensions of image.) Defaults to `false`. [CLI only for now] \r\n\r\n**Examples:**\r\n\r\n1. **Creating an Atlas with Specified Dimensions:**\r\n Generate an atlas with a specific width and height, ignoring automatic sizing.\r\n ```bash\r\n tiny_packer -i image1.png -i image2.png -o atlas.png --width 1024 --height 1024 -a false\r\n ```\r\n\r\n2. **Creating an Atlas with Automatic Sizing:**\r\n Generate an atlas where dimensions are automatically calculated.\r\n ```bash\r\n tiny_packer -i image1.png -i image2.png -i image3.png -o atlas.png\r\n ```\r\n\r\n3. **Creating an Atlas with Padding:**\r\n Generate an atlas with a specified padding between images.\r\n ```bash\r\n tiny_packer -i image1.png -i image2.png -o atlas.png -p 10\r\n ```\r\n\r\n**Additional Tips:**\r\n- Multiple input files should be specified by repeating the `-i` or `--input` option for each file.\r\n- Ensure that file paths are correctly specified and accessible from the command line.\r\n- For best results, images should be of compatible formats and dimensions when padding and auto sizing are considered.\r\n\r\n**Help:**\r\nTo view more information and help regarding the command options, you can use the `--help` flag:\r\n```bash\r\ntiny_packer --help\r\n```\r\n# License\r\nMIT",
|
||||
"category": "Desktop",
|
||||
"github_url": "https://github.com/GKaszewski/tiny_packer",
|
||||
"visit_url": null,
|
||||
"download_url": null,
|
||||
"technologies": [
|
||||
"Rust",
|
||||
"Tauri",
|
||||
"React",
|
||||
"TailwindCSS"
|
||||
],
|
||||
"thumbnails": []
|
||||
}
|
||||
];
|
26
lib/types.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export interface Skill {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Job {
|
||||
id: number;
|
||||
position: string;
|
||||
company: string;
|
||||
still_working: boolean;
|
||||
start_date: string;
|
||||
end_date: string | null;
|
||||
technologies: string[];
|
||||
}
|
||||
|
||||
export interface Project {
|
||||
id: number;
|
||||
name: string;
|
||||
short_description: string;
|
||||
description: string;
|
||||
technologies: string[];
|
||||
thumbnails: string[];
|
||||
category: 'Web' | 'Mobile' | 'Desktop' | 'Api' | 'Game';
|
||||
github_url?: string | null;
|
||||
visit_url?: string | null;
|
||||
download_url?: string | null;
|
||||
}
|
@@ -9,9 +9,14 @@
|
||||
"lint": "eslint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"lucide-react": "^0.542.0",
|
||||
"next": "15.5.2",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"next": "15.5.2"
|
||||
"remark": "^15.0.1",
|
||||
"remark-html": "^16.0.1",
|
||||
"tailwindcss-motion": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
|
BIN
public/cv.pdf
Normal file
@@ -1 +0,0 @@
|
||||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
Before Width: | Height: | Size: 391 B |
@@ -1 +0,0 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
Before Width: | Height: | Size: 1.0 KiB |
BIN
public/images/background.avif
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
public/images/favicon.ico
Normal file
After Width: | Height: | Size: 264 KiB |
BIN
public/images/hero.avif
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
public/images/ja.avif
Normal file
After Width: | Height: | Size: 12 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1 +0,0 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
Before Width: | Height: | Size: 128 B |
@@ -1 +0,0 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
Before Width: | Height: | Size: 385 B |
6
utils/format-date.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString("pl-PL");
|
||||
};
|
||||
|
||||
|
||||
export default formatDate;
|