Merge pull request #3 from selimaj-dev/new-ui

New UI
This commit is contained in:
Klesti Selimaj
2026-01-21 12:59:08 +01:00
committed by GitHub
24 changed files with 2007 additions and 910 deletions
+49 -117
View File
@@ -14,8 +14,10 @@
"@fortawesome/free-regular-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"@icons-pack/react-simple-icons": "^13.8.0",
"@notionhq/client": "^2.2.15",
"@types/react-syntax-highlighter": "^15.5.13",
"clsx": "^2.1.1",
"dotenv": "^16.4.7",
"framer-motion": "^12.23.25",
"katex": "^0.16.19",
@@ -32,10 +34,12 @@
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0",
"tailwind-merge": "^3.4.0",
"tailwindcss-animate": "^1.0.7",
"tsparticles-slim": "^2.12.0"
},
"devDependencies": {
"@types/node": "^20",
"@types/node": "^20.19.30",
"@types/react": "^19",
"@types/react-dom": "^19",
"postcss": "^8",
@@ -47,7 +51,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -157,6 +160,15 @@
"react": ">=16.3"
}
},
"node_modules/@icons-pack/react-simple-icons": {
"version": "13.8.0",
"resolved": "https://registry.npmjs.org/@icons-pack/react-simple-icons/-/react-simple-icons-13.8.0.tgz",
"integrity": "sha512-iZrhL1fSklfCCVn68IYHaAoKfcby3RakUTn2tRPyHBkhr2tkYqeQbjJWf+NizIYBzKBn2IarDJXmTdXd6CuEfw==",
"license": "MIT",
"peerDependencies": {
"react": "^16.13 || ^17 || ^18 || ^19"
}
},
"node_modules/@img/colour": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
@@ -627,7 +639,6 @@
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^5.1.2",
@@ -645,7 +656,6 @@
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
@@ -660,7 +670,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -670,7 +679,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -680,14 +688,12 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
@@ -832,7 +838,6 @@
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
@@ -846,7 +851,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
@@ -856,7 +860,6 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
@@ -883,7 +886,6 @@
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
@@ -954,12 +956,12 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.17.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.11.tgz",
"integrity": "sha512-Ept5glCK35R8yeyIeYlRIZtX6SLRyqMhOFTgj5SOkMpLTdw3SEHI9fHx60xaUZ+V1aJxQJODE+7/j5ocZydYTg==",
"version": "20.19.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz",
"integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
"undici-types": "~6.21.0"
}
},
"node_modules/@types/node-fetch": {
@@ -1016,7 +1018,6 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -1029,7 +1030,6 @@
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -1042,14 +1042,12 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
"dev": true,
"license": "MIT"
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"license": "ISC",
"dependencies": {
"normalize-path": "^3.0.0",
@@ -1063,7 +1061,6 @@
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"dev": true,
"license": "MIT"
},
"node_modules/asynckit": {
@@ -1086,14 +1083,12 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -1106,7 +1101,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
@@ -1116,7 +1110,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
@@ -1129,7 +1122,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
@@ -1209,7 +1201,6 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
"license": "MIT",
"dependencies": {
"anymatch": "~3.1.2",
@@ -1234,7 +1225,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
@@ -1249,11 +1239,19 @@
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
"license": "MIT"
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
@@ -1266,7 +1264,6 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT"
},
"node_modules/combined-stream": {
@@ -1295,7 +1292,6 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
@@ -1305,7 +1301,6 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
@@ -1320,7 +1315,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true,
"license": "MIT",
"bin": {
"cssesc": "bin/cssesc"
@@ -1420,14 +1414,12 @@
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"dev": true,
"license": "MIT"
},
"node_modules/dotenv": {
@@ -1446,14 +1438,12 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"dev": true,
"license": "MIT"
},
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true,
"license": "MIT"
},
"node_modules/entities": {
@@ -1500,7 +1490,6 @@
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
@@ -1517,7 +1506,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
@@ -1530,7 +1518,6 @@
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz",
"integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==",
"dev": true,
"license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
@@ -1553,7 +1540,6 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
@@ -1566,7 +1552,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
"integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
"dev": true,
"license": "ISC",
"dependencies": {
"cross-spawn": "^7.0.0",
@@ -1632,7 +1617,6 @@
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -1647,7 +1631,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -1657,7 +1640,6 @@
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"dev": true,
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
@@ -1678,7 +1660,6 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.3"
@@ -1691,7 +1672,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -2086,7 +2066,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"license": "MIT",
"dependencies": {
"binary-extensions": "^2.0.0"
@@ -2099,7 +2078,6 @@
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
@@ -2125,7 +2103,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -2135,7 +2112,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -2145,7 +2121,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
@@ -2168,7 +2143,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
@@ -2190,14 +2164,12 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true,
"license": "ISC"
},
"node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
@@ -2213,7 +2185,6 @@
"version": "1.21.7",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
"dev": true,
"license": "MIT",
"bin": {
"jiti": "bin/jiti.js"
@@ -2254,7 +2225,6 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14"
@@ -2267,7 +2237,6 @@
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true,
"license": "MIT"
},
"node_modules/longest-streak": {
@@ -2310,7 +2279,6 @@
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true,
"license": "ISC"
},
"node_modules/lucide-react": {
@@ -2652,7 +2620,6 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
@@ -3244,7 +3211,6 @@
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
@@ -3279,7 +3245,6 @@
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
@@ -3295,7 +3260,6 @@
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
@@ -3326,7 +3290,6 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"any-promise": "^1.0.0",
@@ -3456,7 +3419,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -3488,7 +3450,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
@@ -3498,7 +3459,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
"dev": true,
"license": "BlueOak-1.0.0"
},
"node_modules/parse-entities": {
@@ -3542,7 +3502,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -3552,14 +3511,12 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true,
"license": "MIT"
},
"node_modules/path-scurry": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^10.2.0",
@@ -3582,7 +3539,6 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
@@ -3595,7 +3551,6 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -3605,7 +3560,6 @@
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
@@ -3615,7 +3569,6 @@
"version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -3644,7 +3597,6 @@
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
"dev": true,
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.0.0",
@@ -3662,7 +3614,6 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
"dev": true,
"license": "MIT",
"dependencies": {
"camelcase-css": "^2.0.1"
@@ -3682,7 +3633,6 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -3718,7 +3668,6 @@
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -3744,7 +3693,6 @@
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"dev": true,
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
@@ -3758,7 +3706,6 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true,
"license": "MIT"
},
"node_modules/prismjs": {
@@ -3795,7 +3742,6 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true,
"funding": [
{
"type": "github",
@@ -3910,7 +3856,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
"dev": true,
"license": "MIT",
"dependencies": {
"pify": "^2.3.0"
@@ -3920,7 +3865,6 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"license": "MIT",
"dependencies": {
"picomatch": "^2.2.1"
@@ -4195,7 +4139,6 @@
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.0",
@@ -4216,7 +4159,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"dev": true,
"license": "MIT",
"engines": {
"iojs": ">=1.0.0",
@@ -4227,7 +4169,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dev": true,
"funding": [
{
"type": "github",
@@ -4315,7 +4256,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
@@ -4328,7 +4268,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -4338,7 +4277,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=14"
@@ -4370,7 +4308,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dev": true,
"license": "MIT",
"dependencies": {
"eastasianwidth": "^0.2.0",
@@ -4389,7 +4326,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
@@ -4404,7 +4340,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -4414,14 +4349,12 @@
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/string-width-cjs/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
@@ -4448,7 +4381,6 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.0.1"
@@ -4465,7 +4397,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
@@ -4478,7 +4409,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -4520,7 +4450,6 @@
"version": "3.35.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
"integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.2",
@@ -4543,7 +4472,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -4552,11 +4480,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tailwind-merge": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
"integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/dcastil"
}
},
"node_modules/tailwindcss": {
"version": "3.4.17",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
"dev": true,
"license": "MIT",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
@@ -4590,11 +4527,19 @@
"node": ">=14.0.0"
}
},
"node_modules/tailwindcss-animate": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz",
"integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==",
"license": "MIT",
"peerDependencies": {
"tailwindcss": ">=3.0.0 || insiders"
}
},
"node_modules/thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"any-promise": "^1.0.0"
@@ -4604,7 +4549,6 @@
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
"dev": true,
"license": "MIT",
"dependencies": {
"thenify": ">= 3.1.0 < 4"
@@ -4617,7 +4561,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
@@ -4656,7 +4599,6 @@
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/tslib": {
@@ -5126,9 +5068,9 @@
}
},
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"license": "MIT"
},
"node_modules/unified": {
@@ -5250,7 +5192,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true,
"license": "MIT"
},
"node_modules/vfile": {
@@ -5325,7 +5266,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
@@ -5341,7 +5281,6 @@
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^6.1.0",
@@ -5360,7 +5299,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
@@ -5378,7 +5316,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -5388,7 +5325,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
@@ -5404,14 +5340,12 @@
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
@@ -5426,7 +5360,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
@@ -5448,7 +5381,6 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz",
"integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==",
"dev": true,
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
+5 -1
View File
@@ -15,8 +15,10 @@
"@fortawesome/free-regular-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"@icons-pack/react-simple-icons": "^13.8.0",
"@notionhq/client": "^2.2.15",
"@types/react-syntax-highlighter": "^15.5.13",
"clsx": "^2.1.1",
"dotenv": "^16.4.7",
"framer-motion": "^12.23.25",
"katex": "^0.16.19",
@@ -33,10 +35,12 @@
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0",
"tailwind-merge": "^3.4.0",
"tailwindcss-animate": "^1.0.7",
"tsparticles-slim": "^2.12.0"
},
"devDependencies": {
"@types/node": "^20",
"@types/node": "^20.19.30",
"@types/react": "^19",
"@types/react-dom": "^19",
"postcss": "^8",
+411
View File
@@ -0,0 +1,411 @@
"use client";
import {
Github,
Linkedin,
Youtube,
Instagram,
ExternalLink,
MapPin,
Crown,
Code,
Trophy,
Box,
FileText,
} from "lucide-react";
import {
SiRust,
SiTypescript,
SiReact,
SiGo,
SiPython,
SiDocker,
SiLinux,
} from "@icons-pack/react-simple-icons";
import { cn } from "@/lib/utils";
import { motion } from "framer-motion";
import {
staggerContainer,
fadeInUp,
fadeIn,
cardReveal,
timelineItemLeft,
timelineItemRight,
slowFloat,
} from "@/lib/animations";
const timeline = [
{
year: "2020",
title: "Started Programming Journey",
description:
"Began learning programming at age 10, with C# .net WinForms applications.",
},
{
year: "2021",
title: "Started learning Python and Web Development",
description:
"Began learning Python and web development, building my first fully coded applications.",
},
{
year: "2022",
title: "First Open Source Contributions",
description:
"Started contributing to open source projects and building my GitHub presence.",
},
{
year: "2023",
title: "Hacking tools",
description:
"Started building hacking tools and security-focused applications.",
},
{
year: "2024",
title: "Founded ORUS",
description: "Created the orus-dev organization to house major projects.",
},
{
year: "2025",
title: "Expanding Horizons",
description:
"Taking on freelance work and building products that make a difference.",
},
];
export default function About() {
const techStack = [
{ name: "Rust", icon: <SiRust />, color: "text-orange-500" },
{ name: "TypeScript", icon: <SiTypescript />, color: "text-blue-500" },
{ name: "React", icon: <SiReact />, color: "text-cyan-500" },
{ name: "Go", icon: <SiGo />, color: "text-cyan-400" },
{ name: "Python", icon: <SiPython />, color: "text-yellow-500" },
{
name: "Java",
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
d="M8.717 18.555s-.922.561.641.721c1.882.24 2.885.2 4.969-.2a7.515 7.515 0 0 0 1.322.641c-4.688 2.004-10.62-.12-6.933-1.163zm-.6-2.605s-1.002.761.561.923c2.044.2 3.647.24 6.412-.32a2.445 2.445 0 0 0 .962.601c-5.651 1.683-11.982.16-7.935-1.202zm11.06 4.568s.681.561-.761 1.002c-2.685.803-11.261 1.043-13.665 0-.842-.36.761-.881 1.275-.961.521-.12.803-.12.803-.12-.922-.641-6.131 1.322-2.645 1.882 9.578 1.563 17.472-.681 14.987-1.803zM9.15 13.225s-4.368 1.043-1.563 1.402c1.202.161 3.566.12 5.771-.04 1.803-.16 3.608-.48 3.608-.48s-.641.281-1.082.561c-4.448 1.163-12.984.641-10.539-.561 2.084-1.002 3.807-.881 3.807-.881zm7.815 4.368c4.488-2.325 3.004-4.568.962-4.288-.36.08-.521.16-.521.16s.12-.24.401-.32c2.846-1.002 5.089 3.005-0.922 4.568 0 0 .04-.04.08-.12zm-7.373 6.332c4.328.28 10.94-.16 11.1-2.205 0 0-.32.803-3.566 1.402-3.687.681-8.255.6-10.94.16 0 0 .561.48 3.407.641z"
fill="currentColor"
/>
<path
d="M14.247.001s2.485 2.525-2.364 6.332c-3.887 3.086-.881 4.848 0 6.853-2.285-2.044-3.927-3.848-2.805-5.53C10.72 5.17 15.249 3.975 14.247.001zm-1.275 11.501c1.163 1.322-.32 2.525-.32 2.525s2.966-1.523 1.605-3.407c-1.242-1.803-2.205-2.685 3.005-5.69 0 0-8.215 2.044-4.288 6.572z"
fill="currentColor"
/>
</svg>
),
color: "text-red-500",
},
{ name: "Docker", icon: <SiDocker />, color: "text-blue-400" },
{ name: "Linux", icon: <SiLinux />, color: "text-yellow-400" },
];
const achievements = [
{
icon: <Trophy className="w-10 h-10 text-accent mx-auto" />,
title: "Top GitHub Contributor in Albania",
description:
"Ranked #2 nationally based on commits, contributions, and repository activity.",
},
{
icon: <Github className="w-10 h-10 text-accent mx-auto" />,
title: "Contributor to 80+ Public Repositories",
description:
"Actively contributed code, bug fixes, and features to multiple open-source projects.",
},
{
icon: <Box className="w-10 h-10 text-accent mx-auto" />,
title: "Built & Deployed 3 Full-Stack Applications",
description:
"Completed end-to-end development projects using Next.js, React, and TypeScript.",
},
{
icon: <FileText className="w-10 h-10 text-accent mx-auto" />,
title: "Published Technical Guides & Documentation",
description:
"Authored clear technical documentation and tutorials in public repositories.",
},
];
return (
<>
{/* Hero */}
<motion.section
className="pt-24 pb-16 bg-background relative overflow-hidden"
variants={staggerContainer}
initial="hidden"
animate="visible"
>
<motion.div
className="absolute top-1/4 right-1/4 w-96 h-96 bg-accent/5 rounded-full blur-3xl"
variants={slowFloat}
/>
<div className="container mx-auto px-6 relative z-10">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
{/* Image/Avatar */}
<motion.div variants={fadeInUp}>
<div className="relative">
<div className="aspect-square max-w-md mx-auto rounded-3xl overflow-hidden glass-card p-2">
<div className="w-full h-full rounded-2xl bg-gradient-to-br from-accent/20 to-accent/5 flex items-center justify-center">
<img
src="https://avatars.githubusercontent.com/u/103524696?v=4"
alt="Klesti Selimaj"
className="w-full h-full object-cover rounded-2xl"
/>
</div>
</div>
{/* Floating badges */}
<motion.div
className="absolute -bottom-4 -right-4 glass-card rounded-xl px-4 py-2 flex items-center gap-2"
variants={fadeInUp}
transition={{ delay: 0.6, type: "spring" }}
>
<span className="w-2 h-2 rounded-full bg-accent animate-pulse" />
<span className="text-sm text-foreground">Available</span>
</motion.div>
</div>
</motion.div>
{/* Content */}
<motion.div variants={staggerContainer}>
<span className="text-accent text-sm font-medium uppercase tracking-wider">
About Me
</span>
<motion.h1
variants={fadeInUp}
className="text-4xl md:text-5xl font-bold text-foreground mt-2 mb-6"
>
Hey, I'm{" "}
<span className="text-gradient-gold">Klesti Selimaj</span>
</motion.h1>
<motion.p
variants={fadeInUp}
className="text-lg text-muted-foreground mb-6"
>
A 16-year-old software engineer from Albania with 6+ years of
experience. I'm passionate about building things that matter,
from low-level systems to beautiful user interfaces.
</motion.p>
<motion.p
variants={fadeInUp}
className="text-muted-foreground mb-8"
>
My journey started at age 10 when I had my first laptop (a Asus
with pre installed endless linux), and since then I've been
hooked on computers, but i didn't exactly know what programming
was but i was doing it unconsciously, making winforms apps in c#
with visual studio.
</motion.p>
{/* Quick Info */}
<motion.div
variants={fadeInUp}
className="flex flex-wrap gap-4 mb-8"
>
<div className="flex items-center gap-2 text-muted-foreground">
<MapPin className="w-4 h-4 text-accent" />
<span>Albania</span>
</div>
<div className="flex items-center gap-2 text-muted-foreground">
<Code className="w-4 h-4 text-accent" />
<span>6+ Years Experience</span>
</div>
<div className="flex items-center gap-2 text-muted-foreground">
<Crown className="w-4 h-4 text-accent" />
<span>@orus-dev</span>
</div>
</motion.div>
{/* Social Links */}
<motion.div
variants={fadeInUp}
className="flex items-center gap-4"
>
{[Github, Linkedin, Youtube, Instagram, ExternalLink].map(
(Icon, i) => (
<motion.a
key={i}
whileHover={{ y: -4, scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="glass-card p-3 rounded-xl hover:ring-1 hover:ring-accent/30 transition-all"
>
<Icon className="w-5 h-5 text-foreground" />
</motion.a>
),
)}
</motion.div>
</motion.div>
</div>
</div>
</motion.section>
{/* Tech Stack */}
<section className="py-24 bg-card/50">
<div className="container mx-auto px-6">
<div className="max-w-2xl mb-16">
<span className="text-accent text-sm font-medium uppercase tracking-wider">
Tech Stack
</span>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mt-2 mb-4">
Tools I Work With
</h2>
<p className="text-muted-foreground">
A carefully curated set of technologies that I use to build
exceptional products.
</p>
</div>
<motion.div
className="grid grid-cols-2 md:grid-cols-4 gap-4"
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
{techStack.map((tech, index) => (
<motion.div
key={tech.name}
variants={cardReveal}
className="glass-card rounded-2xl p-6 text-center hover-lift group"
>
<div className={cn("text-4xl mb-3 mx-auto w-max", tech.color)}>
{tech.icon}
</div>
<h3 className="font-semibold text-foreground group-hover:text-accent transition-colors">
{tech.name}
</h3>
</motion.div>
))}
</motion.div>
</div>
</section>
{/* Achievements */}
<section className="py-24 bg-background">
<div className="container mx-auto px-6">
<div className="max-w-2xl mb-16">
<span className="text-accent text-sm font-medium uppercase tracking-wider">
Recognition
</span>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mt-2 mb-4">
My Achievements
</h2>
<p className="text-muted-foreground">
Achievements earned through consistent contributions and learning.
</p>
</div>
<motion.div
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
{achievements.map((achievement, index) => (
<motion.div
key={achievement.title}
variants={cardReveal}
className="glass-card rounded-2xl p-6 text-center hover-lift"
>
<div className="text-4xl mb-4">{achievement.icon}</div>
<h3 className="font-semibold text-foreground mb-2">
{achievement.title}
</h3>
<p className="text-sm text-muted-foreground">
{achievement.description}
</p>
</motion.div>
))}
</motion.div>
</div>
</section>
{/* Timeline */}
<section className="py-24 bg-card/50">
<div className="container mx-auto px-6">
<div className="max-w-2xl mb-16">
<span className="text-accent text-sm font-medium uppercase tracking-wider">
Journey
</span>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mt-2 mb-4">
My Story
</h2>
<p className="text-muted-foreground">
From curious kid to entrepreneurial developer, here's how I got
here.
</p>
</div>
<div className="max-w-3xl mx-auto">
{timeline.map((item, index) => (
<motion.div
key={item.year}
variants={
index % 2 === 0 ? timelineItemLeft : timelineItemRight
}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
className="relative flex gap-6 pb-12 last:pb-0"
>
{/* Line */}
{index !== timeline.length - 1 && (
<div className="absolute left-[19px] top-10 w-0.5 h-[calc(100%-2.5rem)] bg-border" />
)}
{/* Dot */}
<div className="relative flex-shrink-0">
<div className="w-10 h-10 rounded-full bg-accent flex items-center justify-center">
<span className="text-accent-foreground text-xs font-bold">
{item.year.slice(2)}
</span>
</div>
</div>
{/* Content */}
<div className="glass-card rounded-2xl p-6 flex-1">
<div className="flex items-center gap-3 mb-2">
<span className="text-sm font-mono text-accent">
{item.year}
</span>
</div>
<h3 className="text-lg font-semibold text-foreground mb-2">
{item.title}
</h3>
<p className="text-muted-foreground text-sm">
{item.description}
</p>
</div>
</motion.div>
))}
</div>
</div>
</section>
{/* Philosophy */}
<section className="py-24 bg-background relative overflow-hidden">
<motion.div
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-accent/5 rounded-full blur-3xl"
variants={slowFloat}
/>
<div className="container mx-auto px-6 relative z-10">
<div className="max-w-3xl mx-auto text-center">
<span className="text-accent text-sm font-medium uppercase tracking-wider">
Philosophy
</span>
<blockquote className="text-xl md:text-2xl text-foreground font-medium italic mb-6 mt-4">
"One day or Day one?"
</blockquote>
<p className="text-muted-foreground">- Klesti Selimaj</p>
</div>
</div>
</section>
</>
);
}
+225 -29
View File
@@ -1,41 +1,237 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
@font-face {
font-family: 'Kodchasan Medium';
src: url('/fonts/Kodchasan-Medium.ttf');
}
/* Premium Monochromatic Design System with Gold Accents */
@font-face {
font-family: 'Kodchasan';
src: url('/fonts/Kodchasan-Regular.ttf');
}
@font-face {
font-family: 'Kodchasan Semi Bold';
src: url('/fonts/Kodchasan-SemiBold.ttf');
}
:root {
--background: #ffffff;
--foreground: #171717;
}
@media (prefers-color-scheme: dark) {
@layer base {
:root {
--background: #0a0a0a;
--foreground: #ededed;
--background: 0 0% 100%;
--foreground: 0 0% 5%;
--card: 0 0% 98%;
--card-foreground: 0 0% 5%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 5%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 94%;
--muted-foreground: 0 0% 45%;
--accent: 43 41% 59%;
--accent-foreground: 0 0% 5%;
--destructive: 0 84% 60%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 90%;
--input: 0 0% 90%;
--ring: 43 41% 59%;
--radius: 0.75rem;
/* Premium Gold Tokens */
--gold: 43 41% 59%;
--gold-light: 43 45% 70%;
--gold-dark: 43 38% 45%;
/* Gradient tokens */
--gradient-gold: linear-gradient(135deg, hsl(43 41% 59%), hsl(43 45% 70%));
--gradient-dark: linear-gradient(180deg, hsl(0 0% 100%), hsl(0 0% 96%));
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.03);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.05), 0 2px 4px -2px rgb(0 0 0 / 0.05);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.05), 0 4px 6px -4px rgb(0 0 0 / 0.05);
--shadow-gold: 0 0 30px -5px hsl(43 41% 59% / 0.15);
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 0 0% 25%;
--sidebar-primary: 0 0% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 0 0% 95%;
--sidebar-accent-foreground: 0 0% 10%;
--sidebar-border: 0 0% 91%;
--sidebar-ring: 43 41% 59%;
}
.dark {
--background: 0 0% 4%;
--foreground: 0 0% 95%;
--card: 0 0% 7%;
--card-foreground: 0 0% 95%;
--popover: 0 0% 6%;
--popover-foreground: 0 0% 95%;
--primary: 0 0% 95%;
--primary-foreground: 0 0% 5%;
--secondary: 0 0% 12%;
--secondary-foreground: 0 0% 95%;
--muted: 0 0% 15%;
--muted-foreground: 0 0% 55%;
--accent: 43 41% 59%;
--accent-foreground: 0 0% 5%;
--destructive: 0 62% 30%;
--destructive-foreground: 0 0% 95%;
--border: 0 0% 15%;
--input: 0 0% 15%;
--ring: 43 41% 59%;
/* Premium Gold Tokens (adjusted for dark) */
--gold: 43 41% 59%;
--gold-light: 43 50% 68%;
--gold-dark: 43 35% 48%;
/* Gradient tokens */
--gradient-gold: linear-gradient(135deg, hsl(43 41% 59%), hsl(43 50% 68%));
--gradient-dark: linear-gradient(180deg, hsl(0 0% 4%), hsl(0 0% 8%));
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.5), 0 4px 6px -4px rgb(0 0 0 / 0.4);
--shadow-gold: 0 0 40px -5px hsl(43 41% 59% / 0.25);
--sidebar-background: 0 0% 6%;
--sidebar-foreground: 0 0% 90%;
--sidebar-primary: 43 41% 59%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 0 0% 12%;
--sidebar-accent-foreground: 0 0% 90%;
--sidebar-border: 0 0% 12%;
--sidebar-ring: 43 41% 59%;
}
}
body {
color: var(--foreground);
background: var(--background);
font-family: 'Kodchasan Semi Bold', sans-serif;
@layer base {
* {
@apply border-border;
}
html {
scroll-behavior: smooth;
}
body {
@apply bg-background text-foreground antialiased;
font-family: 'Inter', system-ui, sans-serif;
}
::selection {
@apply bg-accent/30 text-foreground;
}
}
h1 {
font-size: 30px;
font-weight: bold;
@layer components {
.text-gradient-gold {
@apply bg-clip-text text-transparent;
background-image: linear-gradient(135deg, hsl(var(--gold)), hsl(var(--gold-light)));
}
.border-gradient-gold {
border-image: linear-gradient(135deg, hsl(var(--gold)), hsl(var(--gold-light))) 1;
}
.glow-gold {
box-shadow: 0 0 40px -5px hsl(var(--gold) / 0.3);
}
.glass-card {
@apply bg-card/80 backdrop-blur-xl border border-border/50;
}
.hover-lift {
@apply transition-all duration-300 ease-out;
}
.hover-lift:hover {
@apply -translate-y-1;
box-shadow: var(--shadow-lg);
}
.font-mono {
font-family: 'JetBrains Mono', monospace;
}
}
@layer utilities {
.animate-fade-in {
animation: fadeIn 0.6s ease-out forwards;
}
.animate-fade-in-up {
animation: fadeInUp 0.6s ease-out forwards;
}
.animate-fade-in-delay-1 {
animation: fadeIn 0.6s ease-out 0.1s forwards;
opacity: 0;
}
.animate-fade-in-delay-2 {
animation: fadeIn 0.6s ease-out 0.2s forwards;
opacity: 0;
}
.animate-fade-in-delay-3 {
animation: fadeIn 0.6s ease-out 0.3s forwards;
opacity: 0;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
.animate-shimmer {
background: linear-gradient(
90deg,
hsl(var(--gold) / 0) 0%,
hsl(var(--gold) / 0.3) 50%,
hsl(var(--gold) / 0) 100%
);
background-size: 200% 100%;
animation: shimmer 3s infinite;
}
}
+8 -6
View File
@@ -1,10 +1,10 @@
import type { Metadata } from "next";
import "./globals.css";
import Navbar from "@/components/Navbar";
import Background from "@/components/Background";
import { Navbar } from "@/components/layout/Navbar";
import { Footer } from "@/components/layout/Footer";
export const metadata: Metadata = {
title: "Klesti's Portfolio",
title: "Selimaj.dev",
description: "",
};
@@ -16,9 +16,11 @@ export default function RootLayout({
return (
<html lang="en">
<body className="antialiased bg-black">
<Background>
<div className="relative h-screen">{children}</div>
</Background>
<div className="min-h-screen flex flex-col bg-background dark">
<Navbar />
<main className="flex-1 pt-16">{children}</main>
<Footer />
</div>
</body>
</html>
);
+11 -227
View File
@@ -1,231 +1,15 @@
"use client";
import { motion, spring } from "framer-motion";
import PinnedRepos from "@/components/PinnedRepos";
import Skill from "@/components/Skill";
import {
faGithub,
faGolang,
faInstagram,
faJs,
faLinkedin,
faPython,
faReact,
faRust,
} from "@fortawesome/free-brands-svg-icons";
import { faJava } from "@fortawesome/free-brands-svg-icons/faJava";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { CoffeeIcon, HeartIcon } from "lucide-react";
import { useEffect, useState } from "react";
import GitHubCalendar from "react-github-calendar";
import { fadeInUp, popIn, staggerContainer } from "@/components/effects";
export default function App() {
const [contribs, setContribs] = useState<number>();
useEffect(() => {
const fetchContributions = async () => {
const res = await fetch("/api/contribs");
const data = await res.json();
setContribs(data.total || undefined);
};
fetchContributions();
}, []);
import { HeroSection } from "@/components/home/HeroSection";
import { ProjectsSection } from "@/components/home/ProjectsSection";
import { SkillsSection } from "@/components/home/SkillsSection";
import { CTASection } from "@/components/home/CTASection";
export default function Home() {
return (
<motion.div
className="w-full max-w-screen-lg mx-auto mt-10"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.6 }}
>
<div className="px-8 md:px-10 lg:px-15 xl:px-24">
{/* HEADER */}
<motion.h1
className="py-2 text-gray-300"
variants={fadeInUp}
initial="hidden"
animate="show"
>
Hey, I'm Klesti! 👋🏻
</motion.h1>
<motion.p
className="font-semibold text-gray-300"
variants={fadeInUp}
initial="hidden"
animate="show"
transition={{ delay: 0.15 }}
>
A 15 year old Software Engineer from Tirane, Albania, with 6 years of
experience, specializing in full-stack web development, desktop
applications, TUI apps, and hacking. I also have a deep interest in
low-level programming.
</motion.p>
{/* KO-FI BUTTON */}
<motion.div
className="pt-6"
animate={{ y: [0, -3, 0] }}
transition={{ repeat: Infinity, duration: 3 }}
>
<a href="https://ko-fi.com/X8X01OX1WR" target="_blank">
<motion.span
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="bg-transparent-ish rounded-md border border-slate-400 p-2 flex w-max gap-2 items-center cursor-pointer"
>
<CoffeeIcon /> Support me on Ko-fi
</motion.span>
</a>
</motion.div>
{/* SOCIALS + SKILLS */}
<motion.div
className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6 bg-transparent"
variants={staggerContainer}
initial="hidden"
animate="show"
>
{/* socials */}
<motion.div className="pt-5 flex flex-row gap-5 w-max">
{[faGithub, faLinkedin, faInstagram].map((icon, i) => (
<motion.a
key={i}
href={
icon === faGithub
? "https://github.com/selimaj-dev/"
: icon === faLinkedin
? "https://www.linkedin.com/in/klesti-selimaj-7a0162343/"
: "https://www.instagram.com/selimaj.dev/"
}
target="blank_"
variants={popIn}
whileHover={{ scale: 1.2, rotate: 3 }}
whileTap={{ scale: 0.9 }}
>
<FontAwesomeIcon
icon={icon}
className="text-slate-400 size-[2.3rem]"
/>
</motion.a>
))}
</motion.div>
{/* skills */}
<motion.div variants={popIn}>
<div className="p-3 rounded-xl">
<motion.div
className="mx-auto w-max flex flex-row gap-5"
variants={staggerContainer}
initial="hidden"
animate="show"
>
<motion.div variants={popIn}>
<Skill skill="Rust" icon={faRust} />
</motion.div>
<motion.div variants={popIn}>
<Skill skill="Go" icon={faGolang} />
</motion.div>
<motion.div variants={popIn}>
<Skill skill="JavaScript" icon={faJs} />
</motion.div>
<motion.div variants={popIn}>
<Skill skill="Java" icon={faJava} classInsert="p-1" />
</motion.div>
<motion.div variants={popIn}>
<Skill skill="Python" icon={faPython} />
</motion.div>
<motion.div variants={popIn}>
<Skill skill="React" icon={faReact} />
</motion.div>
</motion.div>
</div>
</motion.div>
</motion.div>
{/* PINNED PROJECTS */}
<motion.h1
className="text-slate-300 mt-4"
variants={fadeInUp}
initial="hidden"
animate="show"
>
Pinned Projects
</motion.h1>
<motion.section>
<PinnedRepos />
</motion.section>
{/* CONTRIBUTIONS */}
<motion.h2
className="text-slate-300 mt-7"
variants={fadeInUp}
initial="hidden"
animate="show"
>
{contribs || "Loading.."} contributions in the last year
</motion.h2>
<motion.section
className="mt-4"
variants={fadeInUp}
initial="hidden"
animate="show"
>
<motion.div variants={fadeInUp}>
<GitHubCalendar
// hideTotalCount
username="selimaj-dev"
theme={{
light: [
"rgba(255, 255, 255, 0.1)",
"rgba(255, 255, 255, 0.3)",
"rgba(255, 255, 255, 0.5)",
"rgba(255, 255, 255, 0.7)",
"rgba(255, 255, 255, 1)",
],
dark: [
"rgba(255, 255, 255, 0.1)",
"rgba(255, 255, 255, 0.3)",
"rgba(255, 255, 255, 0.5)",
"rgba(255, 255, 255, 0.7)",
"rgba(255, 255, 255, 1)",
],
}}
/>
</motion.div>
</motion.section>
{/* FOOTER */}
<footer className="mt-8 border-t pt-3 pb-8">
<motion.p
className="flex gap-1 items-center"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.4 }}
>
If you see this i{" "}
<motion.span
animate={{ scale: [1, 1.16, 1] }}
transition={{ repeat: Infinity, duration: 1.0 }}
>
<HeartIcon className="size-4 text-slate-300" />
</motion.span>{" "}
you
</motion.p>
<motion.p
className="text-slate-300 mx-auto w-max mt-2"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.6 }}
>
© 2025 Klesti. All rights reserved.
</motion.p>
</footer>
</div>
</motion.div>
<>
<HeroSection />
<ProjectsSection />
<SkillsSection />
<CTASection />
</>
);
}
+332
View File
@@ -0,0 +1,332 @@
"use client";
import { Check, ArrowRight, Zap, Crown, Rocket } from "lucide-react";
import { useState } from "react";
import { motion } from "framer-motion";
import {
staggerContainer,
fadeInUp,
cardReveal,
slowFloat,
} from "@/lib/animations";
const plans = [
{
name: "Starter",
description: "Small, clearly defined tasks and improvements",
price: "$500",
period: "project",
icon: Zap,
features: [
"Single feature or small tool",
"Bug fixes or minor enhancements",
"Basic frontend or backend work",
"Limited scope (no architecture changes)",
"1 revision round",
"Async communication",
"Source code included",
],
cta: "Get Started",
popular: false,
},
{
name: "Professional",
description: "Product features, MVPs, and system components",
price: "$2,000+",
period: "project",
icon: Crown,
features: [
"Multi-feature application or system module",
"Backend services or APIs (Rust, Node, etc.)",
"CLI or TUI applications",
"Frontend integration where needed",
"Architecture and technical decisions",
"2–3 revision rounds",
"Documentation for delivered components",
"Performance and reliability considerations",
],
cta: "Start Project",
popular: true,
},
{
name: "Enterprise",
description: "Long-term systems work and technical ownership",
price: "Custom",
period: "quote",
icon: Rocket,
features: [
"Large or distributed system design",
"Cross-team or multi-stakeholder coordination",
"Ongoing development and refactoring",
"System audits and performance reviews",
"Internal tooling and automation",
"Priority access and scheduling",
"Extended documentation and knowledge transfer",
"Training, onboarding, and long-term support",
],
cta: "Contact Me",
popular: false,
},
];
const services = [
{
title: "Web Development",
description:
"Modern, performant web applications built with React, TypeScript, and cutting-edge technologies.",
},
{
title: "Systems Programming",
description:
"Low-level development in Rust and Go for performance-critical applications.",
},
{
title: "TUI/CLI Tools",
description:
"Beautiful terminal user interfaces and command-line tools with my own TUI framework that developers love.",
},
{
title: "API Development",
description: "Robust, scalable backend systems and API architectures.",
},
{
title: "DevOps & Deployment",
description:
"Docker, CI/CD pipelines, and self-hosted deployment solutions with my own infrastructure.",
},
{
title: "Consulting",
description:
"Technical guidance and architecture planning for your projects.",
},
];
export default function Pricing() {
const [hoveredPlan, setHoveredPlan] = useState<string | null>(null);
return (
<>
{/* Hero */}
<motion.section
className="pt-24 pb-16 bg-background relative overflow-hidden"
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
<motion.div
className="absolute top-1/4 left-1/4 w-96 h-96 bg-accent/5 rounded-full blur-3xl"
variants={slowFloat}
/>
<div className="container mx-auto px-6 relative z-10">
<motion.div
variants={fadeInUp}
className="max-w-3xl mx-auto text-center"
>
<span className="text-accent text-sm font-medium uppercase tracking-wider">
Pricing
</span>
<h1 className="text-4xl md:text-5xl font-bold text-foreground mt-2 mb-6">
Transparent Pricing for{" "}
<span className="text-gradient-gold">Premium Quality</span>
</h1>
<p className="text-lg text-muted-foreground">
Choose a plan that fits your needs. Every project receives the
same attention to detail and commitment to excellence.
</p>
</motion.div>
</div>
</motion.section>
{/* Pricing Cards */}
<motion.section
className="py-16 bg-card/50"
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
<div className="container mx-auto px-6">
<div className="grid grid-cols-1 md:px-32 lg:px-0 lg:grid-cols-3 gap-8 max-w-6xl mx-auto">
{plans.map((plan, index) => (
<motion.div
key={plan.name}
variants={cardReveal}
className={`relative glass-card rounded-2xl p-8 flex flex-col transition-all duration-300 ${
plan.popular
? "ring-2 ring-accent lg:scale-110"
: "hover:ring-1 hover:ring-accent/30"
} ${hoveredPlan === plan.name ? "glow-gold" : ""}`}
onMouseEnter={() => setHoveredPlan(plan.name)}
onMouseLeave={() => setHoveredPlan(null)}
>
{plan.popular && (
<div className="absolute -top-4 left-1/2 -translate-x-1/2">
<span className="bg-accent text-accent-foreground px-4 py-1 rounded-full text-sm font-medium">
Most Popular
</span>
</div>
)}
<div className="flex items-center gap-3 mb-4">
<div
className={`w-10 h-10 rounded-xl flex items-center justify-center ${
plan.popular ? "bg-accent" : "bg-secondary"
}`}
>
<plan.icon
className={`w-5 h-5 ${
plan.popular
? "text-accent-foreground"
: "text-foreground"
}`}
/>
</div>
<h3 className="text-xl font-semibold text-foreground">
{plan.name}
</h3>
</div>
<p className="text-muted-foreground text-sm mb-6">
{plan.description}
</p>
<div className="mb-6">
<span className="text-4xl font-bold text-foreground">
{plan.price}
</span>
{plan.period !== "quote" && (
<span className="text-muted-foreground">
/{plan.period}
</span>
)}
</div>
<ul className="space-y-3 mb-8">
{plan.features.map((feature) => (
<li
key={feature}
className="flex items-start gap-3 text-sm"
>
<Check className="w-4 h-4 text-accent mt-0.5 flex-shrink-0" />
<span className="text-foreground">{feature}</span>
</li>
))}
</ul>
<motion.a
href="mailto:[email protected]"
initial="initial"
whileHover="hover"
className={`group mt-auto w-full flex items-center justify-center gap-2 py-3 rounded-xl font-medium transition-all ${
plan.popular
? "bg-accent text-accent-foreground hover:bg-accent/90"
: "bg-secondary text-secondary-foreground hover:bg-secondary/80"
}`}
>
{plan.cta}
<motion.span
variants={{
initial: { x: 0 },
hover: {
x: 6,
transition: {
type: "spring",
stiffness: 400,
damping: 20,
},
},
}}
>
<ArrowRight className="w-4 h-4" />
</motion.span>
</motion.a>
</motion.div>
))}
</div>
</div>
</motion.section>
{/* Services */}
<motion.section
className="py-24 bg-background"
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
<div className="container mx-auto px-6">
<motion.div variants={fadeInUp} className="max-w-2xl mb-16">
<span className="text-accent text-sm font-medium uppercase tracking-wider">
Services
</span>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mt-2 mb-4">
What I Offer
</h2>
<p className="text-muted-foreground">
From concept to deployment, I provide end-to-end development
services tailored to your needs.
</p>
</motion.div>
<motion.div
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"
variants={staggerContainer}
>
{services.map((service, index) => (
<motion.div
key={service.title}
variants={fadeInUp}
className="glass-card rounded-2xl p-6 hover-lift group"
>
<h3 className="text-lg font-semibold text-foreground mb-2 group-hover:text-accent transition-colors">
{service.title}
</h3>
<p className="text-muted-foreground text-sm">
{service.description}
</p>
</motion.div>
))}
</motion.div>
</div>
</motion.section>
{/* Contact CTA */}
<motion.section
className="py-24 bg-card/50 relative overflow-hidden"
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
<motion.div
className="absolute top-0 left-1/2 -translate-x-1/2 w-[800px] h-[400px] bg-accent/5 rounded-full blur-3xl"
variants={slowFloat}
/>
<div className="container mx-auto px-6 relative z-10">
<motion.div
variants={fadeInUp}
className="max-w-3xl mx-auto text-center"
>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-6">
Have a Custom Project in Mind?
</h2>
<p className="text-lg text-muted-foreground mb-10 max-w-xl mx-auto">
Let's discuss your requirements and create a tailored solution
that exceeds expectations.
</p>
<motion.a
href="mailto:[email protected]"
whileHover={{ y: -4, scale: 1.03 }}
className="inline-flex items-center gap-2 bg-accent text-accent-foreground px-8 py-4 rounded-xl font-medium hover:bg-accent/90 transition-all glow-gold"
>
Schedule a Call
<ArrowRight className="w-4 h-4" />
</motion.a>
</motion.div>
</div>
</motion.section>
</>
);
}
-129
View File
@@ -1,129 +0,0 @@
import { Client } from "@notionhq/client";
import { SearchResponse } from "@notionhq/client/build/src/api-endpoints";
import { NotionToMarkdown } from "notion-to-md";
interface Blog {
id: string;
title: string;
tags: { name: string; color: string }[];
date: string;
description: string;
}
type QueryResponse = { cursor: string | null; hasMore: boolean; items: Blog[] };
type Error = { msg: string };
export async function getBlogs(
notion: Client,
databaseId: string,
cursor: string | undefined,
pageSize: number
): Promise<[number, QueryResponse | Error]> {
try {
const response = await notion.databases.query({
database_id: databaseId,
filter: {
property: "Status",
status: {
equals: "Posted",
},
},
start_cursor: cursor,
page_size: pageSize,
});
return [
200,
{
cursor: response.next_cursor,
hasMore: response.has_more,
items: response.results.map(convertBlog),
},
];
} catch (err: any) {
return [400, { msg: String(err) }];
}
}
function convertBlog(page: any): Blog {
return {
id: page.id,
title: page.properties.Title.title[0].plain_text,
description: [
page.properties.Description.rich_text.map(
(d: { plain_text: string }) => d.plain_text
),
].join(" "),
date: page.properties.Date.created_time,
tags: page.properties.Tags.multi_select.map((tag: any) => ({
name: tag.name,
color: tag.color,
})),
};
}
interface FullBlog extends Blog {
markdown: string;
}
export async function getBlog(
notion: Client,
n2m: NotionToMarkdown,
pageId: string
): Promise<[number, FullBlog | Error]> {
try {
const page: any = await notion.pages.retrieve({ page_id: pageId });
if (page.properties.Status.status.name !== "Posted")
return [400, { msg: "Blog not found" }];
try {
const blog = convertBlog(page);
const mdBlocks = await n2m.pageToMarkdown(page.id);
return [
200,
{
id: blog.id,
title: blog.title,
description: blog.description,
tags: blog.tags,
date: blog.date,
markdown: n2m.toMarkdownString(mdBlocks).parent || "",
},
];
} catch (err: any) {
return [500, { msg: String(err) }];
}
} catch {
return [400, { msg: "Blog not found" }];
}
}
export async function searchBlog(
notion: Client,
query: string
): Promise<[200 | 400, QueryResponse | Error]> {
try {
var response = await notion.search({
query,
filter: {
value: "page",
property: "object",
},
sort: {
direction: "ascending",
timestamp: "last_edited_time",
},
});
response.results = response.results.filter(
(item: any) => item.properties.Status.status.name === "Posted"
);
return [
200,
{
cursor: response.next_cursor,
hasMore: response.has_more,
items: response.results.map(convertBlog),
},
];
} catch (err: any) {
return [400, { msg: String(err) }];
}
}
-22
View File
@@ -1,22 +0,0 @@
"use client";
import React from "react";
export default function Background({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<div className="relative w-full min-h-screen overflow-hidden bg-black">
<div
className="absolute inset-0 bg-black"
style={{
backgroundImage:
"radial-gradient(circle, rgba(100, 116, 139, 0.5) 1px, transparent 1px)",
backgroundSize: "20px 20px",
}}
/>
<div className="relative z-50 overflow-y-scroll overflow-x-hidden">{children}</div>
</div>
);
}
-73
View File
@@ -1,73 +0,0 @@
import React from "react";
import Markdown from "react-markdown";
import remarkGfm from "remark-gfm";
import remarkBreaks from "remark-breaks";
import remarkMath from "remark-math";
import rehypeRaw from "rehype-raw";
import rehypeKatex from "rehype-katex";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { gruvboxDark } from "react-syntax-highlighter/dist/esm/styles/prism";
import "katex/dist/katex.min.css";
type MarkdownRendererProps = {
children: string;
};
export default function MarkdownRenderer({
children: markdown,
}: MarkdownRendererProps) {
return (
<Markdown
remarkPlugins={[remarkGfm, remarkMath, remarkBreaks]}
rehypePlugins={[rehypeRaw, rehypeKatex]}
components={{
code({ node, inline, className, children, ...props }: any) {
const match = /language-(\w+)/.exec(className || "");
return !inline && match ? (
<SyntaxHighlighter
style={gruvboxDark}
PreTag="div"
language={match[1]}
{...props}
>
{String(children).replace(/\n$/, "")}
</SyntaxHighlighter>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
ul({ children, ...props }) {
return (
<ul
style={{ listStyleType: "disc", listStylePosition: "inside" }}
{...props}
>
{children}
</ul>
);
},
ol({ children, ...props }) {
return (
<ol
style={{ listStyleType: "decimal", listStylePosition: "inside" }}
{...props}
>
{children}
</ol>
);
},
li({ children, ...props }) {
return (
<li style={{ marginBottom: "4px" }} {...props}>
{children}
</li>
);
},
}}
>
{markdown}
</Markdown>
);
}
-35
View File
@@ -1,35 +0,0 @@
"use client";
import { usePathname } from "next/navigation";
import Link from "next/link";
export default function Navbar() {
const pathname = usePathname();
return (
<nav className="py-4 flex">
<div className="mx-auto flex">
<div className="space-x-4 text-white block font-bold">
<Link
href="/"
className={
"hover:slate-200 transition-colors duration-300" +
(pathname === "/" ? " slate-200" : "")
}
>
Home
</Link>
<Link
href="/pricing"
className={
"hover:slate-200 transition-colors duration-300" +
(pathname === "/pricing" ? " slate-200" : "")
}
>
Pricing
</Link>
</div>
</div>
</nav>
);
}
-178
View File
@@ -1,178 +0,0 @@
"use client";
import { useEffect, useState } from "react";
import { motion } from "framer-motion";
import { fadeInUp, fadeInUp2, staggerContainer } from "./effects";
type PinnedRepo = {
author: string;
name: string;
description: string;
language: string;
stars: number;
forks: number;
href?: string;
};
export function Repo({
name,
description: desc,
language: lang,
stars,
forks,
href,
}: PinnedRepo) {
return (
<motion.div
variants={fadeInUp2}
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.2 }}
className="group relative flex size-full overflow-hidden rounded-xl h-full border border-slate-400 hover:shadow-md transition-shadow duration-300 backdrop-blur-[2px]"
>
<div className="w-full">
<div className="rounded-xl border text-card-foreground shadow h-full bg-transparent border-none flex flex-col">
<div className="flex flex-col space-y-1.5 p-6 flex-grow">
<h3 className="tracking-tight text-base sm:text-lg font-semibold text-slate-300">
<a
href={href}
target="_blank"
rel="noreferrer"
className="hover:underline"
style={{ cursor: "pointer" }}
>
{name}
</a>
</h3>
<p className="text-xs sm:text-sm text-gray-300 line-clamp-2 mt-1">
{desc}
</p>
</div>
<div className="p-6 pt-0 mt-auto">
<div className="flex flex-wrap justify-between items-center gap-2">
<div className="flex items-center gap-2 sm:gap-4">
<div
className="flex items-center gap-1 text-xs sm:text-sm"
data-state="closed"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-star text-gray-300 h-3 w-3 sm:h-4 sm:w-4"
>
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
</svg>
<span className="text-gray-300">{stars}</span>
</div>
<div
className="flex items-center gap-1 text-xs sm:text-sm"
data-state="closed"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-git-fork text-gray-500 h-3 w-3 sm:h-4 sm:w-4"
>
<circle cx="12" cy="18" r="3" />
<circle cx="6" cy="6" r="3" />
<circle cx="18" cy="6" r="3" />
<path d="M18 9v2c0 .6-.4 1-1 1H7c-.6 0-1-.4-1-1V9" />
<path d="M12 12v3" />
</svg>
<span className="text-gray-300">{forks}</span>
</div>
</div>
{lang && (
<div
className="inline-flex items-center rounded-md border px-2.5 py-0.5 font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 text-foreground text-xs bg-slate-400/10 border-slate-400"
data-sentry-component="Badge"
data-sentry-source-file="badge.tsx"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-code-xml h-3 w-3 mr-1"
>
<path d="m18 16 4-4-4-4" />
<path d="m6 8-4 4 4 4" />
<path d="m14.5 4-5 16" />
</svg>
{lang}
</div>
)}
</div>
</div>
</div>
</div>
</motion.div>
);
}
export default function PinnedRepos() {
try {
const [pinned, setPinned] = useState<PinnedRepo[]>([]);
useEffect(() => {
fetch("https://pinned.berrysauce.dev/get/selimaj-dev")
.then((r) => {
r.json()
.then((p) => {
setPinned(p);
})
.catch((e) => {
console.error(e);
});
})
.catch((e) => {
console.error(e);
});
}, []);
return (
<motion.div
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.2 }}
className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6 bg-transparent"
>
{pinned &&
pinned.map((repo, i) => (
<Repo
key={i}
author={repo.author}
name={repo.name}
description={repo.description}
language={repo.language}
stars={repo.stars}
forks={repo.forks}
href={`https://github.com/${repo.author}/${repo.name}`}
/>
))}
</motion.div>
);
} catch (e: any) {
return <p>{e.toString()}</p>;
}
}
-29
View File
@@ -1,29 +0,0 @@
import { IconProp } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
export default function Skill({
skill,
icon,
classInsert,
}: {
skill: string;
icon: IconProp;
classInsert?: string;
}) {
return (
<div className="relative flex items-center justify-center group">
<span
className="absolute bottom-full mb-2 left-1/2 -translate-x-1/2
bg-black text-white text-sm px-2 py-1 rounded
opacity-0 group-hover:opacity-100 transition-opacity duration-300 whitespace-nowrap"
>
Skilled in {skill}
</span>
<FontAwesomeIcon
icon={icon}
className={`text-slate-400 ${classInsert} size-[2.3rem]`}
/>
</div>
);
}
-32
View File
@@ -1,32 +0,0 @@
import { easeOut, spring } from "framer-motion";
export const fadeInUp = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0, transition: { duration: 0.5 } },
};
export const fadeInUp2 = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.4, ease: easeOut },
},
};
export const staggerContainer = {
show: {
transition: {
staggerChildren: 0.12,
},
},
};
export const popIn = {
hidden: { opacity: 0, scale: 0.8 },
show: {
opacity: 1,
scale: 1,
transition: { type: spring, stiffness: 120 },
},
};
+39
View File
@@ -0,0 +1,39 @@
import { ArrowRight } from "lucide-react";
import Link from "next/link";
export function CTASection() {
return (
<section className="py-24 bg-card/50 relative overflow-hidden">
{/* Background Effects */}
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[800px] h-[400px] bg-accent/5 rounded-full blur-3xl" />
<div className="container mx-auto px-6 relative z-10">
<div className="max-w-3xl mx-auto text-center">
<h2 className="text-3xl md:text-5xl font-bold text-foreground mb-6">
Ready to build something{" "}
<span className="text-gradient-gold">extraordinary</span>?
</h2>
<p className="text-lg text-muted-foreground mb-10 max-w-xl mx-auto">
Whether you need a complex system, a modern web application, or
specialized tooling, I'm here to bring your vision to life.
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
<Link
href="/pricing"
className="group flex items-center gap-2 bg-accent text-accent-foreground px-8 py-4 rounded-xl font-medium hover:bg-accent/90 transition-all hover:gap-3 glow-gold"
>
View Pricing Plans
<ArrowRight className="w-4 h-4 transition-transform group-hover:translate-x-1" />
</Link>
<Link
href="/about"
className="flex items-center gap-2 text-foreground hover:text-accent font-medium transition-colors"
>
Learn more about me
</Link>
</div>
</div>
</div>
</section>
);
}
+138
View File
@@ -0,0 +1,138 @@
"use client";
import { ArrowRight, Github, Linkedin, Youtube } from "lucide-react";
import Link from "next/link";
import { motion } from "framer-motion";
import { staggerContainer, fadeInUp, slowFloat } from "@/lib/animations";
export function HeroSection() {
return (
<motion.section
className="relative min-h-svh flex items-center justify-center overflow-hidden"
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
{/* Background Effects */}
<div className="absolute inset-0 bg-gradient-to-b from-background via-background to-card" />
<motion.div
className="absolute top-1/4 left-1/4 w-96 h-96 bg-accent/5 rounded-full blur-3xl"
variants={slowFloat}
/>
<motion.div
className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-accent/10 rounded-full blur-3xl"
variants={slowFloat}
/>
<div className="container mx-auto px-6 relative z-10">
<motion.div
className="max-w-4xl mx-auto text-center"
variants={staggerContainer}
>
{/* Status Badge */}
<motion.div
className="inline-flex items-center gap-2 px-4 py-2 rounded-full glass-card mb-8"
variants={fadeInUp}
>
<span className="w-2 h-2 rounded-full bg-accent animate-pulse" />
<span className="text-sm text-muted-foreground">
Available for new projects
</span>
</motion.div>
{/* Main Heading */}
<motion.h1
className="text-4xl md:text-6xl lg:text-7xl font-bold mb-6"
variants={fadeInUp}
>
<span className="text-foreground">Building</span>{" "}
<span className="text-gradient-gold">exceptional</span>
<br />
<span className="text-foreground">digital experiences</span>
</motion.h1>
{/* Subheading */}
<motion.p
className="text-lg md:text-xl text-muted-foreground max-w-2xl mx-auto mb-10"
variants={fadeInUp}
custom={0.2}
>
I'm{" "}
<span className="text-foreground font-medium">Klesti Selimaj</span>,
a 16-year-old software engineer with 6+ years of experience.
Passionate about low-level systems, modern web apps, and crafting
tools that make a difference.
</motion.p>
{/* CTA Buttons */}
<motion.div
className="flex flex-col sm:flex-row items-center justify-center gap-4 mb-12"
variants={fadeInUp}
custom={0.4}
>
<Link
href="/pricing"
className="group flex items-center gap-2 bg-accent text-accent-foreground px-8 py-4 rounded-xl font-medium hover:bg-accent/90 transition-all hover:gap-3"
>
Work with me
<ArrowRight className="w-4 h-4 transition-transform group-hover:translate-x-1" />
</Link>
<a
href="https://github.com/selimaj-dev"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 bg-secondary text-secondary-foreground px-8 py-4 rounded-xl font-medium hover:bg-secondary/80 transition-colors"
>
<Github className="w-4 h-4" />
View GitHub
</a>
</motion.div>
{/* Social Links */}
<motion.div
className="flex items-center justify-center gap-6"
variants={fadeInUp}
custom={0.6}
>
<a
href="https://github.com/selimaj-dev"
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-accent transition-colors"
>
<Github className="w-5 h-5" />
</a>
<a
href="https://www.linkedin.com/in/klesti-selimaj-7a0162343/"
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-accent transition-colors"
>
<Linkedin className="w-5 h-5" />
</a>
<a
href="https://www.youtube.com/@0xkleo"
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-accent transition-colors"
>
<Youtube className="w-5 h-5" />
</a>
</motion.div>
</motion.div>
</div>
{/* Scroll Indicator */}
<motion.div
className="absolute bottom-8 left-1/2 -translate-x-1/2 mb-14"
animate={{ y: [0, 10, 0] }}
transition={{ repeat: Infinity, duration: 1.5 }}
>
<div className="w-6 h-10 border-2 border-muted-foreground/30 rounded-full flex items-start justify-center p-2">
<div className="w-1 h-2 bg-accent rounded-full animate-pulse" />
</div>
</motion.div>
</motion.section>
);
}
+174
View File
@@ -0,0 +1,174 @@
"use client";
import { ExternalLink, Github, Star } from "lucide-react";
import { motion } from "framer-motion";
import { fadeInUp, staggerContainer } from "@/lib/animations";
const projects = [
{
name: "DockShip",
description: "A powerful self-hosted Deployment Platform for modern teams.",
tech: ["TypeScript", "Docker", "React"],
stars: 1,
github: "https://github.com/orus-dev/dockship",
featured: true,
},
{
name: "OSUI",
description:
"A TUI library written in Rust for building beautiful terminal interfaces.",
tech: ["Rust", "TUI", "Systems"],
stars: 8,
github: "https://github.com/osui-rs/osui",
featured: true,
},
{
name: "Linear Linux",
description:
"Linux support for linear.app - bringing seamless project management to Linux users.",
tech: ["JavaScript", "Electron", "Linux"],
stars: 23,
github: "https://github.com/selimaj-dev/linear-linux",
featured: true,
},
{
name: "Saturn Client",
description:
"A Minecraft client full of customizability and free cosmetics.",
tech: ["Java", "Minecraft", "Gaming"],
stars: 3,
github: "https://github.com/saturnclientmc/saturnclient",
featured: false,
},
{
name: "Axiom Chat",
description: "A modern, feature-rich chat application built for teams.",
tech: ["TypeScript", "Real-time", "WebSockets"],
stars: 1,
github: "https://github.com/AxiomChat/axiom",
featured: false,
},
{
name: "Drift",
description: "An organized productivity app to get rid of distractions.",
tech: ["TypeScript", "Productivity", "Focus"],
stars: 1,
github: "https://github.com/orus-dev/drift",
featured: false,
},
];
export function ProjectsSection() {
return (
<motion.section
className="py-24 bg-card/50"
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
<div className="container mx-auto px-6">
{/* Section Header */}
<motion.div className="max-w-2xl mb-16" variants={fadeInUp}>
<span className="text-accent text-sm font-medium uppercase tracking-wider">
Portfolio
</span>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mt-2 mb-4">
Featured Projects
</h2>
<p className="text-muted-foreground">
A selection of my open-source work and personal projects that
showcase my skills in systems programming, web development, and
tooling.
</p>
</motion.div>
{/* Projects Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{projects.map((project, index) => (
<motion.article
key={project.name}
className={"group glass-card rounded-2xl p-6 hover-lift"}
variants={fadeInUp}
custom={index * 0.1}
>
{/* Header */}
<div className="flex items-start justify-between mb-4">
<div className="flex items-center gap-2">
{project.featured && (
<span className="px-2 py-1 text-xs font-medium bg-accent/10 text-accent rounded-full">
Featured
</span>
)}
</div>
<div className="flex items-center gap-1 text-muted-foreground">
<Star className="w-4 h-4" />
<span className="text-sm">{project.stars}</span>
</div>
</div>
{/* Content */}
<h3 className="text-xl font-semibold text-foreground mb-2 group-hover:text-accent transition-colors">
{project.name}
</h3>
<p className="text-muted-foreground text-sm mb-4 line-clamp-2">
{project.description}
</p>
{/* Tech Stack */}
<div className="flex flex-wrap gap-2 mb-6">
{project.tech.map((tech) => (
<span
key={tech}
className="px-2 py-1 text-xs font-mono bg-secondary text-secondary-foreground rounded"
>
{tech}
</span>
))}
</div>
{/* Actions */}
<div className="flex items-center gap-4">
<a
href={project.github}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 text-sm text-muted-foreground hover:text-accent transition-colors"
>
<Github className="w-4 h-4" />
Source
</a>
<a
href={project.github}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 text-sm text-muted-foreground hover:text-accent transition-colors"
>
<ExternalLink className="w-4 h-4" />
View
</a>
</div>
</motion.article>
))}
</div>
{/* View All CTA */}
<motion.div
className="text-center mt-12"
variants={fadeInUp}
custom={0.8}
>
<a
href="https://github.com/selimaj-dev?tab=repositories"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 text-accent hover:text-accent/80 font-medium transition-colors"
>
View all projects on GitHub
<ExternalLink className="w-4 h-4" />
</a>
</motion.div>
</div>
</motion.section>
);
}
+128
View File
@@ -0,0 +1,128 @@
"use client";
import { motion } from "framer-motion";
import { fadeInUp, staggerContainer } from "@/lib/animations";
const skills = [
{
category: "Languages",
items: [
{ name: "Rust", level: 90 },
{ name: "TypeScript", level: 95 },
{ name: "JavaScript", level: 95 },
{ name: "Python", level: 80 },
{ name: "Go", level: 75 },
{ name: "Java", level: 80 },
],
},
{
category: "Web Development",
items: [
{ name: "React", level: 95 },
{ name: "Next.js", level: 85 },
{ name: "Tailwind CSS", level: 80 },
{ name: "Framer Motion", level: 80 },
],
},
{
category: "Systems & Tools",
items: [
{ name: "Linux", level: 85 },
{ name: "Docker", level: 65 },
{ name: "Git", level: 95 },
{ name: "TUI Development", level: 90 },
],
},
];
const stats = [
{ label: "Years Experience", value: "6+" },
{ label: "GitHub Followers", value: "32" },
{ label: "Open Source Projects", value: "80+" },
{ label: "Technologies Mastered", value: "15+" },
];
export function SkillsSection() {
return (
<motion.section
className="py-24 bg-background"
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
<div className="container mx-auto px-6">
{/* Stats */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 mb-20">
{stats.map((stat, index) => (
<motion.div
key={stat.label}
className="text-center p-6 glass-card rounded-2xl hover-lift"
variants={fadeInUp}
custom={index * 0.1}
>
<div className="text-3xl md:text-4xl font-bold text-gradient-gold mb-2">
{stat.value}
</div>
<div className="text-sm text-muted-foreground">{stat.label}</div>
</motion.div>
))}
</div>
{/* Section Header */}
<motion.div className="max-w-2xl mb-16" variants={fadeInUp}>
<span className="text-accent text-sm font-medium uppercase tracking-wider">
Expertise
</span>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mt-2 mb-4">
Technical Skills
</h2>
<p className="text-muted-foreground">
From low-level systems programming to modern web development, I
bring a diverse skill set to every project.
</p>
</motion.div>
{/* Skills Grid */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{skills.map((category, categoryIndex) => (
<motion.div
key={category.category}
className="glass-card rounded-2xl p-6"
variants={fadeInUp}
custom={categoryIndex * 0.15}
>
<h3 className="text-lg font-semibold text-foreground mb-6 flex items-center gap-2">
<span className="w-2 h-2 rounded-full bg-accent" />
{category.category}
</h3>
<div className="space-y-4">
{category.items.map((skill) => (
<div key={skill.name}>
<div className="flex justify-between text-sm mb-2">
<span className="text-foreground font-medium">
{skill.name}
</span>
<span className="text-muted-foreground">
{skill.level}%
</span>
</div>
<div className="h-1.5 bg-secondary rounded-full overflow-hidden">
<motion.div
className="h-full bg-accent rounded-full"
initial={{ width: 0 }}
whileInView={{ width: `${skill.level}%` }}
transition={{ duration: 1, ease: "easeOut" }}
viewport={{ once: true }}
/>
</div>
</div>
))}
</div>
</motion.div>
))}
</div>
</div>
</motion.section>
);
}
+129
View File
@@ -0,0 +1,129 @@
import { ExternalLink } from "lucide-react";
import Link from "next/link";
export function Footer() {
const currentYear = new Date().getFullYear();
return (
<footer className="border-t border-border bg-card/50">
<div className="container mx-auto px-6 py-12">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
{/* Brand */}
<div className="md:col-span-2">
<Link href="/" className="flex items-center gap-2 mb-4">
<img
className="w-8 h-8 rounded-lg bg-accent flex items-center justify-center"
src="/logo.png"
alt="Logo"
/>
<span className="font-semibold text-foreground">selimaj.dev</span>
</Link>
<p className="text-muted-foreground text-sm max-w-sm">
Building digital experiences with precision and passion.
Specializing in low-level systems, modern web applications, and
everything in between.
</p>
</div>
{/* Quick Links */}
<div>
<h4 className="font-medium text-foreground mb-4">Quick Links</h4>
<ul className="space-y-2">
<li>
<Link
href="/"
className="text-sm text-muted-foreground hover:text-accent transition-colors"
>
Home
</Link>
</li>
<li>
<Link
href="/about"
className="text-sm text-muted-foreground hover:text-accent transition-colors"
>
About
</Link>
</li>
<li>
<Link
href="/pricing"
className="text-sm text-muted-foreground hover:text-accent transition-colors"
>
Hire Me
</Link>
</li>
<li>
<Link
target="_blank"
href="https://ko-fi.com/X8X01OX1WR"
className="text-sm text-muted-foreground hover:text-accent transition-colors flex"
>
Support Me on Ko-fi
<ExternalLink className="ml-1.5 w-4 h-4" />
</Link>
</li>
</ul>
</div>
{/* Connect */}
<div>
<h4 className="font-medium text-foreground mb-4">Connect</h4>
<ul className="space-y-2">
<li>
<a
href="https://github.com/selimaj-dev"
target="_blank"
rel="noopener noreferrer"
className="text-sm text-muted-foreground hover:text-accent transition-colors"
>
GitHub
</a>
</li>
<li>
<a
href="https://www.linkedin.com/in/klesti-selimaj-7a0162343/"
target="_blank"
rel="noopener noreferrer"
className="text-sm text-muted-foreground hover:text-accent transition-colors"
>
LinkedIn
</a>
</li>
<li>
<a
href="https://www.youtube.com/@0xkleo"
target="_blank"
rel="noopener noreferrer"
className="text-sm text-muted-foreground hover:text-accent transition-colors"
>
YouTube
</a>
</li>
<li>
<a
href="https://www.instagram.com/selimaj.dev/"
target="_blank"
rel="noopener noreferrer"
className="text-sm text-muted-foreground hover:text-accent transition-colors"
>
Instagram
</a>
</li>
</ul>
</div>
</div>
<div className="border-t border-border mt-8 pt-8 flex flex-col md:flex-row justify-between items-center gap-4">
<p className="text-sm text-muted-foreground">
© {currentYear} Klesti Selimaj. All rights reserved.
</p>
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<span className="w-2 h-2 rounded-full bg-accent animate-pulse" />
Available for work
</div>
</div>
</div>
</footer>
);
}
+152
View File
@@ -0,0 +1,152 @@
"use client";
import { cn } from "@/lib/utils";
import { CoffeeIcon, Github, Menu, X } from "lucide-react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useState } from "react";
import { motion } from "framer-motion";
const navLinks = [
{ href: "/", label: "Home" },
{ href: "/about", label: "About" },
{ href: "/pricing", label: "Hire Me" },
];
export function Navbar() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const pathname = usePathname();
return (
<header className="fixed top-0 left-0 right-0 z-50 glass-card border-b">
<nav className="container mx-auto px-6 h-16 flex items-center justify-between">
<Link href="/" className="flex items-center gap-2 group">
<img
className="w-8 h-8 rounded-lg bg-accent flex items-center justify-center"
src="/logo.png"
alt="Logo"
/>
<span className="font-semibold text-foreground group-hover:text-accent transition-colors">
selimaj.dev
</span>
</Link>
{/* Desktop Nav */}
<ul className="hidden md:flex items-center gap-8 absolute left-1/2 -translate-x-1/2">
{navLinks.map((link) => {
const isActive = pathname === link.href;
return (
<li key={link.href}>
<Link
href={link.href}
className={cn(
"text-sm font-medium transition-colors hover:text-accent relative py-2",
isActive ? "text-accent" : "text-muted-foreground",
)}
>
{link.label}
{isActive && (
<span className="absolute bottom-0 left-0 right-0 h-0.5 bg-accent rounded-full" />
)}
</Link>
</li>
);
})}
</ul>
<div className="hidden md:flex items-center gap-4">
<motion.li
animate={{ y: [0, -3, 0] }}
transition={{ repeat: Infinity, duration: 3 }}
>
<a href="https://ko-fi.com/X8X01OX1WR" target="_blank">
<motion.span
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="text-muted-foreground hover:text-foreground transition-colors"
>
<CoffeeIcon />
</motion.span>
</a>
</motion.li>
<a
href="https://github.com/selimaj-dev"
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-foreground transition-colors"
>
<Github />
</a>
<Link
href="/pricing"
className="bg-accent text-accent-foreground px-4 py-2 rounded-lg text-sm font-medium hover:bg-accent/90 transition-colors"
>
Get in Touch
</Link>
</div>
{/* Mobile Menu Button */}
<button
className="md:hidden text-foreground p-2"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
>
{mobileMenuOpen ? (
<X className="w-5 h-5" />
) : (
<Menu className="w-5 h-5" />
)}
</button>
</nav>
{/* Mobile Menu */}
{mobileMenuOpen && (
<div className="md:hidden glass-card border-t animate-fade-in">
<ul className="container mx-auto px-6 py-4 space-y-4">
{navLinks.map((link) => (
<li key={link.href}>
<Link
href={link.href}
onClick={() => setMobileMenuOpen(false)}
className={cn(
"block text-sm font-medium transition-colors",
location.pathname === link.href
? "text-accent"
: "text-muted-foreground",
)}
>
{link.label}
</Link>
</li>
))}
<li>
<Link
href="https://github.com/selimaj-dev"
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-foreground transition-colors"
>
<Github />
</Link>
</li>
<motion.li
animate={{ y: [0, -3, 0] }}
transition={{ repeat: Infinity, duration: 3 }}
>
<a href="https://ko-fi.com/X8X01OX1WR" target="_blank">
<motion.span
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="text-muted-foreground hover:text-foreground transition-colors"
>
<CoffeeIcon />
</motion.span>
</a>
</motion.li>
</ul>
</div>
)}
</header>
);
}
+102
View File
@@ -0,0 +1,102 @@
import { Variants } from "framer-motion";
/**
* Container
*/
export const staggerContainer: Variants = {
hidden: {},
visible: {
transition: {
staggerChildren: 0.15,
delayChildren: 0.05,
},
},
};
/**
* Basic entrances
*/
export const fadeInUp: Variants = {
hidden: { opacity: 0, y: 24 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.6,
ease: [0.22, 1, 0.36, 1], // premium easeOut
},
},
};
export const fadeInDown: Variants = {
hidden: { opacity: 0, y: -24 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.6,
ease: [0.22, 1, 0.36, 1],
},
},
};
export const fadeIn: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: { duration: 0.6, ease: "easeOut" },
},
};
/**
* Cards
*/
export const cardReveal: Variants = {
hidden: { opacity: 0, y: 30, scale: 0.96 },
visible: {
opacity: 1,
y: 0,
scale: 1,
transition: {
duration: 0.5,
ease: [0.22, 1, 0.36, 1],
},
},
};
/**
* Timeline
*/
export const timelineItemLeft: Variants = {
hidden: { opacity: 0, x: -40 },
visible: {
opacity: 1,
x: 0,
transition: { duration: 0.5, ease: "easeOut" },
},
};
export const timelineItemRight: Variants = {
hidden: { opacity: 0, x: 40 },
visible: {
opacity: 1,
x: 0,
transition: { duration: 0.5, ease: "easeOut" },
},
};
/**
* Decorative / ambient
*/
export const slowFloat: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
y: [0, -10, 0],
transition: {
duration: 6,
repeat: Infinity,
ease: "easeInOut",
},
},
};
+6
View File
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
-15
View File
@@ -1,15 +0,0 @@
import dotenv from 'dotenv';
import { Client } from '@notionhq/client';
import { NotionToMarkdown } from 'notion-to-md';
dotenv.config();
export const notion = new Client( { auth: process.env.NOTION_API_KEY } )
export const n2m = new NotionToMarkdown({
notionClient: notion,
});
if (process.env.NOTION_DATABASE_ID === undefined) {
console.error('[server] Error: environment variable \'NOTION_DATABASE_ID\' not found')
process.exit();
}
export const databaseId: string = process.env.NOTION_DATABASE_ID;
+98 -17
View File
@@ -1,31 +1,112 @@
import type { Config } from "tailwindcss";
export default {
darkMode: ["class"],
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}",
"./src/**/*.{ts,tsx}",
],
prefix: "",
theme: {
extend: {
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
red: '#f5304c',
'red-400': '#fa5770',
'red-600': '#eb2642',
'transparent-ish': '#0000000A',
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
animation: {
fadeIn: "fadeIn 1s ease-in-out",
},
extend: {
fontFamily: {
sans: ["Inter", "system-ui", "sans-serif"],
mono: ["JetBrains Mono", "monospace"],
},
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
gold: {
DEFAULT: "hsl(var(--gold))",
light: "hsl(var(--gold-light))",
dark: "hsl(var(--gold-dark))",
},
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
sidebar: {
DEFAULT: "hsl(var(--sidebar-background))",
foreground: "hsl(var(--sidebar-foreground))",
primary: "hsl(var(--sidebar-primary))",
"primary-foreground": "hsl(var(--sidebar-primary-foreground))",
accent: "hsl(var(--sidebar-accent))",
"accent-foreground": "hsl(var(--sidebar-accent-foreground))",
border: "hsl(var(--sidebar-border))",
ring: "hsl(var(--sidebar-ring))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
fadeIn: {
"0%": { opacity: "0", transform: "translateY(10px)" },
"100%": { opacity: "1", transform: "translateY(0px)" },
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
"fade-in": {
"0%": { opacity: "0", transform: "translateY(10px)" },
"100%": { opacity: "1", transform: "translateY(0)" },
},
"fade-in-up": {
"0%": { opacity: "0", transform: "translateY(20px)" },
"100%": { opacity: "1", transform: "translateY(0)" },
},
pulse: {
"0%, 100%": { opacity: "1" },
"50%": { opacity: "0.5" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
"fade-in": "fade-in 0.6s ease-out forwards",
"fade-in-up": "fade-in-up 0.6s ease-out forwards",
pulse: "pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
},
},
},
plugins: [],
plugins: [require("tailwindcss-animate")],
} satisfies Config;