diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1d2b1be
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,7 @@
+Copyright 2025 ByteBrush Studios
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
index e215bc4..22e3c8a 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,94 @@
-This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+# CodeMeAPixel Portfolio
-## Getting Started
+[](https://nextjs.org/)
+[](https://react.dev/)
+[](https://www.typescriptlang.org/)
+[](https://tailwindcss.com/)
+[](https://www.framer.com/motion/)
+[](LICENSE)
+[](https://github.com/codemeapixel/portfolio/codespaces)
-First, run the development server:
+> **A modern, full-stack developer portfolio built with Next.js 15, React 19, TypeScript, and Tailwind CSS. Optimized for performance, accessibility, and exceptional developer experience.**
+
+---
+
+## 🚀 Features
+
+- **Next.js 15+**: App Router, SSR, SSG, ISR, API routes, and Turbopack acceleration.
+- **React 19**: Utilizing the latest React version with improved performance.
+- **TypeScript**: End-to-end type safety with full TypeScript integration.
+- **Tailwind CSS**: Utility-first, responsive design with multiple theme support.
+- **MDX Blog**: Write posts with Markdown + JSX featuring syntax highlighting (via Shiki/Rehype Pretty Code), line numbers, and custom components.
+- **Dynamic Projects & Referrals**: Modular TypeScript data files for easy content management.
+- **Animated UI**: Powered by Framer Motion v11, custom CSS animations, and engaging pixel/glitch effects.
+- **Theme System**: Multiple color themes (blue, purple, teal, rose, amber) with instant switching and local storage persistence.
+- **Responsive Design**: Mobile-first approach with optimized layouts for all devices.
+- **Accessibility**: WCAG-compliant with keyboard navigation, semantic HTML, and proper contrast ratios.
+- **Playlist & Music Player**: Custom React audio implementation with playlists and pagination.
+- **Social Integration**: Configurable link hub with tooltips powered by Radix UI.
+- **Advanced SEO**: Comprehensive metadata, Open Graph tags, and Twitter cards for maximum discoverability.
+- **Vercel Optimized**: Ready for edge deployment with Vercel's platform features.
+- **Developer Experience**: ESLint configuration, TypeScript strict mode, and organized project structure.
+
+---
+
+## 🛠️ Tech Stack
+
+| Tool | Version | Description |
+|-----------------------|------------|--------------------------------------------------------------|
+| [Next.js] | 15.3.0 | React framework with App Router, SSR, and edge capabilities |
+| [React] | 19.0.0 | UI library with the latest improvements and concurrent features |
+| [TypeScript] | 5.x | Static typing for safer, more maintainable code |
+| [Tailwind CSS] | 3.4.17 | Utility-first CSS framework with theming support |
+| [Framer Motion] | 11.15.0 | Production-ready motion library for React |
+| [MDX] | 5.0.0 | Markdown + JSX for rich content authoring |
+| [Radix UI] | 1.x-2.x | Unstyled, accessible UI primitives for custom components |
+| [React Icons] | 5.5.0 | Comprehensive icon library with consistent design |
+| [Rehype Pretty Code] | 0.14.0 | Syntax highlighting with themes and line highlighting |
+| [Shiki] | 3.2.1 | TextMate grammar-based syntax highlighter |
+| [Tailwind Merge] | 2.6.0 | Utility for merging Tailwind CSS classes |
+| [date-fns] | 4.1.0 | Modern JavaScript date utility library |
+| [clsx] | 2.1.1 | Tiny utility for constructing className strings conditionally |
+| [Turbopack] | Built-in | Incremental bundler for faster builds and refreshes |
+
+---
+
+## 📦 Project Structure
+
+
+Click to expand
+
+```
+src/
+ app/ # Next.js app directory (routes, pages, layouts)
+ components/ # Reusable UI components (layouts, static, ui, etc.)
+ lib/ # Data, API clients, and utility functions
+ links/ # Link hub, playlists, categories, etc.
+ projects/ # Project data and logic
+ posts/ # Blog posts in MDX format
+ styles/ # Tailwind and global CSS
+ types/ # TypeScript types and interfaces
+public/ # Static assets (images, covers, previews)
+```
+
+
+---
+
+## 📝 Usage
+
+### 1. **Install dependencies**
+
+```bash
+npm install
+# or
+yarn install
+# or
+pnpm install
+# or
+bun install
+```
+
+### 2. **Run the development server**
```bash
npm run dev
@@ -14,23 +100,101 @@ pnpm dev
bun dev
```
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+Open [http://localhost:3000](http://localhost:3000) to view the site.
+
+### 3. **Build for production**
+
+```bash
+npm run build
+npm start
+```
+
+---
+
+## ✍️ Adding Content
+
+### Blog Posts
+
+- Add `.mdx` files to `src/posts/`.
+- Use frontmatter for metadata:
+ ```md
+ ---
+ title: 'My Post'
+ date: '2025-03-01'
+ description: 'A short summary.'
+ tags: ['Next.js', 'React']
+ ---
+ ```
+
+### Projects
+
+- Add or edit files in `src/lib/projects/data/`.
+- Use the `Project` type for structure and documentation.
+
+### Referrals
+
+- Add or edit files in `src/lib/referrals/data/`.
+- Use the `Referral` and `ReferralCategory` types.
+
+---
+
+## 🎨 Theming
+
+- Supports multiple color themes (blue, purple, teal, rose, amber, etc.).
+- Theme is persisted and can be changed via the UI.
+
+---
+
+## 🎵 Playlist & Music Player
+
+- Curated playlists in `src/lib/links/data/playlist/`.
+- Custom React music player with pagination and genre filtering.
+
+---
+
+## 🧩 Customization
+
+- **Navigation:** Edit `src/components/static/Navbar.tsx` for links and icons.
+- **Social Links:** Configure in `src/lib/links/data/`.
+- **Animations:** Customize in `src/styles/globals.css` and Framer Motion props.
+
+---
+
+## 🛡️ Accessibility & SEO
+
+- Keyboard navigable, focus-visible, and color contrast checked.
+- SEO meta tags and Open Graph support for rich sharing.
+
+---
+
+## 📄 License
+
+This project is [MIT licensed](LICENSE).
-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+---
-This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+## 🙋♂️ Author
-## Learn More
+**CodeMeAPixel**
+[Portfolio](https://codemeapixel.dev) • [GitHub](https://github.com/CodeMeAPixel) • [Twitter](https://twitter.com/CodeMeAPixel)
-To learn more about Next.js, take a look at the following resources:
+---
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+## ⭐️ Show your support
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+If you like this project, please consider starring the repo and sharing it!
-## Deploy on Vercel
+[](https://github.com/CodeMeAPixel/portfolio)
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+---
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
+[Next.js]: https://nextjs.org/
+[React]: https://react.dev/
+[TypeScript]: https://www.typescriptlang.org/
+[Tailwind CSS]: https://tailwindcss.com/
+[Framer Motion]: https://www.framer.com/motion/
+[MDX]: https://mdxjs.com/
+[Radix UI]: https://www.radix-ui.com/
+[PrismJS]: https://prismjs.com/
+[React Icons]: https://react-icons.github.io/react-icons/
+[Vercel]: https://vercel.com/
diff --git a/bun.lock b/bun.lock
index 6a9fdfe..fc0636f 100644
--- a/bun.lock
+++ b/bun.lock
@@ -6,18 +6,36 @@
"dependencies": {
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
+ "@radix-ui/react-tabs": "^1.1.2",
"@radix-ui/react-tooltip": "^1.0.7",
"axios": "^1.7.9",
"clsx": "^2.1.1",
+ "crypto": "^1.0.1",
+ "date-fns": "^4.1.0",
"devicons": "^1.8.0",
+ "devicons-react": "^1.5.0-beta.11",
+ "dompurify": "^3.0.5",
"framer-motion": "^11.15.0",
+ "gray-matter": "^4.0.3",
+ "highlight.js": "^11.8.0",
+ "marked": "^4.3.0",
"next": "15.3.0",
+ "next-mdx": "^0.6.2",
+ "next-mdx-remote": "^5.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
- "react-icons": "^5.4.0",
+ "react-icons": "^5.5.0",
+ "react-syntax-highlighter": "^15.6.1",
+ "rehype-pretty-code": "^0.14.0",
+ "rehype-prism-plus": "^2.0.1",
+ "rehype-slug": "^6.0.0",
+ "remark-gfm": "^4.0.0",
+ "shiki": "^3.2.1",
},
"devDependencies": {
"@eslint/eslintrc": "^3",
+ "@types/dompurify": "^3.0.2",
+ "@types/marked": "^5.0.1",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
@@ -35,6 +53,206 @@
"packages": {
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
+ "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
+
+ "@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
+
+ "@babel/compat-data": ["@babel/compat-data@7.26.8", "", {}, "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ=="],
+
+ "@babel/core": ["@babel/core@7.26.10", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.10", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", "@babel/helpers": "^7.26.10", "@babel/parser": "^7.26.10", "@babel/template": "^7.26.9", "@babel/traverse": "^7.26.10", "@babel/types": "^7.26.10", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ=="],
+
+ "@babel/generator": ["@babel/generator@7.27.0", "", { "dependencies": { "@babel/parser": "^7.27.0", "@babel/types": "^7.27.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw=="],
+
+ "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g=="],
+
+ "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.0", "", { "dependencies": { "@babel/compat-data": "^7.26.8", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA=="],
+
+ "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.27.0", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/helper-replace-supers": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/traverse": "^7.27.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg=="],
+
+ "@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.27.0", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-fO8l08T76v48BhpNRW/nQ0MxfnSdoSKUJBMjubOAYffsVuGG5qOfMq7N6Es7UJvi7Y8goXXo07EfcHZXDPuELQ=="],
+
+ "@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.4", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw=="],
+
+ "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ=="],
+
+ "@babel/helper-module-imports": ["@babel/helper-module-imports@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw=="],
+
+ "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.26.0", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw=="],
+
+ "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ=="],
+
+ "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.26.5", "", {}, "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg=="],
+
+ "@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-wrap-function": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw=="],
+
+ "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.26.5", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/traverse": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg=="],
+
+ "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA=="],
+
+ "@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="],
+
+ "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="],
+
+ "@babel/helper-validator-option": ["@babel/helper-validator-option@7.25.9", "", {}, "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw=="],
+
+ "@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.25.9", "", { "dependencies": { "@babel/template": "^7.25.9", "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g=="],
+
+ "@babel/helpers": ["@babel/helpers@7.27.0", "", { "dependencies": { "@babel/template": "^7.27.0", "@babel/types": "^7.27.0" } }, "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg=="],
+
+ "@babel/parser": ["@babel/parser@7.27.0", "", { "dependencies": { "@babel/types": "^7.27.0" }, "bin": "./bin/babel-parser.js" }, "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg=="],
+
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g=="],
+
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw=="],
+
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug=="],
+
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/plugin-transform-optional-chaining": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g=="],
+
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg=="],
+
+ "@babel/plugin-proposal-object-rest-spread": ["@babel/plugin-proposal-object-rest-spread@7.12.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.0", "@babel/plugin-transform-parameters": "^7.12.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA=="],
+
+ "@babel/plugin-proposal-private-property-in-object": ["@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w=="],
+
+ "@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.26.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg=="],
+
+ "@babel/plugin-syntax-import-attributes": ["@babel/plugin-syntax-import-attributes@7.26.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A=="],
+
+ "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA=="],
+
+ "@babel/plugin-syntax-object-rest-spread": ["@babel/plugin-syntax-object-rest-spread@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA=="],
+
+ "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ=="],
+
+ "@babel/plugin-syntax-unicode-sets-regex": ["@babel/plugin-syntax-unicode-sets-regex@7.18.6", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg=="],
+
+ "@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg=="],
+
+ "@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.26.8", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-remap-async-to-generator": "^7.25.9", "@babel/traverse": "^7.26.8" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg=="],
+
+ "@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.25.9", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-remap-async-to-generator": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ=="],
+
+ "@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.26.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ=="],
+
+ "@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.27.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-u1jGphZ8uDI2Pj/HJj6YQ6XQLZCNjOlprjxB5SVz6rq2T6SwAR+CdrWK0CP7F+9rDVMXdB0+r6Am5G5aobOjAQ=="],
+
+ "@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.25.9", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q=="],
+
+ "@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.26.0", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ=="],
+
+ "@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9", "@babel/traverse": "^7.25.9", "globals": "^11.1.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg=="],
+
+ "@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/template": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA=="],
+
+ "@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ=="],
+
+ "@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA=="],
+
+ "@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw=="],
+
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog=="],
+
+ "@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg=="],
+
+ "@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.26.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ=="],
+
+ "@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww=="],
+
+ "@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.26.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg=="],
+
+ "@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.25.9", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA=="],
+
+ "@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw=="],
+
+ "@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ=="],
+
+ "@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q=="],
+
+ "@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA=="],
+
+ "@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw=="],
+
+ "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.26.3", "", { "dependencies": { "@babel/helper-module-transforms": "^7.26.0", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ=="],
+
+ "@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA=="],
+
+ "@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw=="],
+
+ "@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA=="],
+
+ "@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ=="],
+
+ "@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.26.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw=="],
+
+ "@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q=="],
+
+ "@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.25.9", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/plugin-transform-parameters": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg=="],
+
+ "@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A=="],
+
+ "@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g=="],
+
+ "@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A=="],
+
+ "@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g=="],
+
+ "@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.25.9", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw=="],
+
+ "@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw=="],
+
+ "@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA=="],
+
+ "@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ=="],
+
+ "@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/types": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw=="],
+
+ "@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.25.9", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw=="],
+
+ "@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg=="],
+
+ "@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.27.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5", "regenerator-transform": "^0.15.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-LX/vCajUJQDqE7Aum/ELUMZAY19+cDpghxrnyt5I1tV6X5PyC86AOoWXWFYFeIvauyeSA6/ktn4tQVn/3ZifsA=="],
+
+ "@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.26.0", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw=="],
+
+ "@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg=="],
+
+ "@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng=="],
+
+ "@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A=="],
+
+ "@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA=="],
+
+ "@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.26.8", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q=="],
+
+ "@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.27.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+LLkxA9rKJpNoGsbLnAgOCdESl73vwYn+V6b+5wHbrE7OGKVDPHIQvbFSzqE6rwqaCw2RE+zdJrlLkcf8YOA0w=="],
+
+ "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.27.0", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.27.0", "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fRGGjO2UEGPjvEcyAZXRXAS8AfdaQoq7HnxAbJoAoW10B9xOKesmmndJv+Sym2a+9FHWZ9KbyyLCe9s0Sn5jtg=="],
+
+ "@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q=="],
+
+ "@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg=="],
+
+ "@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA=="],
+
+ "@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ=="],
+
+ "@babel/preset-env": ["@babel/preset-env@7.26.9", "", { "dependencies": { "@babel/compat-data": "^7.26.8", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.26.0", "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.25.9", "@babel/plugin-transform-async-generator-functions": "^7.26.8", "@babel/plugin-transform-async-to-generator": "^7.25.9", "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", "@babel/plugin-transform-classes": "^7.25.9", "@babel/plugin-transform-computed-properties": "^7.25.9", "@babel/plugin-transform-destructuring": "^7.25.9", "@babel/plugin-transform-dotall-regex": "^7.25.9", "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-for-of": "^7.26.9", "@babel/plugin-transform-function-name": "^7.25.9", "@babel/plugin-transform-json-strings": "^7.25.9", "@babel/plugin-transform-literals": "^7.25.9", "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", "@babel/plugin-transform-optional-catch-binding": "^7.25.9", "@babel/plugin-transform-optional-chaining": "^7.25.9", "@babel/plugin-transform-parameters": "^7.25.9", "@babel/plugin-transform-private-methods": "^7.25.9", "@babel/plugin-transform-private-property-in-object": "^7.25.9", "@babel/plugin-transform-property-literals": "^7.25.9", "@babel/plugin-transform-regenerator": "^7.25.9", "@babel/plugin-transform-regexp-modifiers": "^7.26.0", "@babel/plugin-transform-reserved-words": "^7.25.9", "@babel/plugin-transform-shorthand-properties": "^7.25.9", "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", "@babel/plugin-transform-template-literals": "^7.26.8", "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", "core-js-compat": "^3.40.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ=="],
+
+ "@babel/preset-modules": ["@babel/preset-modules@0.1.6-no-external-plugins", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA=="],
+
+ "@babel/preset-react": ["@babel/preset-react@7.26.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-transform-react-display-name": "^7.25.9", "@babel/plugin-transform-react-jsx": "^7.25.9", "@babel/plugin-transform-react-jsx-development": "^7.25.9", "@babel/plugin-transform-react-pure-annotations": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw=="],
+
+ "@babel/preset-typescript": ["@babel/preset-typescript@7.27.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-typescript": "^7.27.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vxaPFfJtHhgeOVXRKuHpHPAOgymmy8V8I65T1q53R7GCZlefKeCaTyDs3zOPHTTbmquvNlQYC5klEvWsBAtrBQ=="],
+
+ "@babel/runtime": ["@babel/runtime@7.27.0", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw=="],
+
+ "@babel/template": ["@babel/template@7.27.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/parser": "^7.27.0", "@babel/types": "^7.27.0" } }, "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA=="],
+
+ "@babel/traverse": ["@babel/traverse@7.27.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.27.0", "@babel/parser": "^7.27.0", "@babel/template": "^7.27.0", "@babel/types": "^7.27.0", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA=="],
+
+ "@babel/types": ["@babel/types@7.27.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg=="],
+
"@emnapi/core": ["@emnapi/core@1.4.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.1", "tslib": "^2.4.0" } }, "sha512-4JFstCTaToCFrPqrGzgkF8N2NHjtsaY4uRh6brZQ5L9e4wbMieX8oDT8N7qfVFTQecHFEtkj4ve49VIZ3mKVqw=="],
"@emnapi/runtime": ["@emnapi/runtime@1.4.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-LMshMVP0ZhACNjQNYXiU1iZJ6QCcv0lUdPDPugqGvCGXt5xtRVBPdtA0qU12pEXZzpWAhWlZYptfdAFq10DOVQ=="],
@@ -127,6 +345,12 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
+ "@mdx-js/mdx": ["@mdx-js/mdx@3.1.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw=="],
+
+ "@mdx-js/react": ["@mdx-js/react@1.6.22", "", { "peerDependencies": { "react": "^16.13.1 || ^17.0.0" } }, "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg=="],
+
+ "@mdx-js/util": ["@mdx-js/util@1.6.22", "", {}, "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA=="],
+
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.8", "", { "dependencies": { "@emnapi/core": "^1.4.0", "@emnapi/runtime": "^1.4.0", "@tybys/wasm-util": "^0.9.0" } }, "sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg=="],
"@next/env": ["@next/env@15.3.0", "", {}, "sha512-6mDmHX24nWlHOlbwUiAOmMyY7KELimmi+ed8qWcJYjqXeC+G6JzPZ3QosOAfjNwgMIzwhXBiRiCgdh8axTTdTA=="],
@@ -197,6 +421,8 @@
"@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
+ "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.4", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.3", "@radix-ui/react-primitive": "2.0.3", "@radix-ui/react-roving-focus": "1.1.3", "@radix-ui/react-use-controllable-state": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-fuHMHWSf5SRhXke+DbHXj2wVMo+ghVH30vhX3XVacdXqDl+J4XWafMIGOOER861QpBx1jxgwKXL2dQnfrsd8MQ=="],
+
"@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.0", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.6", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.3", "@radix-ui/react-portal": "1.1.5", "@radix-ui/react-presence": "1.1.3", "@radix-ui/react-primitive": "2.0.3", "@radix-ui/react-slot": "1.2.0", "@radix-ui/react-use-controllable-state": "1.1.1", "@radix-ui/react-visually-hidden": "1.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-b1Sdc75s7zN9B8ONQTGBSHL3XS8+IcjcOIY51fhM4R1Hx8s0YbgqgyNZiri4qcYMVZK8hfCZVBiyCm7N9rs0rw=="],
"@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
@@ -219,24 +445,62 @@
"@rushstack/eslint-patch": ["@rushstack/eslint-patch@1.11.0", "", {}, "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ=="],
+ "@shikijs/core": ["@shikijs/core@3.2.2", "", { "dependencies": { "@shikijs/types": "3.2.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-yvlSKVMLjddAGBa2Yu+vUZxuu3sClOWW1AG+UtJkvejYuGM5BVL35s6Ijiwb75O9QdEx6IkMxinHZSi8ZyrBaA=="],
+
+ "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.2.2", "", { "dependencies": { "@shikijs/types": "3.2.2", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.1.0" } }, "sha512-tlDKfhWpF4jKLUyVAnmL+ggIC+0VyteNsUpBzh1iwWLZu4i+PelIRr0TNur6pRRo5UZIv3ss/PLMuwahg9S2hg=="],
+
+ "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.2.2", "", { "dependencies": { "@shikijs/types": "3.2.2", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-vyXRnWVCSvokwbaUD/8uPn6Gqsf5Hv7XwcW4AgiU4Z2qwy19sdr6VGzMdheKKN58tJOOe5MIKiNb901bgcUXYQ=="],
+
+ "@shikijs/langs": ["@shikijs/langs@3.2.2", "", { "dependencies": { "@shikijs/types": "3.2.2" } }, "sha512-NY0Urg2dV9ETt3JIOWoMPuoDNwte3geLZ4M1nrPHbkDS8dWMpKcEwlqiEIGqtwZNmt5gKyWpR26ln2Bg2ecPgw=="],
+
+ "@shikijs/themes": ["@shikijs/themes@3.2.2", "", { "dependencies": { "@shikijs/types": "3.2.2" } }, "sha512-Zuq4lgAxVKkb0FFdhHSdDkALuRpsj1so1JdihjKNQfgM78EHxV2JhO10qPsMrm01FkE3mDRTdF68wfmsqjt6HA=="],
+
+ "@shikijs/types": ["@shikijs/types@3.2.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-a5TiHk7EH5Lso8sHcLHbVNNhWKP0Wi3yVnXnu73g86n3WoDgEra7n3KszyeCGuyoagspQ2fzvy4cpSc8pKhb0A=="],
+
+ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
+
"@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="],
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
+ "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
+
+ "@types/dompurify": ["@types/dompurify@3.2.0", "", { "dependencies": { "dompurify": "*" } }, "sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg=="],
+
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
+ "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="],
+
+ "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
+
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
+ "@types/marked": ["@types/marked@5.0.2", "", {}, "sha512-OucS4KMHhFzhz27KxmWg7J+kIYqyqoW5kdIEI319hqARQQUTqhao3M/F+uFnDXD0Rg72iDDZxZNxq5gvctmLlg=="],
+
+ "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
+
+ "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="],
+
+ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
+
"@types/node": ["@types/node@20.17.30", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg=="],
+ "@types/parse5": ["@types/parse5@5.0.3", "", {}, "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw=="],
+
+ "@types/prismjs": ["@types/prismjs@1.26.5", "", {}, "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ=="],
+
"@types/react": ["@types/react@19.1.1", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-ePapxDL7qrgqSF67s0h9m412d9DbXyC1n59O2st+9rjuuamWsZuD2w55rqY12CbzsZ7uVXb5Nw0gEp9Z8MMutQ=="],
"@types/react-dom": ["@types/react-dom@19.1.2", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw=="],
+ "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
+
+ "@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
+
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.29.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.29.1", "@typescript-eslint/type-utils": "8.29.1", "@typescript-eslint/utils": "8.29.1", "@typescript-eslint/visitor-keys": "8.29.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.29.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.29.1", "@typescript-eslint/types": "8.29.1", "@typescript-eslint/typescript-estree": "8.29.1", "@typescript-eslint/visitor-keys": "8.29.1", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg=="],
@@ -253,6 +517,8 @@
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.29.1", "", { "dependencies": { "@typescript-eslint/types": "8.29.1", "eslint-visitor-keys": "^4.2.0" } }, "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg=="],
+ "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
+
"@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.5.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YmocNlEcX/AgJv8gI41bhjMOTcKcea4D2nRIbZj+MhRtSH5+vEU8r/pFuTuoF+JjVplLsBueU+CILfBPVISyGQ=="],
"@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.5.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-qpUrXgH4e/0xu1LOhPEdfgSY3vIXOxDQv370NEL8npN8h40HcQDA+Pl2r4HBW6tTXezWIjxUFcP7tj529RZtDw=="],
@@ -325,6 +591,8 @@
"ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="],
+ "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="],
+
"async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
@@ -339,6 +607,18 @@
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
+ "babel-plugin-apply-mdx-type-prop": ["babel-plugin-apply-mdx-type-prop@1.6.22", "", { "dependencies": { "@babel/helper-plugin-utils": "7.10.4", "@mdx-js/util": "1.6.22" }, "peerDependencies": { "@babel/core": "^7.11.6" } }, "sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ=="],
+
+ "babel-plugin-extract-import-names": ["babel-plugin-extract-import-names@1.6.22", "", { "dependencies": { "@babel/helper-plugin-utils": "7.10.4" } }, "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ=="],
+
+ "babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.13", "", { "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.6.4", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g=="],
+
+ "babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.11.1", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.3", "core-js-compat": "^3.40.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ=="],
+
+ "babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.4", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.4" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw=="],
+
+ "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
+
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
@@ -363,14 +643,28 @@
"caniuse-lite": ["caniuse-lite@1.0.30001713", "", {}, "sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q=="],
+ "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
+
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
+ "character-entities": ["character-entities@1.2.4", "", {}, "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw=="],
+
+ "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
+
+ "character-entities-legacy": ["character-entities-legacy@1.1.4", "", {}, "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA=="],
+
+ "character-reference-invalid": ["character-reference-invalid@1.1.4", "", {}, "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg=="],
+
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
"client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="],
+ "clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="],
+
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
+ "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="],
+
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
@@ -381,12 +675,20 @@
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
+ "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
+
"commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
+ "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
+
+ "core-js-compat": ["core-js-compat@3.41.0", "", { "dependencies": { "browserslist": "^4.24.4" } }, "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A=="],
+
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
+ "crypto": ["crypto@1.0.1", "", {}, "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig=="],
+
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
@@ -399,8 +701,12 @@
"data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="],
+ "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="],
+
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+ "decode-named-character-reference": ["decode-named-character-reference@1.1.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w=="],
+
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
@@ -409,18 +715,28 @@
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
+ "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
+
+ "detab": ["detab@2.0.4", "", { "dependencies": { "repeat-string": "^1.5.4" } }, "sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g=="],
+
"detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="],
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
"devicons": ["devicons@1.8.0", "", {}, "sha512-tRS3bNxy9mbDVK0g4/+t5PlwMjjOteuh5LAhjMgimAPUgiKB+wCUCf7+uugufZ1vC9AKMRn5Pljbi210qCbmkQ=="],
+ "devicons-react": ["devicons-react@1.5.0-beta.11", "", { "peerDependencies": { "react": "*" } }, "sha512-JUl2faQQot5Fz3f1G6iw+v0DZThDfiuzir0KHtCA3uNdEyVtlXDci1UR6175BJfyREOs46OSeSQydJn2pQCCIA=="],
+
+ "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
+
"didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="],
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
+ "dompurify": ["dompurify@3.2.5", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ=="],
+
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
@@ -429,6 +745,10 @@
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
+ "emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="],
+
+ "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
+
"es-abstract": ["es-abstract@1.23.9", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.3", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.0", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-regex": "^1.2.1", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.0", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.3", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.18" } }, "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
@@ -445,6 +765,10 @@
"es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="],
+ "esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "unist-util-position-from-estree": "^2.0.0" } }, "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ=="],
+
+ "esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="],
+
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
@@ -473,14 +797,34 @@
"espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="],
+ "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
+
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
+ "estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="],
+
+ "estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" } }, "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ=="],
+
+ "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="],
+
+ "estree-util-scope": ["estree-util-scope@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0" } }, "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ=="],
+
+ "estree-util-to-js": ["estree-util-to-js@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "astring": "^1.8.0", "source-map": "^0.7.0" } }, "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg=="],
+
+ "estree-util-visit": ["estree-util-visit@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/unist": "^3.0.0" } }, "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww=="],
+
+ "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
+
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
+ "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
+
+ "extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="],
+
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
@@ -491,6 +835,8 @@
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
+ "fault": ["fault@1.0.4", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA=="],
+
"fdir": ["fdir@6.4.3", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="],
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
@@ -511,6 +857,8 @@
"form-data": ["form-data@4.0.2", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" } }, "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w=="],
+ "format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="],
+
"fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
"framer-motion": ["framer-motion@11.18.2", "", { "dependencies": { "motion-dom": "^11.18.1", "motion-utils": "^11.18.1", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w=="],
@@ -523,6 +871,8 @@
"functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
+ "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
+
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
@@ -533,6 +883,8 @@
"get-tsconfig": ["get-tsconfig@4.10.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A=="],
+ "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
+
"glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
@@ -545,6 +897,8 @@
"graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
+ "gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="],
+
"has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
@@ -557,16 +911,58 @@
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
+ "hasha": ["hasha@5.2.2", "", { "dependencies": { "is-stream": "^2.0.0", "type-fest": "^0.8.0" } }, "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ=="],
+
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
+ "hast-to-hyperscript": ["hast-to-hyperscript@9.0.1", "", { "dependencies": { "@types/unist": "^2.0.3", "comma-separated-tokens": "^1.0.0", "property-information": "^5.3.0", "space-separated-tokens": "^1.0.0", "style-to-object": "^0.3.0", "unist-util-is": "^4.0.0", "web-namespaces": "^1.0.0" } }, "sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA=="],
+
+ "hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="],
+
+ "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="],
+
+ "hast-util-heading-rank": ["hast-util-heading-rank@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA=="],
+
+ "hast-util-parse-selector": ["hast-util-parse-selector@2.2.5", "", {}, "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ=="],
+
+ "hast-util-raw": ["hast-util-raw@6.0.1", "", { "dependencies": { "@types/hast": "^2.0.0", "hast-util-from-parse5": "^6.0.0", "hast-util-to-parse5": "^6.0.0", "html-void-elements": "^1.0.0", "parse5": "^6.0.0", "unist-util-position": "^3.0.0", "vfile": "^4.0.0", "web-namespaces": "^1.0.0", "xtend": "^4.0.0", "zwitch": "^1.0.0" } }, "sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig=="],
+
+ "hast-util-to-estree": ["hast-util-to-estree@3.1.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-attach-comments": "^3.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w=="],
+
+ "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
+
+ "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="],
+
+ "hast-util-to-parse5": ["hast-util-to-parse5@6.0.0", "", { "dependencies": { "hast-to-hyperscript": "^9.0.0", "property-information": "^5.0.0", "web-namespaces": "^1.0.0", "xtend": "^4.0.0", "zwitch": "^1.0.0" } }, "sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ=="],
+
+ "hast-util-to-string": ["hast-util-to-string@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A=="],
+
+ "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
+
+ "hastscript": ["hastscript@6.0.0", "", { "dependencies": { "@types/hast": "^2.0.0", "comma-separated-tokens": "^1.0.0", "hast-util-parse-selector": "^2.0.0", "property-information": "^5.0.0", "space-separated-tokens": "^1.0.0" } }, "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w=="],
+
+ "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
+
+ "highlightjs-vue": ["highlightjs-vue@1.0.0", "", {}, "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA=="],
+
+ "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
+
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
+ "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
+
+ "inline-style-parser": ["inline-style-parser@0.1.1", "", {}, "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q=="],
+
"internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
+ "is-alphabetical": ["is-alphabetical@1.0.4", "", {}, "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg=="],
+
+ "is-alphanumerical": ["is-alphanumerical@1.0.4", "", { "dependencies": { "is-alphabetical": "^1.0.0", "is-decimal": "^1.0.0" } }, "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A=="],
+
"is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
"is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
@@ -579,6 +975,8 @@
"is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="],
+ "is-buffer": ["is-buffer@2.0.5", "", {}, "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ=="],
+
"is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "^7.7.1" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="],
"is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="],
@@ -589,6 +987,10 @@
"is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="],
+ "is-decimal": ["is-decimal@1.0.4", "", {}, "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw=="],
+
+ "is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="],
+
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="],
@@ -599,18 +1001,24 @@
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
+ "is-hexadecimal": ["is-hexadecimal@1.0.4", "", {}, "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw=="],
+
"is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
+ "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
+
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
"is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="],
"is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="],
+ "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
+
"is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="],
"is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="],
@@ -623,6 +1031,10 @@
"is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="],
+ "is-whitespace-character": ["is-whitespace-character@1.0.4", "", {}, "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w=="],
+
+ "is-word-character": ["is-word-character@1.0.4", "", {}, "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA=="],
+
"isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
@@ -637,6 +1049,8 @@
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
+ "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
+
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
@@ -649,6 +1063,8 @@
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
+ "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
+
"language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="],
"language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="],
@@ -661,16 +1077,142 @@
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
+ "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
+
+ "lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="],
+
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
+ "lodash.uniq": ["lodash.uniq@4.5.0", "", {}, "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ=="],
+
+ "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
+
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
+ "lowlight": ["lowlight@1.20.0", "", { "dependencies": { "fault": "^1.0.0", "highlight.js": "~10.7.0" } }, "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw=="],
+
"lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
+ "markdown-escapes": ["markdown-escapes@1.0.4", "", {}, "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg=="],
+
+ "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="],
+
+ "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
+
+ "marked": ["marked@4.3.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A=="],
+
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
+ "mdast-squeeze-paragraphs": ["mdast-squeeze-paragraphs@4.0.0", "", { "dependencies": { "unist-util-remove": "^2.0.0" } }, "sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ=="],
+
+ "mdast-util-definitions": ["mdast-util-definitions@4.0.0", "", { "dependencies": { "unist-util-visit": "^2.0.0" } }, "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ=="],
+
+ "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="],
+
+ "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="],
+
+ "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="],
+
+ "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="],
+
+ "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="],
+
+ "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="],
+
+ "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="],
+
+ "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="],
+
+ "mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="],
+
+ "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="],
+
+ "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="],
+
+ "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="],
+
+ "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="],
+
+ "mdast-util-to-hast": ["mdast-util-to-hast@10.0.1", "", { "dependencies": { "@types/mdast": "^3.0.0", "@types/unist": "^2.0.0", "mdast-util-definitions": "^4.0.0", "mdurl": "^1.0.0", "unist-builder": "^2.0.0", "unist-util-generated": "^1.0.0", "unist-util-position": "^3.0.0", "unist-util-visit": "^2.0.0" } }, "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA=="],
+
+ "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="],
+
+ "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
+
+ "mdurl": ["mdurl@1.0.1", "", {}, "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g=="],
+
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
+ "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
+
+ "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
+
+ "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="],
+
+ "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="],
+
+ "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="],
+
+ "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="],
+
+ "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="],
+
+ "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="],
+
+ "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="],
+
+ "micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="],
+
+ "micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="],
+
+ "micromark-extension-mdx-md": ["micromark-extension-mdx-md@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ=="],
+
+ "micromark-extension-mdxjs": ["micromark-extension-mdxjs@3.0.0", "", { "dependencies": { "acorn": "^8.0.0", "acorn-jsx": "^5.0.0", "micromark-extension-mdx-expression": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.0", "micromark-extension-mdx-md": "^2.0.0", "micromark-extension-mdxjs-esm": "^3.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ=="],
+
+ "micromark-extension-mdxjs-esm": ["micromark-extension-mdxjs-esm@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A=="],
+
+ "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="],
+
+ "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="],
+
+ "micromark-factory-mdx-expression": ["micromark-factory-mdx-expression@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ=="],
+
+ "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="],
+
+ "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="],
+
+ "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="],
+
+ "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
+
+ "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="],
+
+ "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="],
+
+ "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="],
+
+ "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="],
+
+ "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="],
+
+ "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
+
+ "micromark-util-events-to-acorn": ["micromark-util-events-to-acorn@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg=="],
+
+ "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="],
+
+ "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="],
+
+ "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="],
+
+ "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
+
+ "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="],
+
+ "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
+
+ "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
+
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
@@ -697,6 +1239,12 @@
"next": ["next@15.3.0", "", { "dependencies": { "@next/env": "15.3.0", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.0", "@next/swc-darwin-x64": "15.3.0", "@next/swc-linux-arm64-gnu": "15.3.0", "@next/swc-linux-arm64-musl": "15.3.0", "@next/swc-linux-x64-gnu": "15.3.0", "@next/swc-linux-x64-musl": "15.3.0", "@next/swc-win32-arm64-msvc": "15.3.0", "@next/swc-win32-x64-msvc": "15.3.0", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-k0MgP6BsK8cZ73wRjMazl2y2UcXj49ZXLDEgx6BikWuby/CN+nh81qFFI16edgd7xYpe/jj2OZEIwCoqnzz0bQ=="],
+ "next-mdx": ["next-mdx@0.6.2", "", { "dependencies": { "@mdx-js/react": "^1.6.22", "fast-glob": "^3.2.5", "gray-matter": "^4.0.2", "hasha": "^5.2.2", "next-mdx-remote": "^2.1.3", "node-cache": "^5.1.2" }, "peerDependencies": { "next": ">= 10.0.0", "react": ">= 16.8.0" } }, "sha512-3eX9PrzEZgLBiG1oA6Ig09Ou55WshF8hFtMZJqDQQkWGrmNfk31Rdu4c1apkkkl+Ir+hCHk7OFLZUO6PBCQTGA=="],
+
+ "next-mdx-remote": ["next-mdx-remote@5.0.0", "", { "dependencies": { "@babel/code-frame": "^7.23.5", "@mdx-js/mdx": "^3.0.1", "@mdx-js/react": "^3.0.1", "unist-util-remove": "^3.1.0", "vfile": "^6.0.1", "vfile-matter": "^5.0.0" }, "peerDependencies": { "react": ">=16" } }, "sha512-RNNbqRpK9/dcIFZs/esQhuLA8jANqlH694yqoDBK8hkVdJUndzzGmnPHa2nyi90N4Z9VmzuSWNRpr5ItT3M7xQ=="],
+
+ "node-cache": ["node-cache@5.1.2", "", { "dependencies": { "clone": "2.x" } }, "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg=="],
+
"node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="],
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
@@ -721,6 +1269,10 @@
"object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="],
+ "oniguruma-parser": ["oniguruma-parser@0.11.2", "", {}, "sha512-F7Ld4oDZJCI5/wCZ8AOffQbqjSzIRpKH7I/iuSs1SkhZeCj0wS6PMZ4W6VA16TWHrAo0Y9bBKEJOe7tvwcTXnw=="],
+
+ "oniguruma-to-es": ["oniguruma-to-es@4.2.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "oniguruma-parser": "^0.11.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-MDPs6KSOLS0tKQ7joqg44dRIRZUyotfTy0r+7oEEs6VwWWP0+E2PPDYWMFN0aqOjRyWHBYq7RfKw9GQk2S2z5g=="],
+
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
"own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
@@ -733,6 +1285,12 @@
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
+ "parse-entities": ["parse-entities@2.0.0", "", { "dependencies": { "character-entities": "^1.0.0", "character-entities-legacy": "^1.0.0", "character-reference-invalid": "^1.0.0", "is-alphanumerical": "^1.0.0", "is-decimal": "^1.0.0", "is-hexadecimal": "^1.0.0" } }, "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ=="],
+
+ "parse-numeric-range": ["parse-numeric-range@1.3.0", "", {}, "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ=="],
+
+ "parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="],
+
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
@@ -767,8 +1325,12 @@
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
+ "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
+
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
+ "property-information": ["property-information@7.0.0", "", {}, "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg=="],
+
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
@@ -789,14 +1351,72 @@
"react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
+ "react-syntax-highlighter": ["react-syntax-highlighter@15.6.1", "", { "dependencies": { "@babel/runtime": "^7.3.1", "highlight.js": "^10.4.1", "highlightjs-vue": "^1.0.0", "lowlight": "^1.17.0", "prismjs": "^1.27.0", "refractor": "^3.6.0" }, "peerDependencies": { "react": ">= 0.14.0" } }, "sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg=="],
+
"read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="],
"readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
+ "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="],
+
+ "recma-jsx": ["recma-jsx@1.0.0", "", { "dependencies": { "acorn-jsx": "^5.0.0", "estree-util-to-js": "^2.0.0", "recma-parse": "^1.0.0", "recma-stringify": "^1.0.0", "unified": "^11.0.0" } }, "sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q=="],
+
+ "recma-parse": ["recma-parse@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "esast-util-from-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ=="],
+
+ "recma-stringify": ["recma-stringify@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-to-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g=="],
+
"reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
+ "refractor": ["refractor@3.6.0", "", { "dependencies": { "hastscript": "^6.0.0", "parse-entities": "^2.0.0", "prismjs": "~1.27.0" } }, "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA=="],
+
+ "regenerate": ["regenerate@1.4.2", "", {}, "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A=="],
+
+ "regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.0", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA=="],
+
+ "regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="],
+
+ "regenerator-transform": ["regenerator-transform@0.15.2", "", { "dependencies": { "@babel/runtime": "^7.8.4" } }, "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg=="],
+
+ "regex": ["regex@6.0.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA=="],
+
+ "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
+
+ "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
+
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
+ "regexpu-core": ["regexpu-core@6.2.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", "regjsgen": "^0.8.0", "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" } }, "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA=="],
+
+ "regjsgen": ["regjsgen@0.8.0", "", {}, "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q=="],
+
+ "regjsparser": ["regjsparser@0.12.0", "", { "dependencies": { "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ=="],
+
+ "rehype-parse": ["rehype-parse@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" } }, "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag=="],
+
+ "rehype-pretty-code": ["rehype-pretty-code@0.14.1", "", { "dependencies": { "@types/hast": "^3.0.4", "hast-util-to-string": "^3.0.0", "parse-numeric-range": "^1.3.0", "rehype-parse": "^9.0.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "shiki": "^1.0.0 || ^2.0.0 || ^3.0.0" } }, "sha512-IpG4OL0iYlbx78muVldsK86hdfNoht0z63AP7sekQNW2QOTmjxB7RbTO+rhIYNGRljgHxgVZoPwUl6bIC9SbjA=="],
+
+ "rehype-prism-plus": ["rehype-prism-plus@2.0.1", "", { "dependencies": { "hast-util-to-string": "^3.0.0", "parse-numeric-range": "^1.3.0", "refractor": "^4.8.0", "rehype-parse": "^9.0.0", "unist-util-filter": "^5.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Wglct0OW12tksTUseAPyWPo3srjBOY7xKlql/DPKi7HbsdZTyaLCAoO58QBKSczFQxElTsQlOY3JDOFzB/K++Q=="],
+
+ "rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="],
+
+ "rehype-slug": ["rehype-slug@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "github-slugger": "^2.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-to-string": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A=="],
+
+ "remark-footnotes": ["remark-footnotes@2.0.0", "", {}, "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ=="],
+
+ "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="],
+
+ "remark-mdx": ["remark-mdx@3.1.0", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA=="],
+
+ "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="],
+
+ "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="],
+
+ "remark-squeeze-paragraphs": ["remark-squeeze-paragraphs@4.0.0", "", { "dependencies": { "mdast-squeeze-paragraphs": "^4.0.0" } }, "sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw=="],
+
+ "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="],
+
+ "repeat-string": ["repeat-string@1.6.1", "", {}, "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w=="],
+
"resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
@@ -815,6 +1435,8 @@
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
+ "section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="],
+
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
@@ -829,6 +1451,8 @@
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
+ "shiki": ["shiki@3.2.2", "", { "dependencies": { "@shikijs/core": "3.2.2", "@shikijs/engine-javascript": "3.2.2", "@shikijs/engine-oniguruma": "3.2.2", "@shikijs/langs": "3.2.2", "@shikijs/themes": "3.2.2", "@shikijs/types": "3.2.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-0qWBkM2t/0NXPRcVgtLhtHv6Ak3Q5yI4K/ggMqcgLRKm4+pCs3namgZlhlat/7u2CuqNtlShNs9lENOG6n7UaQ=="],
+
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
@@ -841,10 +1465,18 @@
"simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
+ "source-map": ["source-map@0.7.4", "", {}, "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="],
+
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
+ "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
+
+ "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
+
"stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="],
+ "state-toggle": ["state-toggle@1.0.3", "", {}, "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ=="],
+
"streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="],
"string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
@@ -863,14 +1495,22 @@
"string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="],
+ "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
+
"strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
"strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
+ "strip-bom-string": ["strip-bom-string@1.0.0", "", {}, "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g=="],
+
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
+ "style-to-js": ["style-to-js@1.1.16", "", { "dependencies": { "style-to-object": "1.0.8" } }, "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw=="],
+
+ "style-to-object": ["style-to-object@0.3.0", "", { "dependencies": { "inline-style-parser": "0.1.1" } }, "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA=="],
+
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
"sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="],
@@ -893,6 +1533,14 @@
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
+ "trim": ["trim@0.0.1", "", {}, "sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ=="],
+
+ "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
+
+ "trim-trailing-lines": ["trim-trailing-lines@1.1.4", "", {}, "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ=="],
+
+ "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
+
"ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
@@ -903,6 +1551,8 @@
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
+ "type-fest": ["type-fest@0.8.1", "", {}, "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="],
+
"typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
"typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="],
@@ -917,6 +1567,40 @@
"undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
+ "unherit": ["unherit@1.1.3", "", { "dependencies": { "inherits": "^2.0.0", "xtend": "^4.0.0" } }, "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ=="],
+
+ "unicode-canonical-property-names-ecmascript": ["unicode-canonical-property-names-ecmascript@2.0.1", "", {}, "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg=="],
+
+ "unicode-match-property-ecmascript": ["unicode-match-property-ecmascript@2.0.0", "", { "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" } }, "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q=="],
+
+ "unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.0", "", {}, "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg=="],
+
+ "unicode-property-aliases-ecmascript": ["unicode-property-aliases-ecmascript@2.1.0", "", {}, "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w=="],
+
+ "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
+
+ "unist-builder": ["unist-builder@2.0.3", "", {}, "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw=="],
+
+ "unist-util-filter": ["unist-util-filter@5.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-pHx7D4Zt6+TsfwylH9+lYhBhzyhEnCXs/lbq/Hstxno5z4gVdyc2WEW0asfjGKPyG4pEKrnBv5hdkO6+aRnQJw=="],
+
+ "unist-util-generated": ["unist-util-generated@1.1.6", "", {}, "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg=="],
+
+ "unist-util-is": ["unist-util-is@5.2.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw=="],
+
+ "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="],
+
+ "unist-util-position-from-estree": ["unist-util-position-from-estree@2.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ=="],
+
+ "unist-util-remove": ["unist-util-remove@3.1.1", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", "unist-util-visit-parents": "^5.0.0" } }, "sha512-kfCqZK5YVY5yEa89tvpl7KnBBHu2c6CzMkqHUrlOqaRgGOMp0sMvwWOVrbAtj03KhovQB7i96Gda72v/EFE0vw=="],
+
+ "unist-util-remove-position": ["unist-util-remove-position@2.0.1", "", { "dependencies": { "unist-util-visit": "^2.0.0" } }, "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA=="],
+
+ "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
+
+ "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
+
+ "unist-util-visit-parents": ["unist-util-visit-parents@5.1.3", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0" } }, "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg=="],
+
"unrs-resolver": ["unrs-resolver@1.5.0", "", { "optionalDependencies": { "@unrs/resolver-binding-darwin-arm64": "1.5.0", "@unrs/resolver-binding-darwin-x64": "1.5.0", "@unrs/resolver-binding-freebsd-x64": "1.5.0", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.5.0", "@unrs/resolver-binding-linux-arm-musleabihf": "1.5.0", "@unrs/resolver-binding-linux-arm64-gnu": "1.5.0", "@unrs/resolver-binding-linux-arm64-musl": "1.5.0", "@unrs/resolver-binding-linux-ppc64-gnu": "1.5.0", "@unrs/resolver-binding-linux-riscv64-gnu": "1.5.0", "@unrs/resolver-binding-linux-s390x-gnu": "1.5.0", "@unrs/resolver-binding-linux-x64-gnu": "1.5.0", "@unrs/resolver-binding-linux-x64-musl": "1.5.0", "@unrs/resolver-binding-wasm32-wasi": "1.5.0", "@unrs/resolver-binding-win32-arm64-msvc": "1.5.0", "@unrs/resolver-binding-win32-ia32-msvc": "1.5.0", "@unrs/resolver-binding-win32-x64-msvc": "1.5.0" } }, "sha512-6aia3Oy7SEe0MuUGQm2nsyob0L2+g57w178K5SE/3pvSGAIp28BB2O921fKx424Ahc/gQ6v0DXFbhcpyhGZdOA=="],
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
@@ -929,6 +1613,16 @@
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
+ "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
+
+ "vfile-location": ["vfile-location@3.2.0", "", {}, "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA=="],
+
+ "vfile-matter": ["vfile-matter@5.0.1", "", { "dependencies": { "vfile": "^6.0.0", "yaml": "^2.0.0" } }, "sha512-o6roP82AiX0XfkyTHyRCMXgHfltUNlXSEqCIS80f+mbAyiQBE2fxtDVMtseyytGx75sihiJFo/zR6r/4LTs2Cw=="],
+
+ "vfile-message": ["vfile-message@4.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="],
+
+ "web-namespaces": ["web-namespaces@1.1.4", "", {}, "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw=="],
+
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
@@ -945,10 +1639,24 @@
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
+ "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
+
+ "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
+
"yaml": ["yaml@2.7.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="],
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
+ "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
+
+ "@babel/core/json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
+
+ "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
+
+ "@babel/plugin-transform-classes/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="],
+
+ "@babel/traverse/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="],
+
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@eslint/plugin-kit/@eslint/core": ["@eslint/core@0.13.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw=="],
@@ -957,12 +1665,22 @@
"@next/eslint-plugin-next/fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="],
+ "@types/hast/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "@types/mdast/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
+ "babel-plugin-apply-mdx-type-prop/@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.10.4", "", {}, "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg=="],
+
+ "babel-plugin-extract-import-names/@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.10.4", "", {}, "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg=="],
+
"chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
+ "decode-named-character-reference/character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
+
"eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
"eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
@@ -971,28 +1689,154 @@
"eslint-plugin-react/resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="],
+ "estree-util-visit/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"fdir/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
+ "gray-matter/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
+
+ "hast-to-hyperscript/comma-separated-tokens": ["comma-separated-tokens@1.0.8", "", {}, "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw=="],
+
+ "hast-to-hyperscript/property-information": ["property-information@5.6.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA=="],
+
+ "hast-to-hyperscript/space-separated-tokens": ["space-separated-tokens@1.1.5", "", {}, "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA=="],
+
+ "hast-to-hyperscript/unist-util-is": ["unist-util-is@4.1.0", "", {}, "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg=="],
+
+ "hast-util-from-parse5/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "hast-util-from-parse5/hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="],
+
+ "hast-util-from-parse5/vfile-location": ["vfile-location@5.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg=="],
+
+ "hast-util-from-parse5/web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
+
+ "hast-util-raw/@types/hast": ["@types/hast@2.3.10", "", { "dependencies": { "@types/unist": "^2" } }, "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw=="],
+
+ "hast-util-raw/hast-util-from-parse5": ["hast-util-from-parse5@6.0.1", "", { "dependencies": { "@types/parse5": "^5.0.0", "hastscript": "^6.0.0", "property-information": "^5.0.0", "vfile": "^4.0.0", "vfile-location": "^3.2.0", "web-namespaces": "^1.0.0" } }, "sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA=="],
+
+ "hast-util-raw/html-void-elements": ["html-void-elements@1.0.5", "", {}, "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w=="],
+
+ "hast-util-raw/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="],
+
+ "hast-util-raw/unist-util-position": ["unist-util-position@3.1.0", "", {}, "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA=="],
+
+ "hast-util-raw/vfile": ["vfile@4.2.1", "", { "dependencies": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", "unist-util-stringify-position": "^2.0.0", "vfile-message": "^2.0.0" } }, "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA=="],
+
+ "hast-util-raw/zwitch": ["zwitch@1.0.5", "", {}, "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw=="],
+
+ "hast-util-to-html/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "hast-util-to-html/mdast-util-to-hast": ["mdast-util-to-hast@13.2.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA=="],
+
+ "hast-util-to-jsx-runtime/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "hast-util-to-parse5/property-information": ["property-information@5.6.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA=="],
+
+ "hast-util-to-parse5/zwitch": ["zwitch@1.0.5", "", {}, "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw=="],
+
+ "hastscript/@types/hast": ["@types/hast@2.3.10", "", { "dependencies": { "@types/unist": "^2" } }, "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw=="],
+
+ "hastscript/comma-separated-tokens": ["comma-separated-tokens@1.0.8", "", {}, "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw=="],
+
+ "hastscript/property-information": ["property-information@5.6.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA=="],
+
+ "hastscript/space-separated-tokens": ["space-separated-tokens@1.1.5", "", {}, "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA=="],
+
"is-bun-module/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
+ "lowlight/highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="],
+
+ "mdast-squeeze-paragraphs/unist-util-remove": ["unist-util-remove@2.1.0", "", { "dependencies": { "unist-util-is": "^4.0.0" } }, "sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q=="],
+
+ "mdast-util-definitions/unist-util-visit": ["unist-util-visit@2.0.3", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^4.0.0", "unist-util-visit-parents": "^3.0.0" } }, "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q=="],
+
+ "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
+
+ "mdast-util-find-and-replace/unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="],
+
+ "mdast-util-find-and-replace/unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="],
+
+ "mdast-util-from-markdown/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "mdast-util-mdx-jsx/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "mdast-util-mdx-jsx/parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
+
+ "mdast-util-phrasing/unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="],
+
+ "mdast-util-to-hast/@types/mdast": ["@types/mdast@3.0.15", "", { "dependencies": { "@types/unist": "^2" } }, "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ=="],
+
+ "mdast-util-to-hast/unist-util-position": ["unist-util-position@3.1.0", "", {}, "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA=="],
+
+ "mdast-util-to-hast/unist-util-visit": ["unist-util-visit@2.0.3", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^4.0.0", "unist-util-visit-parents": "^3.0.0" } }, "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q=="],
+
+ "mdast-util-to-markdown/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "micromark-util-events-to-acorn/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
+ "next-mdx/next-mdx-remote": ["next-mdx-remote@2.1.4", "", { "dependencies": { "@babel/core": "^7.12.10", "@babel/preset-env": "^7.12.11", "@babel/preset-react": "^7.12.10", "@babel/preset-typescript": "^7.12.7", "@mdx-js/mdx": "^1.6.22", "@mdx-js/react": "^1.6.22" }, "peerDependencies": { "react": ">=16.x <=17.x", "react-dom": ">=16.x <=17.x" } }, "sha512-HHJasFVgPGjWXW3tWC37NCvHcQUOjJgVUMdwxgVbshwgtDLrGmupNkist/iamDLlvVibU52B9rqM9VAWofq7cA=="],
+
+ "next-mdx-remote/@mdx-js/react": ["@mdx-js/react@3.1.0", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ=="],
+
+ "react-syntax-highlighter/highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="],
+
+ "refractor/prismjs": ["prismjs@1.27.0", "", {}, "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA=="],
+
+ "regjsparser/jsesc": ["jsesc@3.0.2", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g=="],
+
+ "rehype-prism-plus/refractor": ["refractor@4.9.0", "", { "dependencies": { "@types/hast": "^2.0.0", "@types/prismjs": "^1.0.0", "hastscript": "^7.0.0", "parse-entities": "^4.0.0" } }, "sha512-nEG1SPXFoGGx+dcjftjv8cAjEusIh6ED1xhf5DG3C0x/k+rmZ2duKnc3QLpt6qeHv5fPb8uwN3VWN2BT7fr3Og=="],
+
+ "remark-rehype/mdast-util-to-hast": ["mdast-util-to-hast@13.2.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA=="],
+
"sharp/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+ "stringify-entities/character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
+
"strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+ "style-to-js/style-to-object": ["style-to-object@1.0.8", "", { "dependencies": { "inline-style-parser": "0.2.4" } }, "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g=="],
+
"tailwindcss/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="],
"tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
+ "unified/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "unist-util-filter/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "unist-util-filter/unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="],
+
+ "unist-util-filter/unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="],
+
+ "unist-util-position/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "unist-util-position-from-estree/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "unist-util-remove-position/unist-util-visit": ["unist-util-visit@2.0.3", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^4.0.0", "unist-util-visit-parents": "^3.0.0" } }, "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q=="],
+
+ "unist-util-stringify-position/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "unist-util-visit/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "unist-util-visit/unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="],
+
+ "unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="],
+
+ "vfile/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "vfile-message/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
"wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
@@ -1005,10 +1849,122 @@
"glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
+ "gray-matter/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
+
+ "hast-util-from-parse5/hastscript/hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="],
+
+ "hast-util-raw/hast-util-from-parse5/property-information": ["property-information@5.6.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA=="],
+
+ "hast-util-raw/vfile/unist-util-stringify-position": ["unist-util-stringify-position@2.0.3", "", { "dependencies": { "@types/unist": "^2.0.2" } }, "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g=="],
+
+ "hast-util-raw/vfile/vfile-message": ["vfile-message@2.0.4", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^2.0.0" } }, "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ=="],
+
+ "mdast-squeeze-paragraphs/unist-util-remove/unist-util-is": ["unist-util-is@4.1.0", "", {}, "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg=="],
+
+ "mdast-util-definitions/unist-util-visit/unist-util-is": ["unist-util-is@4.1.0", "", {}, "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg=="],
+
+ "mdast-util-definitions/unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@3.1.1", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^4.0.0" } }, "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg=="],
+
+ "mdast-util-find-and-replace/unist-util-is/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "mdast-util-find-and-replace/unist-util-visit-parents/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "mdast-util-mdx-jsx/parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
+
+ "mdast-util-mdx-jsx/parse-entities/character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
+
+ "mdast-util-mdx-jsx/parse-entities/character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
+
+ "mdast-util-mdx-jsx/parse-entities/is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
+
+ "mdast-util-mdx-jsx/parse-entities/is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
+
+ "mdast-util-mdx-jsx/parse-entities/is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
+
+ "mdast-util-phrasing/unist-util-is/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "mdast-util-to-hast/unist-util-visit/unist-util-is": ["unist-util-is@4.1.0", "", {}, "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg=="],
+
+ "mdast-util-to-hast/unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@3.1.1", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^4.0.0" } }, "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx": ["@mdx-js/mdx@1.6.22", "", { "dependencies": { "@babel/core": "7.12.9", "@babel/plugin-syntax-jsx": "7.12.1", "@babel/plugin-syntax-object-rest-spread": "7.8.3", "@mdx-js/util": "1.6.22", "babel-plugin-apply-mdx-type-prop": "1.6.22", "babel-plugin-extract-import-names": "1.6.22", "camelcase-css": "2.0.1", "detab": "2.0.4", "hast-util-raw": "6.0.1", "lodash.uniq": "4.5.0", "mdast-util-to-hast": "10.0.1", "remark-footnotes": "2.0.0", "remark-mdx": "1.6.22", "remark-parse": "8.0.3", "remark-squeeze-paragraphs": "4.0.0", "style-to-object": "0.3.0", "unified": "9.2.0", "unist-builder": "2.0.3", "unist-util-visit": "2.0.3" } }, "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA=="],
+
+ "rehype-prism-plus/refractor/@types/hast": ["@types/hast@2.3.10", "", { "dependencies": { "@types/unist": "^2" } }, "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw=="],
+
+ "rehype-prism-plus/refractor/hastscript": ["hastscript@7.2.0", "", { "dependencies": { "@types/hast": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^3.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw=="],
+
+ "rehype-prism-plus/refractor/parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
+
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+ "style-to-js/style-to-object/inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="],
+
+ "unist-util-remove-position/unist-util-visit/unist-util-is": ["unist-util-is@4.1.0", "", {}, "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg=="],
+
+ "unist-util-remove-position/unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@3.1.1", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^4.0.0" } }, "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg=="],
+
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+
+ "mdast-util-mdx-jsx/parse-entities/is-alphanumerical/is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/@babel/core": ["@babel/core@7.12.9", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/generator": "^7.12.5", "@babel/helper-module-transforms": "^7.12.1", "@babel/helpers": "^7.12.5", "@babel/parser": "^7.12.7", "@babel/template": "^7.12.7", "@babel/traverse": "^7.12.9", "@babel/types": "^7.12.7", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", "json5": "^2.1.2", "lodash": "^4.17.19", "resolve": "^1.3.2", "semver": "^5.4.1", "source-map": "^0.5.0" } }, "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.12.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/remark-mdx": ["remark-mdx@1.6.22", "", { "dependencies": { "@babel/core": "7.12.9", "@babel/helper-plugin-utils": "7.10.4", "@babel/plugin-proposal-object-rest-spread": "7.12.1", "@babel/plugin-syntax-jsx": "7.12.1", "@mdx-js/util": "1.6.22", "is-alphabetical": "1.0.4", "remark-parse": "8.0.3", "unified": "9.2.0" } }, "sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/remark-parse": ["remark-parse@8.0.3", "", { "dependencies": { "ccount": "^1.0.0", "collapse-white-space": "^1.0.2", "is-alphabetical": "^1.0.0", "is-decimal": "^1.0.0", "is-whitespace-character": "^1.0.0", "is-word-character": "^1.0.0", "markdown-escapes": "^1.0.0", "parse-entities": "^2.0.0", "repeat-string": "^1.5.4", "state-toggle": "^1.0.0", "trim": "0.0.1", "trim-trailing-lines": "^1.0.0", "unherit": "^1.0.4", "unist-util-remove-position": "^2.0.0", "vfile-location": "^3.0.0", "xtend": "^4.0.1" } }, "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/unified": ["unified@9.2.0", "", { "dependencies": { "bail": "^1.0.0", "extend": "^3.0.0", "is-buffer": "^2.0.0", "is-plain-obj": "^2.0.0", "trough": "^1.0.0", "vfile": "^4.0.0" } }, "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/unist-util-visit": ["unist-util-visit@2.0.3", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^4.0.0", "unist-util-visit-parents": "^3.0.0" } }, "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q=="],
+
+ "rehype-prism-plus/refractor/hastscript/hast-util-parse-selector": ["hast-util-parse-selector@3.1.1", "", { "dependencies": { "@types/hast": "^2.0.0" } }, "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA=="],
+
+ "rehype-prism-plus/refractor/hastscript/property-information": ["property-information@6.5.0", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="],
+
+ "rehype-prism-plus/refractor/parse-entities/character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
+
+ "rehype-prism-plus/refractor/parse-entities/character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
+
+ "rehype-prism-plus/refractor/parse-entities/is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
+
+ "rehype-prism-plus/refractor/parse-entities/is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
+
+ "rehype-prism-plus/refractor/parse-entities/is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/@babel/core/convert-source-map": ["convert-source-map@1.9.0", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/@babel/core/json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/@babel/core/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/@babel/core/source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/remark-mdx/@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.10.4", "", {}, "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/remark-parse/ccount": ["ccount@1.1.0", "", {}, "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/remark-parse/collapse-white-space": ["collapse-white-space@1.0.6", "", {}, "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/unified/bail": ["bail@1.0.5", "", {}, "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/unified/is-plain-obj": ["is-plain-obj@2.1.0", "", {}, "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/unified/trough": ["trough@1.0.5", "", {}, "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/unified/vfile": ["vfile@4.2.1", "", { "dependencies": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", "unist-util-stringify-position": "^2.0.0", "vfile-message": "^2.0.0" } }, "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/unist-util-visit/unist-util-is": ["unist-util-is@4.1.0", "", {}, "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@3.1.1", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^4.0.0" } }, "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg=="],
+
+ "rehype-prism-plus/refractor/parse-entities/is-alphanumerical/is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/unified/vfile/unist-util-stringify-position": ["unist-util-stringify-position@2.0.3", "", { "dependencies": { "@types/unist": "^2.0.2" } }, "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g=="],
+
+ "next-mdx/next-mdx-remote/@mdx-js/mdx/unified/vfile/vfile-message": ["vfile-message@2.0.4", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^2.0.0" } }, "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ=="],
}
}
diff --git a/eslint.config.mjs b/eslint.config.mjs
index c85fb67..56fb121 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -11,6 +11,28 @@ const compat = new FlatCompat({
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
+ {
+ rules: {
+ // TypeScript rules
+ "@typescript-eslint/no-unused-vars": ["off", {
+ argsIgnorePattern: "^_",
+ varsIgnorePattern: "^_"
+ }],
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/explicit-module-boundary-types": "off",
+
+ // Import rules
+ "import/no-anonymous-default-export": "off",
+
+ "jsx-a11y/alt-text": "error",
+ "jsx-a11y/aria-props": "error",
+ "jsx-a11y/anchor-is-valid": "warn",
+
+ "@next/next/no-img-element": "off",
+ "@next/next/no-html-link-for-pages": "error"
+ }
+ }
];
export default eslintConfig;
diff --git a/next.config.ts b/next.config.ts
index e9ffa30..1385a30 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,7 +1,32 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
- /* config options here */
+ typescript: { ignoreBuildErrors: true },
+ turbopack: {
+ resolveAlias: {
+ underscore: 'lodash'
+ },
+ resolveExtensions: ['.mdx', '.tsx', '.ts', '.jsx', '.js', '.json']
+ },
+ images: {
+ remotePatterns: [
+ {
+ protocol: 'https',
+ hostname: 'i.scdn.co'
+ }
+ ]
+ },
+ webpack(config) {
+ config.module.rules.push({
+ test: /\.(mp3)$/,
+ type: 'asset/resource',
+ generator: {
+ filename: 'static/chunks/[path][name].[hash][ext]'
+ }
+ });
+
+ return config;
+ }
};
export default nextConfig;
diff --git a/package.json b/package.json
index 91a8817..9865d1f 100644
--- a/package.json
+++ b/package.json
@@ -1,31 +1,49 @@
{
"name": "@codemeapixel/website",
- "version": "0.1.0",
+ "version": "0.2.0-beta",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
- "build": "next build",
- "start": "next start",
+ "build": "next build --turbopack",
+ "start": "next start --turbopack",
"lint": "next lint"
},
"dependencies": {
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-tooltip": "^1.0.7",
+ "@radix-ui/react-tabs": "^1.1.2",
"axios": "^1.7.9",
"clsx": "^2.1.1",
+ "crypto": "^1.0.1",
+ "date-fns": "^4.1.0",
"devicons": "^1.8.0",
+ "devicons-react": "^1.5.0-beta.11",
"framer-motion": "^11.15.0",
+ "gray-matter": "^4.0.3",
+ "marked": "^4.3.0",
+ "highlight.js": "^11.8.0",
+ "dompurify": "^3.0.5",
+ "next": "15.3.0",
+ "next-mdx": "^0.6.2",
+ "next-mdx-remote": "^5.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
- "react-icons": "^5.4.0",
- "next": "15.3.0"
+ "react-icons": "^5.5.0",
+ "react-syntax-highlighter": "^15.6.1",
+ "rehype-prism-plus": "^2.0.1",
+ "rehype-pretty-code": "^0.14.0",
+ "rehype-slug": "^6.0.0",
+ "remark-gfm": "^4.0.0",
+ "shiki": "^3.2.1"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
+ "@types/marked": "^5.0.1",
+ "@types/dompurify": "^3.0.2",
"postcss": "^8.4.49",
"autoprefixer": "^10.4.20",
"eslint": "^9",
diff --git a/public/NodeByteLTD/about.png b/public/NodeByteLTD/about.png
new file mode 100644
index 0000000..5a4fc30
Binary files /dev/null and b/public/NodeByteLTD/about.png differ
diff --git a/public/NodeByteLTD/discord.png b/public/NodeByteLTD/discord.png
new file mode 100644
index 0000000..ee207a6
Binary files /dev/null and b/public/NodeByteLTD/discord.png differ
diff --git a/public/NodeByteLTD/home.png b/public/NodeByteLTD/home.png
new file mode 100644
index 0000000..bf6cb25
Binary files /dev/null and b/public/NodeByteLTD/home.png differ
diff --git a/public/NodeByteLTD/services.png b/public/NodeByteLTD/services.png
new file mode 100644
index 0000000..55989f1
Binary files /dev/null and b/public/NodeByteLTD/services.png differ
diff --git a/public/Pixie.png b/public/Pixie.png
new file mode 100644
index 0000000..d2392ca
Binary files /dev/null and b/public/Pixie.png differ
diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png
new file mode 100644
index 0000000..1e6a1ea
Binary files /dev/null and b/public/apple-touch-icon.png differ
diff --git a/public/character.png b/public/character.png
index eb25d3d..0390b75 100644
Binary files a/public/character.png and b/public/character.png differ
diff --git a/public/covers/deadmau5.webp b/public/covers/deadmau5.webp
new file mode 100644
index 0000000..da28083
Binary files /dev/null and b/public/covers/deadmau5.webp differ
diff --git a/public/covers/eminem.webp b/public/covers/eminem.webp
new file mode 100644
index 0000000..78ff592
Binary files /dev/null and b/public/covers/eminem.webp differ
diff --git a/public/covers/paparoach.webp b/public/covers/paparoach.webp
new file mode 100644
index 0000000..f734564
Binary files /dev/null and b/public/covers/paparoach.webp differ
diff --git a/public/covers/tragicallyhip.webp b/public/covers/tragicallyhip.webp
new file mode 100644
index 0000000..c2a9013
Binary files /dev/null and b/public/covers/tragicallyhip.webp differ
diff --git a/public/pixie-banner.png b/public/pixie-banner.png
new file mode 100644
index 0000000..8954011
Binary files /dev/null and b/public/pixie-banner.png differ
diff --git a/public/pixie-logo.png b/public/pixie-logo.png
new file mode 100644
index 0000000..9541153
Binary files /dev/null and b/public/pixie-logo.png differ
diff --git a/public/playlist/eminem/25-to-life.mp3 b/public/playlist/eminem/25-to-life.mp3
new file mode 100644
index 0000000..003b4df
Binary files /dev/null and b/public/playlist/eminem/25-to-life.mp3 differ
diff --git a/public/playlist/eminem/cold-wind-blows.mp3 b/public/playlist/eminem/cold-wind-blows.mp3
new file mode 100644
index 0000000..2ae98a9
Binary files /dev/null and b/public/playlist/eminem/cold-wind-blows.mp3 differ
diff --git a/public/playlist/eminem/fuel-ft-jid.mp3 b/public/playlist/eminem/fuel-ft-jid.mp3
new file mode 100644
index 0000000..0d0dca4
Binary files /dev/null and b/public/playlist/eminem/fuel-ft-jid.mp3 differ
diff --git a/public/playlist/eminem/lose-yourself.mp3 b/public/playlist/eminem/lose-yourself.mp3
new file mode 100644
index 0000000..1317592
Binary files /dev/null and b/public/playlist/eminem/lose-yourself.mp3 differ
diff --git a/public/playlist/eminem/somebody-save-me.mp3 b/public/playlist/eminem/somebody-save-me.mp3
new file mode 100644
index 0000000..e8adc00
Binary files /dev/null and b/public/playlist/eminem/somebody-save-me.mp3 differ
diff --git a/public/playlist/eminem/temporary-ft-skylar-grey.mp3 b/public/playlist/eminem/temporary-ft-skylar-grey.mp3
new file mode 100644
index 0000000..def31a4
Binary files /dev/null and b/public/playlist/eminem/temporary-ft-skylar-grey.mp3 differ
diff --git a/public/playlist/papa-roach/getting-away-with-murder.mp3 b/public/playlist/papa-roach/getting-away-with-murder.mp3
new file mode 100644
index 0000000..11d95e1
Binary files /dev/null and b/public/playlist/papa-roach/getting-away-with-murder.mp3 differ
diff --git a/public/playlist/papa-roach/hollywood-whore.mp3 b/public/playlist/papa-roach/hollywood-whore.mp3
new file mode 100644
index 0000000..26f9c48
Binary files /dev/null and b/public/playlist/papa-roach/hollywood-whore.mp3 differ
diff --git a/public/playlist/papa-roach/she-loves-me-not.mp3 b/public/playlist/papa-roach/she-loves-me-not.mp3
new file mode 100644
index 0000000..f95a8d9
Binary files /dev/null and b/public/playlist/papa-roach/she-loves-me-not.mp3 differ
diff --git a/public/playlist/papa-roach/to-be-loved.mp3 b/public/playlist/papa-roach/to-be-loved.mp3
new file mode 100644
index 0000000..eca7483
Binary files /dev/null and b/public/playlist/papa-roach/to-be-loved.mp3 differ
diff --git a/public/playlist/tragically-hip/ahead-by-a-century.mp3 b/public/playlist/tragically-hip/ahead-by-a-century.mp3
new file mode 100644
index 0000000..5095ba3
Binary files /dev/null and b/public/playlist/tragically-hip/ahead-by-a-century.mp3 differ
diff --git a/public/playlist/tragically-hip/blow-at-high-dough.mp3 b/public/playlist/tragically-hip/blow-at-high-dough.mp3
new file mode 100644
index 0000000..ed5b95a
Binary files /dev/null and b/public/playlist/tragically-hip/blow-at-high-dough.mp3 differ
diff --git a/public/playlist/tragically-hip/hundreth-meridian.mp3 b/public/playlist/tragically-hip/hundreth-meridian.mp3
new file mode 100644
index 0000000..912f3c7
Binary files /dev/null and b/public/playlist/tragically-hip/hundreth-meridian.mp3 differ
diff --git a/public/playlist/tragically-hip/new-orleans-is-sinking.mp3 b/public/playlist/tragically-hip/new-orleans-is-sinking.mp3
new file mode 100644
index 0000000..e31bd1b
Binary files /dev/null and b/public/playlist/tragically-hip/new-orleans-is-sinking.mp3 differ
diff --git a/public/previews/cordx.ca/dashboard.png b/public/previews/cordx.ca/dashboard.png
new file mode 100644
index 0000000..ee7d123
Binary files /dev/null and b/public/previews/cordx.ca/dashboard.png differ
diff --git a/public/previews/cordx.ca/features.png b/public/previews/cordx.ca/features.png
new file mode 100644
index 0000000..05f6121
Binary files /dev/null and b/public/previews/cordx.ca/features.png differ
diff --git a/public/previews/cordx.ca/home.png b/public/previews/cordx.ca/home.png
new file mode 100644
index 0000000..ad079b9
Binary files /dev/null and b/public/previews/cordx.ca/home.png differ
diff --git a/public/previews/cordx.ca/metrics.png b/public/previews/cordx.ca/metrics.png
new file mode 100644
index 0000000..cd6e79e
Binary files /dev/null and b/public/previews/cordx.ca/metrics.png differ
diff --git a/public/previews/infinitybots.gg/ibl-preview-1.png b/public/previews/infinitybots.gg/ibl-preview-1.png
new file mode 100644
index 0000000..7a2de1d
Binary files /dev/null and b/public/previews/infinitybots.gg/ibl-preview-1.png differ
diff --git a/public/previews/infinitybots.gg/ibl-preview-2.png b/public/previews/infinitybots.gg/ibl-preview-2.png
new file mode 100644
index 0000000..f5e11d8
Binary files /dev/null and b/public/previews/infinitybots.gg/ibl-preview-2.png differ
diff --git a/public/previews/infinitybots.gg/ibl-preview-3.png b/public/previews/infinitybots.gg/ibl-preview-3.png
new file mode 100644
index 0000000..500f130
Binary files /dev/null and b/public/previews/infinitybots.gg/ibl-preview-3.png differ
diff --git a/public/previews/infinitybots.gg/ibl-preview-4.png b/public/previews/infinitybots.gg/ibl-preview-4.png
new file mode 100644
index 0000000..9f3732e
Binary files /dev/null and b/public/previews/infinitybots.gg/ibl-preview-4.png differ
diff --git a/public/previews/netsocial.app/ns-preview-1.png b/public/previews/netsocial.app/ns-preview-1.png
new file mode 100644
index 0000000..0031474
Binary files /dev/null and b/public/previews/netsocial.app/ns-preview-1.png differ
diff --git a/public/previews/netsocial.app/ns-preview-2.png b/public/previews/netsocial.app/ns-preview-2.png
new file mode 100644
index 0000000..df306b4
Binary files /dev/null and b/public/previews/netsocial.app/ns-preview-2.png differ
diff --git a/public/previews/netsocial.app/ns-preview-3.png b/public/previews/netsocial.app/ns-preview-3.png
new file mode 100644
index 0000000..5d61e1b
Binary files /dev/null and b/public/previews/netsocial.app/ns-preview-3.png differ
diff --git a/public/previews/netsocial.app/ns-preview-4.png b/public/previews/netsocial.app/ns-preview-4.png
new file mode 100644
index 0000000..2503808
Binary files /dev/null and b/public/previews/netsocial.app/ns-preview-4.png differ
diff --git a/public/previews/nodebyte.co.uk/nbltd-preview-1.png b/public/previews/nodebyte.co.uk/nbltd-preview-1.png
new file mode 100644
index 0000000..800df8f
Binary files /dev/null and b/public/previews/nodebyte.co.uk/nbltd-preview-1.png differ
diff --git a/public/previews/nodebyte.co.uk/nbltd-preview-2.png b/public/previews/nodebyte.co.uk/nbltd-preview-2.png
new file mode 100644
index 0000000..2a9cdf8
Binary files /dev/null and b/public/previews/nodebyte.co.uk/nbltd-preview-2.png differ
diff --git a/public/previews/nodebyte.co.uk/nbltd-preview-3.png b/public/previews/nodebyte.co.uk/nbltd-preview-3.png
new file mode 100644
index 0000000..dfc466c
Binary files /dev/null and b/public/previews/nodebyte.co.uk/nbltd-preview-3.png differ
diff --git a/public/previews/nodebyte.co.uk/nbltd-preview-4.png b/public/previews/nodebyte.co.uk/nbltd-preview-4.png
new file mode 100644
index 0000000..0a715d1
Binary files /dev/null and b/public/previews/nodebyte.co.uk/nbltd-preview-4.png differ
diff --git a/public/previews/nodebyte.host/nbh-preview-1.png b/public/previews/nodebyte.host/nbh-preview-1.png
new file mode 100644
index 0000000..91f521a
Binary files /dev/null and b/public/previews/nodebyte.host/nbh-preview-1.png differ
diff --git a/public/previews/nodebyte.host/nbh-preview-2.png b/public/previews/nodebyte.host/nbh-preview-2.png
new file mode 100644
index 0000000..3571566
Binary files /dev/null and b/public/previews/nodebyte.host/nbh-preview-2.png differ
diff --git a/public/previews/nodebyte.host/nbh-preview-3.png b/public/previews/nodebyte.host/nbh-preview-3.png
new file mode 100644
index 0000000..37e3267
Binary files /dev/null and b/public/previews/nodebyte.host/nbh-preview-3.png differ
diff --git a/public/previews/nodebyte.host/nbh-preview-4.png b/public/previews/nodebyte.host/nbh-preview-4.png
new file mode 100644
index 0000000..578b976
Binary files /dev/null and b/public/previews/nodebyte.host/nbh-preview-4.png differ
diff --git a/public/referrals/cybrancee/banner.png b/public/referrals/cybrancee/banner.png
new file mode 100644
index 0000000..8d4391a
Binary files /dev/null and b/public/referrals/cybrancee/banner.png differ
diff --git a/public/referrals/digital-ocean/banner.png b/public/referrals/digital-ocean/banner.png
new file mode 100644
index 0000000..017c9af
Binary files /dev/null and b/public/referrals/digital-ocean/banner.png differ
diff --git a/public/referrals/digital-ocean/logo.png b/public/referrals/digital-ocean/logo.png
new file mode 100644
index 0000000..d1bcf49
Binary files /dev/null and b/public/referrals/digital-ocean/logo.png differ
diff --git a/public/referrals/linode/banner.png b/public/referrals/linode/banner.png
new file mode 100644
index 0000000..3a4dfd9
Binary files /dev/null and b/public/referrals/linode/banner.png differ
diff --git a/public/referrals/nodebyte/banner.png b/public/referrals/nodebyte/banner.png
new file mode 100644
index 0000000..147aebb
Binary files /dev/null and b/public/referrals/nodebyte/banner.png differ
diff --git a/public/referrals/railway/banner.png b/public/referrals/railway/banner.png
new file mode 100644
index 0000000..cbbe584
Binary files /dev/null and b/public/referrals/railway/banner.png differ
diff --git a/public/referrals/zap/banner.png b/public/referrals/zap/banner.png
new file mode 100644
index 0000000..b4e88e0
Binary files /dev/null and b/public/referrals/zap/banner.png differ
diff --git a/public/scripts/pxl/pxl-console.png b/public/scripts/pxl/pxl-console.png
new file mode 100644
index 0000000..53b0357
Binary files /dev/null and b/public/scripts/pxl/pxl-console.png differ
diff --git a/public/scripts/pxl/pxl-player-join.png b/public/scripts/pxl/pxl-player-join.png
new file mode 100644
index 0000000..f8f8e8c
Binary files /dev/null and b/public/scripts/pxl/pxl-player-join.png differ
diff --git a/public/scripts/pxl/pxl-player-leave.png b/public/scripts/pxl/pxl-player-leave.png
new file mode 100644
index 0000000..1387fcc
Binary files /dev/null and b/public/scripts/pxl/pxl-player-leave.png differ
diff --git a/public/scripts/pxl/pxl-startup.png b/public/scripts/pxl/pxl-startup.png
new file mode 100644
index 0000000..e09331c
Binary files /dev/null and b/public/scripts/pxl/pxl-startup.png differ
diff --git a/src/app/(main)/about/page.tsx b/src/app/(main)/about/page.tsx
new file mode 100644
index 0000000..a15a083
--- /dev/null
+++ b/src/app/(main)/about/page.tsx
@@ -0,0 +1,15 @@
+import AboutContent from "@/components/layouts/about/AboutContent";
+
+export const metadata = {
+ title: "About Me",
+ description: "Learn more about my background, skills, and expertise in web development.",
+ openGraph: {
+ title: "About Me | CodeMeAPixel",
+ description: "Learn more about my background, skills, and expertise in web development.",
+ type: "website",
+ },
+};
+
+export default function AboutPage() {
+ return ;
+}
diff --git a/src/app/(main)/blog/[slug]/page.tsx b/src/app/(main)/blog/[slug]/page.tsx
new file mode 100644
index 0000000..d59efca
--- /dev/null
+++ b/src/app/(main)/blog/[slug]/page.tsx
@@ -0,0 +1,69 @@
+import { getPostBySlug, getPostSlugs, getRecentPosts } from '@/data/blogData';
+import BlogPostContent from '@/components/blog/BlogPostContent';
+import { Metadata } from 'next';
+import { notFound } from 'next/navigation';
+import { calculateReadingTime } from '@/lib/mdx';
+
+interface Props {
+ params: Promise<{ slug: string }>;
+
+}
+
+export async function generateStaticParams() {
+ const slugs = await getPostSlugs();
+ return slugs.map((slug) => ({
+ slug: String(slug)
+ }));
+}
+
+export async function generateMetadata({ params }: Props): Promise {
+ const { slug } = await params;
+ const post = await getPostBySlug(slug);
+
+ if (!post) {
+ return {
+ title: 'Post Not Found',
+ description: 'The requested blog post could not be found.'
+ };
+ }
+
+ const { metadata } = post;
+
+ return {
+ title: metadata.title,
+ description: metadata.description || '',
+ authors: [{ name: metadata.author || 'Author' }],
+ openGraph: {
+ title: metadata.title,
+ description: metadata.description || '',
+ type: 'article',
+ publishedTime: metadata.date,
+ tags: metadata.tags || [],
+ images: metadata.image ? [metadata.image] : [],
+ },
+ };
+}
+
+export default async function BlogPost({ params }: Props) {
+ const { slug } = await params;
+ const post = await getPostBySlug(slug);
+
+ if (!post) {
+ notFound();
+ }
+
+ const { content, metadata } = post;
+ const readingTime = calculateReadingTime(content);
+ const relatedPosts = await getRecentPosts(3);
+
+ return (
+ p.slug !== slug)}
+ />
+ );
+}
\ No newline at end of file
diff --git a/src/app/(main)/blog/page.tsx b/src/app/(main)/blog/page.tsx
new file mode 100644
index 0000000..43def2c
--- /dev/null
+++ b/src/app/(main)/blog/page.tsx
@@ -0,0 +1,27 @@
+import { Metadata } from 'next';
+import BlogContent from '@/components/blog/BlogContent';
+import { getAllPosts, getAllCategories, getAllTags } from '@/data/blogData';
+
+export const metadata: Metadata = {
+ title: 'Blog',
+ description: 'Explore articles and tutorials on web development, programming, and technology.',
+ openGraph: {
+ title: 'Blog',
+ description: 'Explore articles and tutorials on web development, programming, and technology.',
+ type: 'website',
+ },
+};
+
+export default async function BlogIndexPage() {
+ const posts = await getAllPosts();
+ const categories = await getAllCategories();
+ const tags = await getAllTags();
+
+ return (
+
+ );
+}
diff --git a/src/app/(main)/contact/page.tsx b/src/app/(main)/contact/page.tsx
new file mode 100644
index 0000000..d2481ef
--- /dev/null
+++ b/src/app/(main)/contact/page.tsx
@@ -0,0 +1,15 @@
+import ContactContent from "@/components/layouts/contact/ContactContent";
+
+export const metadata = {
+ title: "Contact Me",
+ description: "Get in touch with me for collaboration, questions, or just to say hello!",
+ openGraph: {
+ title: "Contact Me | CodeMeAPixel",
+ description: "Get in touch with me for collaboration, questions, or just to say hello!",
+ type: "website",
+ },
+};
+
+export default function ContactPage() {
+ return ;
+}
diff --git a/src/app/(main)/error-page/page.tsx b/src/app/(main)/error-page/page.tsx
new file mode 100644
index 0000000..e752c37
--- /dev/null
+++ b/src/app/(main)/error-page/page.tsx
@@ -0,0 +1,255 @@
+"use client";
+
+import Navbar from "@/components/static/Navbar";
+import { motion } from "framer-motion";
+import Link from "next/link";
+import { useEffect, useState } from "react";
+import { IoHome, IoArrowBack, IoWarning } from "react-icons/io5";
+
+export const metadata = {
+ title: "Error Page",
+ description: "An error has occurred while processing your request.",
+};
+
+export default function GeneralErrorPage() {
+ const [mounted, setMounted] = useState(false);
+ const [errorCode, setErrorCode] = useState("500");
+
+ useEffect(() => {
+ setMounted(true);
+
+ // Extract error code from URL if present
+ const params = new URLSearchParams(window.location.search);
+ const code = params.get("code");
+ if (code) {
+ setErrorCode(code);
+ }
+ }, []);
+
+ if (!mounted) return null;
+
+ return (
+ <>
+
+
+ {/* Background grid effect */}
+
+
+ {/* Decorative elements */}
+
+ {/* Large background text */}
+
+ {errorCode}
+
+
+ {/* Code brackets */}
+
+ {'<'}
+
+
+
+ {'>'}
+
+
+ {/* Floating "error" elements */}
+
+
+ {`ERROR_${errorCode}`}
+
+
+
+
+
+ {' '}
+
+
+
+
+ {/* Main content */}
+
+
+
+
+ Error {errorCode}
+
+
+
+
+ {getErrorTitle(errorCode)}
+
+
+
+ {getErrorMessage(errorCode)}
+
+
+
+
+
+
+ Back to Home
+
+
+
+
+ window.history.back()}
+ className="btn-secondary flex items-center"
+ >
+
+ Go Back
+
+
+
+ {/* Console-style error message */}
+
+
+
+
+
+ $
+ error.status
+
+
{errorCode}
+
+ $
+ error.message
+
+
{getErrorMessage(errorCode)}
+
+ $
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+// Helper functions for error messages
+function getErrorTitle(code: string): string {
+ const titles: Record = {
+ "400": "Bad Request",
+ "401": "Unauthorized",
+ "403": "Forbidden",
+ "404": "Page Not Found",
+ "500": "Server Error",
+ "503": "Service Unavailable"
+ };
+
+ return titles[code] || "Unknown Error";
+}
+
+function getErrorMessage(code: string): string {
+ const messages: Record = {
+ "400": "The server cannot process the request due to a client error.",
+ "401": "You need to be authenticated to access this resource.",
+ "403": "You don't have permission to access this resource.",
+ "404": "The page you're looking for doesn't exist or has been moved.",
+ "500": "The server encountered an unexpected condition that prevented it from fulfilling the request.",
+ "503": "The server is currently unavailable. Please try again later."
+ };
+
+ return messages[code] || "An unexpected error occurred. Please try again later.";
+}
+
+// Typewriter effect component
+function TypewriterEffect({ text, delay = 50, startDelay = 0 }) {
+ const [displayText, setDisplayText] = useState("");
+
+ useEffect(() => {
+ let timer: NodeJS.Timeout;
+ let currentIndex = 0;
+
+ const startTyping = () => {
+ timer = setInterval(() => {
+ if (currentIndex < text.length) {
+ setDisplayText(text.substring(0, currentIndex + 1));
+ currentIndex++;
+ } else {
+ clearInterval(timer);
+ }
+ }, delay);
+ };
+
+ const initialDelay = setTimeout(startTyping, startDelay);
+
+ return () => {
+ clearTimeout(initialDelay);
+ clearInterval(timer);
+ };
+ }, [text, delay, startDelay]);
+
+ return {displayText} ;
+}
diff --git a/src/app/(main)/fivem/[slug]/page.tsx b/src/app/(main)/fivem/[slug]/page.tsx
new file mode 100644
index 0000000..4537b71
--- /dev/null
+++ b/src/app/(main)/fivem/[slug]/page.tsx
@@ -0,0 +1,46 @@
+import { Metadata } from 'next';
+import { notFound } from 'next/navigation';
+import FivemScriptDetailsContent from '@/components/layouts/fivem/FivemScriptDetailsContent';
+import { getScriptBySlug, getAllScriptSlugs } from '@/data/fivemScriptsData';
+
+// Generate static params for all script slugs
+export async function generateStaticParams() {
+ const slugs = getAllScriptSlugs();
+
+ return slugs.map(slug => ({
+ slug
+ }));
+}
+
+// Generate metadata for each script
+export async function generateMetadata({ params }: { params: { slug: string } }): Promise {
+ const script = getScriptBySlug(params.slug);
+
+ if (!script) {
+ return {
+ title: 'Script Not Found',
+ description: 'The requested FiveM script could not be found.'
+ };
+ }
+
+ return {
+ title: `${script.title} | FiveM Script`,
+ description: script.description,
+ openGraph: {
+ title: `${script.title} | FiveM Script`,
+ description: script.description,
+ images: script.images[0] ? [script.images[0]] : [],
+ type: 'website',
+ },
+ };
+}
+
+export default function ScriptPage({ params }: { params: { slug: string } }) {
+ const script = getScriptBySlug(params.slug);
+
+ if (!script) {
+ notFound();
+ }
+
+ return ;
+}
diff --git a/src/app/(main)/fivem/page.tsx b/src/app/(main)/fivem/page.tsx
new file mode 100644
index 0000000..65d4cdc
--- /dev/null
+++ b/src/app/(main)/fivem/page.tsx
@@ -0,0 +1,20 @@
+import { Metadata } from 'next';
+import FivemScriptsContent from '@/components/layouts/fivem/FivemScriptsContent';
+import { getAllScripts, getAllScriptTags } from '@/data/fivemScriptsData';
+
+export const metadata: Metadata = {
+ title: 'FiveM Scripts | Premium RP Server Resources',
+ description: 'Browse my collection of premium FiveM scripts for roleplay servers. Compatible with ESX and QBCore frameworks.',
+ openGraph: {
+ title: 'FiveM Scripts | Premium RP Server Resources',
+ description: 'Browse my collection of premium FiveM scripts for roleplay servers. Compatible with ESX and QBCore frameworks.',
+ type: 'website',
+ },
+};
+
+export default function FivemPage() {
+ const scripts = getAllScripts();
+ const allTags = getAllScriptTags();
+
+ return ;
+}
diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx
new file mode 100644
index 0000000..6ece181
--- /dev/null
+++ b/src/app/(main)/layout.tsx
@@ -0,0 +1,20 @@
+import type { Metadata } from "next";
+import { Inter } from "next/font/google";
+import { ThemeProvider } from "@/context/ThemeContext";
+import Navbar from "@/components/static/Navbar";
+import Loader from "@/components/static/Loader";
+
+const inter = Inter({ subsets: ["latin"] });
+
+export default function MainLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+
+ {children}
+
+ );
+}
diff --git a/src/app/(main)/not-found.tsx b/src/app/(main)/not-found.tsx
new file mode 100644
index 0000000..996d47b
--- /dev/null
+++ b/src/app/(main)/not-found.tsx
@@ -0,0 +1,230 @@
+"use client";
+
+import Navbar from "@/components/static/Navbar";
+import { motion } from "framer-motion";
+import Link from "next/link";
+import { useEffect, useState } from "react";
+import { IoHome, IoArrowBack, IoCodeSlash } from "react-icons/io5";
+
+export default function NotFoundPage() {
+ const [mounted, setMounted] = useState(false);
+
+ // Random error codes for the tech aesthetic
+ const errorCodes = [
+ "ERR_404_PAGE_NOT_FOUND",
+ "EXCEPTION: NavigationError",
+ "HTTP 404 Not Found",
+ "ResourceNotFoundError",
+ "UnresolvedPathError"
+ ];
+
+ const [errorCode] = useState(() =>
+ errorCodes[Math.floor(Math.random() * errorCodes.length)]
+ );
+
+ // Ensure animations run after mount
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
+ if (!mounted) return null;
+
+ return (
+ <>
+
+ {/* Background grid effect */}
+
+
+ {/* Decorative elements positioned with precise z-index */}
+
+ {/* Large 404 background text */}
+
+ 404
+
+
+ {/* Code brackets - properly spaced */}
+
+ {'<'}
+
+
+
+ {'>'}
+
+
+ {/* Floating "error" elements - properly positioned */}
+
+
+ {errorCode}
+
+
+
+
+
+ {' '}
+
+
+
+
+ {/* Main content - higher z-index to ensure it's above the background */}
+
+
+
+
+ Error 404
+
+
+
+
+ Page Not Found
+
+
+
+ Looks like you've ventured into uncharted territory. The page you're looking for might have been moved, deleted, or never existed in the first place.
+
+
+
+
+
+
+ Back to Home
+
+
+
+
+ window.history.back()}
+ className="btn-secondary flex items-center"
+ >
+
+ Go Back
+
+
+
+ {/* Console-style error message with reduced height */}
+
+
+
+
+
+ $
+ find /path/to/page
+
+
Error: No such file or directory
+
+ $
+ cat error.log
+
+
+
+
+
+ $
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+// Typing animation effect - simplified for better performance
+function TypingEffect({ text, delay = 50, startDelay = 0 }) {
+ const [displayedText, setDisplayedText] = useState("");
+
+ useEffect(() => {
+ let timer;
+ let currentIndex = 0;
+
+ const startTyping = () => {
+ timer = setInterval(() => {
+ if (currentIndex < text.length) {
+ setDisplayedText(text.substring(0, currentIndex + 1));
+ currentIndex++;
+ } else {
+ clearInterval(timer);
+ }
+ }, delay);
+ };
+
+ const initialDelay = setTimeout(startTyping, startDelay);
+
+ return () => {
+ clearTimeout(initialDelay);
+ clearInterval(timer);
+ };
+ }, [text, delay, startDelay]);
+
+ return {displayedText} ;
+}
\ No newline at end of file
diff --git a/src/app/page.tsx b/src/app/(main)/page.tsx
similarity index 100%
rename from src/app/page.tsx
rename to src/app/(main)/page.tsx
diff --git a/src/app/(main)/projects/[id]/page.tsx b/src/app/(main)/projects/[id]/page.tsx
new file mode 100644
index 0000000..5f9fdea
--- /dev/null
+++ b/src/app/(main)/projects/[id]/page.tsx
@@ -0,0 +1,47 @@
+import { getProjectById, getAllProjectIds } from '@/data/projectsData';
+import { notFound } from 'next/navigation';
+import ProjectDetail from '@/components/projects/ProjectDetail';
+
+// Generate static params for all project IDs
+export async function generateStaticParams() {
+ const ids = await getAllProjectIds();
+
+ return ids.map(id => ({
+ id: id
+ }));
+}
+
+// Generate metadata for each project
+export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) {
+ const { id } = await params;
+ const project = await getProjectById(id);
+
+ if (!project) {
+ return {
+ title: 'Project Not Found',
+ description: 'The requested project could not be found.'
+ };
+ }
+
+ return {
+ title: project.title,
+ description: project.description,
+ openGraph: {
+ title: project.title,
+ description: project.description,
+ images: project.images[0] ? [project.images[0]] : [],
+ type: 'website',
+ },
+ };
+}
+
+export default async function ProjectPage({ params }: { params: Promise<{ id: string }> }) {
+ const { id } = await params;
+ const project = await getProjectById(id);
+
+ if (!project) {
+ notFound();
+ }
+
+ return ;
+}
diff --git a/src/app/(main)/projects/page.tsx b/src/app/(main)/projects/page.tsx
new file mode 100644
index 0000000..46e09c3
--- /dev/null
+++ b/src/app/(main)/projects/page.tsx
@@ -0,0 +1,19 @@
+import { getAllProjects, getAllProjectTags } from '@/data/projectsData';
+import ProjectsContent from '@/components/projects/ProjectsContent';
+
+export const metadata = {
+ title: 'Projects',
+ description: 'Explore my portfolio of web development and software engineering projects.',
+ openGraph: {
+ title: 'Projects',
+ description: 'Explore my portfolio of web development and software engineering projects.',
+ type: 'website',
+ },
+};
+
+export default function ProjectsPage() {
+ const projects = getAllProjects();
+ const allTags = getAllProjectTags();
+
+ return ;
+}
diff --git a/src/app/(main)/referrals/page.tsx b/src/app/(main)/referrals/page.tsx
new file mode 100644
index 0000000..0b5309b
--- /dev/null
+++ b/src/app/(main)/referrals/page.tsx
@@ -0,0 +1,20 @@
+import { Metadata } from 'next';
+import ReferralsContent from '@/components/layouts/referrals/ReferralsContent';
+import { getAllReferrals, getAllCategories } from '@/data/referralsData';
+
+export const metadata: Metadata = {
+ title: 'Referrals & Offers | CodeMeAPixel',
+ description: 'Exclusive discount codes and referral links for services and products I recommend.',
+ openGraph: {
+ title: 'Referrals & Offers | CodeMeAPixel',
+ description: 'Exclusive discount codes and referral links for services and products I recommend.',
+ type: 'website',
+ },
+};
+
+export default function ReferralsPage() {
+ const referrals = getAllReferrals();
+ const categories = getAllCategories();
+
+ return ;
+}
diff --git a/src/app/(main)/skills/page.tsx b/src/app/(main)/skills/page.tsx
new file mode 100644
index 0000000..18bc7df
--- /dev/null
+++ b/src/app/(main)/skills/page.tsx
@@ -0,0 +1,16 @@
+import SkillsContent from "@/components/layouts/skills/SkillsContent";
+import { getAllSkillCategories } from "@/data/skillsData";
+
+export const metadata = {
+ title: "Skills & Technologies",
+ description: "Explore my technical skills and expertise in various technologies, frameworks, and tools.",
+ openGraph: {
+ title: "Skills & Technologies | CodeMeAPixel",
+ description: "Explore my technical skills and expertise in various technologies, frameworks, and tools.",
+ },
+};
+
+export default function SkillsPage() {
+ const skillCategories = getAllSkillCategories();
+ return ;
+}
diff --git a/src/app/coming-soon/page.tsx b/src/app/coming-soon/page.tsx
new file mode 100644
index 0000000..095fcd2
--- /dev/null
+++ b/src/app/coming-soon/page.tsx
@@ -0,0 +1,83 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { useSearchParams, useRouter } from "next/navigation";
+import Navbar from "@/components/static/Navbar";
+import ComingSoon from "@/components/ui/ComingSoon";
+import { getRouteInfo } from "@/lib/routes";
+
+export default function ComingSoonPage() {
+ const searchParams = useSearchParams();
+ const router = useRouter();
+ const [title, setTitle] = useState("Coming Soon");
+ const [description, setDescription] = useState("We're working hard to finish the development of this page. Stay tuned for something amazing!");
+ const [completionPercentage, setCompletionPercentage] = useState(45);
+ const [mounted, setMounted] = useState(false);
+ const [isAuthorized, setIsAuthorized] = useState(false);
+
+ useEffect(() => {
+ // Verify this page was accessed through proper channels
+ const token = searchParams.get('token');
+ const fromPath = searchParams.get('from');
+
+ // If we don't have both token and fromPath, this is likely a direct access
+ if (!token || !fromPath) {
+ console.log("Unauthorized direct access to coming-soon page");
+ router.replace('/');
+ return;
+ }
+
+ // Otherwise, we'll let the middleware handle validation
+ setIsAuthorized(true);
+ setMounted(true);
+
+ // Get route info from our centralized route configuration
+ const routeInfo = getRouteInfo(fromPath);
+
+ if (routeInfo) {
+ setTitle(routeInfo.title);
+ setDescription(routeInfo.description);
+ setCompletionPercentage(routeInfo.completionPercentage);
+ }
+ }, [searchParams, router]);
+
+ // Don't render anything while checking authorization or if unauthorized
+ if (!mounted || !isAuthorized) return null;
+
+ // Parse the launch date from query parameter, or use default (30 days from now)
+ const launchDateParam = searchParams.get('launchDate');
+ let launchDate = new Date();
+ launchDate.setDate(launchDate.getDate() + 30);
+
+ if (launchDateParam) {
+ try {
+ const parsedDate = new Date(launchDateParam);
+ if (!isNaN(parsedDate.getTime())) {
+ launchDate = parsedDate;
+ }
+ } catch (e) {
+ console.error("Error parsing launch date:", e);
+ }
+ }
+
+ // Parse notification preference
+ const showNotification = searchParams.get('notify') !== 'false';
+
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/src/app/docs/getting-started/page.tsx b/src/app/docs/getting-started/page.tsx
new file mode 100644
index 0000000..33c149e
--- /dev/null
+++ b/src/app/docs/getting-started/page.tsx
@@ -0,0 +1,145 @@
+import DocsContent from "@/components/docs/DocsContent";
+import TableOfContents from "@/components/docs/TableOfContents";
+import { IoTerminal, IoWarning, IoInformationCircle } from "react-icons/io5";
+
+export const metadata = {
+ title: "Introduction to Documentation",
+ description: "Learn about the purpose and structure of this documentation.",
+};
+
+export default function IntroductionPage() {
+ const meta = {
+ title: "Introduction to Documentation",
+ description: "Learn about the purpose and structure of this documentation.",
+ lastUpdated: "January 15, 2024",
+ readingTime: "3 min read",
+ authors: [
+ { name: "CodeMeAPixel", url: "https://codemeapixel.dev" }
+ ]
+ };
+
+ const nextDoc = {
+ title: "Quick Start Guide",
+ href: "/docs/getting-started/quick-start"
+ };
+
+ return (
+ }>
+ Overview
+
+ Welcome to the documentation hub! This comprehensive resource is designed to help you understand the
+ architecture, components, and best practices for using our platform. Whether you're a developer,
+ designer, or product manager, you'll find valuable information to enhance your workflow.
+
+
+
+
+
+
+
Tip
+
+ Use the search functionality (Ctrl+K or Cmd+K) to quickly find specific topics across all documentation.
+
+
+
+
+
+ Documentation Structure
+
+ The documentation is organized into several main sections to help you find what you need:
+
+
+
+ Getting Started - Introduction, installation, and quick start guides
+ Frontend Development - Components, styling, and UI patterns
+ Backend Development - API references, authentication, and data models
+ Deployment - Build processes, hosting options, and CI/CD pipelines
+
+
+ Code Examples
+
+ Throughout the documentation, you'll find code examples to help illustrate concepts.
+ Here's a simple example of a React component:
+
+
+
+ {`import React from 'react';
+
+function Button({ children, variant = 'primary' }) {
+ return (
+
+ {children}
+
+ );
+}
+
+export default Button;`}
+
+
+ Terminal Commands
+
+ When terminal commands are needed, they'll be presented in a clear format:
+
+
+
+
+
+
+ $
+ npm install @codemeapixel/ui-library
+
+
+
+
+ Warnings and Notes
+
+ Important information will be highlighted using note and warning blocks:
+
+
+
+
+
+
+
Warning
+
+ Always backup your data before performing upgrades or making significant changes to your configuration.
+
+
+
+
+
+ Contributing to Documentation
+
+ This documentation is continuously improved, and we welcome contributions from the community.
+ If you notice errors, have suggestions for improvements, or want to contribute new content,
+ please refer to our contribution guidelines.
+
+
+ Getting Help
+
+ If you can't find what you're looking for in the documentation, there are several ways to get help:
+
+
+
+ Submit an issue in our GitHub repository
+ Join our community Discord server
+ Reach out through the contact form on the website
+
+
+ Next Steps
+
+ Now that you're familiar with the documentation structure, you can proceed to the Quick Start Guide to
+ begin working with our platform or explore specific topics that interest you.
+
+
+ );
+}
diff --git a/src/app/docs/getting-started/quick-start/page.tsx b/src/app/docs/getting-started/quick-start/page.tsx
new file mode 100644
index 0000000..8325c7c
--- /dev/null
+++ b/src/app/docs/getting-started/quick-start/page.tsx
@@ -0,0 +1,228 @@
+import DocsContent from "@/components/docs/DocsContent";
+import TableOfContents from "@/components/docs/TableOfContents";
+import { IoRocket, IoCheckmarkCircle } from "react-icons/io5";
+
+export const metadata = {
+ title: "Quick Start Guide",
+ description: "Get up and running quickly with our starter templates.",
+};
+
+export default function QuickStartPage() {
+ const meta = {
+ title: "Quick Start Guide",
+ description: "Get up and running quickly with our starter templates.",
+ lastUpdated: "January 18, 2024",
+ readingTime: "5 min read",
+ authors: [
+ { name: "CodeMeAPixel", url: "https://codemeapixel.dev" }
+ ]
+ };
+
+ const prevDoc = {
+ title: "Introduction",
+ href: "/docs/getting-started"
+ };
+
+ const nextDoc = {
+ title: "Installation",
+ href: "/docs/getting-started/installation"
+ };
+
+ return (
+ }
+ >
+ Quick Setup
+
+ This guide will help you set up a new project in just a few minutes.
+ Follow these steps to create a basic application with our recommended configuration.
+
+
+ Prerequisites
+ Before you begin, make sure you have the following installed:
+
+ Node.js (v16 or higher)
+ npm (v7 or higher) or Yarn
+ Git
+
+
+ Starter Templates
+
+ We provide several starter templates to help you get up and running quickly.
+ Choose the template that best fits your project requirements:
+
+
+
+
+
+
+ A minimal setup with essential components and configuration.
+ Perfect for small projects or prototypes.
+
+
+ npx create-app@latest --template basic
+
+
+
+
+
+
+ Complete setup with frontend, backend, and database configuration.
+ Ideal for production applications.
+
+
+ npx create-app@latest --template fullstack
+
+
+
+
+ Step-by-Step Setup
+
+ Follow these steps to create a new project using our CLI tool:
+
+
+
+
+ 1
+
+
Create a new project
+
Run the following command to create a new project:
+
+ npx create-app@latest my-awesome-project
+
+
+
+
+
+ 2
+
+
Navigate to the project directory
+
+ cd my-awesome-project
+
+
+
+
+
+ 3
+
+
Install dependencies
+
+ npm install
+ # or
+ yarn
+
+
+
+
+
+ 4
+
+
Start the development server
+
+ npm run dev
+ # or
+ yarn dev
+
+
+
+
+
+ Project Structure
+
+ After setting up your project, you'll have a directory structure similar to this:
+
+
+
+ {`my-awesome-project/
+├── public/ # Static assets
+├── src/
+│ ├── components/ # Reusable UI components
+│ ├── pages/ # Application pages
+│ ├── styles/ # CSS and style definitions
+│ ├── utils/ # Utility functions
+│ ├── App.tsx # Main application component
+│ └── index.tsx # Application entry point
+├── .env # Environment variables
+├── package.json # Project dependencies
+└── tsconfig.json # TypeScript configuration`}
+
+
+ Key Features
+
+ Our starter templates come with several key features to help you build robust applications:
+
+
+
+
+
+
+
TypeScript Support
+
+ Full TypeScript support with pre-configured settings for better type safety.
+
+
+
+
+
+
+
+
Component Library
+
+ Pre-built UI components that follow design best practices.
+
+
+
+
+
+
+
+
Responsive Layouts
+
+ Mobile-first responsive design with Tailwind CSS.
+
+
+
+
+
+
+
+
Authentication
+
+ Ready-to-use authentication system with multiple providers.
+
+
+
+
+
+ What's Next?
+
+ Now that you have your project set up, you can:
+
+
+
+
+
+ For a more detailed installation guide, proceed to the Installation section.
+
+
+ );
+}
diff --git a/src/app/docs/layout.tsx b/src/app/docs/layout.tsx
new file mode 100644
index 0000000..713f468
--- /dev/null
+++ b/src/app/docs/layout.tsx
@@ -0,0 +1,160 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import Link from "next/link";
+import { usePathname } from "next/navigation";
+import { motion } from "framer-motion";
+import { IoSearch, IoMenu, IoClose, IoChevronForward } from "react-icons/io5";
+import DocsSidebar from "@/components/docs/DocsSidebar";
+import DocsSearch from "@/components/docs/DocsSearch";
+import { docsConfig } from "@/config/docs";
+
+export default function DocsLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ const [isSearchOpen, setIsSearchOpen] = useState(false);
+ const [isSidebarOpen, setIsSidebarOpen] = useState(false);
+ const pathname = usePathname();
+
+ // Close sidebar on route change on mobile
+ useEffect(() => {
+ setIsSidebarOpen(false);
+ }, [pathname]);
+
+ // Handle keyboard shortcuts
+ useEffect(() => {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ // Ctrl/Cmd + K to open search
+ if ((e.ctrlKey || e.metaKey) && e.key === "k") {
+ e.preventDefault();
+ setIsSearchOpen(true);
+ }
+ // Escape to close search
+ if (e.key === "Escape") {
+ setIsSearchOpen(false);
+ }
+ };
+
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, []);
+
+ // Get current section and breadcrumb data
+ const currentPath = pathname.split('/').filter(Boolean);
+ const currentSectionSlug = currentPath[1] || 'getting-started';
+ const currentSection = docsConfig.sections.find(section => section.slug === currentSectionSlug);
+
+ // Build breadcrumb items
+ const breadcrumbs = [
+ { label: 'Docs', href: '/docs' }
+ ];
+
+ if (currentSection && currentPath.length > 1) {
+ breadcrumbs.push({
+ label: currentSection.name,
+ href: `/docs/${currentSection.slug}`
+ });
+
+ if (currentPath.length > 2) {
+ const currentPageCategory = currentSection.categories.find(category =>
+ category.items.some(item => item.href === pathname)
+ );
+
+ const currentPageItem = currentPageCategory?.items.find(item => item.href === pathname);
+
+ if (currentPageItem) {
+ breadcrumbs.push({
+ label: currentPageItem.title,
+ href: pathname
+ });
+ }
+ }
+ }
+
+ return (
+
+ {/* Mobile sidebar toggle */}
+
+ setIsSidebarOpen(!isSidebarOpen)}
+ className="btn-icon w-10 h-10"
+ aria-label="Toggle sidebar"
+ >
+ {isSidebarOpen ? (
+
+ ) : (
+
+ )}
+
+
+
+ {/* Documentation layout with sidebar */}
+
+ {/* Sidebar - visible on desktop, conditionally visible on mobile */}
+
+
+ {/* Main content */}
+
+ {/* Breadcrumb and search bar */}
+
+
+ {breadcrumbs.map((crumb, index) => (
+
+ {index > 0 && (
+
+ )}
+ {index === breadcrumbs.length - 1 ? (
+ {crumb.label}
+ ) : (
+
+ {crumb.label}
+
+ )}
+
+ ))}
+
+
setIsSearchOpen(true)}
+ className="flex items-center gap-2 px-3 py-1.5 text-sm text-color-text-muted bg-card rounded-lg border border-color-border hover:border-primary-700/50 transition-colors"
+ >
+
+ Search docs...
+
+ ⌘K
+
+
+
+
+ {/* Documentation content */}
+
+
+ {children}
+
+
+
+
+
+ {/* Search modal */}
+ {isSearchOpen && (
+
setIsSearchOpen(false)} />
+ )}
+
+ );
+}
diff --git a/src/app/docs/page.tsx b/src/app/docs/page.tsx
new file mode 100644
index 0000000..40d44a5
--- /dev/null
+++ b/src/app/docs/page.tsx
@@ -0,0 +1,194 @@
+"use client"
+
+import { docsConfig } from "@/config/docs";
+import Link from "next/link";
+import { motion } from "framer-motion";
+import { IoArrowForward, IoBookmark, IoSearch, IoRocket, IoApps } from "react-icons/io5";
+
+export default function DocsPage() {
+ return (
+
+
+
Documentation
+
+ Comprehensive guides and resources to help you build amazing applications.
+
+
+ {/* Search box */}
+
+
+
+ {/* Featured section */}
+
+
+
+
+ New
+
+
Get Started With Our Platform
+
+ Everything you need to know to get up and running with our platform quickly. Follow our step-by-step guide to build your first application.
+
+
+
+
Quick Start Guide
+
+
+
+
+
+
+ {/* Documentation sections */}
+
Browse Documentation
+
+
+ {docsConfig.sections.map((section, index) => {
+ return (
+
+
+
+
+
+
{section.name}
+
{section.description}
+
+
+ {section.categories.map((category) => (
+
+
{category.title}
+
+ {category.items.slice(0, 2).map((item) => {
+ const Icon = item.icon;
+ return (
+
+
+ {Icon && }
+ {item.title}
+
+
+ );
+ })}
+
+
+ ))}
+
+
+
+
+ View all {section.name} docs
+
+
+
+
+
+ );
+ })}
+
+
+ {/* Popular guides */}
+
+
Popular Guides
+
+ {[
+ {
+ title: "Authentication Guide",
+ description: "Learn how to implement authentication in your application",
+ href: "/docs/backend/auth",
+ icon: docsConfig.sections[2].categories[0].items[1].icon
+ },
+ {
+ title: "Component Library",
+ description: "Explore the UI component library for faster development",
+ href: "/docs/frontend/components",
+ icon: docsConfig.sections[1].categories[0].items[0].icon
+ },
+ {
+ title: "Deployment Guide",
+ description: "Learn how to deploy your application to production",
+ href: "/docs/deployment/build",
+ icon: docsConfig.sections[3].categories[0].items[0].icon
+ },
+ {
+ title: "State Management",
+ description: "Best practices for managing application state",
+ href: "/docs/frontend/state",
+ icon: docsConfig.sections[1].categories[1].items[0].icon
+ }
+ ].map((guide, index) => {
+ const Icon = guide.icon;
+ return (
+
+
+ {Icon &&
}
+
+
{guide.title}
+
{guide.description}
+
+
+
+ );
+ })}
+
+
+
+ {/* Help & support */}
+
+
+
+
+
+
+
Need Help?
+
+ Can't find what you're looking for or have questions? Our support team is ready to help.
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/error.tsx b/src/app/error.tsx
new file mode 100644
index 0000000..16204b3
--- /dev/null
+++ b/src/app/error.tsx
@@ -0,0 +1,193 @@
+"use client";
+
+import { motion } from "framer-motion";
+import Link from "next/link";
+import { IoHome, IoReload, IoClose } from "react-icons/io5";
+import { useEffect, useState } from "react";
+import Navbar from "@/components/static/Navbar";
+
+export default function ErrorPage({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ const [errorMessage, setErrorMessage] = useState("");
+ const [errorId, setErrorId] = useState("");
+ const [mounted, setMounted] = useState(false);
+
+ useEffect(() => {
+ // Set a clean error message for display
+ const cleanMessage = error.message || "An unexpected error occurred";
+ setErrorMessage(cleanMessage.replace(/^Error:\s*/i, ""));
+
+ // Use the error digest or generate a random ID
+ setErrorId(error.digest || `ERR-${Math.random().toString(36).substring(2, 10).toUpperCase()}`);
+
+ // Log the error to console in development
+ if (process.env.NODE_ENV === "development") {
+ console.error("Application error:", error);
+ }
+
+ setMounted(true);
+ }, [error]);
+
+ if (!mounted) return null;
+
+ return (
+ <>
+
+
+ {/* Background grid effect */}
+
+
+ {/* Decorative elements */}
+
+
+ Error
+
+
+
+
+ {errorId}
+
+
+
+
+ {/* Main content */}
+
+
+
+
+ Something went wrong
+
+
+
+
+ Application Error
+
+
+
+ Whoops, it seems something went wrong while processing your request. This could be due to a temporary issue or an unexpected error in the application.
+
+
+
+ reset()}
+ className="btn-primary group relative overflow-hidden"
+ >
+
+
+ Try Again
+
+
+
+
+
+
+ Back to Home
+
+
+
+ {/* Error details console */}
+
+
+
+
+
+ Error ID:
+ {errorId}
+
+
+ Message:
+ {errorMessage}
+
+
+ Location:
+ {typeof window !== 'undefined' ? window.location.pathname : ''}
+
+
+ $
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+// Typewriter effect component
+function TypewriterEffect({ text, delay = 50 }) {
+ const [displayText, setDisplayText] = useState("");
+
+ useEffect(() => {
+ let index = 0;
+ const timer = setInterval(() => {
+ if (index < text.length) {
+ setDisplayText((prev) => prev + text.charAt(index));
+ index++;
+ } else {
+ clearInterval(timer);
+ }
+ }, delay);
+
+ return () => clearInterval(timer);
+ }, [text, delay]);
+
+ return {displayText} ;
+}
diff --git a/src/app/globals.css b/src/app/globals.css
deleted file mode 100644
index 99b2292..0000000
--- a/src/app/globals.css
+++ /dev/null
@@ -1,409 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-@layer base {
- :root,
- [data-theme="blue"] {
- /* Primary colors - Vibrant blue */
- --color-primary-50: 239, 246, 255;
- --color-primary-100: 219, 234, 254;
- --color-primary-200: 191, 219, 254;
- --color-primary-300: 147, 197, 253;
- --color-primary-400: 96, 165, 250;
- --color-primary-500: 59, 130, 246;
- --color-primary-600: 37, 99, 235;
- --color-primary-700: 29, 78, 216;
- --color-primary-800: 30, 64, 175;
- --color-primary-900: 30, 58, 138;
-
- /* Modern, sleek blue theme */
- --color-bg: 15, 23, 42;
- --color-bg-alt: 30, 41, 59;
- --color-bg-accent: 17, 25, 45;
- --color-card: 22, 31, 49;
- --color-card-alt: 25, 39, 67;
- --color-text: 255, 255, 255;
- --color-text-muted: 203, 213, 225;
- --color-border: 51, 65, 85;
- --color-glow: 37, 99, 235;
- }
-
- [data-theme="purple"] {
- /* Primary colors - Vibrant purple */
- --color-primary-50: 250, 245, 255;
- --color-primary-100: 243, 232, 255;
- --color-primary-200: 233, 213, 255;
- --color-primary-300: 216, 180, 254;
- --color-primary-400: 192, 132, 252;
- --color-primary-500: 168, 85, 247;
- --color-primary-600: 147, 51, 234;
- --color-primary-700: 126, 34, 206;
- --color-primary-800: 107, 33, 168;
- --color-primary-900: 88, 28, 135;
-
- /* Modern, sleek purple theme */
- --color-bg: 30, 20, 50;
- --color-bg-alt: 45, 30, 75;
- --color-bg-accent: 35, 25, 60;
- --color-card: 40, 30, 65;
- --color-card-alt: 55, 40, 85;
- --color-text: 255, 255, 255;
- --color-text-muted: 214, 203, 225;
- --color-border: 70, 55, 95;
- --color-glow: 147, 51, 234;
- }
-
- [data-theme="teal"] {
- /* Primary colors - Vibrant teal */
- --color-primary-50: 240, 253, 250;
- --color-primary-100: 204, 251, 241;
- --color-primary-200: 153, 246, 228;
- --color-primary-300: 94, 234, 212;
- --color-primary-400: 45, 212, 191;
- --color-primary-500: 20, 184, 166;
- --color-primary-600: 13, 148, 136;
- --color-primary-700: 15, 118, 110;
- --color-primary-800: 17, 94, 89;
- --color-primary-900: 19, 78, 74;
-
- /* Modern, sleek teal theme */
- --color-bg: 15, 32, 32;
- --color-bg-alt: 22, 48, 48;
- --color-bg-accent: 17, 40, 40;
- --color-card: 24, 45, 45;
- --color-card-alt: 30, 58, 58;
- --color-text: 255, 255, 255;
- --color-text-muted: 203, 225, 220;
- --color-border: 45, 85, 85;
- --color-glow: 13, 148, 136;
- }
-
- [data-theme="rose"] {
- /* Primary colors - Vibrant rose */
- --color-primary-50: 255, 241, 242;
- --color-primary-100: 255, 228, 230;
- --color-primary-200: 254, 205, 211;
- --color-primary-300: 253, 164, 175;
- --color-primary-400: 251, 113, 133;
- --color-primary-500: 244, 63, 94;
- --color-primary-600: 225, 29, 72;
- --color-primary-700: 190, 18, 60;
- --color-primary-800: 159, 18, 57;
- --color-primary-900: 136, 19, 55;
-
- /* Modern, sleek rose theme */
- --color-bg: 28, 15, 20;
- --color-bg-alt: 47, 23, 32;
- --color-bg-accent: 38, 17, 24;
- --color-card: 43, 22, 29;
- --color-card-alt: 62, 30, 42;
- --color-text: 255, 255, 255;
- --color-text-muted: 225, 205, 210;
- --color-border: 85, 45, 60;
- --color-glow: 225, 29, 72;
- }
-
- [data-theme="amber"] {
- /* Primary colors - Vibrant amber */
- --color-primary-50: 255, 251, 235;
- --color-primary-100: 254, 243, 199;
- --color-primary-200: 253, 230, 138;
- --color-primary-300: 252, 211, 77;
- --color-primary-400: 251, 191, 36;
- --color-primary-500: 245, 158, 11;
- --color-primary-600: 217, 119, 6;
- --color-primary-700: 180, 83, 9;
- --color-primary-800: 146, 64, 14;
- --color-primary-900: 120, 53, 15;
-
- /* Modern, sleek amber theme */
- --color-bg: 28, 25, 15;
- --color-bg-alt: 44, 38, 20;
- --color-bg-accent: 35, 30, 15;
- --color-card: 42, 35, 18;
- --color-card-alt: 58, 45, 22;
- --color-text: 255, 255, 255;
- --color-text-muted: 225, 218, 200;
- --color-border: 85, 70, 40;
- --color-glow: 217, 119, 6;
- }
-}
-
-/* Apply modern, sleek background styles */
-html, body {
- color: rgb(var(--color-text));
- background-color: rgb(var(--color-bg));
- min-height: 100vh;
- scroll-behavior: smooth;
-}
-
-/* Pixelated background effect */
-.pixel-bg {
- position: relative;
- overflow: hidden;
-}
-
-.pixel-bg::before {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-image:
- linear-gradient(90deg, rgba(var(--color-primary-400), 0.2) 1px, transparent 1px),
- linear-gradient(rgba(var(--color-primary-400), 0.2) 1px, transparent 1px);
- background-size: 12px 12px;
- background-position: center;
- opacity: 1;
- z-index: 0;
- pointer-events: none;
- animation: pixelGrid 4s linear infinite;
-}
-
-.pixel-bg::after {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-image:
- linear-gradient(90deg, rgba(var(--color-primary-300), 0.1) 1px, transparent 1px),
- linear-gradient(rgba(var(--color-primary-300), 0.1) 1px, transparent 1px);
- background-size: 12px 12px;
- background-position: 6px 6px;
- opacity: 1;
- z-index: 0;
- pointer-events: none;
- animation: pixelGrid 4s linear infinite reverse;
-}
-
-@keyframes pixelGrid {
- 0% {
- background-position: 0 0;
- }
- 100% {
- background-position: 12px 12px;
- }
-}
-
-/* Custom animations */
-@keyframes float {
- 0% { transform: translateY(0px); }
- 50% { transform: translateY(-10px); }
- 100% { transform: translateY(0px); }
-}
-
-@keyframes pulse-glow {
- 0%, 100% { box-shadow: 0 0 15px 2px rgba(var(--color-glow), 0.3); }
- 50% { box-shadow: 0 0 25px 8px rgba(var(--color-glow), 0.5); }
-}
-
-@keyframes gradient-shift {
- 0% { background-position: 0% 50%; }
- 50% { background-position: 100% 50%; }
- 100% { background-position: 0% 50%; }
-}
-
-@keyframes fade-up {
- 0% { opacity: 0; transform: translateY(20px); }
- 100% { opacity: 1; transform: translateY(0); }
-}
-
-.animate-float {
- animation: float 3s ease-in-out infinite;
-}
-
-.animate-pulse-glow {
- animation: pulse-glow 3s ease-in-out infinite;
-}
-
-.animate-gradient {
- animation: gradient-shift 5s ease infinite;
- background-size: 200% 200%;
-}
-
-.animate-fade-up {
- animation: fade-up 0.6s ease-out forwards;
-}
-
-.animate-pixelGrid {
- animation: pixelGrid 4s linear infinite;
-}
-
-/* Custom components */
-@layer components {
- .btn-primary {
- @apply px-6 py-3 rounded-lg bg-primary-600 text-white font-medium transition-all
- relative overflow-hidden hover:bg-primary-700
- shadow-[0_0_15px_rgba(var(--color-glow),0.3)]
- hover:shadow-[0_0_25px_rgba(var(--color-glow),0.5)]
- focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 focus:ring-offset-bg;
- }
-
- .btn-secondary {
- @apply px-6 py-3 rounded-lg text-primary-400 font-medium
- border border-primary-800/50 hover:border-primary-600
- bg-bg-alt/50 backdrop-blur-sm
- hover:bg-bg-alt transition-all
- focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 focus:ring-offset-bg;
- }
-
- .btn-icon {
- @apply p-2 rounded-full bg-bg-alt/80 backdrop-blur-sm hover:bg-bg-alt
- text-primary-400 hover:text-primary-300
- transition-all border border-primary-800/30 hover:border-primary-700/50
- focus:outline-none focus:ring-2 focus:ring-primary-500;
- }
-
- .container-section {
- @apply max-w-6xl mx-auto px-4 sm:px-8 py-24 relative z-10;
- }
-
- .card {
- @apply p-6 rounded-xl bg-card border border-color-border shadow-lg
- backdrop-blur-sm relative z-10
- hover:shadow-xl hover:shadow-primary-900/5 transition-all;
- }
-
- .card-highlight {
- @apply rounded-xl bg-card-alt/50 border border-primary-500/50
- shadow-lg shadow-primary-900/50 relative z-10
- hover:shadow-xl hover:shadow-primary-800/20 transition-all
- backdrop-blur-sm w-72 h-72 md:w-96 md:h-96;
- }
-
- .tag {
- @apply px-3 py-1 rounded-full text-sm font-medium
- bg-primary-900/40 text-primary-300
- border border-primary-700/50 backdrop-blur-sm;
- }
-
- .navbar {
- @apply fixed top-0 left-0 right-0 z-50 transition-all duration-300
- backdrop-blur-md;
- }
-
- .navbar-scrolled {
- @apply bg-bg/80 backdrop-blur-xl border-b border-color-border;
- }
-
- .navbar-container {
- @apply max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between;
- }
-
- .navbar-link {
- @apply relative px-1 py-2 text-color-text-muted hover:text-primary-400
- transition-colors font-medium;
- }
-
- .navbar-link-active {
- @apply text-primary-400;
- }
-
- .navbar-link::after {
- @apply content-[''] absolute -bottom-1 left-0 w-0 h-[2px]
- bg-gradient-to-r from-primary-600 to-primary-400
- transition-all duration-300 rounded-full;
- }
-
- .navbar-link:hover::after, .navbar-link-active::after {
- @apply w-full;
- }
-
- .section-bg-accent {
- @apply bg-[rgb(var(--color-bg-accent))];
- }
-
- .heading-primary {
- @apply text-4xl sm:text-5xl font-bold mb-8
- bg-gradient-to-r from-primary-400 to-primary-300
- bg-clip-text text-transparent;
- }
-
- .heading-secondary {
- @apply text-2xl sm:text-3xl font-bold mb-4 text-primary-300;
- }
-
- .gradient-text {
- @apply bg-gradient-to-r from-primary-400 to-primary-300 bg-clip-text text-transparent;
- }
-
- .gradient-border {
- @apply relative;
- }
-
- .gradient-border::before {
- @apply content-[''] absolute inset-0 rounded-xl p-[1.5px] -z-10
- bg-gradient-to-r from-primary-600 to-primary-400
- mask-gradient-border opacity-50;
- }
-
- .mask-gradient-border {
- mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
- -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
- -webkit-mask-composite: xor;
- mask-composite: exclude;
- }
-
- .glass-effect {
- @apply backdrop-blur-lg bg-opacity-20 border border-white/10 shadow-lg;
- }
-
- .neon-glow {
- box-shadow: 0 0 15px rgba(var(--color-glow), 0.5);
- }
-
- .text-balance {
- text-wrap: balance;
- }
-}
-
-/* Custom utilities */
-@layer utilities {
- .grid-auto-fit {
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
- }
-
- .bg-mesh-gradient {
- background-image:
- radial-gradient(at 40% 20%, rgba(var(--color-primary-600), 0.15) 0px, transparent 50%),
- radial-gradient(at 80% 70%, rgba(var(--color-primary-700), 0.2) 0px, transparent 50%),
- radial-gradient(at 10% 80%, rgba(var(--color-primary-800), 0.1) 0px, transparent 50%);
- background-attachment: fixed;
- }
-
- .text-shadow-sm {
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
- }
-
- .blur-behind {
- backdrop-filter: blur(8px);
- }
-
- .text-gradient {
- background-clip: text;
- -webkit-background-clip: text;
- color: transparent;
- }
-
- .scrollbar-hidden {
- -ms-overflow-style: none;
- scrollbar-width: none;
- }
-
- .scrollbar-hidden::-webkit-scrollbar {
- display: none;
- }
-}
-
-.loader-content {
- opacity: 0;
- transition: opacity 0.3s ease-in-out;
-}
-
-.loader-content.loaded {
- opacity: 1;
-}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index c225928..6751513 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,15 +1,62 @@
+import "../styles/globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
-import "./globals.css";
import { ThemeProvider } from "@/context/ThemeContext";
-import Navbar from "@/components/static/Navbar";
import Loader from "@/components/static/Loader";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
- title: "CodeMeAPixel - Portfolio",
- description: "Personal portfolio website showcasing my projects and skills",
+ title: {
+ default: "CodeMeAPixel | Portfolio",
+ template: "%s | CodeMeAPixel"
+ },
+ description: "I create beautiful and functional web experiences with modern technologies and a passion for design.",
+ applicationName: "CodeMeAPixel | Portfolio",
+ metadataBase: new URL("https://codemeapixel.dev"),
+ openGraph: {
+ siteName: "CodeMeAPixel | Portfolio",
+ description: "I create beautiful and functional web experiences with modern technologies and a passion for design.",
+ images: "/character.png",
+ creators: ["CodeMeAPixel"],
+ locale: "en_US",
+ url: "https://codemeapixel.dev",
+ },
+ twitter: {
+ title: "CodeMeAPixel | Portfolio",
+ description: "I create beautiful and functional web experiences with modern technologies and a passion for design.",
+ images: "/character.png",
+ creator: "@CodeMeAPixel",
+ card: "summary_large_image",
+ images: "/character.png",
+ site: "https://codemeapixel.dev",
+ },
+ appleWebApp: {
+ statusBarStyle: "black-translucent",
+ title: "CodeMeAPixel | Portfolio",
+ },
+ other: {
+ "mobile-we-app-capable": "yes"
+ },
+ formatDetection: {
+ telephone: false
+ },
+ icons: {
+ icon: "/favicon.ico",
+ shortcut: "/favicon.ico",
+ apple: "/apple-touch-icon.png",
+ },
+ robots: {
+ index: true,
+ follow: true,
+ googleBot: {
+ index: true,
+ follow: true,
+ "max-snippet": -1,
+ "max-video-preview": -1,
+ "maxc-image-preview": "large",
+ }
+ }
};
export default function RootLayout({
@@ -45,7 +92,6 @@ export default function RootLayout({
-
{children}
diff --git a/src/app/links/page.tsx b/src/app/links/page.tsx
new file mode 100644
index 0000000..8f277d4
--- /dev/null
+++ b/src/app/links/page.tsx
@@ -0,0 +1,20 @@
+import { Metadata } from 'next';
+import LinkHubContent from '@/components/layouts/links/LinkHubContent';
+import { getProfile, getPlaylist } from '@/data/linksData';
+
+export const metadata: Metadata = {
+ title: 'Links',
+ description: 'Connect with me on various platforms and explore my projects and services.',
+ openGraph: {
+ title: 'Links | CodeMeAPixel',
+ description: 'Connect with me on various platforms and explore my projects and services.',
+ type: 'website',
+ },
+};
+
+export default function LinksPage() {
+ const profile = getProfile();
+ const playlist = getPlaylist();
+
+ return ;
+}
diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx
new file mode 100644
index 0000000..c20c09a
--- /dev/null
+++ b/src/app/not-found.tsx
@@ -0,0 +1,231 @@
+"use client";
+
+import Navbar from "@/components/static/Navbar";
+import { motion } from "framer-motion";
+import Link from "next/link";
+import { useEffect, useState } from "react";
+import { IoHome, IoArrowBack, IoCodeSlash } from "react-icons/io5";
+
+export default function NotFoundPage() {
+ const [mounted, setMounted] = useState(false);
+
+ // Random error codes for the tech aesthetic
+ const errorCodes = [
+ "ERR_404_PAGE_NOT_FOUND",
+ "EXCEPTION: NavigationError",
+ "HTTP 404 Not Found",
+ "ResourceNotFoundError",
+ "UnresolvedPathError"
+ ];
+
+ const [errorCode] = useState(() =>
+ errorCodes[Math.floor(Math.random() * errorCodes.length)]
+ );
+
+ // Ensure animations run after mount
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
+ if (!mounted) return null;
+
+ return (
+ <>
+
+
+ {/* Background grid effect */}
+
+
+ {/* Decorative elements positioned with precise z-index */}
+
+ {/* Large 404 background text */}
+
+ 404
+
+
+ {/* Code brackets - properly spaced */}
+
+ {'<'}
+
+
+
+ {'>'}
+
+
+ {/* Floating "error" elements - properly positioned */}
+
+
+ {errorCode}
+
+
+
+
+
+ {' '}
+
+
+
+
+ {/* Main content - higher z-index to ensure it's above the background */}
+
+
+
+
+ Error 404
+
+
+
+
+ Page Not Found
+
+
+
+ Looks like you've ventured into uncharted territory. The page you're looking for might have been moved, deleted, or never existed in the first place.
+
+
+
+
+
+
+ Back to Home
+
+
+
+
+ window.history.back()}
+ className="btn-secondary flex items-center"
+ >
+
+ Go Back
+
+
+
+ {/* Console-style error message with reduced height */}
+
+
+
+
+
+ $
+ find /path/to/page
+
+
Error: No such file or directory
+
+ $
+ cat error.log
+
+
+
+
+
+ $
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+// Typing animation effect - simplified for better performance
+function TypingEffect({ text, delay = 50, startDelay = 0 }) {
+ const [displayedText, setDisplayedText] = useState("");
+
+ useEffect(() => {
+ let timer;
+ let currentIndex = 0;
+
+ const startTyping = () => {
+ timer = setInterval(() => {
+ if (currentIndex < text.length) {
+ setDisplayedText(text.substring(0, currentIndex + 1));
+ currentIndex++;
+ } else {
+ clearInterval(timer);
+ }
+ }, delay);
+ };
+
+ const initialDelay = setTimeout(startTyping, startDelay);
+
+ return () => {
+ clearTimeout(initialDelay);
+ clearInterval(timer);
+ };
+ }, [text, delay, startDelay]);
+
+ return {displayedText} ;
+}
\ No newline at end of file
diff --git a/src/components/blog/BlogContent.tsx b/src/components/blog/BlogContent.tsx
new file mode 100644
index 0000000..bbd250f
--- /dev/null
+++ b/src/components/blog/BlogContent.tsx
@@ -0,0 +1,821 @@
+"use client";
+
+import Link from 'next/link';
+import { motion, AnimatePresence } from 'framer-motion';
+import type { PostMetadata } from '@/lib/mdx';
+import { calculateReadingTime } from '@/lib/mdx';
+import {
+ IoTimeOutline, IoCalendarOutline, IoArrowForward, IoBookmarkOutline,
+ IoChevronDown, IoGridOutline, IoListOutline, IoSearch, IoClose,
+ IoSwapVertical
+} from 'react-icons/io5';
+import { useRef, useState, useEffect } from 'react';
+import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
+import { BackgroundEffects } from '@/components/ui/BackgroundEffects';
+
+interface BlogContentProps {
+ posts: Array<{ content: string; metadata: PostMetadata }>;
+ categories: string[];
+ tags: string[];
+}
+
+type LayoutType = 'grid' | 'list';
+type SortOption = 'date-desc' | 'date-asc' | 'alphabetical';
+
+export default function BlogContent({ posts, categories, tags }: BlogContentProps) {
+ // State for filtering and display
+ const [hoveredCard, setHoveredCard] = useState(null);
+ const [filterOpen, setFilterOpen] = useState(false);
+ const [sortOpen, setSortOpen] = useState(false);
+ const [activeFilter, setActiveFilter] = useState('All');
+ const [searchQuery, setSearchQuery] = useState('');
+ const [isSearching, setIsSearching] = useState(false);
+ const [debouncedSearchQuery, setDebouncedSearchQuery] = useState('');
+ const [layout, setLayout] = useState('list'); // Changed to 'list' as default
+ const [sortBy, setSortBy] = useState('date-desc');
+
+ // Include 'All' at the beginning of the categories and tags arrays
+ const allCategories = ['All', ...categories];
+ const allTags = ['All', ...tags];
+
+ // Debounce search query to prevent excessive filtering during typing
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ setDebouncedSearchQuery(searchQuery);
+ }, 300);
+
+ return () => clearTimeout(timer);
+ }, [searchQuery]);
+
+ // Filter posts based on selected category/tag and search query
+ const filteredPosts = posts
+ .filter(post => {
+ if (activeFilter === 'All') return true;
+ return post.metadata.category === activeFilter ||
+ post.metadata.tags?.includes(activeFilter);
+ })
+ .filter(post => {
+ if (!debouncedSearchQuery) return true;
+
+ const query = debouncedSearchQuery.toLowerCase();
+ return (
+ post.metadata.title.toLowerCase().includes(query) ||
+ (post.metadata.description && post.metadata.description.toLowerCase().includes(query)) ||
+ post.metadata.tags?.some(tag => tag.toLowerCase().includes(query))
+ );
+ })
+ .sort((a, b) => {
+ // Sort by selected sort option
+ switch (sortBy) {
+ case 'date-desc':
+ return new Date(b.metadata.date).getTime() - new Date(a.metadata.date).getTime();
+ case 'date-asc':
+ return new Date(a.metadata.date).getTime() - new Date(b.metadata.date).getTime();
+ case 'alphabetical':
+ return a.metadata.title.localeCompare(b.metadata.title);
+ default:
+ return 0;
+ }
+ });
+
+ // Toggle search input visibility
+ const toggleSearch = () => {
+ setIsSearching(!isSearching);
+ if (!isSearching) {
+ // Focus the search input when it becomes visible
+ setTimeout(() => {
+ const searchInput = document.getElementById('blog-search');
+ if (searchInput) searchInput.focus();
+ }, 100);
+ } else {
+ // Clear search when closing
+ setSearchQuery('');
+ }
+ };
+
+ // Get text for the active sort option
+ const getSortOptionText = (option: SortOption): string => {
+ switch (option) {
+ case 'date-desc': return 'Newest first';
+ case 'date-asc': return 'Oldest first';
+ case 'alphabetical': return 'A-Z';
+ default: return 'Sort';
+ }
+ };
+
+ return (
+
+
+
+ {/* Header section */}
+
+
+
+
+ My Blog
+
+
+ {filteredPosts.length} article{filteredPosts.length !== 1 ? 's' : ''} on web development and technology
+
+
+
+
+ {/* Layout switcher - Hidden on mobile */}
+
+ setLayout('grid')}
+ className={`p-2 flex items-center justify-center transition-colors ${layout === 'grid'
+ ? 'bg-primary-800/40 text-primary-300'
+ : 'bg-primary-800/20 text-primary-400/70 hover:bg-primary-800/30 hover:text-primary-300'
+ }`}
+ aria-label="Grid view"
+ title="Grid view"
+ >
+
+
+ setLayout('list')}
+ className={`p-2 flex items-center justify-center transition-colors ${layout === 'list'
+ ? 'bg-primary-800/40 text-primary-300'
+ : 'bg-primary-800/20 text-primary-400/70 hover:bg-primary-800/30 hover:text-primary-300'
+ }`}
+ aria-label="List view"
+ title="List view"
+ >
+
+
+
+
+ {/* Sort dropdown - Hidden on mobile */}
+
+
+
+
+
+ {getSortOptionText(sortBy)}
+
+
+
+
+ setSortBy('date-desc')}
+ >
+
+
+ Newest first
+ {sortBy === 'date-desc' && ✓ }
+
+
+ setSortBy('date-asc')}
+ >
+
+
+ Oldest first
+ {sortBy === 'date-asc' && ✓ }
+
+
+ setSortBy('alphabetical')}
+ >
+
+ A
+ Alphabetical (A-Z)
+ {sortBy === 'alphabetical' && ✓ }
+
+
+
+
+
+
+ {/* Search button and input - Hidden on mobile */}
+
+ {isSearching && (
+
+ setSearchQuery(e.target.value)}
+ placeholder="Search articles..."
+ className="w-[200px] px-3 py-2 rounded-l-xl bg-card border border-r-0 border-primary-700/20 text-color-text placeholder:text-color-text-muted text-sm focus:ring-1 focus:ring-primary-500 focus:border-primary-500 outline-none"
+ onKeyDown={(e) => {
+ if (e.key === 'Escape') {
+ toggleSearch();
+ }
+ }}
+ />
+
+
+
+
+ )}
+
+ {!isSearching && (
+
+
+ Search
+
+ )}
+
+
+ {/* Category filter dropdown - Hidden on mobile */}
+
+
+
+
+
+ {activeFilter === 'All' ? 'All categories' : activeFilter}
+
+
+
+
+ {/* Categories section */}
+
+ Categories
+
+
+ {allCategories.map(category => (
+ {
+ setActiveFilter(category);
+ setFilterOpen(false);
+ }}
+ >
+ {category}
+ {activeFilter === category && (
+ ✓
+ )}
+
+ ))}
+
+ {/* Tags section - if we have tags */}
+ {tags.length > 0 && (
+ <>
+
+
+ Tags
+
+
+ {allTags.slice(1).map(tag => (
+ {
+ setActiveFilter(tag);
+ setFilterOpen(false);
+ }}
+ >
+ {tag}
+ {activeFilter === tag && (
+ ✓
+ )}
+
+ ))}
+ >
+ )}
+
+
+
+
+
+
+ {/* Mobile controls section */}
+
+ {/* Layout switcher for mobile */}
+
+
+ View:
+
+
+ setLayout('grid')}
+ className={`p-2 flex items-center justify-center transition-colors ${layout === 'grid'
+ ? 'bg-primary-800/40 text-primary-300'
+ : 'bg-primary-800/20 text-primary-400/70 hover:bg-primary-800/30 hover:text-primary-300'
+ }`}
+ aria-label="Grid view"
+ >
+
+
+ setLayout('list')}
+ className={`p-2 flex items-center justify-center transition-colors ${layout === 'list'
+ ? 'bg-primary-800/40 text-primary-300'
+ : 'bg-primary-800/20 text-primary-400/70 hover:bg-primary-800/30 hover:text-primary-300'
+ }`}
+ aria-label="List view"
+ >
+
+
+
+
+
+ {/* Search input for mobile */}
+
+
+
+
+
setSearchQuery(e.target.value)}
+ placeholder="Search articles..."
+ className="w-full pl-10 pr-4 py-2.5 rounded-xl bg-card border border-primary-700/20 text-color-text placeholder:text-color-text-muted text-sm focus:ring-1 focus:ring-primary-500 focus:border-primary-500 outline-none"
+ onKeyDown={(e) => {
+ if (e.key === 'Escape') {
+ setSearchQuery('');
+ }
+ }}
+ />
+ {searchQuery && (
+
setSearchQuery('')}
+ aria-label="Clear search"
+ >
+
+
+ )}
+
+
+ {/* Mobile filter tabs (categories and tags) */}
+
+
+ {allCategories.map(category => (
+ setActiveFilter(category)}
+ >
+ {category}
+
+ ))}
+
+
+
+ {/* Mobile sort options */}
+
+
+
+ Sort by:
+
+
+ setSortBy('date-desc')}
+ className={`px-3 py-1.5 rounded-lg text-xs transition-colors ${sortBy === 'date-desc'
+ ? 'bg-primary-800/40 text-primary-300 border border-primary-700/40'
+ : 'bg-card text-color-text-muted border border-color-border'
+ }`}
+ >
+ Newest
+
+ setSortBy('date-asc')}
+ className={`px-3 py-1.5 rounded-lg text-xs transition-colors ${sortBy === 'date-asc'
+ ? 'bg-primary-800/40 text-primary-300 border border-primary-700/40'
+ : 'bg-card text-color-text-muted border border-color-border'
+ }`}
+ >
+ Oldest
+
+ setSortBy('alphabetical')}
+ className={`px-3 py-1.5 rounded-lg text-xs transition-colors ${sortBy === 'alphabetical'
+ ? 'bg-primary-800/40 text-primary-300 border border-primary-700/40'
+ : 'bg-card text-color-text-muted border border-color-border'
+ }`}
+ >
+ A-Z
+
+
+
+
+
+ {/* Search result summary */}
+ {debouncedSearchQuery && (
+
+
+ Found {filteredPosts.length} result{filteredPosts.length !== 1 ? 's' : ''} for "{debouncedSearchQuery} "
+ {activeFilter !== 'All' && (
+ <> with {allTags.includes(activeFilter) ? 'tag' : 'category'} {activeFilter} >
+ )}
+
+
setSearchQuery('')}
+ className="text-color-text-muted hover:text-color-text p-1 rounded-full hover:bg-card-alt"
+ aria-label="Clear search"
+ >
+
+
+
+ )}
+
+ {filteredPosts.length > 0 ? (
+
+ {layout === 'grid' ? (
+ // Grid layout - our classic card view
+
+ {filteredPosts.map((post, index) => {
+ const readingTime = calculateReadingTime(post.content);
+ const formattedDate = new Date(post.metadata.date).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ });
+
+ const isHovered = hoveredCard === post.metadata.slug;
+
+ return (
+
+ );
+ })}
+
+ ) : (
+ // List layout - more compact view
+
+ {filteredPosts.map((post, index) => {
+ const readingTime = calculateReadingTime(post.content);
+ const formattedDate = new Date(post.metadata.date).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ });
+
+ const isHovered = hoveredCard === post.metadata.slug;
+
+ return (
+
+ );
+ })}
+
+ )}
+
+ ) : (
+
+
+ {debouncedSearchQuery
+ ? `No articles found matching "${debouncedSearchQuery}"`
+ : "No articles found in this category"}
+
+
+ {debouncedSearchQuery
+ ? "Try different search terms or clear your search"
+ : "Try selecting a different filter or check back later."}
+
+
+ {debouncedSearchQuery && (
+ setSearchQuery('')}
+ className="btn-secondary text-sm py-2"
+ >
+ Clear Search
+
+ )}
+ {activeFilter !== 'All' && (
+ setActiveFilter('All')}
+ className="btn-secondary text-sm py-2"
+ >
+ Show All Articles
+
+ )}
+
+
+ )}
+
+
+ );
+}
+
+interface CardItemProps {
+ post: { content: string; metadata: PostMetadata };
+ index: number;
+ readingTime: string;
+ formattedDate: string;
+ isHovered: boolean;
+ onHover: (slug: string | null) => void;
+ searchQuery: string;
+}
+
+function CardItem({ post, index, readingTime, formattedDate, isHovered, onHover, searchQuery }: CardItemProps) {
+ const cardRef = useRef(null);
+
+ return (
+ onHover(post.metadata.slug)}
+ onMouseLeave={() => onHover(null)}
+ >
+
+
+ {/* Top gradient accent bar */}
+
+
+
+ {/* Top section - Tags and badge */}
+
+
+ {post.metadata.tags && post.metadata.tags.slice(0, 3).map(tag => (
+
+ {highlightMatchedText(tag, searchQuery)}
+
+ ))}
+ {post.metadata.tags && post.metadata.tags.length > 3 && (
+
+ +{post.metadata.tags.length - 3}
+
+ )}
+
+
+ {/* New post badge - conditional rendering */}
+ {isNew(post.metadata.date) && (
+
+ New
+
+ )}
+
+
+ {/* Title with hover effect */}
+
+ {highlightMatchedText(post.metadata.title, searchQuery)}
+
+
+ {/* Description with line clamp */}
+ {post.metadata.description && (
+
+ {highlightMatchedText(post.metadata.description, searchQuery)}
+
+ )}
+
+ {/* Bottom metadata section - now wider to accommodate content */}
+
+
+
+
+
+ {formattedDate}
+
+
+
+
+ {readingTime}
+
+
+
+
+
+ Read post
+
+
+
+
+
+
+
+ {/* Subtle hover glow effect */}
+
+
+ {/* Subtle corner decoration */}
+
+
+
+
+ );
+}
+
+function ListItem({ post, index, readingTime, formattedDate, isHovered, onHover, searchQuery }: CardItemProps) {
+ return (
+ onHover(post.metadata.slug)}
+ onMouseLeave={() => onHover(null)}
+ >
+
+
+ {/* Top gradient accent bar - same as grid view */}
+
+
+
+ {/* Top section with date and reading time */}
+
+
+
+
+ {formattedDate}
+
+
+
+ {readingTime}
+
+
+
+ {/* New badge */}
+ {isNew(post.metadata.date) && (
+
+ New
+
+ )}
+
+
+ {/* Title with hover effect - same as grid view */}
+
+ {highlightMatchedText(post.metadata.title, searchQuery)}
+
+
+ {/* Description */}
+ {post.metadata.description && (
+
+ {highlightMatchedText(post.metadata.description, searchQuery)}
+
+ )}
+
+ {/* Tags and read more */}
+
+
+ {post.metadata.tags && post.metadata.tags.slice(0, 4).map(tag => (
+
+ {highlightMatchedText(tag, searchQuery)}
+
+ ))}
+
+
+
+
+ Read post
+
+
+
+
+
+
+ {/* Subtle hover glow effect - same as grid view */}
+
+
+ {/* Subtle corner decoration - same as grid view */}
+
+
+
+
+ );
+}
+
+// Helper function to check if a post is less than 2 weeks old
+function isNew(dateString: string): boolean {
+ const postDate = new Date(dateString);
+ const now = new Date();
+ const twoWeeksAgo = new Date(now.setDate(now.getDate() - 14));
+ return postDate > twoWeeksAgo;
+}
+
+// Helper function to highlight matched text
+function highlightMatchedText(text: string, query: string): React.ReactNode {
+ if (!query) return text;
+
+ const parts = text.split(new RegExp(`(${query})`, 'gi'));
+
+ return (
+ <>
+ {parts.map((part, index) =>
+ part.toLowerCase() === query.toLowerCase()
+ ? {part}
+ : part
+ )}
+ >
+ );
+}
diff --git a/src/components/blog/BlogPostContent.tsx b/src/components/blog/BlogPostContent.tsx
new file mode 100644
index 0000000..4e459a3
--- /dev/null
+++ b/src/components/blog/BlogPostContent.tsx
@@ -0,0 +1,126 @@
+"use client";
+
+import { motion } from 'framer-motion';
+import Link from 'next/link';
+import type { PostMetadata } from '@/lib/mdx';
+import { useState, useEffect } from 'react';
+import { IoTimeOutline, IoCalendarOutline } from 'react-icons/io5';
+import { BackgroundEffects } from '@/components/ui/BackgroundEffects';
+import { MDXContent } from '../mdx/MDXContent';
+
+interface BlogPostContentProps {
+ content: string;
+ metadata: PostMetadata;
+ readingTime: string;
+}
+
+export default function BlogPostContent({ content, metadata, readingTime }: BlogPostContentProps) {
+ const [isMounted, setIsMounted] = useState(false);
+
+ // Ensure hydration is complete before rendering content
+ useEffect(() => {
+ setIsMounted(true);
+ }, []);
+
+ return (
+
+
+
+ {/* Background gradient overlay */}
+
+
+
←
+
Back to all posts
+
+
+
+
+ {metadata.title}
+
+
+
+
+
+ {new Date(metadata.date).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ })}
+
+
+
+
+
+ {readingTime}
+
+
+ {metadata.tags && (
+
+ {metadata.tags.map(tag => (
+
+ {tag}
+
+ ))}
+
+ )}
+
+
+
+
+ {isMounted && (
+
+ )}
+
+
+ {/* Share and tags section at the bottom */}
+
+
+
+
Tags:
+
+ {metadata.tags?.map(tag => (
+
+ {tag}
+
+ ))}
+
+
+
+
+
Share:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/docs/DocsContent.tsx b/src/components/docs/DocsContent.tsx
new file mode 100644
index 0000000..8b6a3b6
--- /dev/null
+++ b/src/components/docs/DocsContent.tsx
@@ -0,0 +1,121 @@
+import React from 'react';
+import Link from 'next/link';
+import { IoTimeOutline, IoCalendarOutline, IoInformationCircleOutline } from "react-icons/io5";
+
+interface DocsMeta {
+ title: string;
+ description?: string;
+ lastUpdated?: string;
+ readingTime?: string;
+ authors?: Array<{ name: string; url?: string }>;
+}
+
+interface DocsContentProps {
+ meta: DocsMeta;
+ children: React.ReactNode;
+ toc?: React.ReactNode;
+ nextDoc?: { title: string; href: string };
+ prevDoc?: { title: string; href: string };
+}
+
+export default function DocsContent({ meta, children, toc, nextDoc, prevDoc }: DocsContentProps) {
+ return (
+
+ {/* Document header */}
+
+
{meta.title}
+ {meta.description && (
+
{meta.description}
+ )}
+
+
+ {meta.lastUpdated && (
+
+
+ Updated: {meta.lastUpdated}
+
+ )}
+
+ {meta.readingTime && (
+
+
+ {meta.readingTime}
+
+ )}
+
+ {meta.authors && meta.authors.length > 0 && (
+
+
+
+ By:{" "}
+ {meta.authors.map((author, i) => (
+
+ {i > 0 && ", "}
+ {author.url ? (
+
+ {author.name}
+
+ ) : (
+ author.name
+ )}
+
+ ))}
+
+
+ )}
+
+
+
+ {/* Main content with optional table of contents */}
+
+
+
+ {children}
+
+
+ {/* Previous/next navigation */}
+ {(prevDoc || nextDoc) && (
+
+ {prevDoc && (
+
+
Previous
+
{prevDoc.title}
+
+ )}
+
+ {nextDoc && (
+
+
Next
+
{nextDoc.title}
+
+ )}
+
+ )}
+
+
+ {/* Table of contents sidebar */}
+ {toc && (
+
+ )}
+
+
+ );
+}
diff --git a/src/components/docs/DocsSearch.tsx b/src/components/docs/DocsSearch.tsx
new file mode 100644
index 0000000..6c63be4
--- /dev/null
+++ b/src/components/docs/DocsSearch.tsx
@@ -0,0 +1,137 @@
+"use client";
+
+import { useState, useEffect, useRef } from "react";
+import { createPortal } from "react-dom";
+import { motion, AnimatePresence } from "framer-motion";
+import { IoSearch, IoClose } from "react-icons/io5";
+import { useRouter } from "next/navigation";
+import { docsConfig } from "@/config/docs";
+
+interface DocsSearchProps {
+ onClose: () => void;
+}
+
+// Flatten docs items for search with null check
+const searchableItems = docsConfig?.sidebarNav
+ ? docsConfig.sidebarNav.flatMap(section =>
+ section.items.map(item => ({
+ ...item,
+ section: section.title,
+ }))
+ )
+ : [];
+
+export default function DocsSearch({ onClose }: DocsSearchProps) {
+ const [searchQuery, setSearchQuery] = useState("");
+ const [results, setResults] = useState(searchableItems);
+ const inputRef = useRef(null);
+ const router = useRouter();
+
+ // Filter results based on search query
+ useEffect(() => {
+ if (!searchQuery.trim()) {
+ setResults(searchableItems);
+ return;
+ }
+
+ const filtered = searchableItems.filter(item => {
+ const searchableText = `${item.title} ${item.description} ${item.keywords?.join(" ") || ""}`.toLowerCase();
+ return searchableText.includes(searchQuery.toLowerCase());
+ });
+
+ setResults(filtered);
+ }, [searchQuery]);
+
+ // Focus input on mount
+ useEffect(() => {
+ if (inputRef.current) {
+ inputRef.current.focus();
+ }
+ }, []);
+
+ // Handle selecting a result
+ const handleSelectResult = (href: string) => {
+ router.push(href);
+ onClose();
+ };
+
+ // Handle click outside to close
+ const handleBackdropClick = (e: React.MouseEvent) => {
+ if (e.target === e.currentTarget) {
+ onClose();
+ }
+ };
+
+ return createPortal(
+
+
+
+ {/* Search input */}
+
+
+ setSearchQuery(e.target.value)}
+ className="bg-transparent border-none outline-none flex-grow text-color-text placeholder:text-color-text-muted"
+ />
+
+
+
+
+
+ {/* Search results */}
+
+ {results.length === 0 ? (
+
+ No results found for "{searchQuery}"
+
+ ) : (
+
+ {results.map((item) => (
+
+ handleSelectResult(item.href)}
+ className="w-full text-left px-4 py-3 hover:bg-bg-alt transition-colors flex items-start gap-3"
+ >
+ {item.icon && }
+
+
{item.title}
+
{item.description}
+
{item.section}
+
+
+
+ ))}
+
+ )}
+
+
+ {/* Keyboard shortcut help */}
+
+
Press ↑ ↓ to navigate
+
Press ESC to close
+
+
+
+ ,
+ document.body
+ );
+}
diff --git a/src/components/docs/DocsSidebar.tsx b/src/components/docs/DocsSidebar.tsx
new file mode 100644
index 0000000..1835a94
--- /dev/null
+++ b/src/components/docs/DocsSidebar.tsx
@@ -0,0 +1,180 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import Link from "next/link";
+import { usePathname, useRouter } from "next/navigation";
+import { motion, AnimatePresence } from "framer-motion";
+import { IoChevronDown, IoChevronForward, IoApps } from "react-icons/io5";
+import { docsConfig } from "@/config/docs";
+
+export default function DocsSidebar() {
+ const pathname = usePathname();
+ const router = useRouter();
+ const [expandedSections, setExpandedSections] = useState>({});
+ const [selectedSection, setSelectedSection] = useState("");
+ const [isSectionDropdownOpen, setSectionDropdownOpen] = useState(false);
+
+ // Determine current section based on pathname
+ useEffect(() => {
+ const currentSection = docsConfig.sections.find(section =>
+ pathname.includes(`/docs/${section.slug}`) ||
+ (pathname === "/docs" && section.slug === "getting-started")
+ );
+
+ if (currentSection) {
+ setSelectedSection(currentSection.name);
+
+ // Only expand the relevant categories in the current section
+ const newExpandedSections: Record = {};
+ currentSection.categories.forEach(category => {
+ const isActive = category.items.some(item => pathname === item.href);
+ if (isActive) {
+ newExpandedSections[category.title] = true;
+ }
+ });
+ setExpandedSections(newExpandedSections);
+ }
+ }, [pathname]);
+
+ const toggleSection = (title: string) => {
+ setExpandedSections(prev => ({
+ ...prev,
+ [title]: !prev[title]
+ }));
+ };
+
+ const handleSectionChange = (sectionSlug: string) => {
+ setSectionDropdownOpen(false);
+
+ // Navigate to the first page of the selected section
+ const section = docsConfig.sections.find(s => s.slug === sectionSlug);
+ if (section && section.categories.length > 0 && section.categories[0].items.length > 0) {
+ router.push(section.categories[0].items[0].href);
+ }
+ };
+
+ // Get current section data
+ const currentSection = docsConfig.sections.find(section =>
+ section.slug === (pathname.split('/')[2] || "getting-started")
+ );
+
+ if (!currentSection) return null;
+
+ return (
+
+ {/* Section selector dropdown */}
+
+
setSectionDropdownOpen(!isSectionDropdownOpen)}
+ className="w-full flex items-center justify-between bg-card rounded-lg p-2.5 border border-color-border hover:border-primary-600/30 transition-colors"
+ >
+
+ {currentSection.icon && }
+ {currentSection.name}
+
+
+
+
+
+ {isSectionDropdownOpen && (
+
+ {docsConfig.sections.map((section) => (
+ handleSectionChange(section.slug)}
+ className={`w-full text-left px-3 py-2 flex items-center gap-2 hover:bg-bg-alt transition-colors ${section.slug === currentSection.slug ? 'text-primary-400 bg-bg-alt/50' : 'text-color-text-muted'
+ }`}
+ >
+ {section.icon && }
+ {section.name}
+
+ ))}
+
+ )}
+
+
+
+ {/* Category navigation for current section */}
+
+ {currentSection.categories.map((category) => (
+
+
toggleSection(category.title)}
+ className="flex items-center justify-between w-full py-1.5 px-2 text-sm font-medium rounded-md hover:bg-bg-alt transition-colors group"
+ >
+
+ {category.title}
+
+ {expandedSections[category.title] ? (
+
+ ) : (
+
+ )}
+
+
+
+ {expandedSections[category.title] && (
+
+ {category.items.map((item) => {
+ const isActive = pathname === item.href;
+
+ return (
+
+
+
+ {item.icon && }
+ {item.title}
+
+
+
+ );
+ })}
+
+ )}
+
+
+ ))}
+
+
+ {/* Quick links at the bottom */}
+
+
+ );
+}
diff --git a/src/components/docs/TableOfContents.tsx b/src/components/docs/TableOfContents.tsx
new file mode 100644
index 0000000..d413d39
--- /dev/null
+++ b/src/components/docs/TableOfContents.tsx
@@ -0,0 +1,96 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { usePathname } from "next/navigation";
+
+interface TocItem {
+ id: string;
+ text: string;
+ level: number;
+}
+
+export default function TableOfContents() {
+ const [headings, setHeadings] = useState([]);
+ const [activeId, setActiveId] = useState("");
+ const pathname = usePathname();
+
+ // Extract headings from the page
+ useEffect(() => {
+ const elements = Array.from(document.querySelectorAll("h2, h3, h4")).map(
+ (element) => ({
+ id: element.id,
+ text: element.textContent || "",
+ level: Number(element.tagName.substring(1)),
+ })
+ );
+
+ setHeadings(elements);
+ }, [pathname]);
+
+ // Track active heading based on scroll position
+ useEffect(() => {
+ if (headings.length === 0) return;
+
+ const observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ setActiveId(entry.target.id);
+ }
+ });
+ },
+ {
+ rootMargin: "0px 0px -80% 0px",
+ threshold: 1.0,
+ }
+ );
+
+ headings.forEach((heading) => {
+ const element = document.getElementById(heading.id);
+ if (element) observer.observe(element);
+ });
+
+ return () => {
+ headings.forEach((heading) => {
+ const element = document.getElementById(heading.id);
+ if (element) observer.unobserve(element);
+ });
+ };
+ }, [headings]);
+
+ if (headings.length === 0) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/src/components/layouts/about/AboutContent.tsx b/src/components/layouts/about/AboutContent.tsx
new file mode 100644
index 0000000..c5b0665
--- /dev/null
+++ b/src/components/layouts/about/AboutContent.tsx
@@ -0,0 +1,292 @@
+"use client";
+
+import { motion } from "framer-motion";
+import Image from "next/image";
+import Link from "next/link";
+import { IoPersonOutline, IoSchoolOutline, IoBriefcaseOutline, IoTimeOutline, IoCode, IoRocketOutline } from "react-icons/io5";
+import { BackgroundEffects } from '@/components/ui/BackgroundEffects';
+
+export default function AboutContent() {
+ return (
+
+
+
+ {/* Main content section */}
+
+
+ About Me
+
+
+ {/* Profile image section */}
+
+
+
+
+
+
+
+ {/* Decorative elements */}
+
+
+ © CodeMeAPixel
+
+
+
+
+
+ {/* Bio section */}
+
+
+
+ Hello! I'm Tyler , a passionate fullstack developer with a keen eye for design and a love for creating exceptional digital experiences.
+
+
+
+ My journey in web development began over 10 years ago, driven by curiosity and a desire to build things that people love to use. I specialize in creating modern, responsive, and accessible web applications that not only look great but also deliver outstanding user experiences.
+
+
+
+ When I'm not coding, I enjoy spending quality time with my family. I'm a proud husband and father of two wonderful children who inspire me every day. Family time is precious to me, and I strive to maintain a healthy work-life balance.
+
+
+
+ I also enjoy exploring new technologies, contributing to open-source projects, and sharing my knowledge through writing and mentoring. I believe in continuous learning and staying up to date with the latest trends and best practices in web development.
+
+
+
+
+
+ Get In Touch
+
+
+ View My Work
+
+
+
+
+
+
+ {/* Experience section */}
+
+
+
+ Experience
+
+
+
+
+
+
+
+
+
+
+ {/* Education section */}
+
+
+
+ Certification
+
+
+
+
+
+
+
+
+
+ {/* Personal interests section */}
+
+
+
+ Beyond Coding
+
+
+
+ }
+ title="Open Source"
+ description="I actively contribute to open-source projects and believe in giving back to the community."
+ delay={0.1}
+ />
+
+ }
+ title="Continuous Learning"
+ description="I dedicate time to learning new technologies and improving my skills through courses and self-study."
+ delay={0.2}
+ />
+
+ }
+ title="Technology Exploration"
+ description="I enjoy exploring emerging technologies and experimenting with new tools and frameworks."
+ delay={0.3}
+ />
+
+ }
+ title="Mentoring"
+ description="I love mentoring aspiring developers and sharing my knowledge to help them grow in their careers."
+ delay={0.4}
+ />
+
+ }
+ title="Family Time"
+ description="Spending quality time with my family is important to me. I cherish moments with my wife and two kids."
+ delay={0.5}
+ />
+
+ }
+ title="Web Development"
+ description="I have a passion for finding and exploring new areas in web development, from frontend to backend."
+ delay={0.6}
+ />
+
+
+
+
+
+ );
+}
+
+interface TimelineItemProps {
+ title: string;
+ company: string;
+ period: string;
+ description: string;
+ delay: number;
+ isLast?: boolean;
+}
+
+function TimelineItem({ title, company, period, description, delay, isLast = false }: TimelineItemProps) {
+ return (
+
+ {/* Timeline line */}
+ {!isLast && (
+
+ )}
+
+ {/* Timeline dot */}
+
+
+
+
{title}
+
+ {company}
+
+ {period}
+
+
+
{description}
+
+
+ );
+}
+
+interface InterestCardProps {
+ icon: React.ReactNode;
+ title: string;
+ description: string;
+ delay: number;
+}
+
+function InterestCard({ icon, title, description, delay }: InterestCardProps) {
+ return (
+
+
+ {title}
+ {description}
+
+ );
+}
diff --git a/src/components/layouts/contact/ContactContent.tsx b/src/components/layouts/contact/ContactContent.tsx
new file mode 100644
index 0000000..9b2ef1c
--- /dev/null
+++ b/src/components/layouts/contact/ContactContent.tsx
@@ -0,0 +1,300 @@
+"use client";
+
+import { useState } from "react";
+import { motion } from "framer-motion";
+import { IoMailOutline, IoLocationOutline, IoSendOutline, IoPersonOutline, IoCheckmarkCircleOutline, IoCloseCircleOutline, IoLogoGithub, IoLogoTwitter, IoLogoLinkedin, IoCodeSlashOutline } from "react-icons/io5";
+import { BackgroundEffects } from '@/components/ui/BackgroundEffects';
+
+export default function ContactContent() {
+ const [formState, setFormState] = useState<'idle' | 'submitting' | 'success' | 'error'>('idle');
+ const [formData, setFormData] = useState({
+ name: "",
+ email: "",
+ subject: "",
+ message: ""
+ });
+
+ const handleChange = (e: React.ChangeEvent) => {
+ const { name, value } = e.target;
+ setFormData(prev => ({ ...prev, [name]: value }));
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setFormState('submitting');
+
+ try {
+ // Create mailto URL with form data
+ const subject = encodeURIComponent(formData.subject);
+ const body = encodeURIComponent(
+ `Name: ${formData.name}\nEmail: ${formData.email}\n\nMessage:\n${formData.message}`
+ );
+
+ // Open default email client with pre-filled fields
+ window.location.href = `mailto:hey@codemeapixel.dev?subject=${subject}&body=${body}`;
+
+ // Set success state and reset form
+ setFormState('success');
+ setFormData({ name: "", email: "", subject: "", message: "" });
+ } catch (error) {
+ console.error("Error opening email client:", error);
+ setFormState('error');
+ }
+ };
+
+ return (
+
+
+
+
+ Get In Touch
+
+ Have a question or want to work together? Feel free to reach out using the form below
+ or connect with me on social media.
+
+
+
+
+ {/* Contact info column */}
+
+
+
+
Contact Information
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Location
+
Alberta, Canada
+
+
+
+
+
+
+
Connect With Me
+
+ }
+ label="GitHub"
+ href="https://github.com/codemeapixel"
+ />
+ }
+ label="Twitter"
+ href="https://twitter.com/codemeapixel"
+ />
+ }
+ label="LinkedIn"
+ href="https://linkedin.com/in/codemeapixel"
+ />
+ }
+ label="Portfolio"
+ href="/"
+ />
+
+
+
+
+
+ {/* Contact form column */}
+
+
+
Send Me a Message
+
+ {formState === 'success' ? (
+
+
+
+
+
Message Sent!
+
+ Thank you for reaching out. I'll get back to you as soon as possible!
+
+
setFormState('idle')}
+ >
+ Send Another Message
+
+
+ ) : formState === 'error' ? (
+
+
+
+
+
Something Went Wrong
+
+ There was an error sending your message. Please try again or contact me directly via email.
+
+
setFormState('idle')}
+ >
+ Try Again
+
+
+ ) : (
+
+ )}
+
+
+
+
+
+ );
+}
+
+interface SocialButtonProps {
+ icon: React.ReactNode;
+ label: string;
+ href: string;
+}
+
+function SocialButton({ icon, label, href }: SocialButtonProps) {
+ return (
+
+ {icon}
+ {label}
+
+ );
+}
diff --git a/src/components/layouts/fivem/FivemScriptDetailsContent.tsx b/src/components/layouts/fivem/FivemScriptDetailsContent.tsx
new file mode 100644
index 0000000..e1dcb02
--- /dev/null
+++ b/src/components/layouts/fivem/FivemScriptDetailsContent.tsx
@@ -0,0 +1,330 @@
+"use client";
+
+import { motion } from 'framer-motion';
+import { useState } from 'react';
+import Link from 'next/link';
+
+import {
+ IoArrowBack, IoCalendarOutline, IoCheckmarkCircle, IoCodeSlashOutline,
+ IoLogoGithub, IoPricetagOutline, IoTimeOutline, IoChevronDown, IoChevronUp,
+ IoLinkOutline, IoInformationCircleOutline
+} from 'react-icons/io5';
+
+import type { FivemScript } from '@/types/fivem';
+import { ImageCarousel } from '@/components/ui/ImageCarousel';
+
+interface FivemScriptDetailsContentProps {
+ script: FivemScript;
+}
+
+export default function FivemScriptDetailsContent({ script }: FivemScriptDetailsContentProps) {
+ const [activeTab, setActiveTab] = useState<'description' | 'installation' | 'requirements'>('description');
+ const [isRequirementsExpanded, setIsRequirementsExpanded] = useState(false);
+
+ return (
+
+
+
+
+
Back to all scripts
+
+
+
+ {/* Left column - Images and quick info */}
+
+ {/* Script title and status */}
+
+
+ {script.title}
+
+
+
+
+ {/* Image carousel - Improved for FiveM screenshots */}
+
+
+
+
+ {/* Tabs for description, installation, etc. */}
+
+
+ setActiveTab('description')}
+ >
+ Description
+
+ {script.installation && (
+ setActiveTab('installation')}
+ >
+ Installation
+
+ )}
+ {script.requirements && (
+ setActiveTab('requirements')}
+ >
+ Requirements
+
+ )}
+
+
+
+ {activeTab === 'description' && (
+
+
{script.longDescription}
+
+ )}
+
+ {activeTab === 'installation' && script.installation && (
+
+
+ {script.installation.split('\n').map((step, index) => (
+
+
+ {index + 1}
+
+ {step.replace(/^\d+\.\s+/, '')}
+
+ ))}
+
+
+ )}
+
+ {activeTab === 'requirements' && script.requirements && (
+
+
+ {script.requirements.map((req, index) => (
+
+
+ {req}
+
+ ))}
+
+
+ )}
+
+
+
+ {/* Feature list */}
+
+
+
+ Features
+
+
+ {script.features.map((feature, index) => (
+
+
+ {feature}
+
+ ))}
+
+
+
+ {/* Embedded video if available */}
+ {script.video && (
+
+ )}
+
+
+ {/* Right column - Purchase info, details, etc. */}
+
+ {/* Purchase card */}
+
+
+
{script.price}
+
v{script.version}
+
+
+ {script.status === 'Released' && script.links.purchase && (
+
+
+ Purchase Now
+
+ )}
+
+ {script.status === 'In Development' && (
+
+ Coming Soon
+
+ )}
+
+ {script.links.demo && (
+
+ Watch Demo
+
+ )}
+
+ {script.links.github && (
+
+
+ View on GitHub
+
+ )}
+
+ {script.links.documentation && (
+
+
+ Documentation
+
+ )}
+
+
+ {/* Script details */}
+
+
Script Details
+
+
+
+
+
+
Framework
+
{script.framework}
+
+
+
+
+
+
+
Last Updated
+
{script.lastUpdated}
+
+
+
+
+
+
+
Status
+
{script.status}
+
+
+
+
+
+ {/* Requirements (collapsible on mobile) */}
+ {script.requirements && (
+
+
setIsRequirementsExpanded(!isRequirementsExpanded)}
+ >
+ Requirements
+ {isRequirementsExpanded ? (
+
+ ) : (
+
+ )}
+
+
+ {isRequirementsExpanded && (
+
+ {script.requirements.map((req, index) => (
+
+
+ {req}
+
+ ))}
+
+ )}
+
+ )}
+
+ {/* Tags */}
+
+
Tags
+
+ {script.tags.map(tag => (
+
+ {tag}
+
+ ))}
+
+
+
+
+
+
+ );
+}
+
+function StatusBadge({ status }: { status: FivemScript['status'] }) {
+ let bgColor = 'bg-primary-900/30';
+ let textColor = 'text-primary-300';
+ let borderColor = 'border-primary-700/30';
+
+ if (status === 'Released') {
+ bgColor = 'bg-green-900/30';
+ textColor = 'text-green-300';
+ borderColor = 'border-green-700/30';
+ } else if (status === 'In Development') {
+ bgColor = 'bg-amber-900/30';
+ textColor = 'text-amber-300';
+ borderColor = 'border-amber-700/30';
+ } else if (status === 'Coming Soon') {
+ bgColor = 'bg-purple-900/30';
+ textColor = 'text-purple-300';
+ borderColor = 'border-purple-700/30';
+ }
+
+ return (
+
+ {status}
+
+ );
+}
diff --git a/src/components/layouts/fivem/FivemScriptsContent.tsx b/src/components/layouts/fivem/FivemScriptsContent.tsx
new file mode 100644
index 0000000..f93c1f9
--- /dev/null
+++ b/src/components/layouts/fivem/FivemScriptsContent.tsx
@@ -0,0 +1,901 @@
+"use client";
+
+import { useState, useEffect } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import Link from 'next/link';
+import Image from 'next/image';
+import {
+ IoArrowForward, IoFilterOutline, IoGridOutline, IoListOutline, IoSearch, IoClose,
+ IoTimeOutline, IoChevronDown, IoSwapVertical, IoCalendarOutline, IoLogoGithub, IoGlobeOutline
+} from 'react-icons/io5';
+import type { FivemScript } from '@/types/fivem';
+import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
+import { format } from 'date-fns';
+import { TechIcon } from '@/components/ui/TechIcon';
+
+interface FivemScriptsContentProps {
+ scripts: FivemScript[];
+ allTags: string[];
+}
+
+type ViewMode = 'grid' | 'list';
+type Filter = 'All' | 'ESX' | 'QBCore' | 'Standalone' | 'Released' | 'In Development' | 'Coming Soon';
+type SortOption = 'date-desc' | 'date-asc' | 'alphabetical' | 'price-asc' | 'price-desc';
+
+export default function FivemScriptsContent({ scripts, allTags }: FivemScriptsContentProps) {
+ // State for filtering and display
+ const [viewMode, setViewMode] = useState('list'); // Default to table/list view
+ const [filter, setFilter] = useState('All');
+ const [hoveredScript, setHoveredScript] = useState(null);
+ const [filterOpen, setFilterOpen] = useState(false);
+ const [sortOpen, setSortOpen] = useState(false);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [isSearching, setIsSearching] = useState(false);
+ const [debouncedSearchQuery, setDebouncedSearchQuery] = useState('');
+ const [sortBy, setSortBy] = useState('date-desc');
+
+ // Extract all frameworks and statuses
+ const frameworks = ['ESX', 'QBCore', 'Standalone'];
+ const statuses = ['Released', 'In Development', 'Coming Soon'];
+
+ // Debounce search query to prevent excessive filtering during typing
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ setDebouncedSearchQuery(searchQuery);
+ }, 300);
+
+ return () => clearTimeout(timer);
+ }, [searchQuery]);
+
+ // Filter scripts based on selected filter, search query, and sort
+ const filteredScripts = scripts
+ .filter(script => {
+ // Filter by framework or status
+ if (filter === 'All') return true;
+ if (filter === 'ESX' || filter === 'QBCore' || filter === 'Standalone') {
+ return script.framework === filter || script.framework === 'Both';
+ }
+ return script.status === filter;
+ })
+ .filter(script => {
+ // Filter by search query
+ if (!debouncedSearchQuery) return true;
+
+ const query = debouncedSearchQuery.toLowerCase();
+ return (
+ script.title.toLowerCase().includes(query) ||
+ script.description.toLowerCase().includes(query) ||
+ script.tags.some(tag => tag.toLowerCase().includes(query)) ||
+ (script.longDescription && script.longDescription.toLowerCase().includes(query))
+ );
+ })
+ .sort((a, b) => {
+ // Sort by selected option
+ switch (sortBy) {
+ case 'date-desc':
+ return new Date(b.lastUpdated || '').getTime() - new Date(a.lastUpdated || '').getTime();
+ case 'date-asc':
+ return new Date(a.lastUpdated || '').getTime() - new Date(b.lastUpdated || '').getTime();
+ case 'alphabetical':
+ return a.title.localeCompare(b.title);
+ case 'price-asc':
+ return extractPriceNumber(a.price) - extractPriceNumber(b.price);
+ case 'price-desc':
+ return extractPriceNumber(b.price) - extractPriceNumber(a.price);
+ default:
+ return 0;
+ }
+ });
+
+ // Helper function to extract numeric price for sorting
+ function extractPriceNumber(price: string): number {
+ const match = price.match(/\d+(\.\d+)?/);
+ return match ? parseFloat(match[0]) : 0;
+ }
+
+ // Toggle search input visibility
+ const toggleSearch = () => {
+ setIsSearching(!isSearching);
+ if (!isSearching) {
+ // Focus the search input when it becomes visible
+ setTimeout(() => {
+ const searchInput = document.getElementById('script-search');
+ if (searchInput) searchInput.focus();
+ }, 100);
+ } else {
+ // Clear search when closing
+ setSearchQuery('');
+ }
+ };
+
+ // Get text for the active sort option
+ const getSortOptionText = (option: SortOption): string => {
+ switch (option) {
+ case 'date-desc': return 'Newest first';
+ case 'date-asc': return 'Oldest first';
+ case 'alphabetical': return 'A-Z';
+ case 'price-asc': return 'Price: Low to High';
+ case 'price-desc': return 'Price: High to Low';
+ default: return 'Sort';
+ }
+ };
+
+ return (
+
+
+
+
+ FiveM Scripts
+
+
+ Premium scripts for your FiveM roleplay server. Browse my collection of high-quality scripts
+ compatible with ESX and QBCore frameworks.
+
+
+
+ {/* Desktop controls */}
+
+ {/* Filter dropdown */}
+
+
+
+
+ {filter === 'All' ? 'All Scripts' : filter}
+
+
+
+
+
+
+ Filter Options
+
+ setFilter('All')}
+ >
+ All Scripts
+ {filter === 'All' && ✓ }
+
+
+
+
+ By Framework
+
+
+ {frameworks.map(framework => (
+ setFilter(framework as Filter)}
+ >
+ {framework}
+ {filter === framework && ✓ }
+
+ ))}
+
+
+
+ By Status
+
+
+ {statuses.map(status => (
+ setFilter(status as Filter)}
+ >
+ {status}
+ {filter === status && ✓ }
+
+ ))}
+
+
+
+
+
+ {/* Search button and input */}
+
+ {isSearching && (
+
+ setSearchQuery(e.target.value)}
+ placeholder="Search scripts..."
+ className="w-[200px] px-3 py-2 rounded-l-xl bg-card border border-r-0 border-primary-700/20 text-color-text placeholder:text-color-text-muted text-sm focus:ring-1 focus:ring-primary-500 focus:border-primary-500 outline-none"
+ onKeyDown={(e) => {
+ if (e.key === 'Escape') {
+ toggleSearch();
+ }
+ }}
+ />
+
+
+
+
+ )}
+
+ {!isSearching && (
+
+
+ Search
+
+ )}
+
+
+ {/* Sort dropdown */}
+
+
+
+
+ {getSortOptionText(sortBy)}
+
+
+
+
+ setSortBy('date-desc')}
+ >
+
+
+ Newest first
+ {sortBy === 'date-desc' && ✓ }
+
+
+ setSortBy('date-asc')}
+ >
+
+
+ Oldest first
+ {sortBy === 'date-asc' && ✓ }
+
+
+ setSortBy('alphabetical')}
+ >
+
+ A
+ Alphabetical (A-Z)
+ {sortBy === 'alphabetical' && ✓ }
+
+
+ setSortBy('price-asc')}
+ >
+
+ $
+ Price: Low to High
+ {sortBy === 'price-asc' && ✓ }
+
+
+ setSortBy('price-desc')}
+ >
+
+ $
+ Price: High to Low
+ {sortBy === 'price-desc' && ✓ }
+
+
+
+
+
+ {/* View mode toggle */}
+
+ setViewMode('grid')}
+ aria-label="Grid view"
+ >
+
+
+ setViewMode('list')}
+ aria-label="List view"
+ >
+
+
+
+
+
+
+ {/* Mobile controls section */}
+
+ {/* Layout switcher for mobile */}
+
+
+ View:
+
+
+ setViewMode('grid')}
+ className={`p-2 flex items-center justify-center transition-colors ${viewMode === 'grid'
+ ? 'bg-primary-800/40 text-primary-300'
+ : 'bg-primary-800/20 text-primary-400/70 hover:bg-primary-800/30 hover:text-primary-300'
+ }`}
+ aria-label="Grid view"
+ >
+
+
+ setViewMode('list')}
+ className={`p-2 flex items-center justify-center transition-colors ${viewMode === 'list'
+ ? 'bg-primary-800/40 text-primary-300'
+ : 'bg-primary-800/20 text-primary-400/70 hover:bg-primary-800/30 hover:text-primary-300'
+ }`}
+ aria-label="Table view"
+ >
+
+
+
+
+
+ {/* Search input for mobile */}
+
+
+
+
+
setSearchQuery(e.target.value)}
+ placeholder="Search scripts..."
+ className="w-full pl-10 pr-4 py-2.5 rounded-xl bg-card border border-primary-700/20 text-color-text placeholder:text-color-text-muted text-sm focus:ring-1 focus:ring-primary-500 focus:border-primary-500 outline-none"
+ onKeyDown={(e) => {
+ if (e.key === 'Escape') {
+ setSearchQuery('');
+ }
+ }}
+ />
+ {searchQuery && (
+
setSearchQuery('')}
+ aria-label="Clear search"
+ >
+
+
+ )}
+
+
+ {/* Mobile filter buttons */}
+
+
+ setFilter('All')}
+ >
+ All Scripts
+
+ {frameworks.map(framework => (
+ setFilter(framework as Filter)}
+ >
+ {framework}
+
+ ))}
+ {statuses.map(status => (
+ setFilter(status as Filter)}
+ >
+ {status}
+
+ ))}
+
+
+
+ {/* Mobile sort options */}
+
+
+
+ Sort by:
+
+
+ setSortBy('date-desc')}
+ className={`px-3 py-1.5 rounded-lg text-xs transition-colors ${sortBy === 'date-desc'
+ ? 'bg-primary-800/40 text-primary-300 border border-primary-700/40'
+ : 'bg-card text-color-text-muted border border-color-border'
+ }`}
+ >
+ Newest
+
+ setSortBy('alphabetical')}
+ className={`px-3 py-1.5 rounded-lg text-xs transition-colors ${sortBy === 'alphabetical'
+ ? 'bg-primary-800/40 text-primary-300 border border-primary-700/40'
+ : 'bg-card text-color-text-muted border border-color-border'
+ }`}
+ >
+ A-Z
+
+ setSortBy('price-asc')}
+ className={`px-3 py-1.5 rounded-lg text-xs transition-colors ${sortBy === 'price-asc'
+ ? 'bg-primary-800/40 text-primary-300 border border-primary-700/40'
+ : 'bg-card text-color-text-muted border border-color-border'
+ }`}
+ >
+ $ Low-High
+
+
+
+
+
+ {/* Search result summary */}
+ {debouncedSearchQuery && (
+
+
+ Found {filteredScripts.length} result{filteredScripts.length !== 1 ? 's' : ''} for "{debouncedSearchQuery} "
+ {filter !== 'All' && (
+ <> with filter {filter} >
+ )}
+
+
setSearchQuery('')}
+ className="text-color-text-muted hover:text-color-text p-1 rounded-full hover:bg-card-alt"
+ aria-label="Clear search"
+ >
+
+
+
+ )}
+
+ {filteredScripts.length === 0 ? (
+
+ No scripts found
+
+ {debouncedSearchQuery
+ ? "No scripts match your search query. Try different search terms or clear your search."
+ : "No scripts match your current filter. Try selecting a different filter."}
+
+
+ {debouncedSearchQuery && (
+ setSearchQuery('')}
+ className="btn-secondary text-sm py-2"
+ >
+ Clear Search
+
+ )}
+ {filter !== 'All' && (
+ setFilter('All')}
+ className="btn-secondary text-sm py-2"
+ >
+ Show All Scripts
+
+ )}
+
+
+ ) : (
+
+ {viewMode === 'grid' ? (
+ // Grid layout
+
+ {filteredScripts.map((script, index) => (
+
+ ))}
+
+ ) : (
+ // Table/List layout
+
+ {filteredScripts.map((script, index) => (
+
+ ))}
+
+ )}
+
+ )}
+
+
+ );
+}
+
+interface ScriptCardProps {
+ script: FivemScript;
+ index: number;
+ isHovered: boolean;
+ onHover: (id: string | null) => void;
+ searchQuery: string;
+}
+
+function ScriptGridCard({ script, index, isHovered, onHover }: ScriptCardProps) {
+ return (
+ onHover(script.id)}
+ onMouseLeave={() => onHover(null)}
+ >
+
+
+ {/* Top gradient accent bar */}
+
+
+ {/* Status badge */}
+
+
+
+
+ {/* Image */}
+
+
+ {/* Content */}
+
+ {/* Framework badges */}
+
+
+
+
+ {/* Title with hover effect */}
+
+ {script.title}
+
+
+ {/* Description */}
+
+ {script.description}
+
+
+ {/* Price and version info */}
+
+
+ {script.price}
+
+
+ v{script.version}
+
+
+
+ {/* Tags */}
+
+ {script.tags.slice(0, 3).map(tag => (
+
+ {tag}
+
+ ))}
+ {script.tags.length > 3 && (
+
+ +{script.tags.length - 3}
+
+ )}
+
+
+ {/* View details link */}
+
+ View details
+
+
+
+
+ {/* Subtle hover glow effect */}
+
+
+ {/* Subtle corner decoration */}
+
+
+
+
+ );
+}
+
+function ScriptListItem({ script, index, isHovered, onHover }: ScriptCardProps) {
+ return (
+ onHover(script.id)}
+ onMouseLeave={() => onHover(null)}
+ >
+
+
+ {/* Top gradient accent bar */}
+
+
+
+ {/* Image (only shown on md screens and up) */}
+
+
+
+
+ {/* Status badge (mobile position) */}
+
+
+
+
+
+ {/* Content */}
+
+
+
+
+
+ {/* Status badge (desktop position) */}
+
+
+
+
+
+
+
+ {script.price}
+
+
+ v{script.version}
+
+
+
+
+ {/* Title with hover animation */}
+
+ {script.title}
+
+
+ {/* Description */}
+
+ {script.description}
+
+
+
+ {/* Tags */}
+
+ {script.tags.slice(0, 3).map(tag => (
+
+ {tag}
+
+ ))}
+ {script.tags.length > 3 && (
+
+ +{script.tags.length - 3}
+
+ )}
+
+
+ {/* View details link */}
+
+ View details
+
+
+
+
+
+
+ {/* Subtle hover glow effect */}
+
+
+ {/* Subtle corner decoration */}
+
+
+
+
+ );
+}
+
+// Helper function to highlight matched text
+function highlightMatchedText(text: string, query: string): React.ReactNode {
+ if (!query) return text;
+
+ const parts = text.split(new RegExp(`(${query})`, 'gi'));
+
+ return (
+ <>
+ {parts.map((part, index) =>
+ part.toLowerCase() === query.toLowerCase()
+ ? {part}
+ : part
+ )}
+ >
+ );
+}
+
+function StatusBadge({ status }: { status: FivemScript['status'] }) {
+ let bgColor = 'bg-primary-900/30';
+ let textColor = 'text-primary-300';
+ let borderColor = 'border-primary-700/30';
+
+ if (status === 'Released') {
+ bgColor = 'bg-green-900/30';
+ textColor = 'text-green-300';
+ borderColor = 'border-green-700/30';
+ } else if (status === 'In Development') {
+ bgColor = 'bg-amber-900/30';
+ textColor = 'text-amber-300';
+ borderColor = 'border-amber-700/30';
+ } else if (status === 'Coming Soon') {
+ bgColor = 'bg-purple-900/30';
+ textColor = 'text-purple-300';
+ borderColor = 'border-purple-700/30';
+ }
+
+ return (
+
+ {status}
+
+ );
+}
+
+function FrameworkBadge({ framework }: { framework: FivemScript['framework'] }) {
+ if (framework === 'Both') {
+ return (
+ <>
+
+ ESX
+
+
+ QBCore
+
+ >
+ );
+ }
+
+ let bgColor = 'bg-primary-900/30';
+ let textColor = 'text-primary-300';
+ let borderColor = 'border-primary-700/30';
+
+ if (framework === 'ESX') {
+ bgColor = 'bg-blue-900/30';
+ textColor = 'text-blue-300';
+ borderColor = 'border-blue-700/30';
+ } else if (framework === 'QBCore') {
+ bgColor = 'bg-green-900/30';
+ textColor = 'text-green-300';
+ borderColor = 'border-green-700/30';
+ }
+
+ return (
+
+ {framework}
+
+ );
+}
diff --git a/src/components/layouts/home/Contact.tsx b/src/components/layouts/home/Contact.tsx
index f1ee425..d6a3d1b 100644
--- a/src/components/layouts/home/Contact.tsx
+++ b/src/components/layouts/home/Contact.tsx
@@ -1,5 +1,5 @@
import Link from "next/link";
-import { IoLogoGithub, IoLogoLinkedin, IoLogoTwitter } from "react-icons/io5";
+import { IoLogoDiscord, IoLogoGithub, IoLogoLinkedin, IoLogoTwitter } from "react-icons/io5";
export default function Contact() {
return (
@@ -13,8 +13,8 @@ export default function Contact() {
-
- Contact Me
+
+ Send me a Email
@@ -29,6 +29,9 @@ export default function Contact() {
+
+
+
diff --git a/src/components/layouts/home/Hero.tsx b/src/components/layouts/home/Hero.tsx
index 250b8bc..1c02048 100644
--- a/src/components/layouts/home/Hero.tsx
+++ b/src/components/layouts/home/Hero.tsx
@@ -1,14 +1,54 @@
import { motion } from "framer-motion";
import Link from "next/link";
-import { IoArrowDown } from "react-icons/io5";
+import { IoChevronDown } from "react-icons/io5";
+import { useEffect, useState } from "react";
export default function Hero() {
+ const [scrolled, setScrolled] = useState(false);
+ const [glitchActive, setGlitchActive] = useState(false);
+
+ // Track scroll position for animations
+ useEffect(() => {
+ const handleScroll = () => {
+ const scrollPosition = window.scrollY;
+ setScrolled(scrollPosition > 150);
+ };
+
+ window.addEventListener('scroll', handleScroll);
+ return () => window.removeEventListener('scroll', handleScroll);
+ }, []);
+
+ // Occasionally trigger glitch effect
+ useEffect(() => {
+ const glitchInterval = setInterval(() => {
+ setGlitchActive(true);
+ setTimeout(() => setGlitchActive(false), 500);
+ }, 8000);
+
+ return () => clearInterval(glitchInterval);
+ }, []);
+
return (
-
+
- {/* Background decoration elements */}
-
-
+ {/* Enhanced background decoration elements */}
+
+
+
+
+ {/* Enhanced glitching pixel grid */}
+
+
+
+ {/* Glitch overlay only appears when glitching */}
+ {glitchActive && (
+ <>
+
+
+
+ >
+ )}
+
+
+
+ Fullstack Developer
+
+
+
- Hi, I'm CodeMeAPixel
+ Hi, I'm
+ CodeMeAPixel
+
+ {/* Occasional glitch overlay on name */}
+ {glitchActive && (
+ CodeMeAPixel
+ )}
+
-
- View My Work
+
+ View My Work
+
Contact Me
+ {/* Simplified scroll indicator */}
-
-
-
+
+ Scroll
+
+
diff --git a/src/components/layouts/home/Projects.tsx b/src/components/layouts/home/Projects.tsx
index c44af42..ca07861 100644
--- a/src/components/layouts/home/Projects.tsx
+++ b/src/components/layouts/home/Projects.tsx
@@ -2,115 +2,156 @@ import { motion } from "framer-motion";
import Link from "next/link";
import { projects } from "@/data/projects";
import { ImageCarousel } from "@/components/ui/ImageCarousel";
+import { IoArrowForward } from "react-icons/io5";
+import { useState } from "react";
export default function Projects() {
+ const [hoveredProject, setHoveredProject] = useState(null);
+
return (
-
+
-
- Featured Projects
-
-
-
+
+ Featured Projects
+
+
+ Some of my recent work that I'm proud of
+
+
+
+
{projects.map((project, index) => (
-
setHoveredProject(project.id)}
+ onMouseLeave={() => setHoveredProject(null)}
+ onTouchStart={() => setHoveredProject(project.id)}
>
-
- {/* Inset frame effect - simplified */}
-
-
-
- {/* Image carousel */}
-
-
- {/* Seamless content transition */}
-
-
-
-
-
- {project.title}
-
-
-
- {project.description}
-
-
-
- {project.tags.map((tag) => (
-
- {tag}
-
- ))}
-
-
-
- {project.links.demo && (
-
+ {/* Top gradient accent bar */}
+
+
+
+ {/* Image carousel - with responsive height */}
+
+
+
+ {/* Content section with improved padding for mobile */}
+
+ {/* Tags with better mobile spacing */}
+
+ {project.tags.slice(0, 3).map(tag => (
+
+ {tag}
+
+ ))}
+ {project.tags.length > 3 && (
+
+ +{project.tags.length - 3}
+
+ )}
+
+
+ {/* Project title with improved responsive sizing */}
+
+
- Live Demo
- ↗
-
- )}
- {project.links.github && (
-
+
+
+ {/* Description with fewer lines on mobile */}
+
+ {project.description}
+
+
+ {/* Links section with better spacing on mobile */}
+
+
+ {project.links.demo && (
+
+ Live Demo
+ ↗
+
+ )}
+ {project.links.github && (
+
+ Code
+ ↗
+
+ )}
+
+
+ {/* View details button */}
+
- Source Code
- ↗
+ View details
+
- )}
-
+
+
+
+ {/* Touch highlight effect for mobile */}
+
))}
+
+
+
+ Explore all projects
+
+
+
);
diff --git a/src/components/layouts/links/LinkHubContent.tsx b/src/components/layouts/links/LinkHubContent.tsx
new file mode 100644
index 0000000..cdad0e5
--- /dev/null
+++ b/src/components/layouts/links/LinkHubContent.tsx
@@ -0,0 +1,284 @@
+"use client";
+
+import { useState } from 'react';
+import { motion } from 'framer-motion';
+import Image from 'next/image';
+import Link from 'next/link';
+import { LinkHubProfile, LinkItem, SocialLink } from '@/types/links';
+import * as IoIcons from 'react-icons/io5';
+import { IoArrowForward } from 'react-icons/io5';
+import PlaylistSection from './PlaylistSection';
+import { Profile, Playlist } from '@/data/linksData';
+import { BackgroundEffects } from '@/components/ui/BackgroundEffects';
+
+interface LinkHubContentProps {
+ profile: Profile;
+ playlist: Playlist;
+}
+
+export default function LinkHubContent({ profile, playlist }: LinkHubContentProps) {
+ const [activeCategory, setActiveCategory] = useState
(null);
+ const [showPlaylist, setShowPlaylist] = useState(false);
+
+ // Get the dynamic icon component
+ const getIconComponent = (iconName: string) => {
+ const IconComponent = (IoIcons as any)[iconName];
+ if (IconComponent) {
+ return ;
+ }
+ return null;
+ };
+
+ // Filter links by active category
+ const visibleLinks = activeCategory
+ ? profile.categories.find(cat => cat.id === activeCategory)?.links || []
+ : profile.featuredLinks;
+
+ return (
+
+
+
+ {/* Background gradient overlay */}
+
+
+ {/* Profile section */}
+
+
+
+
+
+
+ {/* Glow effect behind avatar */}
+
+
+ {/* Discord status indicator */}
+ {profile.discord && (
+
+
+ )}
+
+
+ {profile.name}
+ {profile.title}
+
+ {profile.bio}
+
+ {/* Social links */}
+
+ {profile.socialLinks.map(social => (
+
+ ))}
+
+
+
+ {/* Category filter - Modified to include playlist tab */}
+
+ {
+ setActiveCategory(null);
+ setShowPlaylist(false);
+ }}
+ >
+ Featured
+
+
+ {profile.categories.map(category => (
+ {
+ setActiveCategory(category.id);
+ setShowPlaylist(false);
+ }}
+ >
+ {category.name}
+
+ ))}
+
+ {/* Playlist Tab */}
+ setShowPlaylist(true)}
+ >
+ My Playlist
+
+
+
+ {/* Content Area - Either Links or Playlist */}
+ {!showPlaylist ? (
+ /* Links list */
+
+ {visibleLinks.map((link, index) => (
+
+ ))}
+
+ ) : (
+ /* Playlist Section */
+
+
+
+ )}
+
+ {/* Back to website link */}
+
+
+ Back to Website
+
+
+
+
+
+
+ );
+}
+
+// Social button component
+function SocialButton({ social, getIconComponent }: { social: SocialLink, getIconComponent: (iconName: string) => JSX.Element | null }) {
+ return (
+
+ {social.icon && getIconComponent(social.icon)}
+
+ );
+}
+
+// Link card component with enhanced animations
+function LinkCard({
+ link,
+ index,
+ getIconComponent
+}: {
+ link: LinkItem,
+ index: number,
+ getIconComponent: (iconName: string) => JSX.Element | null
+}) {
+ const [isHovered, setIsHovered] = useState(false);
+
+ return (
+ setIsHovered(true)}
+ onMouseLeave={() => setIsHovered(false)}
+ whileHover={{
+ y: -5,
+ boxShadow: '0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)'
+ }}
+ whileTap={{ scale: 0.98 }}
+ >
+ {/* Top gradient accent bar */}
+
+
+
+ {link.icon && (
+
+ {getIconComponent(link.icon)}
+
+ )}
+
+
+
+ {link.title}
+
+
+ {link.description && (
+
{link.description}
+ )}
+
+
+
+
+
+
+
+ {/* Subtle hover glow effect */}
+
+
+ {/* Subtle corner decoration */}
+
+
+ );
+}
diff --git a/src/components/layouts/links/MusicPlayer.tsx b/src/components/layouts/links/MusicPlayer.tsx
new file mode 100644
index 0000000..b115077
--- /dev/null
+++ b/src/components/layouts/links/MusicPlayer.tsx
@@ -0,0 +1,200 @@
+"use client";
+
+import { useState, useRef, useEffect } from 'react';
+import Image from 'next/image';
+import { motion } from 'framer-motion';
+import { IoPlaySharp, IoPauseSharp, IoPlaySkipBackSharp, IoPlaySkipForwardSharp, IoVolumeMuteOutline, IoVolumeMediumOutline } from 'react-icons/io5';
+
+interface MusicPlayerProps {
+ track: Track;
+ onNext: () => void;
+ onPrevious: () => void;
+ hasNext: boolean;
+ hasPrevious: boolean;
+}
+
+function MusicPlayer({ track, onNext, onPrevious, hasNext, hasPrevious }: MusicPlayerProps) {
+ const [isPlaying, setIsPlaying] = useState(false);
+ const [duration, setDuration] = useState(0);
+ const [currentTime, setCurrentTime] = useState(0);
+ const [volume, setVolume] = useState(0.7);
+ const [isMuted, setIsMuted] = useState(false);
+ const [prevVolume, setPrevVolume] = useState(0.7);
+ const audioRef = useRef(null);
+ const progressBarRef = useRef(null);
+
+ // Load audio when track changes
+ useEffect(() => {
+ if (audioRef.current) {
+ audioRef.current.pause();
+ setIsPlaying(false);
+ setCurrentTime(0);
+
+ const loadAudio = () => {
+ if (audioRef.current) {
+ setDuration(audioRef.current.duration);
+ // Autoplay when loaded
+ audioRef.current.play().then(() => {
+ setIsPlaying(true);
+ }).catch(error => {
+ console.warn("Autoplay prevented:", error);
+ // Handle the error appropriately (e.g., show a message to the user)
+ });
+ }
+ };
+
+ audioRef.current.addEventListener('loadedmetadata', loadAudio);
+ return () => {
+ if (audioRef.current) {
+ audioRef.current.removeEventListener('loadedmetadata', loadAudio);
+ }
+ };
+ }
+ }, [track]);
+
+ // Handle play/pause
+ const togglePlay = () => {
+ if (audioRef.current) {
+ if (isPlaying) {
+ audioRef.current.pause();
+ } else {
+ audioRef.current.play();
+ }
+ setIsPlaying(!isPlaying);
+ }
+ };
+
+ // Update progress bar
+ const updateProgress = () => {
+ if (audioRef.current) {
+ setCurrentTime(audioRef.current.currentTime);
+ // Auto next track when current one ends
+ if (audioRef.current.ended && hasNext) {
+ onNext();
+ }
+ }
+ };
+
+ // Handle seek
+ const handleSeek = (e: React.MouseEvent) => {
+ if (progressBarRef.current && audioRef.current) {
+ const progressBar = progressBarRef.current;
+ const rect = progressBar.getBoundingClientRect();
+ const percent = (e.clientX - rect.left) / progressBar.offsetWidth;
+ const seekTime = percent * duration;
+ audioRef.current.currentTime = seekTime;
+ setCurrentTime(seekTime);
+ }
+ };
+
+ // Handle volume change
+ const handleVolumeChange = (e: React.ChangeEvent) => {
+ const newVolume = parseFloat(e.target.value);
+ setVolume(newVolume);
+ if (audioRef.current) {
+ audioRef.current.volume = newVolume;
+ }
+ if (newVolume === 0) {
+ setIsMuted(true);
+ } else {
+ setIsMuted(false);
+ }
+ };
+
+ // Toggle mute
+ const toggleMute = () => {
+ if (audioRef.current) {
+ if (isMuted) {
+ setVolume(prevVolume);
+ audioRef.current.volume = prevVolume;
+ } else {
+ setPrevVolume(volume);
+ setVolume(0);
+ audioRef.current.volume = 0;
+ }
+ setIsMuted(!isMuted);
+ }
+ };
+
+ // Format time in MM:SS
+ const formatTime = (time: number): string => {
+ const minutes = Math.floor(time / 60);
+ const seconds = Math.floor(time % 60);
+ return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
+ };
+
+ return (
+
+
setDuration(audioRef.current?.duration || 0)}
+ />
+
+
+
+
+
+
{track.title}
+
{track.artist}
+
+
+
+
+
+
+
+
+ {formatTime(currentTime)}
+ {formatTime(duration)}
+
+
+
+
+
+ {isMuted ? : }
+
+
+
+
+
+
+
+
+
+ {isPlaying ? : }
+
+
+
+
+
+
+
+ );
+}
+
+export default MusicPlayer;
diff --git a/src/components/layouts/links/PlaylistSection.tsx b/src/components/layouts/links/PlaylistSection.tsx
new file mode 100644
index 0000000..3e696dc
--- /dev/null
+++ b/src/components/layouts/links/PlaylistSection.tsx
@@ -0,0 +1,223 @@
+"use client";
+
+import { useState, useMemo } from 'react';
+import Image from 'next/image';
+import { motion } from 'framer-motion';
+import { IoMusicalNotes, IoPlay, IoPause, IoSearch, IoChevronBack, IoChevronForward } from 'react-icons/io5';
+import MusicPlayer from './MusicPlayer';
+import { Track } from '@/types/links';
+
+interface PlaylistSectionProps {
+ playlist: any;
+}
+
+const TRACKS_PER_PAGE = 6;
+
+const TrackItem = ({ track, onPlay, isActive }: { track: Track; onPlay: (track: Track) => void; isActive: boolean }) => {
+ return (
+ onPlay(track)}
+ >
+
+
+
+ {isActive ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {track.title}
+
+
+ {track.artist}
+
+
+
+ );
+};
+
+function PlaylistSection({ playlist }: PlaylistSectionProps) {
+ const [currentTrack, setCurrentTrack] = useState(null);
+ const [activeGenre, setActiveGenre] = useState('all');
+ const [searchTerm, setSearchTerm] = useState('');
+ const [currentPage, setCurrentPage] = useState(1);
+
+ // Extract unique genres from tracks
+ const genres = ['all', ...new Set(playlist.tracks.map((track: Track) => track.genre || 'Uncategorized'))];
+
+ // Filter tracks based on active genre and search term
+ const filteredTracks = playlist.tracks.filter((track: Track) => {
+ const matchesGenre = activeGenre === 'all' || track.genre === activeGenre || (!track.genre && activeGenre === 'Uncategorized');
+ const matchesSearch = track.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ track.artist.toLowerCase().includes(searchTerm.toLowerCase());
+ return matchesGenre && matchesSearch;
+ });
+
+ const totalPages = Math.ceil(filteredTracks.length / TRACKS_PER_PAGE);
+
+ const paginatedTracks = useMemo(() => {
+ const startIndex = (currentPage - 1) * TRACKS_PER_PAGE;
+ return filteredTracks.slice(startIndex, startIndex + TRACKS_PER_PAGE);
+ }, [filteredTracks, currentPage]);
+
+ const handlePlay = (track: Track) => {
+ if (currentTrack?.id === track.id) {
+ setCurrentTrack(null); // Toggle off if clicking the same track
+ } else {
+ setCurrentTrack(track);
+ }
+ };
+
+ const handleNext = () => {
+ if (!currentTrack) return;
+
+ const currentIndex = filteredTracks.findIndex(track => track.id === currentTrack.id);
+ if (currentIndex < filteredTracks.length - 1) {
+ setCurrentTrack(filteredTracks[currentIndex + 1]);
+ }
+ };
+
+ const handlePrevious = () => {
+ if (!currentTrack) return;
+
+ const currentIndex = filteredTracks.findIndex(track => track.id === currentTrack.id);
+ if (currentIndex > 0) {
+ setCurrentTrack(filteredTracks[currentIndex - 1]);
+ }
+ };
+
+ const handlePageChange = (page: number) => {
+ setCurrentPage(page);
+ };
+
+ // Determine if next/previous buttons should be enabled
+ const currentIndex = currentTrack ?
+ filteredTracks.findIndex(track => track.id === currentTrack.id) : -1;
+
+ const hasNext = currentIndex < filteredTracks.length - 1;
+ const hasPrevious = currentIndex > 0;
+
+ return (
+
+
+
+
{playlist.title}
+
{playlist.description}
+
+
+ {/* Search and filter UI */}
+
+
+
setSearchTerm(e.target.value)}
+ />
+
+
+
+
+
+
+ {genres.map(genre => (
+ setActiveGenre(genre)}
+ className={`px-2 py-0.5 text-xs rounded-full transition-colors ${activeGenre === genre
+ ? 'bg-primary-800/40 text-primary-300 border border-primary-700/40'
+ : 'bg-card text-color-text-muted border border-color-border hover:bg-card-alt'
+ }`}
+ >
+ {genre.charAt(0).toUpperCase() + genre.slice(1)}
+
+ ))}
+
+
+
+ {/* Current Playing Track */}
+ {currentTrack && (
+
+
+
+ )}
+
+ {/* Track list */}
+
+ {paginatedTracks.length > 0 ? (
+ paginatedTracks.map((track: Track) => (
+
+ ))
+ ) : (
+
+ No tracks found. Try adjusting your filters.
+
+ )}
+
+
+ {/* Pagination controls */}
+ {totalPages > 1 && (
+
+ handlePageChange(currentPage - 1)}
+ disabled={currentPage === 1}
+ className="p-2 rounded-md bg-primary-700/30 hover:bg-primary-600/40 text-primary-300 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
+ >
+
+
+
+ {Array.from({ length: totalPages }, (_, i) => i + 1).map(pageNumber => (
+ handlePageChange(pageNumber)}
+ className={`px-2 py-1 rounded-md transition-colors ${currentPage === pageNumber
+ ? 'bg-primary-500 text-white'
+ : 'bg-card hover:bg-card-alt text-color-text-muted'
+ }`}
+ >
+ {pageNumber}
+
+ ))}
+
+ handlePageChange(currentPage + 1)}
+ disabled={currentPage === totalPages}
+ className="p-2 rounded-md bg-primary-700/30 hover:bg-primary-600/40 text-primary-300 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
+ >
+
+
+
+ )}
+
+
+ );
+}
+
+export default PlaylistSection;
diff --git a/src/components/layouts/referrals/ReferralsContent.tsx b/src/components/layouts/referrals/ReferralsContent.tsx
new file mode 100644
index 0000000..a49c906
--- /dev/null
+++ b/src/components/layouts/referrals/ReferralsContent.tsx
@@ -0,0 +1,429 @@
+"use client";
+
+import { useState } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import Image from 'next/image';
+import { Referral, ReferralCategory } from '@/types/referrals';
+import * as IoIcons from 'react-icons/io5';
+import { IoClipboardOutline, IoCheckmarkOutline, IoArrowForward, IoSearchOutline, IoClose } from 'react-icons/io5';
+
+interface ReferralsContentProps {
+ referrals: Referral[];
+ categories: ReferralCategory[];
+}
+
+export default function ReferralsContent({ referrals, categories }: ReferralsContentProps) {
+ const [activeCategory, setActiveCategory] = useState(null);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [copiedCode, setCopiedCode] = useState(null);
+
+ // Get the dynamic icon component
+ const getIconComponent = (iconName: string) => {
+ const IconComponent = (IoIcons as any)[iconName];
+ if (IconComponent) {
+ return ;
+ }
+ return null;
+ };
+
+ // Filter referrals based on active category and search query
+ const filteredReferrals = referrals
+ .filter(referral =>
+ !activeCategory || referral.category === activeCategory
+ )
+ .filter(referral => {
+ if (!searchQuery) return true;
+
+ const query = searchQuery.toLowerCase();
+ return (
+ referral.title.toLowerCase().includes(query) ||
+ referral.description.toLowerCase().includes(query) ||
+ referral.company.toLowerCase().includes(query) ||
+ (referral.code && referral.code.toLowerCase().includes(query))
+ );
+ });
+
+ // Handle copy code to clipboard
+ const handleCopyCode = async (code: string) => {
+ try {
+ await navigator.clipboard.writeText(code);
+ setCopiedCode(code);
+
+ // Reset copied state after 2 seconds
+ setTimeout(() => {
+ setCopiedCode(null);
+ }, 2000);
+ } catch (err) {
+ console.error('Failed to copy code: ', err);
+ }
+ };
+
+ return (
+
+ {/* Background grid effect */}
+
+
+ {/* Decorative elements */}
+
+
+
+ {/* Floating elements */}
+
+
+
+
+
+
+ Referrals & Offers
+
+ Use these referral links and promo codes to get discounts on products and services I recommend.
+ I may receive a commission or credit when you use these links at no extra cost to you.
+
+
+
+
+ {/* Search bar */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ placeholder="Search referrals..."
+ className="w-full pl-12 pr-10 py-3 rounded-xl bg-card border border-color-border text-color-text placeholder:text-color-text-muted text-sm focus:ring-1 focus:ring-primary-500 focus:border-primary-500 outline-none"
+ />
+ {searchQuery && (
+ setSearchQuery('')}
+ aria-label="Clear search"
+ >
+
+
+ )}
+
+
+ {/* Category tabs */}
+
+ setActiveCategory(null)}
+ >
+ All Referrals
+
+
+ {categories.map(category => (
+ setActiveCategory(category.id)}
+ >
+ {category.icon && getIconComponent(category.icon)}
+ {category.name}
+
+ ))}
+
+
+
+ {/* Display referrals */}
+ {filteredReferrals.length > 0 ? (
+
+ {filteredReferrals.map((referral, index) => (
+
+ ))}
+
+ ) : (
+
+ No referrals found
+
+ Try adjusting your search or selecting a different category.
+
+
+ {searchQuery && (
+ setSearchQuery('')}
+ className="btn-secondary text-sm py-2"
+ >
+ Clear Search
+
+ )}
+ {activeCategory !== null && (
+ setActiveCategory(null)}
+ className="btn-secondary text-sm py-2"
+ >
+ Show All Referrals
+
+ )}
+
+
+ )}
+
+
+ );
+}
+
+// Enhanced ReferralCard component with more animation
+function ReferralCard({
+ referral,
+ index,
+ copiedCode,
+ onCopyCode
+}: {
+ referral: Referral;
+ index: number;
+ copiedCode: string | null;
+ onCopyCode: (code: string) => void;
+}) {
+ const [isHovered, setIsHovered] = useState(false);
+ const [imageError, setImageError] = useState(false);
+
+ return (
+ setIsHovered(true)}
+ onMouseLeave={() => setIsHovered(false)}
+ whileHover={{
+ y: -5,
+ boxShadow: '0 15px 30px -5px rgba(0, 0, 0, 0.1), 0 10px 15px -5px rgba(0, 0, 0, 0.05)'
+ }}
+ >
+
+ {/* Top gradient accent bar */}
+
+
+ {/* New badge */}
+ {referral.new && (
+
+ New
+
+ )}
+
+ {/* Banner image section */}
+
+ {referral.bannerImage && !imageError ? (
+ <>
+
setImageError(true)}
+ />
+
+ >
+ ) : (
+ // Fallback gradient background when no banner
+
+ {/* Decorative background elements */}
+
+
+
+ {/* Company name as text when no image */}
+
+
+ {referral.company}
+
+
+
+ )}
+
+ {/* Category badge */}
+
+ {referral.categoryName}
+
+
+
+
+ {/* Custom color accent based on referral.color if provided */}
+ {referral.color && (
+
+ )}
+
+ {/* Title */}
+
+ {referral.title}
+
+
+ {/* Description */}
+
+ {referral.description}
+
+
+ {/* Discount highlight with improved styling */}
+ {referral.discount && (
+
+ {/* Subtle accent */}
+
+
{referral.discount}
+
+ )}
+
+ {/* Benefits list - if available */}
+ {referral.benefits && referral.benefits.length > 0 && (
+
+
Benefits:
+
+ {referral.benefits.slice(0, 2).map((benefit, i) => (
+
+ •
+ {benefit}
+
+ ))}
+ {referral.benefits.length > 2 && (
+
+ +{referral.benefits.length - 2} more benefits
+
+ )}
+
+
+ )}
+
+ {/* Promo code with improved styling */}
+ {referral.code && (
+
+
Promo Code:
+
+
+ {referral.code}
+
+
onCopyCode(referral.code!)}
+ className={`px-3 py-2 border border-primary-700/40 rounded-r-lg transition-colors flex items-center justify-center ${copiedCode === referral.code
+ ? 'bg-green-800/40 text-green-300 border-green-700/40'
+ : 'bg-primary-800/40 hover:bg-primary-800/60 text-primary-300'
+ }`}
+ title={copiedCode === referral.code ? "Copied!" : "Copy code"}
+ >
+ {copiedCode === referral.code ? (
+
+ ) : (
+
+ )}
+
+
+
+ )}
+
+ {/* Visit button with enhanced animation */}
+
+ Get Offer
+
+
+
+
+ {/* Subtle hover glow effect */}
+
+
+ {/* Subtle corner decoration */}
+
+
+
+ );
+}
diff --git a/src/components/layouts/skills/SkillsContent.tsx b/src/components/layouts/skills/SkillsContent.tsx
new file mode 100644
index 0000000..a6cc4c3
--- /dev/null
+++ b/src/components/layouts/skills/SkillsContent.tsx
@@ -0,0 +1,312 @@
+"use client";
+
+import { useState } from 'react';
+import { motion } from 'framer-motion';
+import { SkillCategory } from '@/data/skills';
+import { TechIcon } from '@/components/ui/TechIcon';
+import { IoSearch, IoBookmarkOutline, IoCodeSlashOutline, IoServerOutline, IoCloudUploadOutline, IoHammerOutline, IoLayersOutline } from 'react-icons/io5';
+import { BackgroundEffects } from '@/components/ui/BackgroundEffects';
+import * as Tabs from '@radix-ui/react-tabs';
+
+interface SkillsContentProps {
+ skills: SkillCategory[];
+}
+
+export default function SkillsContent({ skills }: SkillsContentProps) {
+ const [searchQuery, setSearchQuery] = useState('');
+ const [activeCategory, setActiveCategory] = useState('all');
+
+ // Get all unique skill tags across all categories
+ const allSkills = skills.flatMap(category =>
+ category.skills.map(skill => skill.name.toLowerCase())
+ );
+ const uniqueSkills = Array.from(new Set(allSkills));
+
+ // Filter skills based on search query
+ const filteredSkills = skills.map(category => ({
+ ...category,
+ skills: category.skills.filter(skill =>
+ skill.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ (skill.description && skill.description.toLowerCase().includes(searchQuery.toLowerCase()))
+ )
+ }));
+
+ // Get icon component based on category name
+ const getCategoryIcon = (name: string) => {
+ const icons: Record = {
+ 'Frontend Development': ,
+ 'Backend Development': ,
+ 'Database & Storage': ,
+ 'DevOps & Deployment': ,
+ 'Tools & Utilities': ,
+ 'UI Libraries & Frameworks':
+ };
+
+ return icons[name] || ;
+ };
+
+ return (
+
+
+
+
+
+ Skills & Expertise
+
+
+ I'e worked with a variety of technologies across the stack.
+ Here's a comprehensive overview of my technical skills and proficiency levels.
+
+
+
+ {/* Search and filter section */}
+
+
+
+
+
+
setSearchQuery(e.target.value)}
+ />
+
+
+
+ {/* Skills Tabs */}
+
+
+
+
+ All Categories
+
+
+ {skills.map((category, index) => (
+
+ {getCategoryIcon(category.name)}
+ {category.name}
+
+ ))}
+
+
+
+
+ {filteredSkills.map((category, categoryIndex) => (
+ category.skills.length > 0 && (
+
+
+
+ {getCategoryIcon(category.name)}
+
+
{category.name}
+
+ {category.description}
+
+
+ {category.skills.map((skill, skillIndex) => (
+
+ {/* Subtle decoration */}
+
+
+
+
+
+
+
+
+ {skill.name}
+
+ {skill.level && (
+
+ {Array.from({ length: 5 }).map((_, i) => (
+
+ ))}
+
+ )}
+
+
+
+ {skill.description && (
+
+ {skill.description}
+
+ )}
+
+ ))}
+
+
+ )
+ ))}
+
+
+ {skills.map((category, categoryIndex) => (
+
+
+
+
+ {getCategoryIcon(category.name)}
+
+
{category.name}
+
+ {category.description}
+
+
+ {filteredSkills.find(c => c.name === category.name)?.skills.length === 0 ? (
+
+ No skills found
+ Try a different search query or category.
+ setSearchQuery('')}
+ className="btn-secondary text-sm py-2"
+ >
+ Clear search
+
+
+ ) : (
+
+ {filteredSkills.find(c => c.name === category.name)?.skills.map((skill, skillIndex) => (
+
+ {/* Subtle decoration */}
+
+
+
+
+
+
+
+
+ {skill.name}
+
+ {skill.level && (
+
+ {Array.from({ length: 5 }).map((_, i) => (
+
+ ))}
+
+ )}
+
+
+
+ {skill.description && (
+
+ {skill.description}
+
+ )}
+
+ ))}
+
+ )}
+
+ ))}
+
+
+ {/* Skills Statistics */}
+
+
+
+
{skills.reduce((acc, category) => acc + category.skills.length, 0)}
+
Total Skills
+
+
+
{skills.length}
+
Skill Categories
+
+
+
{skills.flatMap(c => c.skills).filter(s => s.level === 5).length}
+
Expert Level Skills
+
+
+
{uniqueSkills.length}
+
Unique Technologies
+
+
+
+
+
+ );
+}
diff --git a/src/components/mdx/MDXComponents.tsx b/src/components/mdx/MDXComponents.tsx
new file mode 100644
index 0000000..149533d
--- /dev/null
+++ b/src/components/mdx/MDXComponents.tsx
@@ -0,0 +1,167 @@
+"use client";
+
+import React from 'react';
+import Image from 'next/image';
+import Link from 'next/link';
+import { motion } from 'framer-motion';
+
+// Custom Link component that wraps Next.js Link
+const CustomLink = ({ href, children, ...props }: React.ComponentPropsWithoutRef<"a">) => {
+ const isInternalLink = href && (href.startsWith('/') || href.startsWith('#'));
+
+ if (isInternalLink) {
+ return (
+
+ {children}
+
+ );
+ }
+
+ return (
+
+ {children}
+ ↗
+
+ );
+};
+
+// Custom heading components with animations
+const headingVariants = {
+ hidden: { opacity: 0, y: 10 },
+ visible: { opacity: 1, y: 0, transition: { duration: 0.5 } }
+};
+
+const H1 = (props: React.ComponentPropsWithoutRef<"h1">) => (
+
+);
+
+const H2 = (props: React.ComponentPropsWithoutRef<"h2">) => (
+
+);
+
+const H3 = (props: React.ComponentPropsWithoutRef<"h3">) => (
+
+);
+
+// Custom code block with syntax highlighting
+const CodeBlock = ({ children, className }: { children: React.ReactNode; className?: string }) => {
+ const language = className ? className.replace(/language-/, '') : '';
+
+ return (
+
+
+ {language}
+
+
+ {children}
+
+
+ );
+};
+
+// Custom image component
+const CustomImage = (props: React.ComponentPropsWithoutRef<"img"> & { width?: number, height?: number }) => {
+ // If the image is served from an external domain, use the default img tag
+ if (props.src && (props.src.startsWith('http://') || props.src.startsWith('https://'))) {
+ return (
+
+
+
+ );
+ }
+
+ // Otherwise use Next.js optimized Image component
+ return (
+
+
+
+ );
+};
+
+// Custom blockquote component
+const BlockQuote = (props: React.ComponentPropsWithoutRef<"blockquote">) => (
+
+);
+
+// Custom list items
+const UL = (props: React.ComponentPropsWithoutRef<"ul">) => (
+
+);
+
+const OL = (props: React.ComponentPropsWithoutRef<"ol">) => (
+
+);
+
+const LI = (props: React.ComponentPropsWithoutRef<"li">) => (
+
+);
+
+// Custom table components
+const Table = (props: React.ComponentPropsWithoutRef<"table">) => (
+
+);
+
+const TH = (props: React.ComponentPropsWithoutRef<"th">) => (
+
+);
+
+const TD = (props: React.ComponentPropsWithoutRef<"td">) => (
+
+);
+
+export const MDXComponents = {
+ h1: H1,
+ h2: H2,
+ h3: H3,
+ a: CustomLink,
+ img: CustomImage,
+ pre: CodeBlock,
+ blockquote: BlockQuote,
+ ul: UL,
+ ol: OL,
+ li: LI,
+ table: Table,
+ th: TH,
+ td: TD
+};
diff --git a/src/components/mdx/MDXContent.tsx b/src/components/mdx/MDXContent.tsx
new file mode 100644
index 0000000..ee2fbcc
--- /dev/null
+++ b/src/components/mdx/MDXContent.tsx
@@ -0,0 +1,198 @@
+"use client";
+
+import React, { useState, useEffect } from 'react';
+import { marked } from 'marked';
+import DOMPurify from 'dompurify';
+import hljs from 'highlight.js';
+
+// Import core highlight.js and a theme
+import 'highlight.js/styles/night-owl.css';
+
+// Configure marked with highlight.js for syntax highlighting
+marked.setOptions({
+ highlight: function (code, lang) {
+ if (lang && hljs.getLanguage(lang)) {
+ try {
+ // Apply syntax highlighting first
+ const highlighted = hljs.highlight(code, { language: lang }).value;
+ return highlighted;
+ } catch (error) {
+ console.error("Highlight.js error:", error);
+ }
+ }
+ return hljs.highlightAuto(code).value;
+ },
+ gfm: true,
+ breaks: true,
+ smartLists: true
+});
+
+// Custom renderer
+const renderer = new marked.Renderer();
+
+// Add IDs to headings for linking
+renderer.heading = function (text, level) {
+ // Convert text to string and ensure it's safe to use as an ID
+ const textStr = String(text || '');
+ const id = textStr.toLowerCase()
+ .replace(/[^\w\s-]/g, '')
+ .replace(/\s+/g, '-');
+
+ return `
+
+ ${textStr}
+
+ `;
+};
+
+// Custom code renderer to add language label and copy button
+renderer.code = function (code, language) {
+ const lang = language || 'text';
+ const langDisplay = lang === 'jsx' ? 'React' :
+ lang === 'tsx' ? 'React+TS' :
+ lang === 'js' ? 'JavaScript' :
+ lang.charAt(0).toUpperCase() + lang.slice(1);
+
+ // Process the code with highlight.js
+ const highlightedCode = hljs.highlight(code, { language: lang }).value;
+
+ // Wrap in custom container with line numbers support
+ return `
+
+ `;
+};
+
+// Set the custom renderer
+marked.use({ renderer });
+
+interface MDXContentProps {
+ source: string;
+}
+
+export function MDXContent({ source }: MDXContentProps) {
+ const [html, setHtml] = useState('');
+
+ useEffect(() => {
+ try {
+ // Log source content for debugging
+ console.log("MDX source length:", source?.length);
+
+ if (!source) {
+ console.error("Empty source content received");
+ setHtml('No content available
');
+ return;
+ }
+
+ // Convert markdown to HTML
+ const renderedHtml = marked.parse(source);
+
+ // Sanitize HTML for security (this removes potentially harmful scripts)
+ const sanitizedHtml = DOMPurify.sanitize(renderedHtml);
+
+ setHtml(sanitizedHtml);
+ } catch (error) {
+ console.error("Error rendering markdown:", error);
+ setHtml(`Error rendering content: ${error.message}
`);
+ }
+ }, [source]);
+
+ // Add copy functionality and line numbers to code blocks after rendering
+ useEffect(() => {
+ if (!html) return;
+
+ try {
+ // Add copy button functionality
+ const copyButtons = document.querySelectorAll('.copy-button');
+
+ copyButtons.forEach((button) => {
+ button.addEventListener('click', async () => {
+ try {
+ const encodedCode = (button as HTMLElement).dataset.code || '';
+ const code = decodeURIComponent(encodedCode);
+ await navigator.clipboard.writeText(code);
+
+ // Update UI for feedback
+ const copyIcon = button.querySelector('.copy-icon');
+ const copiedIcon = button.querySelector('.copied-icon');
+
+ if (copyIcon && copiedIcon) {
+ copyIcon.setAttribute('style', 'display:none');
+ copiedIcon.setAttribute('style', 'display:inline-flex');
+ button.classList.add('copied');
+
+ setTimeout(() => {
+ copyIcon.setAttribute('style', 'display:inline-flex');
+ copiedIcon.setAttribute('style', 'display:none');
+ button.classList.remove('copied');
+ }, 2000);
+ }
+ } catch (err) {
+ console.error('Failed to copy code:', err);
+ }
+ });
+ });
+
+ // Add line numbers to code blocks with improved spacing
+ const codeBlocks = document.querySelectorAll('.has-line-numbers code');
+ codeBlocks.forEach((block) => {
+ const lines = (block.textContent || '').split('\n');
+ const linesCount = lines.length;
+
+ // Skip if we already added line numbers
+ if (block.parentElement?.querySelector('.line-numbers-rows')) return;
+
+ // Create line numbers container
+ const lineNumbersContainer = document.createElement('div');
+ lineNumbersContainer.className = 'line-numbers-rows';
+
+ // Generate line numbers
+ for (let i = 1; i <= linesCount; i++) {
+ const lineSpan = document.createElement('span');
+ lineSpan.setAttribute('data-line', i.toString());
+ lineNumbersContainer.appendChild(lineSpan);
+ }
+
+ // Add line numbers to the pre element (parent of code)
+ const pre = block.parentElement;
+ if (pre) {
+ pre.appendChild(lineNumbersContainer);
+ pre.classList.add('with-line-numbers');
+ }
+ });
+ } catch (error) {
+ console.error("Error enhancing code blocks:", error);
+ }
+ }, [html]);
+
+ return (
+
+ );
+}
diff --git a/src/components/mdx/PrismCode.tsx b/src/components/mdx/PrismCode.tsx
new file mode 100644
index 0000000..31bc288
--- /dev/null
+++ b/src/components/mdx/PrismCode.tsx
@@ -0,0 +1,39 @@
+"use client";
+
+import React, { useEffect } from 'react';
+import Prism from 'prismjs';
+import 'prismjs/components/prism-javascript';
+import 'prismjs/components/prism-jsx';
+import 'prismjs/components/prism-typescript';
+import 'prismjs/components/prism-tsx';
+import 'prismjs/components/prism-css';
+import 'prismjs/components/prism-scss';
+import 'prismjs/components/prism-bash';
+import 'prismjs/components/prism-markdown';
+import 'prismjs/components/prism-json';
+import 'prismjs/components/prism-python';
+import 'prismjs/components/prism-php';
+import 'prismjs/components/prism-java';
+import 'prismjs/components/prism-c';
+import 'prismjs/components/prism-cpp';
+import 'prismjs/components/prism-csharp';
+import 'prismjs/components/prism-yaml';
+
+interface PrismCodeProps {
+ code: string;
+ language: string;
+}
+
+export function PrismCode({ code, language }: PrismCodeProps) {
+ useEffect(() => {
+ if (typeof window !== 'undefined') {
+ Prism.highlightAll();
+ }
+ }, [code, language]);
+
+ return (
+
+ {code}
+
+ );
+}
diff --git a/src/components/projects/ProjectDetail.tsx b/src/components/projects/ProjectDetail.tsx
new file mode 100644
index 0000000..59b8ccd
--- /dev/null
+++ b/src/components/projects/ProjectDetail.tsx
@@ -0,0 +1,412 @@
+"use client";
+
+import { motion } from 'framer-motion';
+import Link from 'next/link';
+import { Project } from '@/types/project';
+import { useState, useEffect } from 'react';
+import { ImageCarousel } from '@/components/ui/ImageCarousel';
+import { IoArrowBack, IoCalendarOutline, IoCodeSlash, IoLogoGithub, IoGlobeOutline, IoDocumentText, IoPersonOutline, IoPeopleOutline } from 'react-icons/io5';
+import { TechIcon } from '@/components/ui/TechIcon';
+import { MDXRemote } from 'next-mdx-remote';
+import { serialize } from 'next-mdx-remote/serialize';
+import { BackgroundEffects } from '@/components/ui/BackgroundEffects';
+
+interface ProjectDetailProps {
+ project: Project;
+}
+
+export default function ProjectDetail({ project }: ProjectDetailProps) {
+ const [activeTab, setActiveTab] = useState<'overview' | 'challenges' | 'technologies'>('overview');
+ const [activeTestimonial, setActiveTestimonial] = useState(0);
+ const [serializedContent, setSerializedContent] = useState(null);
+ const [showAllFeatures, setShowAllFeatures] = useState(false);
+
+ useEffect(() => {
+ // Convert single testimonial to array format for backward compatibility
+ if (project.testimonial && (!project.testimonials || project.testimonials.length === 0)) {
+ project.testimonials = [project.testimonial];
+ }
+
+ // Serialize markdown content
+ if (project.longDescription) {
+ serialize(project.longDescription).then(setSerializedContent);
+ }
+ }, [project]);
+
+ // MDX components using your existing MDX setup
+ const mdxComponents = {
+ h1: (props: any) => ,
+ h2: (props: any) => ,
+ h3: (props: any) => ,
+ p: (props: any) =>
,
+ ul: (props: any) => ,
+ ol: (props: any) => ,
+ li: (props: any) => ,
+ blockquote: (props: any) => (
+
+ ),
+ a: (props: any) => (
+
+ ),
+ code: (props: any) => (
+
+ ),
+ pre: (props: any) => (
+
+ ),
+ table: (props: any) => (
+
+ ),
+ thead: (props: any) => ,
+ th: (props: any) => (
+
+ ),
+ td: (props: any) => (
+
+ ),
+ };
+
+ return (
+
+
+
+
+
+
Back to all projects
+
+
+
+
+ {project.title}
+
+
+ {project.date && (
+
+
+
+ {new Date(project.date).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long'
+ })}
+
+
+ )}
+
+ {project.role && (
+
+
+ {project.role}
+
+ )}
+
+ {project.teamSize && (
+
+
+ Team of {project.teamSize}
+
+ )}
+
+
+ {project.tags.map(tag => (
+
+ {tag}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {project.keyFeatures && (
+
+
Key Features
+
+ {project.keyFeatures.slice(0, showAllFeatures ? project.keyFeatures.length : 2).map((feature, index) => (
+ {feature}
+ ))}
+
+ {project.keyFeatures.length > 2 && (
+
setShowAllFeatures(!showAllFeatures)}
+ className="mt-3 text-sm text-primary-400 hover:text-primary-300 transition-colors underline"
+ >
+ {showAllFeatures ? 'Show less' : `View ${project.keyFeatures.length - 2} more`}
+
+ )}
+
+ )}
+
+
+
+
+
+ setActiveTab('overview')}
+ className={`px-6 py-3 text-sm font-medium border-b-2 transition-colors ${activeTab === 'overview'
+ ? 'border-primary-400 text-primary-300'
+ : 'border-transparent text-color-text-muted hover:text-color-text'
+ }`}
+ >
+ Overview
+
+
+ {(project.challenges || project.solutions) && (
+ setActiveTab('challenges')}
+ className={`px-6 py-3 text-sm font-medium border-b-2 transition-colors ${activeTab === 'challenges'
+ ? 'border-primary-400 text-primary-300'
+ : 'border-transparent text-color-text-muted hover:text-color-text'
+ }`}
+ >
+ Challenges & Solutions
+
+ )}
+
+ {project.technologies && (
+ setActiveTab('technologies')}
+ className={`px-6 py-3 text-sm font-medium border-b-2 transition-colors ${activeTab === 'technologies'
+ ? 'border-primary-400 text-primary-300'
+ : 'border-transparent text-color-text-muted hover:text-color-text'
+ }`}
+ >
+ Technologies
+
+ )}
+
+
+
+ {activeTab === 'overview' && (
+
+ {/* Render MDX for long description or fallback to regular description */}
+
+ {serializedContent && project.longDescription ? (
+
+ ) : (
+
+ {project.description}
+
+ )}
+
+
+ {/* Testimonials Section - Now supports multiple testimonials */}
+ {project.testimonials && project.testimonials.length > 0 && (
+
+
Client Testimonials
+
+ {project.testimonials.length > 1 && (
+
+ {project.testimonials.map((_, index) => (
+ setActiveTestimonial(index)}
+ className={`w-2.5 h-2.5 rounded-full transition-all ${index === activeTestimonial
+ ? "bg-primary-400 hover:bg-primary-600/60"
+ : "bg-primary-700/40 hover:bg-primary-600/60"
+ }`}
+ aria-label={`View testimonial ${index + 1}`}
+ />
+ ))}
+
+ )}
+
+ {/* Decorative elements */}
+
+
+ {/* Large quote mark backgrounds - positioned further away from content */}
+ "
+ "
+
+ {/* Added padding to prevent overlap with quote marks */}
+
+ "{project.testimonials[activeTestimonial].quote}"
+
+
+ {/* Avatar placeholder */}
+
+
+ {project.testimonials[activeTestimonial].author.charAt(0)}
+
+
+
+
+ {project.testimonials[activeTestimonial].author}
+
+ {project.testimonials[activeTestimonial].position && (
+
+ {project.testimonials[activeTestimonial].position}
+
+ )}
+
+
+
+
+
+
+ )}
+
+ )}
+ {activeTab === 'challenges' && (
+
+ {project.challenges && (
+
+
+
+
+
+ Challenges
+
+
+ {project.challenges.map((challenge, index) => (
+ {challenge}
+ ))}
+
+
+ )}
+ {project.solutions && (
+
+
+
+
+
+ Solutions
+
+
+ {project.solutions.map((solution, index) => (
+ {solution}
+ ))}
+
+
+ )}
+
+ )}
+ {activeTab === 'technologies' && project.technologies && (
+
+ {project.technologies.map((tech, index) => (
+
+ {/* Gradient background */}
+
+ {/* Tech icon */}
+
+ {/* Tech name and description */}
+
+
{tech.name}
+ {tech.description && (
+
{tech.description}
+ )}
+
+ {/* Decorative element */}
+
+
+ ))}
+
+ )}
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/projects/ProjectsContent.tsx b/src/components/projects/ProjectsContent.tsx
new file mode 100644
index 0000000..1439b4f
--- /dev/null
+++ b/src/components/projects/ProjectsContent.tsx
@@ -0,0 +1,752 @@
+"use client";
+
+import { useState, useEffect } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import Link from 'next/link';
+import { Project } from '@/types/project';
+import {
+ IoArrowForward, IoBookmarkOutline, IoChevronDown, IoSearch, IoClose,
+ IoGridOutline, IoListOutline, IoTimeOutline, IoCalendarOutline, IoSwapVertical,
+ IoGlobeOutline
+} from 'react-icons/io5';
+import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
+import { ImageCarousel } from '@/components/ui/ImageCarousel';
+import ComingSoon from '@/components/ui/ComingSoon';
+import { TechIcon } from '../ui/TechIcon';
+import { format } from 'date-fns';
+import { BackgroundEffects } from '@/components/ui/BackgroundEffects';
+
+type LayoutType = 'grid' | 'table';
+type SortOption = 'date-desc' | 'date-asc' | 'alphabetical';
+
+interface ProjectsContentProps {
+ projects: Project[];
+ allTags: string[];
+ showComingSoonIfEmpty?: boolean;
+}
+
+export default function ProjectsContent({
+ projects,
+ allTags,
+ showComingSoonIfEmpty = true
+}: ProjectsContentProps) {
+ // State for filtering and display
+ const [activeFilter, setActiveFilter] = useState('All');
+ const [filterOpen, setFilterOpen] = useState(false);
+ const [sortOpen, setSortOpen] = useState(false);
+ const [hoveredProject, setHoveredProject] = useState(null);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [isSearching, setIsSearching] = useState(false);
+ const [debouncedSearchQuery, setDebouncedSearchQuery] = useState('');
+ const [layout, setLayout] = useState('grid');
+ const [sortBy, setSortBy] = useState('date-desc');
+
+ // Add "All" to the tags list
+ const filterTags = ['All', ...allTags];
+
+ // Debounce search query to prevent excessive filtering during typing
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ setDebouncedSearchQuery(searchQuery);
+ }, 300);
+
+ return () => clearTimeout(timer);
+ }, [searchQuery]);
+
+ // Filter projects based on selected tag and search query
+ const filteredProjects = projects
+ .filter(project => activeFilter === 'All' || project.tags.includes(activeFilter))
+ .filter(project => {
+ if (!debouncedSearchQuery) return true;
+
+ const query = debouncedSearchQuery.toLowerCase();
+ return (
+ project.title.toLowerCase().includes(query) ||
+ project.description.toLowerCase().includes(query) ||
+ project.tags.some(tag => tag.toLowerCase().includes(query)) ||
+ (project.longDescription && project.longDescription.toLowerCase().includes(query))
+ );
+ })
+ .sort((a, b) => {
+ // Sort by selected sort option
+ switch (sortBy) {
+ case 'date-desc':
+ return new Date(b.date || 0).getTime() - new Date(a.date || 0).getTime();
+ case 'date-asc':
+ return new Date(a.date || 0).getTime() - new Date(b.date || 0).getTime();
+ case 'alphabetical':
+ return a.title.localeCompare(b.title);
+ default:
+ return 0;
+ }
+ });
+
+ // If we have no projects and showComingSoonIfEmpty is true, show the coming soon component
+ if (projects.length === 0 && showComingSoonIfEmpty) {
+ return (
+
+ );
+ }
+
+ // Toggle search input visibility
+ const toggleSearch = () => {
+ setIsSearching(!isSearching);
+ if (!isSearching) {
+ // Focus the search input when it becomes visible
+ setTimeout(() => {
+ const searchInput = document.getElementById('project-search');
+ if (searchInput) searchInput.focus();
+ }, 100);
+ } else {
+ // Clear search when closing
+ setSearchQuery('');
+ }
+ };
+
+ // Get text for the active sort option
+ const getSortOptionText = (option: SortOption): string => {
+ switch (option) {
+ case 'date-desc': return 'Newest first';
+ case 'date-asc': return 'Oldest first';
+ case 'alphabetical': return 'A-Z';
+ default: return 'Sort';
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+ My Projects
+
+
+ {filteredProjects.length} project{filteredProjects.length !== 1 ? 's' : ''} showcasing my skills and experience
+
+
+
+
+ {/* Layout switcher - Hidden on mobile */}
+
+ setLayout('grid')}
+ className={`p-2 flex items-center justify-center transition-colors ${layout === 'grid'
+ ? 'bg-primary-800/40 text-primary-300'
+ : 'bg-primary-800/20 text-primary-400/70 hover:bg-primary-800/30 hover:text-primary-300'
+ }`}
+ aria-label="Grid view"
+ title="Grid view"
+ >
+
+
+ setLayout('table')}
+ className={`p-2 flex items-center justify-center transition-colors ${layout === 'table'
+ ? 'bg-primary-800/40 text-primary-300'
+ : 'bg-primary-800/20 text-primary-400/70 hover:bg-primary-800/30 hover:text-primary-300'
+ }`}
+ aria-label="Table view"
+ title="List view"
+ >
+
+
+
+
+ {/* Sort dropdown - Hidden on mobile */}
+
+
+
+
+
+ {getSortOptionText(sortBy)}
+
+
+
+
+ setSortBy('date-desc')}
+ >
+
+
+ Newest first
+ {sortBy === 'date-desc' && ✓ }
+
+
+ setSortBy('date-asc')}
+ >
+
+
+ Oldest first
+ {sortBy === 'date-asc' && ✓ }
+
+
+ setSortBy('alphabetical')}
+ >
+
+ A
+ Alphabetical (A-Z)
+ {sortBy === 'alphabetical' && ✓ }
+
+
+
+
+
+
+ {/* Search button and input - Hidden on mobile */}
+
+ {isSearching && (
+
+ setSearchQuery(e.target.value)}
+ placeholder="Search projects..."
+ className="w-[200px] px-3 py-2 rounded-l-xl bg-card border border-r-0 border-primary-700/20 text-color-text placeholder:text-color-text-muted text-sm focus:ring-1 focus:ring-primary-500 focus:border-primary-500 outline-none"
+ onKeyDown={(e) => {
+ if (e.key === 'Escape') {
+ toggleSearch();
+ }
+ }}
+ />
+
+
+
+
+ )}
+
+ {!isSearching && (
+
+
+ Search
+
+ )}
+
+
+ {/* Category filter dropdown - Hidden on mobile */}
+
+
+
+
+
+ {activeFilter === 'All' ? 'All technologies' : activeFilter}
+ Filter
+
+
+
+
+ {filterTags.map(tag => (
+ {
+ setActiveFilter(tag);
+ setFilterOpen(false);
+ }}
+ >
+ {tag}
+ {activeFilter === tag && (
+ ✓
+ )}
+
+ ))}
+
+
+
+
+
+
+ {/* Mobile controls section */}
+
+ {/* Layout switcher for mobile */}
+
+
+ View:
+
+
+ setLayout('grid')}
+ className={`p-2 flex items-center justify-center transition-colors ${layout === 'grid'
+ ? 'bg-primary-800/40 text-primary-300'
+ : 'bg-primary-800/20 text-primary-400/70 hover:bg-primary-800/30 hover:text-primary-300'
+ }`}
+ aria-label="Grid view"
+ >
+
+
+ setLayout('table')}
+ className={`p-2 flex items-center justify-center transition-colors ${layout === 'table'
+ ? 'bg-primary-800/40 text-primary-300'
+ : 'bg-primary-800/20 text-primary-400/70 hover:bg-primary-800/30 hover:text-primary-300'
+ }`}
+ aria-label="Table view"
+ >
+
+
+
+
+
+ {/* Search input for mobile */}
+
+
+
+
+
setSearchQuery(e.target.value)}
+ placeholder="Search projects..."
+ className="w-full pl-10 pr-4 py-2.5 rounded-xl bg-card border border-primary-700/20 text-color-text placeholder:text-color-text-muted text-sm focus:ring-1 focus:ring-primary-500 focus:border-primary-500 outline-none"
+ onKeyDown={(e) => {
+ if (e.key === 'Escape') {
+ setSearchQuery('');
+ }
+ }}
+ />
+ {searchQuery && (
+
setSearchQuery('')}
+ aria-label="Clear search"
+ >
+
+
+ )}
+
+
+ {/* Mobile tag filter */}
+
+
+ {filterTags.map(tag => (
+ setActiveFilter(tag)}
+ >
+ {tag}
+
+ ))}
+
+
+
+ {/* Mobile sort options */}
+
+
+
+ Sort by:
+
+
+ setSortBy('date-desc')}
+ className={`px-3 py-1.5 rounded-lg text-xs transition-colors ${sortBy === 'date-desc'
+ ? 'bg-primary-800/40 text-primary-300 border border-primary-700/40'
+ : 'bg-card text-color-text-muted border border-color-border'
+ }`}
+ >
+ Newest
+
+ setSortBy('date-asc')}
+ className={`px-3 py-1.5 rounded-lg text-xs transition-colors ${sortBy === 'date-asc'
+ ? 'bg-primary-800/40 text-primary-300 border border-primary-700/40'
+ : 'bg-card text-color-text-muted border border-color-border'
+ }`}
+ >
+ Oldest
+
+ setSortBy('alphabetical')}
+ className={`px-3 py-1.5 rounded-lg text-xs transition-colors ${sortBy === 'alphabetical'
+ ? 'bg-primary-800/40 text-primary-300 border border-primary-700/40'
+ : 'bg-card text-color-text-muted border border-color-border'
+ }`}
+ >
+ A-Z
+
+
+
+
+
+ {/* Search result summary */}
+ {debouncedSearchQuery && (
+
+
+ Found {filteredProjects.length} result{filteredProjects.length !== 1 ? 's' : ''} for "{debouncedSearchQuery} "
+ {activeFilter !== 'All' && (
+ <> with tag {activeFilter} >
+ )}
+
+
setSearchQuery('')}
+ className="text-color-text-muted hover:text-color-text p-1 rounded-full hover:bg-card-alt"
+ aria-label="Clear search"
+ >
+
+
+
+ )}
+
+ {filteredProjects.length > 0 ? (
+
+ {layout === 'grid' ? (
+ // Grid layout
+
+ {filteredProjects.map((project, index) => (
+ setHoveredProject(project.id)}
+ onMouseLeave={() => setHoveredProject(null)}
+ >
+
+ {/* Top gradient accent bar */}
+
+
+
+ {/* Image carousel */}
+
+
+ {project.date && (
+
+
+ {format(new Date(project.date), 'MMM yyyy')}
+
+ )}
+
+
+
+
+ {/* Content section */}
+
+ {/* Tags */}
+
+ {project.tags.slice(0, 4).map(tag => (
+
+ {tag}
+
+ ))}
+ {project.tags.length > 4 && (
+
+ +{project.tags.length - 4}
+
+ )}
+
+
+ {/* Title with hover effect */}
+
+
+ {highlightMatchedText(project.title, debouncedSearchQuery)}
+
+
+
+ {/* Description */}
+
+ {highlightMatchedText(project.description, debouncedSearchQuery)}
+
+
+ {/* View Project Button */}
+
+
+ View Project
+
+
+
+
+
+ {/* Subtle hover glow effect */}
+
+
+
+ ))}
+
+ ) : (
+ // Table layout
+
+ {filteredProjects.map((project, index) => (
+ setHoveredProject(project.id)}
+ onMouseLeave={() => setHoveredProject(null)}
+ >
+
+ {/* Top gradient accent bar */}
+
+
+
+ {/* Project thumbnail */}
+
+
+
+
+
+
+
+ {/* Project content */}
+
+
+ {/* Title and date */}
+
+
+
+ {highlightMatchedText(project.title, debouncedSearchQuery)}
+
+
+ {project.date && (
+
+
+ {format(new Date(project.date), 'MMMM yyyy')}
+
+ )}
+
+
+ {/* Description */}
+
+ {highlightMatchedText(project.description, debouncedSearchQuery)}
+
+
+ {/* Tags */}
+
+ {project.tags.map(tag => (
+
+ {tag}
+
+ ))}
+
+
+
+ {/* Links */}
+
+
+ {project.links?.github && (
+
+
+
+ )}
+ {project.links?.demo && (
+
+
+
+ )}
+
+
+
+
+ View Details
+
+
+
+
+
+
+
+ {/* Subtle hover glow effect */}
+
+
+ {/* Subtle corner decoration */}
+
+
+
+ ))}
+
+ )}
+
+ ) : (
+
+
+ {debouncedSearchQuery
+ ? `No projects found matching "${debouncedSearchQuery}"`
+ : "No projects found with this technology"}
+
+
+ {debouncedSearchQuery
+ ? "Try different search terms or clear your search"
+ : "Try selecting a different filter or check back later."}
+
+
+ {debouncedSearchQuery && (
+ setSearchQuery('')}
+ className="btn-secondary text-sm py-2"
+ >
+ Clear Search
+
+ )}
+ {activeFilter !== 'All' && (
+ setActiveFilter('All')}
+ className="btn-secondary text-sm py-2"
+ >
+ Show All Projects
+
+ )}
+
+
+ )}
+
+
+ );
+}
+
+// Helper function to highlight matched text
+function highlightMatchedText(text: string, query: string): React.ReactNode {
+ if (!query) return text;
+
+ const parts = text.split(new RegExp(`(${query})`, 'gi'));
+
+ return (
+ <>
+ {parts.map((part, index) =>
+ part.toLowerCase() === query.toLowerCase()
+ ? {part}
+ : part
+ )}
+ >
+ );
+}
diff --git a/src/components/static/MobileThemeMenu.tsx b/src/components/static/MobileThemeMenu.tsx
index 3f5fd75..e0d68de 100644
--- a/src/components/static/MobileThemeMenu.tsx
+++ b/src/components/static/MobileThemeMenu.tsx
@@ -9,7 +9,7 @@ import { IoCheckmark } from "react-icons/io5";
import { IoClose } from "react-icons/io5";
type ThemeOption = {
- name: "blue" | "purple" | "teal" | "rose" | "amber";
+ name: "blue" | "purple" | "teal" | "rose" | "amber" | "sunset" | "emerald" | "crimson" | "nord" | "cyberpunk" | "mint";
label: string;
color: string;
gradient: string;
@@ -30,7 +30,7 @@ const themeOptions: ThemeOption[] = [
},
{
name: "teal",
- label: "Emerald",
+ label: "Teal",
color: "rgb(13, 148, 136)",
gradient: "from-teal-600 to-teal-400"
},
@@ -46,6 +46,42 @@ const themeOptions: ThemeOption[] = [
color: "rgb(217, 119, 6)",
gradient: "from-amber-600 to-amber-400"
},
+ {
+ name: "sunset",
+ label: "Sunset",
+ color: "rgb(234, 88, 12)",
+ gradient: "from-orange-600 to-orange-400"
+ },
+ {
+ name: "emerald",
+ label: "Emerald",
+ color: "rgb(5, 150, 105)",
+ gradient: "from-emerald-600 to-emerald-400"
+ },
+ {
+ name: "crimson",
+ label: "Crimson",
+ color: "rgb(220, 38, 38)",
+ gradient: "from-red-600 to-red-400"
+ },
+ {
+ name: "nord",
+ label: "Nord",
+ color: "rgb(49, 112, 179)",
+ gradient: "from-blue-700 to-blue-500"
+ },
+ {
+ name: "cyberpunk",
+ label: "Cyberpunk",
+ color: "rgb(236, 236, 0)",
+ gradient: "from-yellow-400 to-fuchsia-600"
+ },
+ {
+ name: "mint",
+ label: "Mint",
+ color: "rgb(34, 197, 94)",
+ gradient: "from-green-600 to-green-400"
+ }
];
export default function MobileThemeMenu() {
@@ -76,7 +112,7 @@ export default function MobileThemeMenu() {
-
+
Choose Your Theme
diff --git a/src/components/static/Navbar.tsx b/src/components/static/Navbar.tsx
index b87b81e..2e8b795 100644
--- a/src/components/static/Navbar.tsx
+++ b/src/components/static/Navbar.tsx
@@ -1,14 +1,64 @@
"use client";
-import { useState, useEffect } from "react";
+import React, { useState, useEffect } from "react";
import Link from "next/link";
import { useTheme } from "@/context/ThemeContext";
import ThemeSelector from "./ThemeSelector";
import MobileThemeMenu from "./MobileThemeMenu";
import { motion, AnimatePresence } from "framer-motion";
-import { IoMenu, IoClose, IoHomeOutline, IoPersonOutline, IoFolderOutline, IoMailOutline, IoConstructOutline } from "react-icons/io5";
+import { IoMenu, IoClose } from "react-icons/io5";
import { CMAP } from "@/components/icons/CMAP";
-import { FaStar } from "react-icons/fa";
+import { FaRegHandPeace, FaStar } from "react-icons/fa";
+import { usePathname } from "next/navigation";
+import packageJson from "@/../package.json";
+import { IoHomeOutline, IoPersonOutline, IoNewspaperOutline, IoCodeSlashOutline, IoFolderOutline, IoStarOutline, IoMailOutline } from "react-icons/io5";
+
+const navLinks = [
+ { href: "/", label: "Home", icon: IoHomeOutline },
+ { href: "/about", label: "About", icon: IoPersonOutline },
+ { href: "/blog", label: "Blog", icon: IoNewspaperOutline },
+ { href: "/skills", label: "Skills", icon: IoCodeSlashOutline },
+ { href: "/projects", label: "Projects", icon: IoFolderOutline },
+ { href: "/referrals", label: "Referrals", icon: IoStarOutline },
+ { href: "/contact", label: "Contact", icon: IoMailOutline }
+];
+
+interface LinkComponentProps {
+ href: string;
+ className?: string;
+ children: React.ReactNode;
+ [key: string]: any;
+}
+
+export function LinkComponent({
+ href,
+ className,
+ children,
+ ...rest
+}: LinkComponentProps) {
+ const pathname = usePathname();
+ const isActive = pathname === href ||
+ (pathname === "/" && href.startsWith("/#")) ||
+ (pathname?.startsWith("/blog") && href === "/blog");
+
+ return href.startsWith("/#") || href.startsWith("http") ? (
+
+ {children}
+
+ ) : (
+
+ {children}
+
+ );
+}
export default function Navbar() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
@@ -78,19 +128,15 @@ export default function Navbar() {
{/* Desktop Navigation */}
-
- Home
-
-
- About
-
-
- Projects
-
-
- Contact
-
-
+ {navLinks.map(({ href, label }) => (
+
+ {label}
+
+ ))}
@@ -126,54 +172,26 @@ export default function Navbar() {
>
- setIsMenuOpen(false)}
- >
-
-
- Home
-
-
- setIsMenuOpen(false)}
- >
-
-
- About
-
-
- setIsMenuOpen(false)}
- >
-
-
- Projects
-
-
- setIsMenuOpen(false)}
- >
-
-
- Contact
-
-
+ {navLinks.map(({ href, label, icon: Icon }) => (
+ setIsMenuOpen(false)}
+ >
+
+ {/* Use dynamic icon based on the 'icon' property */}
+ {Icon && (
+
+ )}
+ {label}
+
+
+ ))}
@@ -195,8 +213,8 @@ export default function Navbar() {
Development Preview
-
- Beta - Under Active Development
+
+ Welcome - v{packageJson.version}
diff --git a/src/components/static/ThemeSelector.tsx b/src/components/static/ThemeSelector.tsx
index 5f71806..e4daa23 100644
--- a/src/components/static/ThemeSelector.tsx
+++ b/src/components/static/ThemeSelector.tsx
@@ -10,7 +10,7 @@ import { IoChevronDown } from "react-icons/io5";
import { IoCheckmark } from "react-icons/io5";
type ThemeOption = {
- name: "blue" | "purple" | "teal" | "rose" | "amber";
+ name: "blue" | "purple" | "teal" | "rose" | "amber" | "sunset" | "emerald" | "crimson" | "nord" | "cyberpunk" | "mint";
label: string;
color: string;
gradient: string;
@@ -47,6 +47,42 @@ const themeOptions: ThemeOption[] = [
color: "rgb(217, 119, 6)",
gradient: "from-amber-600 to-amber-400"
},
+ {
+ name: "sunset",
+ label: "Sunset",
+ color: "rgb(234, 88, 12)",
+ gradient: "from-orange-600 to-orange-400"
+ },
+ {
+ name: "emerald",
+ label: "Emerald",
+ color: "rgb(5, 150, 105)",
+ gradient: "from-emerald-600 to-emerald-400"
+ },
+ {
+ name: "crimson",
+ label: "Crimson",
+ color: "rgb(220, 38, 38)",
+ gradient: "from-red-600 to-red-400"
+ },
+ {
+ name: "nord",
+ label: "Nord",
+ color: "rgb(49, 112, 179)",
+ gradient: "from-blue-700 to-blue-500"
+ },
+ {
+ name: "cyberpunk",
+ label: "Cyberpunk",
+ color: "rgb(236, 236, 0)",
+ gradient: "from-yellow-400 to-fuchsia-600"
+ },
+ {
+ name: "mint",
+ label: "Mint",
+ color: "rgb(34, 197, 94)",
+ gradient: "from-green-600 to-green-400"
+ }
];
export default function ThemeSelector({ minimal = false }: { minimal?: boolean }) {
@@ -122,7 +158,7 @@ export default function ThemeSelector({ minimal = false }: { minimal?: boolean }
{isOpen && (
+ {/* Background grid effect */}
+
+
+ {/* Decorative elements */}
+
+
+ {/* Floating elements */}
+
+
+
+ >
+ );
+}
diff --git a/src/components/ui/ComingSoon.tsx b/src/components/ui/ComingSoon.tsx
new file mode 100644
index 0000000..03cb94d
--- /dev/null
+++ b/src/components/ui/ComingSoon.tsx
@@ -0,0 +1,271 @@
+"use client";
+
+import { motion } from "framer-motion";
+import Link from "next/link";
+import { useEffect, useState } from "react";
+import { IoHome, IoTimeOutline, IoRocketOutline } from "react-icons/io5";
+
+interface ComingSoonProps {
+ title?: string;
+ description?: string;
+ showNavbar?: boolean;
+ showBackToHome?: boolean;
+ showNotification?: boolean;
+ launchDate?: Date;
+ completionPercentage?: number;
+ customBackLink?: {
+ href: string;
+ label: string;
+ };
+}
+
+export default function ComingSoon({
+ title = "Coming Soon",
+ description = "We're working hard to finish the development of this page. Stay tuned for something amazing!",
+ showNavbar = false,
+ showBackToHome = true,
+ showNotification = true,
+ launchDate,
+ completionPercentage = 75,
+ customBackLink,
+}: ComingSoonProps) {
+ const [mounted, setMounted] = useState(false);
+ const [countdown, setCountdown] = useState({
+ days: 0,
+ hours: 0,
+ minutes: 0,
+ seconds: 0,
+ });
+
+ // Set a future date if not provided - 30 days from now by default
+ useEffect(() => {
+ setMounted(true);
+
+ const targetDate = launchDate || new Date(new Date().setDate(new Date().getDate() + 30));
+
+ const updateCountdown = () => {
+ const now = new Date();
+ const difference = targetDate.getTime() - now.getTime();
+
+ if (difference <= 0) {
+ setCountdown({ days: 0, hours: 0, minutes: 0, seconds: 0 });
+ return;
+ }
+
+ const days = Math.floor(difference / (1000 * 60 * 60 * 24));
+ const hours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
+ const minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60));
+ const seconds = Math.floor((difference % (1000 * 60)) / 1000);
+
+ setCountdown({ days, hours, minutes, seconds });
+ };
+
+ updateCountdown();
+ const intervalId = setInterval(updateCountdown, 1000);
+
+ return () => clearInterval(intervalId);
+ }, [launchDate]);
+
+ if (!mounted) return null;
+
+ return (
+
+ {/* Background grid effect */}
+
+
+ {/* Decorative elements */}
+
+
+ Coming Soon
+
+
+
+
+ {' '}
+
+
+
+
+ {/* Main content */}
+
+
+
+
+ Under Construction
+
+
+
+
+ {title}
+
+
+
+ {description}
+
+
+ {/* Countdown timer */}
+
+
+
+
+
+
+
+ {/* Progress indicator - MOVED ABOVE BUTTONS */}
+
+
+
+
+
Development progress
+
+
+
+
+
{completionPercentage}% complete
+
+
+
+ {/* Action buttons - NOW BELOW PROGRESS BAR */}
+
+ {showBackToHome && (
+
+
+
+ {customBackLink?.label || "Back to Home"}
+
+
+
+ )}
+
+ {showNotification && }
+
+
+
+ );
+}
+
+// Countdown unit component
+function CountdownUnit({ value, label }: { value: number; label: string }) {
+ return (
+
+
+ {value.toString().padStart(2, '0')}
+
+
+ {label}
+
+
+ );
+}
+
+// Notification signup form
+function NotificationForm() {
+ const [email, setEmail] = useState("");
+ const [isSubmitted, setIsSubmitted] = useState(false);
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ // Here you would typically send the email to your API
+ console.log("Email submitted:", email);
+ setIsSubmitted(true);
+ setEmail("");
+
+ // Reset the submitted state after 3 seconds
+ setTimeout(() => {
+ setIsSubmitted(false);
+ }, 3000);
+ };
+
+ return (
+
+ {!isSubmitted ? (
+
document.getElementById('notify-form')?.classList.toggle('hidden')}
+ className="btn-secondary flex items-center"
+ >
+ Notify Me
+
+ ) : (
+
+ ✓ We'll notify you!
+
+ )}
+
+
+ setEmail(e.target.value)}
+ className="w-full p-2 mb-2 rounded bg-bg-alt border border-primary-800/30 text-color-text text-sm focus:ring-1 focus:ring-primary-500 focus:border-primary-500 outline-none"
+ required
+ />
+
+ Subscribe
+
+
+
+ );
+}
diff --git a/src/components/ui/ImageCarousel.tsx b/src/components/ui/ImageCarousel.tsx
index d957736..9c5e054 100644
--- a/src/components/ui/ImageCarousel.tsx
+++ b/src/components/ui/ImageCarousel.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { motion, AnimatePresence } from "framer-motion";
import { useState, useEffect } from "react";
import Image from "next/image";
@@ -13,7 +15,7 @@ export function ImageCarousel({ images, className = "" }: ImageCarouselProps) {
useEffect(() => {
if (isHovered) return;
-
+
const interval = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % images.length);
}, 5000);
@@ -30,14 +32,14 @@ export function ImageCarousel({ images, className = "" }: ImageCarouselProps) {
};
return (
- setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
- {/* Subtle inner shadow */}
-
-
+ {/* Subtle inner shadow for depth */}
+
+
-
+
+
-
+
{/* Navigation Controls */}
{images.length > 1 && (
<>
@@ -68,30 +68,37 @@ export function ImageCarousel({ images, className = "" }: ImageCarouselProps) {
- {/* Navigation Dots - Moved up slightly for better visibility */}
-
+ {currentIndex + 1} / {images.length}
+
+
+ {/* Navigation Dots - Enhanced style */}
+ setCurrentIndex(index)}
- className={`rounded-full transition-all duration-300 ${
- index === currentIndex
- ? "bg-primary-400 w-6 h-1.5"
- : "bg-primary-600/30 w-2 h-1.5 hover:bg-primary-400/50"
- }`}
+ className={`rounded-full transition-all duration-300 ${index === currentIndex
+ ? "bg-primary-400 w-6 h-1.5"
+ : "bg-primary-600/40 w-2 h-1.5 hover:bg-primary-400/60"
+ }`}
whileHover={{ scaleX: 1.2 }}
whileTap={{ scaleX: 0.8 }}
+ aria-label={`Go to image ${index + 1}`}
/>
))}
diff --git a/src/components/ui/TechBadge.tsx b/src/components/ui/TechBadge.tsx
new file mode 100644
index 0000000..1dd6996
--- /dev/null
+++ b/src/components/ui/TechBadge.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import { TechIcon } from './TechIcon';
+
+interface TechBadgeProps {
+ name: string;
+ size?: 'sm' | 'md' | 'lg';
+ showIcon?: boolean;
+}
+
+export default function TechBadge({
+ name,
+ size = 'md',
+ showIcon = true
+}: TechBadgeProps) {
+ const sizeClasses = {
+ sm: 'text-xs py-0.5 px-2',
+ md: 'text-sm py-1 px-2.5',
+ lg: 'text-base py-1.5 px-3',
+ };
+
+ const iconSize = {
+ sm: 12,
+ md: 16,
+ lg: 20,
+ };
+
+ return (
+
+ {showIcon && (
+
+
+
+ )}
+ {name}
+
+ );
+}
diff --git a/src/components/ui/TechIcon.tsx b/src/components/ui/TechIcon.tsx
new file mode 100644
index 0000000..69b1f2d
--- /dev/null
+++ b/src/components/ui/TechIcon.tsx
@@ -0,0 +1,171 @@
+"use client";
+
+import Image from 'next/image';
+import { FaServer, FaDatabase, FaCode, FaEnvelope, FaKey, FaJava } from 'react-icons/fa';
+import { IconType } from 'react-icons';
+
+import {
+ DiAngularSimple, DiBootstrap, DiDjango, DiExpressjs,
+ DiFastify, DiFramer, DiGithubBadge, DiGit, DiGoogleCloudPlatform,
+ DiHtml5, DiJavascript1, DiJqueryLogo, DiLaravel, DiMaterializecss,
+ DiMicrosoft, DiNodejsSmall, DiPhp, DiPostgresql, DiRedis, DiRuby,
+ DiSass, DiServer, DiStripe, DiTwilio, DiVsCode, DiWebpack, DiAmazonAws,
+ DiVisualstudio, DiDocker, DiGithub
+} from 'react-icons/di';
+
+import {
+ SiReact, SiNextdotjs, SiVuedotjs, SiAngular, SiSvelte, SiJavascript,
+ SiTypescript, SiHtml5, SiCss3, SiTailwindcss, SiBootstrap, SiJquery,
+ SiSass, SiFramer, SiMui, SiRedux, SiGatsby, SiNodedotjs, SiExpress,
+ SiDjango, SiFlask, SiRubyonrails, SiPhp, SiLaravel, SiGo, SiSpringboot,
+ SiDotnet, SiGraphql, SiNestjs, SiFastify, SiMongodb, SiMysql,
+ SiPostgresql, SiSqlite, SiFirebase, SiRedis, SiSupabase,
+ SiDocker, SiKubernetes, SiGooglecloud, SiChakraui,
+ SiTerraform, SiJenkins, SiCircleci, SiGithubactions, SiWebpack,
+ SiVite, SiBabel, SiEslint, SiStripe, SiPaypal, SiCloudflare,
+ SiVercel, SiNetlify, SiHeroku, SiDigitalocean, SiPython,
+ SiCplusplus, SiRust, SiSwift, SiKotlin, SiRuby, SiWordpress,
+ SiContentful, SiStrapi, SiSanity, SiMarkdown, SiJest, SiCypress, SiMocha,
+ SiSelenium, SiFlutter, SiIonic, SiRadixui, SiGit, SiFigma, SiStorybook,
+ SiAmazonwebservices, SiPrisma, SiAuth0, SiTwilio, SiMailchimp, SiReactquery,
+ SiGithub
+} from 'react-icons/si';
+
+import { PiFramerLogoFill } from 'react-icons/pi';
+import { BsMicrosoft } from 'react-icons/bs';
+import { TbBrandRadixUi } from 'react-icons/tb';
+
+// Improved: More aliases, better normalization, fallback SVG, and iconMap enhancements
+const iconMap: Record = {
+ javascript: SiJavascript,
+ typescript: SiTypescript,
+ react: SiReact,
+ nextjs: SiNextdotjs,
+ "next.js": SiNextdotjs,
+ vue: SiVuedotjs,
+ vuejs: SiVuedotjs,
+ angular: SiAngular,
+ svelte: SiSvelte,
+ tailwind: SiTailwindcss,
+ tailwindcss: SiTailwindcss,
+ css: SiCss3,
+ css3: SiCss3,
+ html: SiHtml5,
+ html5: SiHtml5,
+ node: SiNodedotjs,
+ nodejs: SiNodedotjs,
+ express: SiExpress,
+ fastify: SiFastify,
+ go: SiGo,
+ python: SiPython,
+ java: FaJava,
+ php: SiPhp,
+ graphql: SiGraphql,
+ mongodb: SiMongodb,
+ postgresql: SiPostgresql,
+ postgres: SiPostgresql,
+ mysql: SiMysql,
+ redis: SiRedis,
+ firebase: SiFirebase,
+ docker: SiDocker,
+ kubernetes: SiKubernetes,
+ github: SiGithub,
+ git: SiGit,
+ vscode: DiVisualstudio,
+ figma: SiFigma,
+ webpack: SiWebpack,
+ jest: SiJest,
+ cypress: SiCypress,
+ storybook: SiStorybook,
+ mui: SiMui,
+ chakra: SiChakraui,
+ bootstrap: SiBootstrap,
+ aws: SiAmazonwebservices,
+ amazonwebservices: SiAmazonwebservices,
+ vercel: SiVercel,
+ netlify: SiNetlify,
+ heroku: SiHeroku,
+ digitalocean: SiDigitalocean,
+ gcp: SiGooglecloud,
+ googlecloud: SiGooglecloud,
+ prisma: SiPrisma,
+ supabase: SiSupabase,
+ auth0: SiAuth0,
+ twilio: SiTwilio,
+ mailchimp: SiMailchimp,
+ microsoft: BsMicrosoft,
+ framer: PiFramerLogoFill,
+ "framer motion": PiFramerLogoFill,
+ bun: FaCode,
+ nestjs: SiNestjs,
+ drizzle: FaDatabase,
+ terraform: SiTerraform,
+ api: FaCode,
+ database: FaDatabase,
+ headless: FaCode,
+ shadcn: FaCode,
+ hono: FaServer,
+ cicd: FaCode,
+ postmark: FaEnvelope,
+ resend: FaEnvelope,
+ sendgrid: FaEnvelope,
+ oauth: FaKey,
+ jwt: FaKey,
+ nextauth: FaKey,
+ "nextauth.js": FaKey,
+ clerk: FaKey,
+ "testing-library": FaCode,
+ playwright: FaCode,
+ vitest: FaCode,
+ radix: TbBrandRadixUi,
+ "radix ui": TbBrandRadixUi,
+ reactquery: SiReactquery,
+ "githubactions": SiGithubactions,
+ "githubapi": SiGithub,
+ rust: SiRust,
+ swift: SiSwift,
+ kotlin: SiKotlin,
+ ruby: SiRuby,
+ wordpress: SiWordpress,
+ contentful: SiContentful,
+ strapi: SiStrapi,
+ sanity: SiSanity,
+ markdown: SiMarkdown,
+ mocha: SiMocha,
+ selenium: SiSelenium,
+ flutter: SiFlutter,
+ ionic: SiIonic,
+ zustand: FaCode,
+ zod: FaCode,
+};
+
+function normalizeName(name: string) {
+ return name.toLowerCase().replace(/[\s\.\-]/g, '');
+}
+
+interface TechIconProps {
+ name: string;
+ size?: number;
+ className?: string;
+}
+
+export function TechIcon({ name, size = 20, className = '' }: TechIconProps) {
+ const normalizedName = normalizeName(name);
+
+ const IconComponent = iconMap[normalizedName];
+
+ if (IconComponent) {
+ return ;
+ }
+
+ // Fallback: show a generic SVG icon with the first letter
+ return (
+
+ {name.charAt(0).toUpperCase()}
+
+ );
+}
diff --git a/src/config/docs.ts b/src/config/docs.ts
new file mode 100644
index 0000000..4da4ee7
--- /dev/null
+++ b/src/config/docs.ts
@@ -0,0 +1,272 @@
+import {
+ IoBook,
+ IoBrowsers,
+ IoServer,
+ IoHammer,
+ IoTerminal,
+ IoGitBranch,
+ IoRocket,
+ IoSpeedometer,
+ IoLayers,
+ IoCodeSlash,
+ IoGlobe,
+ IoLogoGithub,
+ IoCloud,
+ IoHelpCircle,
+ IoExtensionPuzzle,
+ IoGrid,
+ IoPulse,
+ IoShield,
+ IoColorPalette,
+} from "react-icons/io5";
+import { IconType } from "react-icons";
+
+interface DocItem {
+ title: string;
+ href: string;
+ icon?: IconType;
+ description: string;
+ keywords?: string[];
+}
+
+interface DocCategory {
+ title: string;
+ items: DocItem[];
+}
+
+interface DocSection {
+ name: string;
+ slug: string;
+ icon: IconType;
+ description: string;
+ categories: DocCategory[];
+}
+
+interface QuickLink {
+ name: string;
+ href: string;
+ icon: IconType;
+}
+
+export const docsConfig: {
+ sections: DocSection[];
+ quickLinks: QuickLink[];
+} = {
+ sections: [
+ {
+ name: "Getting Started",
+ slug: "getting-started",
+ icon: IoRocket,
+ description: "Start your journey with our platform",
+ categories: [
+ {
+ title: "Introduction",
+ items: [
+ {
+ title: "Overview",
+ href: "/docs/getting-started",
+ icon: IoBook,
+ description: "Learn about the purpose and structure of this documentation.",
+ },
+ {
+ title: "Quick Start Guide",
+ href: "/docs/getting-started/quick-start",
+ icon: IoRocket,
+ description: "Get up and running quickly with our starter templates.",
+ },
+ {
+ title: "Installation",
+ href: "/docs/getting-started/installation",
+ icon: IoHammer,
+ description: "Step-by-step installation instructions for different platforms.",
+ },
+ ],
+ },
+ {
+ title: "Core Concepts",
+ items: [
+ {
+ title: "Project Structure",
+ href: "/docs/getting-started/structure",
+ icon: IoGrid,
+ description: "Understanding the organization of project files and directories.",
+ },
+ {
+ title: "Configuration",
+ href: "/docs/getting-started/config",
+ icon: IoTerminal,
+ description: "Configuring your environment and application settings.",
+ },
+ ],
+ },
+ ],
+ },
+ {
+ name: "Frontend",
+ slug: "frontend",
+ icon: IoBrowsers,
+ description: "Build beautiful user interfaces",
+ categories: [
+ {
+ title: "UI Components",
+ items: [
+ {
+ title: "Component Library",
+ href: "/docs/frontend/components",
+ icon: IoExtensionPuzzle,
+ description: "Explore the component library and usage examples.",
+ keywords: ["ui", "interface", "react", "jsx", "tsx"],
+ },
+ {
+ title: "Styling System",
+ href: "/docs/frontend/styling",
+ icon: IoColorPalette,
+ description: "Learn how to use the styling system with Tailwind CSS.",
+ },
+ {
+ title: "Animations",
+ href: "/docs/frontend/animations",
+ icon: IoSpeedometer,
+ description: "Add motion and animations to your application.",
+ },
+ ],
+ },
+ {
+ title: "Data & State",
+ items: [
+ {
+ title: "State Management",
+ href: "/docs/frontend/state",
+ icon: IoPulse,
+ description: "Managing application state effectively.",
+ },
+ {
+ title: "Data Fetching",
+ href: "/docs/frontend/data-fetching",
+ icon: IoCloud,
+ description: "Strategies for fetching and caching data.",
+ },
+ ],
+ },
+ ],
+ },
+ {
+ name: "Backend",
+ slug: "backend",
+ icon: IoServer,
+ description: "Server-side development and APIs",
+ categories: [
+ {
+ title: "API",
+ items: [
+ {
+ title: "API Reference",
+ href: "/docs/backend/api",
+ icon: IoGlobe,
+ description: "Complete API documentation with examples.",
+ },
+ {
+ title: "Authentication",
+ href: "/docs/backend/auth",
+ icon: IoShield,
+ description: "Learn about authentication methods and implementation.",
+ },
+ {
+ title: "Database Models",
+ href: "/docs/backend/models",
+ icon: IoLayers,
+ description: "Database schema and model reference.",
+ },
+ ],
+ },
+ {
+ title: "Server",
+ items: [
+ {
+ title: "Server Configuration",
+ href: "/docs/backend/server-config",
+ icon: IoTerminal,
+ description: "Configuring and optimizing your server.",
+ },
+ {
+ title: "Middleware",
+ href: "/docs/backend/middleware",
+ icon: IoExtensionPuzzle,
+ description: "Using and creating custom middleware.",
+ },
+ ],
+ },
+ ],
+ },
+ {
+ name: "Deployment",
+ slug: "deployment",
+ icon: IoCloud,
+ description: "Deploy your application to production",
+ categories: [
+ {
+ title: "Build & Deploy",
+ items: [
+ {
+ title: "Build Process",
+ href: "/docs/deployment/build",
+ icon: IoHammer,
+ description: "Learn how to build your application for production.",
+ },
+ {
+ title: "Hosting Options",
+ href: "/docs/deployment/hosting",
+ icon: IoCloud,
+ description: "Compare different hosting providers and deployment strategies.",
+ },
+ {
+ title: "CI/CD Pipeline",
+ href: "/docs/deployment/ci-cd",
+ icon: IoGitBranch,
+ description: "Set up continuous integration and deployment workflows.",
+ },
+ ],
+ },
+ {
+ title: "Production",
+ items: [
+ {
+ title: "Performance",
+ href: "/docs/deployment/performance",
+ icon: IoSpeedometer,
+ description: "Optimizing performance in production environments.",
+ },
+ {
+ title: "Monitoring",
+ href: "/docs/deployment/monitoring",
+ icon: IoPulse,
+ description: "Monitoring and maintaining your application.",
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ quickLinks: [
+ {
+ name: "GitHub",
+ href: "https://github.com/codemeapixel",
+ icon: IoLogoGithub,
+ },
+ {
+ name: "Support",
+ href: "/contact",
+ icon: IoHelpCircle,
+ },
+ {
+ name: "API Status",
+ href: "/status",
+ icon: IoPulse,
+ },
+ {
+ name: "Examples",
+ href: "/examples",
+ icon: IoCodeSlash,
+ },
+ ],
+};
diff --git a/src/context/ThemeContext.tsx b/src/context/ThemeContext.tsx
index a5252db..2f7a139 100644
--- a/src/context/ThemeContext.tsx
+++ b/src/context/ThemeContext.tsx
@@ -2,7 +2,7 @@
import { createContext, useContext, useEffect, useState } from "react";
-type ThemeColor = "blue" | "purple" | "teal" | "rose" | "amber";
+type ThemeColor = "blue" | "purple" | "teal" | "rose" | "amber" | "sunset" | "emerald" | "crimson" | "nord" | "cyberpunk" | "mint";
interface ThemeContextType {
themeColor: ThemeColor;
@@ -27,7 +27,7 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
try {
const savedTheme = localStorage.getItem("themeColor") as ThemeColor;
- if (savedTheme && ["blue", "purple", "teal", "rose", "amber"].includes(savedTheme)) {
+ if (savedTheme && ["blue", "purple", "teal", "rose", "amber", "sunset", "emerald", "crimson", "nord", "cyberpunk", "mint"].includes(savedTheme)) {
setThemeColor(savedTheme);
}
} catch (error) {
diff --git a/src/data/blogData.ts b/src/data/blogData.ts
new file mode 100644
index 0000000..770d223
--- /dev/null
+++ b/src/data/blogData.ts
@@ -0,0 +1,38 @@
+import blogClient from '@/lib/blog/client';
+import { BlogPost } from '@/types/blog';
+
+export async function getAllPosts(): Promise {
+ return blogClient.getAllPosts();
+}
+
+export async function getPostBySlug(slug: string): Promise {
+ return blogClient.getPostBySlug(slug);
+}
+
+export async function getPostsByCategory(category: string): Promise {
+ return blogClient.getPostsByCategory(category);
+}
+
+export async function getPostsByTag(tag: string): Promise {
+ return blogClient.getPostsByTag(tag);
+}
+
+export async function getFeaturedPosts(): Promise {
+ return blogClient.getFeaturedPosts();
+}
+
+export async function getRecentPosts(count: number = 5): Promise {
+ return blogClient.getRecentPosts(count);
+}
+
+export async function getAllCategories(): Promise<{ name: string; count: number; slug: string }[]> {
+ return blogClient.getAllCategories();
+}
+
+export async function getAllTags(): Promise {
+ return blogClient.getAllTags();
+}
+
+export async function getPostSlugs(): Promise {
+ return blogClient.getPostSlugs();
+}
diff --git a/src/data/fivemScripts.ts b/src/data/fivemScripts.ts
new file mode 100644
index 0000000..d7ad8bf
--- /dev/null
+++ b/src/data/fivemScripts.ts
@@ -0,0 +1,20 @@
+// This file is deprecated, please use fivemScriptsData.ts instead
+import {
+ getAllScripts,
+ getScriptById,
+ getReleasedScripts,
+ getInDevelopmentScripts,
+ getComingSoonScripts
+} from './fivemScriptsData';
+
+export type { FivemScript } from '@/types/fivem';
+export {
+ getAllScripts,
+ getScriptById,
+ getReleasedScripts,
+ getInDevelopmentScripts,
+ getComingSoonScripts
+};
+
+// Re-export for backward compatibility
+export const fivemScripts = getAllScripts();
diff --git a/src/data/fivemScriptsData.ts b/src/data/fivemScriptsData.ts
new file mode 100644
index 0000000..bc87299
--- /dev/null
+++ b/src/data/fivemScriptsData.ts
@@ -0,0 +1,38 @@
+import { FivemScript } from "@/types/fivem";
+import fivemScriptsClient from "@/lib/fivem/client";
+
+export function getScriptBySlug(slug: string): FivemScript | undefined {
+ return fivemScriptsClient.getScriptBySlug(slug);
+}
+
+export function getScriptById(id: string): FivemScript | undefined {
+ return fivemScriptsClient.getScriptById(id);
+}
+
+export function getAllScripts(): FivemScript[] {
+ return fivemScriptsClient.getAllScripts();
+}
+
+export function getAllScriptSlugs(): string[] {
+ return fivemScriptsClient.getAllScriptSlugs();
+}
+
+export function getAllScriptTags(): string[] {
+ return fivemScriptsClient.getAllScriptTags();
+}
+
+export function getReleasedScripts(): FivemScript[] {
+ return fivemScriptsClient.getReleasedScripts();
+}
+
+export function getInDevelopmentScripts(): FivemScript[] {
+ return fivemScriptsClient.getInDevelopmentScripts();
+}
+
+export function getScriptsByTag(tag: string): FivemScript[] {
+ return fivemScriptsClient.getScriptsByTag(tag);
+}
+
+export function getAllFrameworks(): string[] {
+ return fivemScriptsClient.getAllFrameworks();
+}
diff --git a/src/data/linksData.ts b/src/data/linksData.ts
new file mode 100644
index 0000000..c9ab063
--- /dev/null
+++ b/src/data/linksData.ts
@@ -0,0 +1,34 @@
+import { LinkHubProfile, LinkItem, SocialLink, LinkCategory, Playlist } from "@/types/links";
+import linksClient from "@/lib/links/client";
+
+export function getProfile(): LinkHubProfile {
+ return linksClient.getProfile();
+}
+
+export function getAllLinks(): LinkItem[] {
+ return linksClient.getAllLinks();
+}
+
+export function getSocialLinks(): SocialLink[] {
+ return linksClient.getSocialLinks();
+}
+
+export function getFeaturedLinks(): LinkItem[] {
+ return linksClient.getFeaturedLinks();
+}
+
+export function getCategories(): LinkCategory[] {
+ return linksClient.getCategories();
+}
+
+export function getLinksByCategory(categoryId: string): LinkItem[] {
+ return linksClient.getLinksByCategory(categoryId);
+}
+
+export function getLinkById(linkId: string): LinkItem | undefined {
+ return linksClient.getLinkById(linkId);
+}
+
+export function getPlaylist(): Playlist {
+ return linksClient.getPlaylist();
+}
diff --git a/src/data/projects.ts b/src/data/projects.ts
index 7968d47..24e6e87 100644
--- a/src/data/projects.ts
+++ b/src/data/projects.ts
@@ -3,16 +3,19 @@ import { Project } from "@/types/project";
export const projects: Project[] = [
{
id: "1",
- title: "Infinity List",
- description: "Search our vast list of bots for an exciting start to your server.",
+ title: "NodeByte LTD",
+ description: "Helping businesses transform their digital presence with cutting edge solutions.",
images: [
- "/Infinity/Home.png",
- "/Infinity/BotPages.png"
+ "/NodeByteLTD/home.png",
+ "/NodeByteLTD/about.png",
+ "/NodeByteLTD/services.png",
+ "/NodeByteLTD/discord.png"
],
- tags: ["Next.js", "React", "Tailwind CSS", "PostgreSQL", "Go"],
+ tags: ["Next.js", "React", "TypeScript", "Framer Motion", "RadixUI"],
links: {
- demo: "https://infinitybots.gg",
- github: "https://github.com/InfinityBotList"
+ slug: "nodebyte-ltd",
+ demo: "https://nodebyte.co.uk",
+ github: "https://github.com/NodeByteHosting"
},
featured: true
},
@@ -26,9 +29,10 @@ export const projects: Project[] = [
],
tags: ["Next.js", "React", "TypeScript", "Framer Motion"],
links: {
+ slug: "nodebyte-hosting",
demo: "https://nodebyte.host",
github: "https://github.com/NodeByteHosting/Website"
},
featured: true
}
-];
\ No newline at end of file
+];
\ No newline at end of file
diff --git a/src/data/projectsData.ts b/src/data/projectsData.ts
new file mode 100644
index 0000000..c424fc6
--- /dev/null
+++ b/src/data/projectsData.ts
@@ -0,0 +1,26 @@
+import { Project } from "@/types/project";
+import projectsClient from "@/lib/projects/client";
+
+export function getProjectById(id: string): Project | undefined {
+ return projectsClient.getProjectById(id);
+}
+
+export function getAllProjectIds(): string[] {
+ return projectsClient.getAllProjectIds();
+}
+
+export function getAllProjects(): Project[] {
+ return projectsClient.getAllProjects();
+}
+
+export function getFeaturedProjects(): Project[] {
+ return projectsClient.getFeaturedProjects();
+}
+
+export function getProjectsByTag(tag: string): Project[] {
+ return projectsClient.getProjectsByTag(tag);
+}
+
+export function getAllProjectTags(): string[] {
+ return projectsClient.getAllProjectTags();
+}
diff --git a/src/data/referralsData.ts b/src/data/referralsData.ts
new file mode 100644
index 0000000..b5e03ae
--- /dev/null
+++ b/src/data/referralsData.ts
@@ -0,0 +1,34 @@
+import { Referral, ReferralCategory } from "@/types/referrals";
+import referralsClient from "@/lib/referrals/client";
+
+export function getAllReferrals(): Referral[] {
+ return referralsClient.getAllReferrals();
+}
+
+export function getAllCategories(): ReferralCategory[] {
+ return referralsClient.getAllCategories();
+}
+
+export function getReferralById(id: string): Referral | undefined {
+ return referralsClient.getReferralById(id);
+}
+
+export function getReferralsByCategory(categoryId: string): Referral[] {
+ return referralsClient.getReferralsByCategory(categoryId);
+}
+
+export function getFeaturedReferrals(): Referral[] {
+ return referralsClient.getFeaturedReferrals();
+}
+
+export function getNewReferrals(): Referral[] {
+ return referralsClient.getNewReferrals();
+}
+
+export function getCategoryById(id: string): ReferralCategory | undefined {
+ return referralsClient.getCategoryById(id);
+}
+
+export function getAllCategoryIds(): string[] {
+ return referralsClient.getAllCategoryIds();
+}
diff --git a/src/data/skills.ts b/src/data/skills.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/data/skillsData.ts b/src/data/skillsData.ts
new file mode 100644
index 0000000..be86760
--- /dev/null
+++ b/src/data/skillsData.ts
@@ -0,0 +1,34 @@
+import { Skill, SkillCategory } from "@/types/skills";
+import skillsClient from "@/lib/skills/client";
+
+export function getAllSkillCategories(): SkillCategory[] {
+ return skillsClient.getAllSkillCategories();
+}
+
+export function getSkillCategoryByName(name: string): SkillCategory | undefined {
+ return skillsClient.getSkillCategoryByName(name);
+}
+
+export function getAllSkills(): Skill[] {
+ return skillsClient.getAllSkills();
+}
+
+export function getSkillByName(name: string): Skill | undefined {
+ return skillsClient.getSkillByName(name);
+}
+
+export function getExpertSkills(): Skill[] {
+ return skillsClient.getExpertSkills();
+}
+
+export function getAllSkillNames(): string[] {
+ return skillsClient.getAllSkillNames();
+}
+
+export function getAllCategoryNames(): string[] {
+ return skillsClient.getAllCategoryNames();
+}
+
+export function searchSkills(query: string): Skill[] {
+ return skillsClient.searchSkills(query);
+}
diff --git a/src/lib/blog/client.ts b/src/lib/blog/client.ts
new file mode 100644
index 0000000..8b6d082
--- /dev/null
+++ b/src/lib/blog/client.ts
@@ -0,0 +1,138 @@
+import { BlogPost, BlogCategory } from "@/types/blog";
+import { getAllPosts as fetchAllPosts } from "@/lib/mdx";
+
+export class BlogClient {
+ private static instance: BlogClient;
+ private postsCache: BlogPost[] | null = null;
+ private categoriesCache: BlogCategory[] | null = null;
+
+ private constructor() { }
+
+ public static getInstance(): BlogClient {
+ if (!BlogClient.instance) {
+ BlogClient.instance = new BlogClient();
+ }
+ return BlogClient.instance;
+ }
+
+ public async getAllPosts(): Promise {
+ if (this.postsCache) {
+ return this.postsCache;
+ }
+
+ try {
+ const posts = await fetchAllPosts();
+ this.postsCache = posts;
+ return posts;
+ } catch (error) {
+ console.error("Error fetching blog posts:", error);
+ return [];
+ }
+ }
+
+ public async getPostBySlug(slug: string): Promise {
+ console.log(`Looking for post with slug: "${slug}"`);
+ const posts = await this.getAllPosts();
+ console.log(`Total posts loaded: ${posts.length}`);
+
+ const post = posts.find(post => {
+ const postSlug = post.slug || post.metadata.slug;
+ console.log(`Checking post slug: "${postSlug}" against requested: "${slug}"`);
+ return postSlug === slug;
+ });
+
+ if (!post) {
+ console.warn(`Post with slug "${slug}" not found. Available slugs:`,
+ posts.map(p => p.slug || p.metadata.slug));
+ } else {
+ console.log(`Found post with slug "${slug}":`, post.metadata.title);
+ }
+
+ return post || null;
+ }
+
+ public async getPostsByCategory(category: string): Promise {
+ const posts = await this.getAllPosts();
+ return posts.filter(post =>
+ post.metadata.categories?.some(cat =>
+ cat.toLowerCase() === category.toLowerCase()
+ )
+ );
+ }
+
+ public async getPostsByTag(tag: string): Promise {
+ const posts = await this.getAllPosts();
+ return posts.filter(post =>
+ post.metadata.tags?.some(postTag =>
+ postTag.toLowerCase() === tag.toLowerCase()
+ )
+ );
+ }
+
+ public async getFeaturedPosts(): Promise {
+ const posts = await this.getAllPosts();
+ return posts.filter(post => post.metadata.featured);
+ }
+
+ public async getRecentPosts(count: number = 5): Promise {
+ const posts = await this.getAllPosts();
+ return posts
+ .sort((a, b) => new Date(b.metadata.date).getTime() - new Date(a.metadata.date).getTime())
+ .slice(0, count);
+ }
+
+ public async getAllCategories(): Promise {
+ if (this.categoriesCache) {
+ return this.categoriesCache;
+ }
+
+ const posts = await this.getAllPosts();
+ const categoryCounts = new Map();
+
+ posts.forEach(post => {
+ post.metadata.categories?.forEach(category => {
+ const normalizedCategory = category.toLowerCase();
+ categoryCounts.set(
+ normalizedCategory,
+ (categoryCounts.get(normalizedCategory) || 0) + 1
+ );
+ });
+ });
+
+ const categories = Array.from(categoryCounts.entries())
+ .map(([name, count]) => ({
+ name,
+ count,
+ slug: name.toLowerCase().replace(/\s+/g, '-')
+ }))
+ .sort((a, b) => a.name.localeCompare(b.name));
+
+ this.categoriesCache = categories;
+ return categories;
+ }
+
+ public async getAllTags(): Promise {
+ const posts = await this.getAllPosts();
+ const tags = new Set();
+
+ posts.forEach(post => {
+ post.metadata.tags?.forEach(tag => {
+ tags.add(tag.toLowerCase());
+ });
+ });
+
+ return Array.from(tags).sort();
+ }
+
+ public async getPostSlugs(): Promise {
+ const posts = await this.getAllPosts();
+ return posts.map(post => post.slug);
+ }
+
+ public clearCache(): void {
+ this.postsCache = null;
+ this.categoriesCache = null;
+ }
+}
+
+export default BlogClient.getInstance();
diff --git a/src/lib/fivem/client.ts b/src/lib/fivem/client.ts
new file mode 100644
index 0000000..be8d21e
--- /dev/null
+++ b/src/lib/fivem/client.ts
@@ -0,0 +1,80 @@
+import { FivemScript } from "@/types/fivem";
+import { allScripts } from "./data";
+
+export class FivemScriptsClient {
+ private static instance: FivemScriptsClient;
+ private scriptsCache: FivemScript[] | null = null;
+
+ private constructor() { }
+
+ public static getInstance(): FivemScriptsClient {
+ if (!FivemScriptsClient.instance) {
+ FivemScriptsClient.instance = new FivemScriptsClient();
+ }
+ return FivemScriptsClient.instance;
+ }
+
+ public getAllScripts(): FivemScript[] {
+ if (this.scriptsCache) {
+ return this.scriptsCache;
+ }
+
+ // Use the statically imported scripts
+ this.scriptsCache = allScripts;
+ return this.scriptsCache;
+ }
+
+ public getScriptById(id: string): FivemScript | undefined {
+ return this.getAllScripts().find(script => script.id === id);
+ }
+
+ public getScriptBySlug(slug: string): FivemScript | undefined {
+ return this.getAllScripts().find(script => script.links.slug === slug);
+ }
+
+ public getReleasedScripts(): FivemScript[] {
+ return this.getAllScripts().filter(script => script.status === "Released");
+ }
+
+ public getInDevelopmentScripts(): FivemScript[] {
+ return this.getAllScripts().filter(script => script.status === "In Development");
+ }
+
+ public getComingSoonScripts(): FivemScript[] {
+ return this.getAllScripts().filter(script => script.status === "Coming Soon");
+ }
+
+ public getScriptsByFramework(framework: string): FivemScript[] {
+ return this.getAllScripts().filter(script =>
+ script.framework === framework || script.framework === "Both"
+ );
+ }
+
+ public getScriptsByTag(tag: string): FivemScript[] {
+ return this.getAllScripts().filter(script => script.tags.includes(tag));
+ }
+
+ public getAllScriptTags(): string[] {
+ const tags = new Set();
+ this.getAllScripts().forEach(script => {
+ script.tags.forEach(tag => {
+ tags.add(tag);
+ });
+ });
+ return Array.from(tags).sort();
+ }
+
+ public getAllScriptSlugs(): string[] {
+ return this.getAllScripts().map(script => script.links.slug);
+ }
+
+ public getAllFrameworks(): string[] {
+ const frameworks = new Set();
+ this.getAllScripts().forEach(script => {
+ frameworks.add(script.framework);
+ });
+ return Array.from(frameworks).sort();
+ }
+}
+
+export default FivemScriptsClient.getInstance();
diff --git a/src/lib/fivem/data/index.ts b/src/lib/fivem/data/index.ts
new file mode 100644
index 0000000..f044b93
--- /dev/null
+++ b/src/lib/fivem/data/index.ts
@@ -0,0 +1,9 @@
+import { FivemScript } from "@/types/fivem";
+import pixelLogs from "./pixel-logs";
+// Import additional scripts here as they are created
+// import scriptName from "./script-file-name";
+
+export const allScripts: FivemScript[] = [
+ pixelLogs,
+ // Add additional scripts here
+];
diff --git a/src/lib/fivem/data/pixel-logs.ts b/src/lib/fivem/data/pixel-logs.ts
new file mode 100644
index 0000000..b15bc5b
--- /dev/null
+++ b/src/lib/fivem/data/pixel-logs.ts
@@ -0,0 +1,49 @@
+import { FivemScript } from "@/types/fivem";
+
+const script: FivemScript = {
+ id: "pxl",
+ title: "Pixel Logs",
+ description: "An advanced Discord logging system for FiveM and RedM servers, providing comprehensive logging capabilities with a clean and modern interface.",
+ longDescription: "Pixel Logs is a powerful and customizable Discord logging system designed specifically for FiveM and RedM servers. It provides comprehensive event tracking, detailed debug capabilities, extensive configuration options, and seamless Discord integration to help server administrators monitor and troubleshoot their servers with ease. Whether you're tracking player activities, monitoring administrative actions, or debugging server issues, Pixel Logs delivers the information you need with a clean, organized interface.",
+ price: "$5.00",
+ framework: "Standalone",
+ status: "In Development",
+ version: "1.px0001a",
+ lastUpdated: "2025-04-10",
+ features: [
+ "📝 Event Logging for player joins/leaves, chat messages, deaths, commands, admin actions, resources, and custom events",
+ "🔍 Detailed death tracking with cause, weapon details, location, and killer information",
+ "👮 Administrative action logging including bans, kicks, and warns with reason and duration tracking",
+ "🐛 Advanced debug system with in-memory logging and separate webhook for critical errors",
+ "🔄 txAdmin integration for server management event tracking",
+ "⚙️ Extensive configuration with per-event toggling and customizable templates",
+ "🎨 Custom embed colors and player avatar support for Discord logs",
+ "🔗 Multiple webhook support with different channels for different log types",
+ "🛡️ Proxy support for routing Discord webhook requests",
+ "📋 Player identifier control for privacy management",
+ "💡 Automatic error catching with stack traces and error details",
+ "📊 Resource information inclusion in error logs for easier debugging"
+ ],
+ images: [
+ "/scripts/pxl/pxl-console.png",
+ "/scripts/pxl/pxl-player-join.png",
+ "/scripts/pxl/pxl-player-leave.png",
+ "/scripts/pxl/pxl-startup.png"
+ ],
+ video: "https://www.youtube.com/embed/dQw4w9WgXcQ",
+ tags: ["Logging", "Discord", "Admin", "Debug", "Standalone"],
+ links: {
+ demo: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
+ purchase: "https://tebex.io/",
+ documentation: "https://docs.codemeapixel.dev/pixel-logs",
+ slug: "pixel-logs"
+ },
+ requirements: [
+ "FiveM or RedM server",
+ "Discord webhook permissions",
+ "Server owner or access to server.cfg"
+ ],
+ installation: "1. Download the script files\n2. Upload to your resources folder\n3. Add to server.cfg\n4. Configure your Discord webhooks\n5. Set the appropriate convars in server.cfg\n6. Restart your server"
+};
+
+export default script;
diff --git a/src/lib/links/client.ts b/src/lib/links/client.ts
new file mode 100644
index 0000000..40ea96e
--- /dev/null
+++ b/src/lib/links/client.ts
@@ -0,0 +1,94 @@
+import { LinkHubProfile, LinkItem, SocialLink, LinkCategory, Track, Playlist } from "@/types/links";
+import { linkHubProfile } from "./data";
+import {
+ playlist,
+ getTracksByGenre,
+ getAllGenres,
+ getTrackById,
+ rockTracks,
+ electronicTracks,
+ hipHopTracks
+} from "./data/playlist";
+
+export class LinksClient {
+ private static instance: LinksClient;
+ private profile: LinkHubProfile;
+
+ private constructor() {
+ this.profile = linkHubProfile;
+ }
+
+ public static getInstance(): LinksClient {
+ if (!LinksClient.instance) {
+ LinksClient.instance = new LinksClient();
+ }
+ return LinksClient.instance;
+ }
+
+ public getProfile(): LinkHubProfile {
+ return this.profile;
+ }
+
+ public getAllLinks(): LinkItem[] {
+ const featuredLinks = this.profile.featuredLinks;
+ const categoryLinks = this.profile.categories.flatMap(category => category.links);
+ return [...featuredLinks, ...categoryLinks];
+ }
+
+ public getSocialLinks(): SocialLink[] {
+ return this.profile.socialLinks;
+ }
+
+ public getFeaturedLinks(): LinkItem[] {
+ return this.profile.featuredLinks;
+ }
+
+ public getCategories(): LinkCategory[] {
+ return this.profile.categories;
+ }
+
+ public getLinksByCategory(categoryId: string): LinkItem[] {
+ const category = this.profile.categories.find(cat => cat.id === categoryId);
+ return category ? category.links : [];
+ }
+
+ public getLinkById(linkId: string): LinkItem | undefined {
+ const allLinks = this.getAllLinks();
+ return allLinks.find(link => link.id === linkId);
+ }
+
+ public updateDiscordStatus(discordStatus: any): void {
+ this.profile.discord = discordStatus;
+ }
+
+ public getPlaylist(): Playlist {
+ return playlist;
+ }
+
+ public getTrackById(trackId: string): Track | undefined {
+ return getTrackById(trackId);
+ }
+
+ public getTracksByGenre(genre: string): Track[] {
+ return getTracksByGenre(genre);
+ }
+
+ public getAllGenres(): string[] {
+ return getAllGenres();
+ }
+
+ // New methods to directly access genre collections
+ public getRockTracks(): Track[] {
+ return rockTracks;
+ }
+
+ public getElectronicTracks(): Track[] {
+ return electronicTracks;
+ }
+
+ public getHipHopTracks(): Track[] {
+ return hipHopTracks;
+ }
+}
+
+export default LinksClient.getInstance();
diff --git a/src/lib/links/data/categories.ts b/src/lib/links/data/categories.ts
new file mode 100644
index 0000000..67fc30d
--- /dev/null
+++ b/src/lib/links/data/categories.ts
@@ -0,0 +1,108 @@
+import { LinkCategory } from "@/types/links";
+
+export const categories: LinkCategory[] = [
+ {
+ id: "projects",
+ name: "Projects",
+ icon: "IoCodeSlashOutline",
+ color: "text-purple-500",
+ links: [
+ {
+ id: "portfolio",
+ title: "Portfolio Website",
+ url: "https://codemeapixel.dev",
+ icon: "IoGlobeOutline",
+ description: "My personal portfolio built with Next.js",
+ color: "bg-gray-500"
+ },
+ {
+ id: "nodebyte-ltd",
+ title: "NodeByte LTD",
+ url: "https://nodebyte.co.uk",
+ icon: "IoGlobeOutline",
+ description: "Professional web development company",
+ color: "bg-green-500"
+ },
+ {
+ id: "nodebyte-hosting",
+ title: "NodeByte Hosting",
+ url: "https://nodebyte.host",
+ icon: "IoGlobeOutline",
+ description: "Premium game and web hosting with blazing fast performance.",
+ color: "bg-green-500"
+ },
+ {
+ id: "cordx",
+ title: "CordX",
+ url: "https://cordx.ca",
+ icon: "IoGlobeOutline",
+ description: "The next generation of Media Storage, Sharing and Metrics.",
+ color: "bg-yellow-500"
+ },
+ {
+ id: "infinity-list",
+ title: "Infinity List",
+ url: "https://infinitybots.gg",
+ icon: "IoGlobeOutline",
+ description: "Browse our vast list of Discord Bots, Servers and more.",
+ color: "bg-yellow-500"
+ },
+ {
+ id: "net-social",
+ title: "Net Social",
+ url: "https://netsocial.app",
+ icon: "IoGlobeOutline",
+ description: "Empowering communities to be who they want to be.",
+ color: "bg-red-500"
+ },
+ ]
+ },
+ {
+ id: "services",
+ name: "Services",
+ icon: "IoServerOutline",
+ color: "text-blue-500",
+ links: [
+ {
+ id: "fivem",
+ title: "FiveM Scripts",
+ url: "/fivem",
+ icon: "IoGameControllerOutline",
+ description: "Custom scripts for FiveM servers",
+ color: "bg-orange-500"
+ },
+ {
+ id: "web-development",
+ title: "Web Development",
+ url: "/contact",
+ icon: "IoCodeOutline",
+ description: "Custom web development services",
+ color: "bg-indigo-500"
+ }
+ ]
+ },
+ {
+ id: "resources",
+ name: "Resources",
+ icon: "IoBookOutline",
+ color: "text-green-500",
+ links: [
+ {
+ id: "blog",
+ title: "Blog",
+ url: "/blog",
+ icon: "IoNewspaperOutline",
+ description: "Tutorials and articles on web development",
+ color: "bg-pink-500"
+ },
+ {
+ id: "github",
+ title: "GitHub Repositories",
+ url: "https://github.com/codemeapixel",
+ icon: "IoLogoGithub",
+ description: "Open source projects and code examples",
+ color: "bg-gray-800"
+ }
+ ]
+ }
+];
diff --git a/src/lib/links/data/index.ts b/src/lib/links/data/index.ts
new file mode 100644
index 0000000..28f070d
--- /dev/null
+++ b/src/lib/links/data/index.ts
@@ -0,0 +1,41 @@
+import { LinkHubProfile } from "@/types/links";
+import { socialLinks } from "./social-links";
+import { categories } from "./categories";
+
+export const linkHubProfile: LinkHubProfile = {
+ name: "CodeMeAPixel",
+ avatar: "/character.png",
+ title: "Full Stack Developer & Designer",
+ bio: "I build web applications and design user experiences. Connect with me on these platforms or check out my projects.",
+ featuredLinks: [
+ {
+ id: "portfolio",
+ title: "Portfolio",
+ url: "https://codemeapixel.dev",
+ icon: "IoGlobeOutline",
+ description: "Check out my portfolio website",
+ color: "bg-blue-500",
+ featured: true
+ },
+ {
+ id: "github",
+ title: "GitHub",
+ url: "https://github.com/codemeapixel",
+ icon: "IoLogoGithub",
+ description: "View my open source projects",
+ color: "bg-gray-800",
+ featured: true
+ },
+ {
+ id: "projects",
+ title: "Projects",
+ url: "/projects",
+ icon: "IoCodeSlashOutline",
+ description: "See my latest work",
+ color: "bg-purple-500",
+ featured: true
+ }
+ ],
+ socialLinks,
+ categories
+};
diff --git a/src/lib/links/data/playlist/classics.ts b/src/lib/links/data/playlist/classics.ts
new file mode 100644
index 0000000..24c78ab
--- /dev/null
+++ b/src/lib/links/data/playlist/classics.ts
@@ -0,0 +1,36 @@
+import { Track } from "@/types/links";
+
+export const classicTracks: Track[] = [
+ {
+ id: "nois-tth",
+ title: "New Orleans Is Sinking",
+ artist: "The Tragically Hip",
+ albumCover: "/covers/tragicallyhip.webp",
+ audioFile: "/playlist/tragically-hip/new-orleans-is-sinking.mp3",
+ genre: "Classics"
+ },
+ {
+ id: "bahd-tth",
+ title: "Blow At High Dough",
+ artist: "The Tragically Hip",
+ albumCover: "/covers/tragicallyhip.webp",
+ audioFile: "/playlist/tragically-hip/blow-at-high-dough.mp3",
+ genre: "Classics"
+ },
+ {
+ id: "athm-tth",
+ title: "At The Hundredth Meridian",
+ artist: "The Tragically Hip",
+ albumCover: "/covers/tragicallyhip.webp",
+ audioFile: "/playlist/tragically-hip/hundreth-meridian.mp3",
+ genre: "Classics"
+ },
+ {
+ id: "abac-tth",
+ title: "Ahead By A Century",
+ artist: "The Tragically Hip",
+ albumCover: "/covers/tragicallyhip.webp",
+ audioFile: "/playlist/tragically-hip/ahead-by-a-century.mp3",
+ genre: "Classics"
+ },
+];
diff --git a/src/lib/links/data/playlist/electronic.ts b/src/lib/links/data/playlist/electronic.ts
new file mode 100644
index 0000000..42fa522
--- /dev/null
+++ b/src/lib/links/data/playlist/electronic.ts
@@ -0,0 +1,3 @@
+import { Track } from "@/types/links";
+
+export const electronicTracks: Track[] = [];
diff --git a/src/lib/links/data/playlist/hiphop.ts b/src/lib/links/data/playlist/hiphop.ts
new file mode 100644
index 0000000..820b55c
--- /dev/null
+++ b/src/lib/links/data/playlist/hiphop.ts
@@ -0,0 +1,52 @@
+import { Track } from "@/types/links";
+
+export const hipHopTracks: Track[] = [
+ {
+ id: "fuel-eminem",
+ title: "Fuel (Feat. JID)",
+ artist: "Eminem",
+ albumCover: "/covers/eminem.webp",
+ audioFile: "/playlist/eminem/fuel-ft-jid.mp3",
+ genre: "Hip Hop"
+ },
+ {
+ id: "somebody-save-me-eminem",
+ title: "Somebody Save Me",
+ artist: "Eminem",
+ albumCover: "/covers/eminem.webp",
+ audioFile: "/playlist/eminem/somebody-save-me.mp3",
+ genre: "Hip Hop"
+ },
+ {
+ id: "lose-yourself-eminem",
+ title: "Lose Yourself",
+ artist: "Eminem",
+ albumCover: "/covers/eminem.webp",
+ audioFile: "/playlist/eminem/lose-yourself.mp3",
+ genre: "Hip Hop"
+ },
+ {
+ id: "25-to-life-eminem",
+ title: "25 to Life",
+ artist: "Eminem",
+ albumCover: "/covers/eminem.webp",
+ audioFile: "/playlist/eminem/25-to-life.mp3",
+ genre: "Hip Hop"
+ },
+ {
+ id: "cold-wind-blows-eminem",
+ title: "Cold Wind Blows",
+ artist: "Eminem",
+ albumCover: "/covers/eminem.webp",
+ audioFile: "/playlist/eminem/cold-wind-blows.mp3",
+ genre: "Hip Hop"
+ },
+ {
+ id: "temporary-eminem",
+ title: "Temporary (Feat. Skylar Grey)",
+ artist: "Eminem",
+ albumCover: "/covers/eminem.webp",
+ audioFile: "/playlist/eminem/temporary-ft-skylar-grey.mp3",
+ genre: "Hip Hop"
+ }
+];
diff --git a/src/lib/links/data/playlist/index.ts b/src/lib/links/data/playlist/index.ts
new file mode 100644
index 0000000..551297e
--- /dev/null
+++ b/src/lib/links/data/playlist/index.ts
@@ -0,0 +1,46 @@
+import { Playlist, Track } from "@/types/links";
+import { rockTracks } from "./rock";
+import { electronicTracks } from "./electronic";
+import { hipHopTracks } from "./hiphop";
+import { classicTracks } from "./classics";
+
+// Combine all tracks
+export const allTracks: Track[] = [
+ ...classicTracks,
+ ...rockTracks,
+ ...electronicTracks,
+ ...hipHopTracks
+];
+
+// Export individual genre collections for direct access
+export { rockTracks, electronicTracks, hipHopTracks };
+
+// Export the playlist
+export const playlist: Playlist = {
+ title: "My Current Favorites",
+ description: "A collection of tracks I'm enjoying right now. Click on any track to listen!",
+ tracks: allTracks
+};
+
+// Function to get tracks by genre
+export function getTracksByGenre(genre: string): Track[] {
+ return allTracks.filter(track =>
+ track.genre?.toLowerCase() === genre.toLowerCase()
+ );
+}
+
+// Function to get all unique genres
+export function getAllGenres(): string[] {
+ const genres = new Set();
+ allTracks.forEach(track => {
+ if (track.genre) {
+ genres.add(track.genre);
+ }
+ });
+ return Array.from(genres).sort();
+}
+
+// Function to get a track by ID
+export function getTrackById(trackId: string): Track | undefined {
+ return allTracks.find(track => track.id === trackId);
+}
diff --git a/src/lib/links/data/playlist/rock.ts b/src/lib/links/data/playlist/rock.ts
new file mode 100644
index 0000000..0aa32a0
--- /dev/null
+++ b/src/lib/links/data/playlist/rock.ts
@@ -0,0 +1,36 @@
+import { Track } from "@/types/links";
+
+export const rockTracks: Track[] = [
+ {
+ id: "gawm-paparoach",
+ title: "Getting Away With Murder",
+ artist: "Papa Roach",
+ albumCover: "/covers/paparoach.webp",
+ audioFile: "/playlist/papa-roach/getting-away-with-murder.mp3",
+ genre: "Rock"
+ },
+ {
+ id: "slmn-paparoach",
+ title: "She Loves Me Not",
+ artist: "Papa Roach",
+ albumCover: "/covers/paparoach.webp",
+ audioFile: "/playlist/papa-roach/she-loves-me-not.mp3",
+ genre: "Rock"
+ },
+ {
+ id: "hww-paparoach",
+ title: "Hollywood Whore",
+ artist: "Papa Roach",
+ albumCover: "/covers/paparoach.webp",
+ audioFile: "/playlist/papa-roach/hollywood-whore.mp3",
+ genre: "Rock"
+ },
+ {
+ id: "tbl-paparoach",
+ title: "To Be Loved",
+ artist: "Papa Roach",
+ albumCover: "/covers/paparoach.webp",
+ audioFile: "/playlist/papa-roach/to-be-loved.mp3",
+ genre: "Rock"
+ }
+];
diff --git a/src/lib/links/data/social-links.ts b/src/lib/links/data/social-links.ts
new file mode 100644
index 0000000..6daf073
--- /dev/null
+++ b/src/lib/links/data/social-links.ts
@@ -0,0 +1,36 @@
+import { SocialLink } from "@/types/links";
+
+export const socialLinks: SocialLink[] = [
+ {
+ id: "twitter",
+ title: "Twitter",
+ url: "https://twitter.com/codemeapixel",
+ icon: "IoLogoTwitter",
+ username: "@codemeapixel",
+ color: "bg-blue-400"
+ },
+ {
+ id: "github",
+ title: "GitHub",
+ url: "https://github.com/codemeapixel",
+ icon: "IoLogoGithub",
+ username: "codemeapixel",
+ color: "bg-gray-800"
+ },
+ {
+ id: "linkedin",
+ title: "LinkedIn",
+ url: "https://linkedin.com/in/codemeapixel",
+ icon: "IoLogoLinkedin",
+ username: "codemeapixel",
+ color: "bg-blue-600"
+ },
+ {
+ id: "discord",
+ title: "Discord",
+ url: "https://discord.gg/Vv2bdC44Ge",
+ icon: "IoLogoDiscord",
+ username: "codemeapixel",
+ color: "bg-indigo-500"
+ }
+];
diff --git a/src/lib/mdx.ts b/src/lib/mdx.ts
new file mode 100644
index 0000000..2697970
--- /dev/null
+++ b/src/lib/mdx.ts
@@ -0,0 +1,193 @@
+// Imports for server-side processing only
+import matter from 'gray-matter';
+
+// This will be true in a browser environment (client-side), false in Node.js (server-side)
+const isClient = typeof window !== 'undefined';
+
+export interface PostMetadata {
+ title: string;
+ date: string;
+ slug: string;
+ description?: string;
+ tags?: string[];
+ [key: string]: any;
+}
+
+// Mock data for development and fallback in client-side rendering
+const mockPosts = [
+ {
+ metadata: {
+ title: 'Building a Real-Time Chat Application',
+ date: '2025-02-02',
+ slug: 'real-time-chat',
+ description: 'Learn how to build a real-time chat application using Socket.io and React.',
+ tags: ['React', 'Socket.io', 'JavaScript']
+ },
+ content: `# Building a Real-Time Chat Application\n\nReal-time chat applications are essential in today's web landscape. This guide will show you how to build one using Socket.io and React.`
+ },
+ {
+ metadata: {
+ title: 'Getting Started with Next.js and MDX',
+ date: '2025-01-15',
+ slug: 'nextjs-mdx-blog',
+ description: 'A guide to creating a blog with Next.js and MDX for rich content.',
+ tags: ['Next.js', 'MDX', 'React']
+ },
+ content: `# Getting Started with Next.js and MDX\n\nMDX combines the power of Markdown with JSX components, making it perfect for rich content blogs.`
+ }
+];
+
+export async function getPostSlugs(): Promise {
+ // Client-side: Return mock slugs or fetch from API
+ if (isClient) {
+ console.log("Client-side: Using mock post slugs");
+ return mockPosts.map(post => post.metadata.slug);
+ }
+
+ // Server-side: Use file system
+ try {
+ // Dynamic import fs and path modules server-side only
+ const fs = await import('fs/promises');
+ const path = await import('path');
+
+ const postsDirectory = path.join(process.cwd(), 'src', 'posts');
+
+ // Create directory if it doesn't exist
+ try {
+ await fs.access(postsDirectory);
+ } catch (error) {
+ console.log("Posts directory doesn't exist, creating at:", postsDirectory);
+ await fs.mkdir(postsDirectory, { recursive: true });
+ return [];
+ }
+
+ const files = await fs.readdir(postsDirectory);
+ console.log("Found files in posts directory:", files);
+
+ const slugs = files
+ .filter(file => path.extname(file) === '.mdx')
+ .map(file => file.replace(/\.mdx$/, ''));
+
+ console.log("Found slugs:", slugs);
+ return slugs;
+ } catch (error) {
+ console.error("Error getting post slugs:", error);
+ return [];
+ }
+}
+
+export async function getPostBySlug(slug: string): Promise<{ content: string; metadata: PostMetadata }> {
+ // Client-side: Return mock post or fetch from API
+ if (isClient) {
+ console.log("Client-side: Looking for mock post with slug:", slug);
+ const post = mockPosts.find(post => post.metadata.slug === slug);
+
+ if (post) {
+ return { content: post.content, metadata: post.metadata };
+ }
+
+ return {
+ content: "# Post Not Found\n\nThe requested post could not be found.",
+ metadata: {
+ title: "Post Not Found",
+ date: new Date().toISOString(),
+ slug: slug
+ }
+ };
+ }
+
+ // Server-side: Use file system
+ try {
+ // Dynamic import fs and path modules server-side only
+ const fs = await import('fs/promises');
+ const path = await import('path');
+
+ const postsDirectory = path.join(process.cwd(), 'src', 'posts');
+ const fullPath = path.join(postsDirectory, `${slug}.mdx`);
+
+ try {
+ await fs.access(fullPath);
+ } catch (error) {
+ console.error("Post file does not exist:", fullPath);
+ return {
+ content: "# Post not found\n\nThe requested post could not be found.",
+ metadata: {
+ title: "Post Not Found",
+ date: new Date().toISOString(),
+ slug: slug
+ }
+ };
+ }
+
+ const fileContents = await fs.readFile(fullPath, 'utf8');
+ const { data, content } = matter(fileContents);
+
+ const metadata: PostMetadata = {
+ title: data.title || 'Untitled Post',
+ date: data.date || new Date().toISOString(),
+ slug: slug,
+ description: data.description || '',
+ tags: data.tags || [],
+ ...data,
+ };
+
+ return { content, metadata };
+ } catch (error) {
+ console.error("Error getting post by slug:", error);
+ return {
+ content: `# Error Loading Post\n\nThere was an error loading this post: ${error.message}`,
+ metadata: {
+ title: "Error Loading Post",
+ date: new Date().toISOString(),
+ slug: slug
+ }
+ };
+ }
+}
+
+export async function getAllPosts(): Promise<{ content: string; metadata: PostMetadata }[]> {
+ // Client-side: Return mock posts or fetch from API
+ if (isClient) {
+ console.log("Client-side: Using mock posts");
+ return mockPosts;
+ }
+
+ // Server-side: Use file system
+ try {
+ const slugs = await getPostSlugs();
+
+ if (slugs.length === 0) {
+ console.log("No posts found, returning example post");
+ return [{
+ content: `# Welcome to the Blog\n\nThis is a default post.`,
+ metadata: {
+ title: "Welcome to the Blog",
+ date: new Date().toISOString(),
+ slug: "example-post",
+ tags: ["Welcome"]
+ }
+ }];
+ }
+
+ const postsPromises = slugs.map(slug => getPostBySlug(slug));
+ const posts = await Promise.all(postsPromises);
+
+ // Sort posts by date in descending order
+ return posts.sort((post1, post2) =>
+ (new Date(post1.metadata.date) > new Date(post2.metadata.date) ? -1 : 1)
+ );
+ } catch (error) {
+ console.error("Error getting all posts:", error);
+ return mockPosts;
+ }
+}
+
+// Calculate reading time based on the content
+export function calculateReadingTime(content: string): string {
+ if (!content) return "1 min read";
+
+ const wordsPerMinute = 200;
+ const wordCount = content.split(/\s+/).length;
+ const readingTime = Math.max(1, Math.ceil(wordCount / wordsPerMinute));
+ return `${readingTime} min read`;
+}
diff --git a/src/lib/projects/client.ts b/src/lib/projects/client.ts
new file mode 100644
index 0000000..1701717
--- /dev/null
+++ b/src/lib/projects/client.ts
@@ -0,0 +1,54 @@
+import { Project } from "@/types/project";
+import { allProjects } from "./data";
+
+export class ProjectsClient {
+ private static instance: ProjectsClient;
+ private projectsCache: Project[] | null = null;
+
+ private constructor() { }
+
+ public static getInstance(): ProjectsClient {
+ if (!ProjectsClient.instance) {
+ ProjectsClient.instance = new ProjectsClient();
+ }
+ return ProjectsClient.instance;
+ }
+
+ public getAllProjects(): Project[] {
+ if (this.projectsCache) {
+ return this.projectsCache;
+ }
+
+ // Use the statically imported projects instead of dynamic file reading
+ this.projectsCache = allProjects;
+ return this.projectsCache;
+ }
+
+ public getProjectById(id: string): Project | undefined {
+ return this.getAllProjects().find(project => project.id === id);
+ }
+
+ public getFeaturedProjects(): Project[] {
+ return this.getAllProjects().filter(project => project.featured);
+ }
+
+ public getProjectsByTag(tag: string): Project[] {
+ return this.getAllProjects().filter(project => project.tags.includes(tag));
+ }
+
+ public getAllProjectTags(): string[] {
+ const tags = new Set();
+ this.getAllProjects().forEach(project => {
+ project.tags.forEach(tag => {
+ tags.add(tag);
+ });
+ });
+ return Array.from(tags);
+ }
+
+ public getAllProjectIds(): string[] {
+ return this.getAllProjects().map(project => project.id);
+ }
+}
+
+export default ProjectsClient.getInstance();
diff --git a/src/lib/projects/data/cordx.ts b/src/lib/projects/data/cordx.ts
new file mode 100644
index 0000000..f34d8ab
--- /dev/null
+++ b/src/lib/projects/data/cordx.ts
@@ -0,0 +1,105 @@
+import { Project } from "@/types/project";
+
+const project: Project = {
+ id: "cordx",
+ title: "CordX",
+ description: "The all-in-one platform for seamless file sharing and media hosting with customizable features.",
+ longDescription: `
+# CordX
+
+**CordX** is revolutionizing media sharing by providing a fast, secure, and highly customizable file hosting platform tailored for communities, creators, and developers alike. Whether you're sharing large media files, hosting images, or managing uploads for projects, CordX makes it effortless with a focus on speed, security, and user control.
+
+## Key Features
+
+For users and organizations, CordX offers:
+
+- **Custom Domains & CNAMEs**: Fully configurable domains for branding and ease of access.
+- **Fast Uploads & Downloads**: Powered by streamlined server architecture designed for large files and high traffic.
+- **Secure Uploads**: Supports authentication, permissions, and encrypted transfers.
+- **Rich Media Support**: Automatic handling for images, videos, and other media types with preview capabilities.
+- **API Access**: Robust REST API for integrating uploads, management, and retrieval into your own apps.
+
+## Technology Stack
+
+CordX is built for reliability and scalability, leveraging:
+
+- **Node.js & Express**: For fast, scalable backend API endpoints.
+- **React & Next.js**: For an intuitive, responsive user interface.
+- **PostgreSQL**: Robust relational database for storing user data, upload metadata, and permissions.
+- **Cloud Storage Solutions**: Integration with services like AWS S3 or equivalent for file hosting.
+- **OAuth2 & JWT**: For secure user authentication and permissions management.
+- **CDN & Caching**: To ensure fast delivery of media content worldwide.
+- **Webhooks & API Hooks**: Automate workflows and integrations.
+
+## Impact
+
+CordX aims to be the go-to decentralized media hosting platform for artists, developers, and communities looking for privacy, speed, and customization. It empowers users to deploy their own branded upload solutions, integrate with existing tools, and manage media efficiently, all in a secure environment.
+
+> With CordX, sharing your digital world has never been easier or more customizable!
+
+## Development Journey
+
+This project showcases my expertise in:
+
+1. **Full-stack Development** - Combining React for front-end and Node.js for backend APIs.
+2. **Cloud & Infrastructure Management** - Deploying scalable solutions with cloud storage and CDN.
+3. **Security Best Practices** - Implementing secure file transfers, authentication, and user permissions.
+4. **API Design & Integration** - Building comprehensive and developer-friendly APIs.
+5. **User Experience** - Focusing on accessibility, mobile-first design, and smooth workflows.
+
+The platform continues to evolve, adding new features to give users complete control over their media sharing experience.
+`,
+ images: [
+ "/previews/cordx.ca/home.png",
+ "/previews/cordx.ca/features.png",
+ "/previews/cordx.ca/dashboard.png",
+ "/previews/cordx.ca/metrics.png"
+ ],
+ tags: ["Next.js", "React", "Node.js", "PostgreSQL", "Prisma", "Tailwind CSS"],
+ links: {
+ demo: "https://cordx.ca",
+ github: "https://github.com/CordXApp",
+ documentation: "https://docs.cordx.ca",
+ support: "https://discord.gg/cordx"
+ },
+ featured: true,
+ technologies: [
+ { name: "Next.js", description: "For server-side rendering and frontend optimization." },
+ { name: "React", description: "Building an intuitive, responsive user interface." },
+ { name: "Node.js", description: "Backend API development for upload management." },
+ { name: "Fastify", description: "Fast and low overhead web framework for Node.js with high-performance routing." },
+ { name: "PostgreSQL", description: "Storing user profiles, permissions, and upload metadata with ACID compliance." },
+ { name: "Prisma", description: "Type-safe database ORM for seamless database operations and migrations." },
+ { name: "Next Auth", description: "Secure authentication with OAuth providers and session management." },
+ { name: "Tailwind CSS", description: "Utility-first CSS framework for rapid UI development and consistent design." },
+ { name: "Radix", description: "Unstyled, accessible UI components for building custom design systems." },
+ { name: "Framer", description: "Production-ready motion library for React animations and interactions." },
+ { name: "Zustand", description: "Small, fast, and scalable state management solution for React applications." },
+ { name: "Zod", description: "TypeScript-first schema validation with static type inference for data validation." }
+ ],
+ challenges: [
+ " Ensuring secure, scalable uploads handling media of various sizes.",
+ " Implementing flexible domain configurations for user customization.",
+ " Maintaining high speed and low latency for large media files.",
+ " Providing comprehensive permissions and access controls."
+ ],
+ solutions: [
+ " Leveraged cloud storage optimized for large file hosting with CDN integration.",
+ " Developed a microservice-based architecture with OAuth2 for secure auth.",
+ " Built an adaptive, responsive UI with React and Next.js for a seamless user experience.",
+ " Implemented role-based permissions and comprehensive API controls."
+ ],
+ keyFeatures: [
+ "Custom domain and CNAME support for branding.",
+ "Fast, secure media uploads and downloads.",
+ "Media previews, thumbnail generation, and auto-optimization.",
+ "User authentication with OAuth2 and JWT.",
+ "API access for integration into other tools and workflows.",
+ "Robust permissions and privacy controls."
+ ],
+ date: "2021-07-31",
+ role: "Founder & Lead Developer",
+ teamSize: 5
+};
+
+export default project;
\ No newline at end of file
diff --git a/src/lib/projects/data/index.ts b/src/lib/projects/data/index.ts
new file mode 100644
index 0000000..15c0f9a
--- /dev/null
+++ b/src/lib/projects/data/index.ts
@@ -0,0 +1,20 @@
+import infinityList from './infinity-list';
+import nodebyteHosting from './nodebyte-hosting';
+import nodebyteLtd from './nodebyte-ltd';
+import cordx from './cordx'
+
+// Export all projects as an array
+export const allProjects = [
+ cordx,
+ infinityList,
+ nodebyteHosting,
+ nodebyteLtd
+];
+
+// Export individual projects for direct import if needed
+export {
+ cordx,
+ infinityList,
+ nodebyteHosting,
+ nodebyteLtd
+};
diff --git a/src/lib/projects/data/infinity-list.ts b/src/lib/projects/data/infinity-list.ts
new file mode 100644
index 0000000..01f5c8d
--- /dev/null
+++ b/src/lib/projects/data/infinity-list.ts
@@ -0,0 +1,103 @@
+import { Project } from "@/types/project";
+
+const project: Project = {
+ id: "infinity-list",
+ title: "Infinity List",
+ description: "Begin your Discord journey with our extensive directory, featuring a wide array of bots and servers.",
+ longDescription: `
+# Infinity List
+
+**Infinity List** is pioneering the future of Discord bot and server discovery. As one of the most comprehensive platforms in the ecosystem, it empowers developers to showcase their bots with custom vanity links, attractive widgets, and tailored packs, making promotion effortless and effective.
+
+## Key Features
+
+For server owners and community managers, Infinity List offers:
+
+- **Smart Discovery**: Intuitive filters by category, tags, or keywords enabling lightning-fast discovery of the perfect bots
+- **Custom Widgets**: Beautiful, customizable widgets for bot promotion
+- **Vanity Links**: Professional custom URLs for your bots and servers
+- **Real-time Analytics**: Comprehensive dashboards with live statistics
+
+## Technology Stack
+
+Beyond simple listings, Infinity List emphasizes transparency and engagement through:
+
+- **Real-time Updates**: Powered by WebSockets for instant status changes
+- **Seamless Authentication**: OAuth2 integration with Discord for effortless management
+- **Scalable Architecture**: Ensures performance remains smooth regardless of traffic
+- **Advanced Search**: Lightning-fast discovery with intelligent filtering
+
+## Impact
+
+Making it the go-to hub for **millions of users worldwide**.
+
+Whether you're a developer eager to grow your bot's reach or a server owner seeking the best tools for your community, Infinity List provides an all-in-one solution that simplifies growth, fosters discovery, and enhances your Discord experience.
+
+> Join us on the forefront of Discord's bot ecosystem where discovery meets innovation!
+
+## Development Journey
+
+This project showcases my expertise in:
+
+1. **Full-stack Development** - Next.js frontend with Go backend
+2. **Database Design** - PostgreSQL optimization for millions of records
+3. **Real-time Systems** - WebSocket implementation for live updates
+4. **Community Building** - Growing from 0 to millions of users
+5. **Team Leadership** - Managing a team of 9 developers
+
+The platform continues to evolve, setting new standards for Discord bot and server discovery.
+ `,
+ images: [
+ "/previews/infinitybots.gg/ibl-preview-1.png",
+ "/previews/infinitybots.gg/ibl-preview-2.png",
+ "/previews/infinitybots.gg/ibl-preview-3.png",
+ "/previews/infinitybots.gg/ibl-preview-4.png"
+ ],
+ tags: ["Next.js", "React", "Tailwind CSS", "PostgreSQL", "Go", "WebSocket", "OAuth2"],
+ links: {
+ demo: "https://infinitybots.gg",
+ github: "https://github.com/InfinityBotList",
+ documentation: "https://docs.infinitybots.gg",
+ support: "https://discord.gg/infinitybots"
+ },
+ featured: true,
+ technologies: [
+ { name: "Next.js", description: "React framework for server-side rendering, static site generation, and high-performance frontend." },
+ { name: "React", description: "Component-based UI library for building interactive user interfaces." },
+ { name: "TypeScript", description: "Typed superset of JavaScript for safer, more maintainable code." },
+ { name: "Python", description: "Used for Discord bots and automation tasks within the ecosystem." },
+ { name: "Go", description: "Backend API and worker services, chosen for concurrency and performance." },
+ { name: "Rust", description: "Performance-critical microservices and background jobs." },
+ { name: "PostgreSQL", description: "Relational database for storing user data, bot/server info, and analytics." },
+ { name: "Tailwind CSS", description: "Utility-first CSS framework for rapid, responsive UI development." },
+ { name: "WebSocket", description: "Real-time updates for bot status, metrics, and notifications." },
+ { name: "OAuth2", description: "Secure Discord authentication for bot and server management." },
+ { name: "Redis", description: "In-memory data store used for caching in the website and API for fast data access." },
+ { name: "SWR", description: "React Hooks library for remote data fetching and caching, used as a query client from the website to the API." }
+ ],
+ challenges: [
+ "Scaling infrastructure to support millions of bot and server listings efficiently.",
+ "Maintaining real-time data synchronization globally with minimal latency.",
+ "Creating an intuitive UI that balances simplicity with advanced search and filter options.",
+ "Moderation and community engagement while ensuring data privacy and security."
+ ],
+ solutions: [
+ "Implemented horizontal scaling with load balancers and database sharding.",
+ "Developed websocket-based real-time updates for bot statuses and user interactions.",
+ "Built an accessible, mobile-optimized UI with Tailwind CSS components and state management.",
+ "Integrated OAuth2 for secure login and permissions, leveraging Discord's API."
+ ],
+ keyFeatures: [
+ "Smart search filters for bots and servers based on categories, tags, or custom keywords.",
+ "Custom widget and vanity link generation for easier bot promotion.",
+ "Comprehensive analytics dashboard for bot owners and server admins.",
+ "Automated moderation tools and community moderation support.",
+ "Seamless Discord OAuth2 login for effortless bot management.",
+ "Powerful API for developers to integrate platform features into their own tools."
+ ],
+ date: "2020-09-24",
+ role: "Founder & Lead Developer",
+ teamSize: 9
+};
+
+export default project;
\ No newline at end of file
diff --git a/src/lib/projects/data/nodebyte-hosting.ts b/src/lib/projects/data/nodebyte-hosting.ts
new file mode 100644
index 0000000..cbf9cf9
--- /dev/null
+++ b/src/lib/projects/data/nodebyte-hosting.ts
@@ -0,0 +1,126 @@
+import { Project } from "@/types/project";
+
+const project: Project = {
+ id: "nodebyte-hosting",
+ title: "NodeByte Hosting",
+ description: "Fast, reliable, scalable and secure hosting services for your gaming experience.",
+ longDescription: `
+# NodeByte Hosting
+
+**NodeByte Hosting** delivers premium game server hosting solutions with an unwavering focus on performance, reliability, and user experience. Built from the ground up to cater to both casual gamers and professional esports teams, our platform simplifies server management while providing enterprise-grade infrastructure.
+
+## Core Features
+
+Our hosting platform offers comprehensive solutions for the gaming community:
+
+- **One-Click Deployment**: Instant setup for popular games including Minecraft, Rust, ARK, and more
+- **Custom Control Panel**: Intuitive interface with real-time server monitoring and management
+- **Live Console Access**: Direct server console access with command history and auto-completion
+- **Automatic Backups**: Scheduled backups with one-click restoration capabilities
+- **DDoS Protection**: Enterprise-grade protection ensuring uninterrupted gameplay
+
+## Technical Excellence
+
+NodeByte Hosting leverages cutting-edge technology to deliver exceptional performance:
+
+- **High-Performance Hardware**: NVMe SSD storage with DDR4 RAM for lightning-fast load times
+- **Global Network**: Strategic server locations worldwide for minimal latency
+- **Container Isolation**: Secure, isolated environments with dedicated resource allocation
+- **Auto-Scaling**: Dynamic resource allocation based on server load and player count
+- **99.9% Uptime SLA**: Redundant infrastructure with automatic failover systems
+
+## Developer Experience
+
+Built with modern web technologies to ensure a seamless user experience:
+
+- **React & Next.js**: Server-side rendering for optimal performance and SEO
+- **TypeScript**: Type-safe development ensuring reliability and maintainability
+- **Framer Motion**: Smooth, polished animations enhancing user interaction
+- **Real-time Updates**: Live server status and player count updates
+- **GitHub Integration**: Automated deployments and version control integration
+
+## Impact & Growth
+
+Since launch, NodeByte Hosting has achieved remarkable milestones:
+
+> **6-person team** managing game servers across multiple continents
+
+## Customer Success
+
+Our commitment to excellence is reflected in customer feedback:
+
+- **Sub-10 minute support response times** - even on holidays
+- **Intuitive panel design** praised by both technical and non-technical users
+- **Competitive pricing** with transparent, no-hidden-fees structure
+- **Community-focused approach** with dedicated Discord support
+
+## Innovation Journey
+
+This project demonstrates expertise in:
+
+1. **System Architecture** - Designing scalable, fault-tolerant hosting infrastructure
+2. **User Experience** - Creating intuitive interfaces for complex server management
+3. **Performance Optimization** - Ensuring minimal latency and maximum uptime
+4. **Security Implementation** - Protecting customer data and server environments
+5. **Team Leadership** - Coordinating development and operations across multiple time zones
+
+NodeByte Hosting continues to push the boundaries of game server hosting, setting new standards for performance, reliability, and customer satisfaction in the gaming industry.
+ `,
+ images: [
+ "/previews/nodebyte.host/nbh-preview-1.png",
+ "/previews/nodebyte.host/nbh-preview-2.png",
+ "/previews/nodebyte.host/nbh-preview-3.png",
+ "/previews/nodebyte.host/nbh-preview-4.png"
+ ],
+ tags: ["Next.js", "React", "TypeScript", "Framer Motion"],
+ links: {
+ demo: "https://nodebyte.host",
+ github: "https://github.com/NodeByteHosting/Website"
+ },
+ featured: true,
+ technologies: [
+ { name: "Next.js", description: "Powers the frontend with server-side rendering and static site generation" },
+ { name: "React", description: "Building an intuitive, responsive user interface." },
+ { name: "Node.js", description: "Runs the backend API for game server management" },
+ { name: "TypeScript", description: "Ensures type safety and improves developer experience" },
+ { name: "Framer", description: "Creates smooth, polished animations and transitions" },
+ { name: "Radix", description: "Provides accessible, unstyled UI components" },
+ { name: "Tailwind CSS", description: "Enables rapid UI development with utility-first approach" },
+ { name: "React Query", description: "Manages server state and caching for optimized data fetching" },
+ { name: "GitHub API", description: "Powers the Knowledge Base, Legal Pages and more." }
+ ],
+ challenges: [
+ "Creating an intuitive control panel for non-technical users",
+ "Ensuring high uptime and performance for game servers",
+ "Implementing secure isolation between customer environments"
+ ],
+ solutions: [
+ "Developed a custom UI with real-time server controls and status monitoring",
+ "Built a distributed architecture with automatic failover systems",
+ "Implemented containerization with resource limits and network isolation"
+ ],
+ keyFeatures: [
+ "One-click game server deployment",
+ "Custom control panel with live console access",
+ "Automatic backups and server snapshots",
+ "DDoS protection for all game servers",
+ "24/7 server monitoring and alerting"
+ ],
+ date: "2024-01-22",
+ role: "Chief Operations Officer",
+ teamSize: 6,
+ testimonials: [
+ {
+ quote: "Quick and easy support system. I opened a ticket and in under 10 minutes I got a response. I was very pleasantly surprised as today is Christmas Eve so I'm certain the team is working with limited members.",
+ author: "Wolfie_Gamer",
+ position: "24 January 2024"
+ },
+ {
+ quote: "The excellent panel design Fast response from staff members seeking support. Good prices for what they offer.",
+ author: "Ollie",
+ position: "21 March 2024"
+ }
+ ]
+};
+
+export default project;
diff --git a/src/lib/projects/data/nodebyte-ltd.ts b/src/lib/projects/data/nodebyte-ltd.ts
new file mode 100644
index 0000000..c908da0
--- /dev/null
+++ b/src/lib/projects/data/nodebyte-ltd.ts
@@ -0,0 +1,37 @@
+import { Project } from "@/types/project";
+
+const project: Project = {
+ id: "nodebyte-ltd",
+ title: "NodeByte LTD",
+ description: "Helping businesses transform their digital presence with cutting edge solutions.",
+ longDescription: "A passionate team of tech experts dedicated to helping businesses succeed through innovative technology solutions.",
+ images: [
+ "/NodeByteLTD/home.png",
+ "/NodeByteLTD/about.png",
+ "/NodeByteLTD/services.png",
+ "/NodeByteLTD/discord.png"
+ ],
+ tags: ["Next.js", "React", "TypeScript", "Framer Motion", "RadixUI"],
+ links: {
+ demo: "https://nodebyte.co.uk",
+ github: "https://github.com/NodeByteHosting"
+ },
+ featured: true,
+ technologies: [
+ { name: "Next.js", description: "For server-side rendering and static site generation" },
+ { name: "Node.js", description: "Backend API development for upload management." },
+ { name: "React", description: "Building interactive user interfaces" },
+ { name: "TypeScript", description: "Type-safe code development" },
+ { name: "Framer", description: "Creating smooth animations" },
+ { name: "Radix", description: "Accessible component primitives" }
+ ],
+ keyFeatures: [
+ "Responsive web design",
+ "Custom software solutions",
+ "Business process automation",
+ "Digital transformation consulting"
+ ],
+ date: "2022-05-15"
+};
+
+export default project;
diff --git a/src/lib/referrals/client.ts b/src/lib/referrals/client.ts
new file mode 100644
index 0000000..ad8b314
--- /dev/null
+++ b/src/lib/referrals/client.ts
@@ -0,0 +1,61 @@
+import { Referral, ReferralCategory } from "@/types/referrals";
+import { referrals, categories } from "./data";
+
+export class ReferralsClient {
+ private static instance: ReferralsClient;
+ private referralsCache: Referral[] | null = null;
+ private categoriesCache: ReferralCategory[] | null = null;
+
+ private constructor() { }
+
+ public static getInstance(): ReferralsClient {
+ if (!ReferralsClient.instance) {
+ ReferralsClient.instance = new ReferralsClient();
+ }
+ return ReferralsClient.instance;
+ }
+
+ public getAllReferrals(): Referral[] {
+ if (this.referralsCache) {
+ return this.referralsCache;
+ }
+
+ this.referralsCache = referrals;
+ return this.referralsCache;
+ }
+
+ public getAllCategories(): ReferralCategory[] {
+ if (this.categoriesCache) {
+ return this.categoriesCache;
+ }
+
+ this.categoriesCache = categories;
+ return this.categoriesCache;
+ }
+
+ public getReferralById(id: string): Referral | undefined {
+ return this.getAllReferrals().find(referral => referral.id === id);
+ }
+
+ public getReferralsByCategory(categoryId: string): Referral[] {
+ return this.getAllReferrals().filter(referral => referral.category === categoryId);
+ }
+
+ public getFeaturedReferrals(): Referral[] {
+ return this.getAllReferrals().filter(referral => referral.featured);
+ }
+
+ public getNewReferrals(): Referral[] {
+ return this.getAllReferrals().filter(referral => referral.new);
+ }
+
+ public getCategoryById(id: string): ReferralCategory | undefined {
+ return this.getAllCategories().find(category => category.id === id);
+ }
+
+ public getAllCategoryIds(): string[] {
+ return this.getAllCategories().map(category => category.id);
+ }
+}
+
+export default ReferralsClient.getInstance();
diff --git a/src/lib/referrals/data/categories.ts b/src/lib/referrals/data/categories.ts
new file mode 100644
index 0000000..650ae2a
--- /dev/null
+++ b/src/lib/referrals/data/categories.ts
@@ -0,0 +1,46 @@
+import { ReferralCategory } from "@/types/referrals";
+
+export const categories: ReferralCategory[] = [
+ {
+ id: "hosting",
+ name: "Web Hosting",
+ description: "Hosting services for your websites and applications",
+ icon: "IoServerOutline",
+ color: "text-blue-500"
+ },
+ {
+ id: "cloud",
+ name: "Cloud Platforms",
+ description: "Cloud computing platforms and infrastructure services",
+ icon: "IoCloudOutline",
+ color: "text-cyan-500"
+ },
+ {
+ id: "tools",
+ name: "Development Tools",
+ description: "Software and tools for developers",
+ icon: "IoCodeSlashOutline",
+ color: "text-purple-500"
+ },
+ {
+ id: "services",
+ name: "Online Services",
+ description: "Subscription services and SaaS products",
+ icon: "IoCloudOutline",
+ color: "text-green-500"
+ },
+ {
+ id: "gaming",
+ name: "Gaming",
+ description: "Gaming platforms and services",
+ icon: "IoGameControllerOutline",
+ color: "text-red-500"
+ },
+ {
+ id: "education",
+ name: "Education",
+ description: "Learning platforms and courses",
+ icon: "IoSchoolOutline",
+ color: "text-yellow-500"
+ }
+];
diff --git a/src/lib/referrals/data/cloud.ts b/src/lib/referrals/data/cloud.ts
new file mode 100644
index 0000000..bc5e00f
--- /dev/null
+++ b/src/lib/referrals/data/cloud.ts
@@ -0,0 +1,28 @@
+import { Referral } from "@/types/referrals";
+
+const cloudReferrals: Referral[] = [
+ {
+ id: "railway-cloud",
+ title: 'Railway',
+ description: 'Railway is a deployment platform designed to streamline the software development life-cycle, starting with instant deployments and effortless scaling.',
+ company: 'Railway',
+ url: 'https://railway.com?referralCode=pixelateme',
+ discount: '$5/30 Day Credit',
+ code: 'pixelateme',
+ benefits: [
+ 'Instant deployments from Git',
+ 'Auto-scaling infrastructure',
+ 'Built-in databases',
+ 'Zero-config deployments',
+ 'Pay-per-use pricing'
+ ],
+ featured: false,
+ category: 'cloud',
+ categoryName: 'Cloud Platform',
+ color: 'bg-purple-500',
+ bannerImage: '/referrals/railway/banner.png',
+ new: false
+ },
+];
+
+export default cloudReferrals;
diff --git a/src/lib/referrals/data/education.ts b/src/lib/referrals/data/education.ts
new file mode 100644
index 0000000..fbabb88
--- /dev/null
+++ b/src/lib/referrals/data/education.ts
@@ -0,0 +1,5 @@
+import { Referral } from "@/types/referrals";
+
+const educationReferrals: Referral[] = [];
+
+export default educationReferrals;
diff --git a/src/lib/referrals/data/gaming.ts b/src/lib/referrals/data/gaming.ts
new file mode 100644
index 0000000..bd428f1
--- /dev/null
+++ b/src/lib/referrals/data/gaming.ts
@@ -0,0 +1,5 @@
+import { Referral } from "@/types/referrals";
+
+const gamingReferrals: Referral[] = [];
+
+export default gamingReferrals;
diff --git a/src/lib/referrals/data/hosting.ts b/src/lib/referrals/data/hosting.ts
new file mode 100644
index 0000000..b86699d
--- /dev/null
+++ b/src/lib/referrals/data/hosting.ts
@@ -0,0 +1,114 @@
+import { Referral } from "@/types/referrals";
+
+const hostingReferrals: Referral[] = [
+ {
+ id: "linode-cloud-credits",
+ title: 'Linode',
+ description: 'Get $100 in free cloud credits when you sign up for Linode. Perfect for testing and deploying your applications with high-performance cloud infrastructure.',
+ company: 'Linode',
+ url: 'https://www.linode.com/lp/free-credit-100/?promo=sitelin100-02162023&promo_value=100&promo_length=60&utm_source=google&utm_medium=cpc&utm_campaign=f-mc-65659&utm_id=cloud&utm_content=US-EN_NB_CL_PLG_VPS&utm_placement=NORAM&gad_source=1&gad_campaignid=1706209438&gbraid=0AAAAAD_kTnU_6NOh2232VK6X1aifmopsu&gclid=CjwKCAjw6ZTCBhBOEiwAqfwJd_vB7YSVVRGC3YdUCYBTg08Q0gDRNwZHeGzxJKn7r9h0jXH0cpRDTxoCfx0QAvD_BwE',
+ code: 'sitelin100-02162023',
+ discount: '$100/60 Day Credit',
+ benefits: [
+ 'High-performance SSD storage',
+ '11 global data centers',
+ '24/7 customer support',
+ 'Easy-to-use cloud manager'
+ ],
+ featured: true,
+ category: 'hosting',
+ categoryName: 'Cloud Hosting',
+ color: 'bg-green-500',
+ bannerImage: '/referrals/linode/banner.png',
+ new: true
+ },
+ {
+ id: "digitalocean-free-credits",
+ title: 'Digital Ocean',
+ description: 'Cloud platform to deploy, manage & scale applications. Get free $200 credit over 60 days to build and scale your projects.',
+ company: 'DigitalOcean',
+ url: 'https://m.do.co/c/a7f497dd62e5',
+ discount: '$200/60 Day Credit',
+ code: 'a7f497dd62e5',
+ benefits: [
+ 'Simple cloud computing',
+ 'Predictable pricing',
+ 'Developer-friendly tools',
+ 'Global data centers'
+ ],
+ featured: true,
+ category: 'hosting',
+ categoryName: 'Cloud Hosting',
+ color: 'bg-blue-500',
+ bannerImage: '/referrals/digital-ocean/banner.png',
+ new: false
+ },
+ {
+ id: "nodebyte-free-month",
+ title: 'NodeByte',
+ description: 'Fast, reliable, scalable and secure hosting services for your business or gaming experience. Get 10% off at checkout.',
+ company: 'NodeByte',
+ url: 'https://nodebyte.host',
+ code: 'INFINITY',
+ discount: '10% off at checkout',
+ benefits: [
+ '10% discount on all plans',
+ 'High-performance hardware',
+ 'Custom modpack support',
+ '24/7 server monitoring',
+ 'Easy server management panel'
+ ],
+ featured: true,
+ category: 'hosting',
+ categoryName: 'Game Hosting',
+ color: 'bg-green-600',
+ bannerImage: '/referrals/nodebyte/banner.png',
+ new: true
+ },
+ {
+ id: "cybrancee-hosting",
+ title: 'Cybrancee',
+ description: 'Cybrancee provides users with a powerful platform to host their digital projects. Get one month free with our exclusive code.',
+ company: 'Cybrancee',
+ url: 'https://cybrancee.com',
+ code: 'INFINITYBOTS',
+ discount: 'One month free',
+ benefits: [
+ 'One month free hosting',
+ 'Powerful hosting platform',
+ 'Digital project hosting',
+ 'Reliable infrastructure',
+ 'Easy project management'
+ ],
+ featured: false,
+ category: 'hosting',
+ categoryName: 'Web Hosting',
+ color: 'bg-purple-600',
+ bannerImage: '/referrals/cybrancee/banner.png',
+ new: false
+ },
+ {
+ id: "zap-hosting-affiliate",
+ title: 'ZAP-Hosting',
+ description: 'Get a 20% discount for the entire duration of all rental servers (excluding dedicated servers).',
+ company: 'ZAP-Hosting',
+ url: 'https://zap-hosting.com',
+ code: 'CodeMeAPixel-a-2410',
+ discount: '20% off rental servers',
+ benefits: [
+ '20% discount for entire duration',
+ 'All rental servers included',
+ 'Game server hosting',
+ 'Web hosting solutions',
+ 'DDoS protection included'
+ ],
+ featured: true,
+ category: 'hosting',
+ categoryName: 'Game Hosting',
+ color: 'bg-red-500',
+ bannerImage: '/referrals/zap/banner.png',
+ new: true
+ }
+];
+
+export default hostingReferrals;
diff --git a/src/lib/referrals/data/index.ts b/src/lib/referrals/data/index.ts
new file mode 100644
index 0000000..5adb4aa
--- /dev/null
+++ b/src/lib/referrals/data/index.ts
@@ -0,0 +1,20 @@
+import { Referral, ReferralCategory } from "@/types/referrals";
+import { categories } from "./categories";
+import hostingReferrals from "./hosting";
+import toolsReferrals from "./tools";
+import servicesReferrals from "./services";
+import gamingReferrals from "./gaming";
+import educationReferrals from "./education";
+import cloudReferrals from "./cloud";
+
+// Combine all referrals from different categories
+export const referrals: Referral[] = [
+ ...cloudReferrals,
+ ...hostingReferrals,
+ ...toolsReferrals,
+ ...servicesReferrals,
+ ...gamingReferrals,
+ ...educationReferrals
+];
+
+export { categories };
diff --git a/src/lib/referrals/data/services.ts b/src/lib/referrals/data/services.ts
new file mode 100644
index 0000000..681ffba
--- /dev/null
+++ b/src/lib/referrals/data/services.ts
@@ -0,0 +1,5 @@
+import { Referral } from "@/types/referrals";
+
+const servicesReferrals: Referral[] = [];
+
+export default servicesReferrals;
diff --git a/src/lib/referrals/data/tools.ts b/src/lib/referrals/data/tools.ts
new file mode 100644
index 0000000..e60d39b
--- /dev/null
+++ b/src/lib/referrals/data/tools.ts
@@ -0,0 +1,5 @@
+import { Referral } from "@/types/referrals";
+
+const toolsReferrals: Referral[] = [];
+
+export default toolsReferrals;
diff --git a/src/lib/routes.ts b/src/lib/routes.ts
new file mode 100644
index 0000000..35d4e45
--- /dev/null
+++ b/src/lib/routes.ts
@@ -0,0 +1,50 @@
+interface RouteInfo {
+ title: string;
+ description: string;
+ completionPercentage: number;
+ inDevelopment: boolean;
+ launchDate?: Date;
+ showNotification?: boolean;
+}
+
+export const routeConfig: Record = {
+ docs: {
+ title: "Documentation Coming Soon",
+ description: "My comprehensive documentation is currently under development. Check back soon for detailed guides and resources.",
+ completionPercentage: 10,
+ inDevelopment: true,
+ launchDate: new Date(new Date().setDate(new Date().getDate() + 120)),
+ showNotification: false
+ },
+ fivem: {
+ title: "FiveM Resources Coming Soon",
+ description: "I am working on an exciting new section where you can browse, purchase and download my FiveM scripts, view instructions for adding them to your server and more.",
+ completionPercentage: 25,
+ inDevelopment: true,
+ launchDate: new Date(new Date().setDate(new Date().getDate() + 60)),
+ showNotification: false
+ },
+};
+
+/**
+ * Check if a route is in development mode
+ * @param path The route path to check
+ * @returns Boolean indicating if the route is in development
+ */
+export function isRouteInDevelopment(path: string): boolean {
+ // Extract the first segment of the path
+ const segment = path.split('/')[1];
+
+ // Check if this segment exists in our route config and is marked as in development
+ return !!routeConfig[segment]?.inDevelopment;
+}
+
+/**
+ * Get route information for a specific path
+ * @param path The route path
+ * @returns Route information or undefined if not found
+ */
+export function getRouteInfo(path: string): RouteInfo | undefined {
+ const segment = path.split('/')[1];
+ return routeConfig[segment];
+}
diff --git a/src/lib/skills/client.ts b/src/lib/skills/client.ts
new file mode 100644
index 0000000..6460130
--- /dev/null
+++ b/src/lib/skills/client.ts
@@ -0,0 +1,68 @@
+import { Skill, SkillCategory } from "@/types/skills";
+import { allSkillCategories } from "./data";
+
+export class SkillsClient {
+ private static instance: SkillsClient;
+ private skillCategoriesCache: SkillCategory[] | null = null;
+
+ private constructor() { }
+
+ public static getInstance(): SkillsClient {
+ if (!SkillsClient.instance) {
+ SkillsClient.instance = new SkillsClient();
+ }
+ return SkillsClient.instance;
+ }
+
+ public getAllSkillCategories(): SkillCategory[] {
+ if (this.skillCategoriesCache) {
+ return this.skillCategoriesCache;
+ }
+
+ // Use the statically imported skill categories
+ this.skillCategoriesCache = allSkillCategories;
+ return this.skillCategoriesCache;
+ }
+
+ public getSkillCategoryByName(name: string): SkillCategory | undefined {
+ return this.getAllSkillCategories().find(category =>
+ category.name.toLowerCase() === name.toLowerCase()
+ );
+ }
+
+ public getAllSkills(): Skill[] {
+ return this.getAllSkillCategories().flatMap(category => category.skills);
+ }
+
+ public getSkillByName(name: string): Skill | undefined {
+ return this.getAllSkills().find(skill =>
+ skill.name.toLowerCase() === name.toLowerCase()
+ );
+ }
+
+ public getSkillsByLevel(level: number): Skill[] {
+ return this.getAllSkills().filter(skill => skill.level === level);
+ }
+
+ public getExpertSkills(): Skill[] {
+ return this.getSkillsByLevel(5);
+ }
+
+ public getAllSkillNames(): string[] {
+ return this.getAllSkills().map(skill => skill.name);
+ }
+
+ public getAllCategoryNames(): string[] {
+ return this.getAllSkillCategories().map(category => category.name);
+ }
+
+ public searchSkills(query: string): Skill[] {
+ const lowerQuery = query.toLowerCase();
+ return this.getAllSkills().filter(skill =>
+ skill.name.toLowerCase().includes(lowerQuery) ||
+ (skill.description && skill.description.toLowerCase().includes(lowerQuery))
+ );
+ }
+}
+
+export default SkillsClient.getInstance();
diff --git a/src/lib/skills/data/auth.ts b/src/lib/skills/data/auth.ts
new file mode 100644
index 0000000..f1ef515
--- /dev/null
+++ b/src/lib/skills/data/auth.ts
@@ -0,0 +1,48 @@
+import { SkillCategory } from "@/types/skills";
+
+const authSkills: SkillCategory = {
+ name: "Authentication & Security",
+ description: "Implementing secure authentication and authorization systems.",
+ icon: "IoShieldCheckmarkOutline",
+ color: "text-red-400",
+ skills: [
+ {
+ name: "OAuth 2.0",
+ icon: "oauth",
+ level: 4,
+ description: "Industry-standard protocol for authorization."
+ },
+ {
+ name: "JWT",
+ icon: "jwt",
+ level: 4,
+ description: "JSON Web Tokens for secure data transmission and authentication."
+ },
+ {
+ name: "NextAuth.js",
+ icon: "nextauth",
+ level: 4,
+ description: "Authentication solution for Next.js applications."
+ },
+ {
+ name: "Clerk",
+ icon: "clerk",
+ level: 4,
+ description: "Complete user management solution with authentication and user profiles."
+ },
+ {
+ name: "Auth0",
+ icon: "auth0",
+ level: 3,
+ description: "Identity platform for authentication, authorization, and user management."
+ },
+ {
+ name: "Firebase Auth",
+ icon: "firebase",
+ level: 4,
+ description: "Authentication service with multiple sign-in methods."
+ }
+ ]
+};
+
+export default authSkills;
diff --git a/src/lib/skills/data/backend.ts b/src/lib/skills/data/backend.ts
new file mode 100644
index 0000000..f003ebf
--- /dev/null
+++ b/src/lib/skills/data/backend.ts
@@ -0,0 +1,84 @@
+import { SkillCategory } from "@/types/skills";
+
+const backendSkills: SkillCategory = {
+ name: "Backend Development",
+ description: "Creating scalable server-side applications and APIs to power web experiences.",
+ icon: "IoServerOutline",
+ color: "text-green-400",
+ skills: [
+ {
+ name: "Node.js",
+ icon: "nodejs",
+ level: 4,
+ description: "Building server-side applications and APIs with JavaScript/TypeScript."
+ },
+ {
+ name: "Express.js",
+ icon: "express",
+ level: 4,
+ description: "Creating REST APIs and web servers with the Express framework."
+ },
+ {
+ name: "Fastify",
+ icon: "fastify",
+ level: 4,
+ description: "High-performance, low-overhead web framework for Node.js."
+ },
+ {
+ name: "Elysia.js",
+ icon: "bun",
+ level: 3,
+ description: "TypeScript-first, Bun-focused framework for building blazing fast web services."
+ },
+ {
+ name: "NestJS",
+ icon: "nestjs",
+ level: 3,
+ description: "Progressive Node.js framework for scalable server-side applications."
+ },
+ {
+ name: "Hono",
+ icon: "hono",
+ level: 3,
+ description: "Ultrafast web framework for the Edges, supporting all JavaScript runtimes."
+ },
+ {
+ name: "Go",
+ icon: "go",
+ level: 3,
+ description: "Developing high-performance microservices and APIs."
+ },
+ {
+ name: "Python",
+ icon: "python",
+ level: 3,
+ description: "Server-side development and automation scripting."
+ },
+ {
+ name: "Java",
+ icon: "java",
+ level: 3,
+ description: "Backend development with Spring Boot and enterprise applications."
+ },
+ {
+ name: "PHP",
+ icon: "php",
+ level: 3,
+ description: "Web development with PHP frameworks like Laravel."
+ },
+ {
+ name: "GraphQL",
+ icon: "graphql",
+ level: 4,
+ description: "Designing and implementing GraphQL APIs for flexible data fetching."
+ },
+ {
+ name: "REST API",
+ icon: "api",
+ level: 5,
+ description: "Designing and implementing RESTful services following best practices."
+ }
+ ]
+};
+
+export default backendSkills;
diff --git a/src/lib/skills/data/database.ts b/src/lib/skills/data/database.ts
new file mode 100644
index 0000000..6aeb1ce
--- /dev/null
+++ b/src/lib/skills/data/database.ts
@@ -0,0 +1,66 @@
+import { SkillCategory } from "@/types/skills";
+
+const databaseSkills: SkillCategory = {
+ name: "Database & Storage",
+ description: "Managing and optimizing data storage solutions for applications.",
+ icon: "IoServerOutline",
+ color: "text-yellow-400",
+ skills: [
+ {
+ name: "MongoDB",
+ icon: "mongodb",
+ level: 4,
+ description: "Document-based NoSQL database design and optimization."
+ },
+ {
+ name: "PostgreSQL",
+ icon: "postgresql",
+ level: 4,
+ description: "Relational database design, optimization, and advanced queries."
+ },
+ {
+ name: "MySQL",
+ icon: "mysql",
+ level: 4,
+ description: "Relational database management and optimization."
+ },
+ {
+ name: "Redis",
+ icon: "redis",
+ level: 3,
+ description: "In-memory data structure store used for caching and real-time applications."
+ },
+ {
+ name: "Firestore",
+ icon: "firebase",
+ level: 4,
+ description: "Cloud-hosted NoSQL database with real-time capabilities."
+ },
+ {
+ name: "SQL",
+ icon: "database",
+ level: 4,
+ description: "Writing complex queries, joins, and optimizing database performance."
+ },
+ {
+ name: "Supabase",
+ icon: "supabase",
+ level: 4,
+ description: "Open-source Firebase alternative with PostgreSQL database, authentication, and storage."
+ },
+ {
+ name: "Prisma",
+ icon: "prisma",
+ level: 4,
+ description: "Next-generation ORM for Node.js and TypeScript."
+ },
+ {
+ name: "Drizzle ORM",
+ icon: "drizzle",
+ level: 3,
+ description: "TypeScript ORM with a focus on type safety and developer experience."
+ }
+ ]
+};
+
+export default databaseSkills;
diff --git a/src/lib/skills/data/devops.ts b/src/lib/skills/data/devops.ts
new file mode 100644
index 0000000..82c84ed
--- /dev/null
+++ b/src/lib/skills/data/devops.ts
@@ -0,0 +1,72 @@
+import { SkillCategory } from "@/types/skills";
+
+const devopsSkills: SkillCategory = {
+ name: "DevOps & Deployment",
+ description: "Automating, deploying, and maintaining applications in production environments.",
+ icon: "IoCloudUploadOutline",
+ color: "text-purple-400",
+ skills: [
+ {
+ name: "Docker",
+ icon: "docker",
+ level: 4,
+ description: "Containerizing applications for consistent development and deployment."
+ },
+ {
+ name: "Docker Compose",
+ icon: "docker",
+ level: 4,
+ description: "Defining and running multi-container Docker applications."
+ },
+ {
+ name: "Kubernetes",
+ icon: "kubernetes",
+ level: 3,
+ description: "Orchestrating containerized applications for scaling and management."
+ },
+ {
+ name: "AWS",
+ icon: "aws",
+ level: 3,
+ description: "Cloud infrastructure including EC2, S3, Lambda, and more."
+ },
+ {
+ name: "Vercel",
+ icon: "vercel",
+ level: 5,
+ description: "Deploying and scaling frontend applications and serverless functions."
+ },
+ {
+ name: "Netlify",
+ icon: "netlify",
+ level: 4,
+ description: "Continuous deployment for static sites and serverless functions."
+ },
+ {
+ name: "GitHub Actions",
+ icon: "github",
+ level: 4,
+ description: "CI/CD workflows for automated testing and deployment."
+ },
+ {
+ name: "Google Cloud",
+ icon: "gcp",
+ level: 3,
+ description: "Cloud infrastructure and services for application hosting."
+ },
+ {
+ name: "Terraform",
+ icon: "terraform",
+ level: 3,
+ description: "Infrastructure as code for provisioning and managing cloud resources."
+ },
+ {
+ name: "CI/CD",
+ icon: "cicd",
+ level: 4,
+ description: "Implementing continuous integration and deployment pipelines."
+ }
+ ]
+};
+
+export default devopsSkills;
diff --git a/src/lib/skills/data/email.ts b/src/lib/skills/data/email.ts
new file mode 100644
index 0000000..22c5c7e
--- /dev/null
+++ b/src/lib/skills/data/email.ts
@@ -0,0 +1,48 @@
+import { SkillCategory } from "@/types/skills";
+
+const emailSkills: SkillCategory = {
+ name: "Email & Communications",
+ description: "Tools and services for handling email and communication functionalities.",
+ icon: "IoMailOutline",
+ color: "text-cyan-400",
+ skills: [
+ {
+ name: "Resend",
+ icon: "resend",
+ level: 4,
+ description: "Email API service for developers to send transactional emails."
+ },
+ {
+ name: "Postmark",
+ icon: "postmark",
+ level: 4,
+ description: "Reliable email delivery service for transactional emails."
+ },
+ {
+ name: "Sendgrid",
+ icon: "sendgrid",
+ level: 3,
+ description: "Cloud-based email service for transactional and marketing emails."
+ },
+ {
+ name: "Mailchimp",
+ icon: "mailchimp",
+ level: 3,
+ description: "Marketing automation platform specializing in email marketing."
+ },
+ {
+ name: "Microsoft 365",
+ icon: "microsoft",
+ level: 4,
+ description: "Cloud-based suite of productivity and collaboration tools."
+ },
+ {
+ name: "Twilio",
+ icon: "twilio",
+ level: 3,
+ description: "Cloud communications platform for building SMS, voice, and messaging applications."
+ }
+ ]
+};
+
+export default emailSkills;
diff --git a/src/lib/skills/data/frontend.ts b/src/lib/skills/data/frontend.ts
new file mode 100644
index 0000000..f47548c
--- /dev/null
+++ b/src/lib/skills/data/frontend.ts
@@ -0,0 +1,84 @@
+import { SkillCategory } from "@/types/skills";
+
+const frontendSkills: SkillCategory = {
+ name: "Frontend Development",
+ description: "Building responsive and performant user interfaces with modern frameworks and libraries.",
+ icon: "IoCodeSlashOutline",
+ color: "text-blue-400",
+ skills: [
+ {
+ name: "React",
+ icon: "react",
+ level: 5,
+ description: "Building complex, interactive UIs with React and its ecosystem including hooks, context, and state management libraries."
+ },
+ {
+ name: "Next.js",
+ icon: "nextjs",
+ level: 5,
+ description: "Creating high-performance web applications with server-side rendering, static generation, and API routes."
+ },
+ {
+ name: "TypeScript",
+ icon: "typescript",
+ level: 4,
+ description: "Developing type-safe applications with TypeScript for improved code quality and developer experience."
+ },
+ {
+ name: "JavaScript",
+ icon: "javascript",
+ level: 5,
+ description: "Extensive experience with modern JavaScript (ES6+) features and patterns."
+ },
+ {
+ name: "HTML5",
+ icon: "html5",
+ level: 5,
+ description: "Semantic markup and accessible web development."
+ },
+ {
+ name: "CSS3",
+ icon: "css3",
+ level: 5,
+ description: "Advanced styling including Flexbox, Grid, and CSS animations."
+ },
+ {
+ name: "Tailwind CSS",
+ icon: "tailwindcss",
+ level: 5,
+ description: "Rapid UI development using utility classes and customizing design systems."
+ },
+ {
+ name: "Framer Motion",
+ icon: "framer",
+ level: 4,
+ description: "Creating fluid animations and interactive UI components."
+ },
+ {
+ name: "Redux",
+ icon: "redux",
+ level: 4,
+ description: "State management for complex applications."
+ },
+ {
+ name: "Vue.js",
+ icon: "vuejs",
+ level: 3,
+ description: "Building reactive interfaces with Vue's component system."
+ },
+ {
+ name: "Angular",
+ icon: "angular",
+ level: 3,
+ description: "Developing enterprise-grade applications with Angular's comprehensive framework."
+ },
+ {
+ name: "Svelte",
+ icon: "svelte",
+ level: 3,
+ description: "Building highly efficient reactive applications with minimal boilerplate."
+ }
+ ]
+};
+
+export default frontendSkills;
diff --git a/src/lib/skills/data/index.ts b/src/lib/skills/data/index.ts
new file mode 100644
index 0000000..4cfa360
--- /dev/null
+++ b/src/lib/skills/data/index.ts
@@ -0,0 +1,22 @@
+import { SkillCategory } from "@/types/skills";
+import frontendSkills from "./frontend";
+import backendSkills from "./backend";
+import databaseSkills from "./database";
+import devopsSkills from "./devops";
+import toolsSkills from "./tools";
+import uiLibrariesSkills from "./ui-libraries";
+import emailSkills from "./email";
+import authSkills from "./auth";
+import testingSkills from "./testing";
+
+export const allSkillCategories: SkillCategory[] = [
+ frontendSkills,
+ backendSkills,
+ databaseSkills,
+ devopsSkills,
+ toolsSkills,
+ uiLibrariesSkills,
+ emailSkills,
+ authSkills,
+ testingSkills
+];
diff --git a/src/lib/skills/data/testing.ts b/src/lib/skills/data/testing.ts
new file mode 100644
index 0000000..4d86dc8
--- /dev/null
+++ b/src/lib/skills/data/testing.ts
@@ -0,0 +1,48 @@
+import { SkillCategory } from "@/types/skills";
+
+const testingSkills: SkillCategory = {
+ name: "Testing & Quality Assurance",
+ description: "Ensuring application quality through comprehensive testing strategies.",
+ icon: "IoCheckmarkCircleOutline",
+ color: "text-amber-400",
+ skills: [
+ {
+ name: "Jest",
+ icon: "jest",
+ level: 4,
+ description: "JavaScript testing framework for unit and integration tests."
+ },
+ {
+ name: "React Testing Library",
+ icon: "testing-library",
+ level: 4,
+ description: "Testing utilities focused on user behavior for React applications."
+ },
+ {
+ name: "Cypress",
+ icon: "cypress",
+ level: 3,
+ description: "End-to-end testing framework for web applications."
+ },
+ {
+ name: "Playwright",
+ icon: "playwright",
+ level: 3,
+ description: "Framework for reliable end-to-end testing for modern web apps."
+ },
+ {
+ name: "Vitest",
+ icon: "vitest",
+ level: 3,
+ description: "Next-generation testing framework powered by Vite."
+ },
+ {
+ name: "Storybook",
+ icon: "storybook",
+ level: 4,
+ description: "Tool for developing UI components in isolation."
+ }
+ ]
+};
+
+export default testingSkills;
diff --git a/src/lib/skills/data/tools.ts b/src/lib/skills/data/tools.ts
new file mode 100644
index 0000000..8c6831e
--- /dev/null
+++ b/src/lib/skills/data/tools.ts
@@ -0,0 +1,60 @@
+import { SkillCategory } from "@/types/skills";
+
+const toolsSkills: SkillCategory = {
+ name: "Tools & Utilities",
+ description: "Essential tools and utilities that support the development workflow.",
+ icon: "IoHammerOutline",
+ color: "text-rose-400",
+ skills: [
+ {
+ name: "Git",
+ icon: "git",
+ level: 5,
+ description: "Version control and collaborative development workflows."
+ },
+ {
+ name: "GitHub",
+ icon: "github",
+ level: 5,
+ description: "Collaborative development using pull requests, issues, and project management."
+ },
+ {
+ name: "VS Code",
+ icon: "vscode",
+ level: 5,
+ description: "Primary code editor with advanced extensions and customizations."
+ },
+ {
+ name: "Figma",
+ icon: "figma",
+ level: 4,
+ description: "UI/UX design and collaboration with design teams."
+ },
+ {
+ name: "Webpack",
+ icon: "webpack",
+ level: 4,
+ description: "Module bundling and asset optimization for web applications."
+ },
+ {
+ name: "Jest",
+ icon: "jest",
+ level: 4,
+ description: "JavaScript testing framework for unit and integration tests."
+ },
+ {
+ name: "Cypress",
+ icon: "cypress",
+ level: 3,
+ description: "End-to-end testing for web applications."
+ },
+ {
+ name: "Storybook",
+ icon: "storybook",
+ level: 4,
+ description: "Component development and documentation in isolation."
+ }
+ ]
+};
+
+export default toolsSkills;
diff --git a/src/lib/skills/data/ui-libraries.ts b/src/lib/skills/data/ui-libraries.ts
new file mode 100644
index 0000000..3915d78
--- /dev/null
+++ b/src/lib/skills/data/ui-libraries.ts
@@ -0,0 +1,54 @@
+import { SkillCategory } from "@/types/skills";
+
+const uiLibrariesSkills: SkillCategory = {
+ name: "UI Libraries & Frameworks",
+ description: "Component libraries and frameworks for building consistent UI experiences.",
+ icon: "IoLayersOutline",
+ color: "text-indigo-400",
+ skills: [
+ {
+ name: "Shadcn UI",
+ icon: "shadcn",
+ level: 4,
+ description: "Building beautiful, accessible components with Radix UI and Tailwind CSS."
+ },
+ {
+ name: "Material UI",
+ icon: "mui",
+ level: 4,
+ description: "React component library based on Google's Material Design."
+ },
+ {
+ name: "Radix UI",
+ icon: "radix",
+ level: 4,
+ description: "Unstyled, accessible components for building design systems."
+ },
+ {
+ name: "Chakra UI",
+ icon: "chakra",
+ level: 4,
+ description: "Component library focused on accessibility and ease of use."
+ },
+ {
+ name: "Headless UI",
+ icon: "headless",
+ level: 4,
+ description: "Unstyled, accessible UI components with great flexibility."
+ },
+ {
+ name: "Tailwind CSS",
+ icon: "tailwindcss",
+ level: 5,
+ description: "Utility-first CSS framework for rapid UI development."
+ },
+ {
+ name: "Bootstrap",
+ icon: "bootstrap",
+ level: 4,
+ description: "Component-based CSS framework for responsive designs."
+ }
+ ]
+};
+
+export default uiLibrariesSkills;
diff --git a/src/middleware.ts b/src/middleware.ts
new file mode 100644
index 0000000..a8fc3f2
--- /dev/null
+++ b/src/middleware.ts
@@ -0,0 +1,119 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { getRouteInfo } from './lib/routes';
+
+const REDIRECT_SECRET = process.env.REDIRECT_SECRET;
+
+/**
+ * Generate a secure token for authorized redirects using Web Crypto API
+ * @param path The original path being redirected from
+ * @returns A secure token
+ */
+async function generateRedirectToken(path: string): Promise {
+ // Create a token string by combining the path and secret
+ const tokenData = `${path}:${REDIRECT_SECRET}`;
+
+ // Convert the string to a buffer
+ const encoder = new TextEncoder();
+ const data = encoder.encode(tokenData);
+
+ // Use Web Crypto API to hash the data (SHA-256)
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
+
+ // Convert the hash buffer to a hex string
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
+ const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
+
+ // Return the first 32 chars of the hash
+ return hashHex.substring(0, 32);
+}
+
+/**
+ * Simplified token generation for synchronous use in middleware
+ * This is less secure but works in the edge runtime without async
+ */
+function generateSimpleToken(path: string): string {
+ // Create a simple hash by combining the path and our secret
+ const combined = `${path}:${REDIRECT_SECRET}`;
+
+ // Simple hash function that's available in the edge runtime
+ let hash = 0;
+ for (let i = 0; i < combined.length; i++) {
+ const char = combined.charCodeAt(i);
+ hash = ((hash << 5) - hash) + char;
+ hash = hash & hash; // Convert to 32bit integer
+ }
+
+ // Convert to a hex string and add some of the original text to make it more unique
+ const hexHash = Math.abs(hash).toString(16);
+
+ // Ensure the token is long enough by repeating if necessary
+ const tokenBase = hexHash + combined.replace(/[^a-zA-Z0-9]/g, '');
+ return tokenBase.substring(0, 32);
+}
+
+export function middleware(request: NextRequest) {
+ // Get the pathname from the URL
+ const path = request.nextUrl.pathname;
+
+ // CASE 1: Direct access to coming-soon page without proper redirect token
+ if (path === '/coming-soon') {
+ const token = request.nextUrl.searchParams.get('token');
+ const fromPath = request.nextUrl.searchParams.get('from');
+
+ // If there's no token or from parameter, or token doesn't match what we expect
+ if (!token || !fromPath || token !== generateSimpleToken(fromPath)) {
+ // Redirect unauthorized access to home page
+ return NextResponse.redirect(new URL('/', request.url));
+ }
+
+ // If token is valid, allow access to the coming-soon page
+ return NextResponse.next();
+ }
+
+ // CASE 2: Access to under construction pages (docs or fivem)
+ const isUnderConstruction = path.startsWith('/docs') || path.startsWith('/fivem');
+
+ // Redirect in production mode (or if we want to test in development)
+ const isDevelopment = process.env.NODE_ENV === 'development';
+
+ // Toggle this to true if you want to test the coming soon page in development
+ const forceRedirectInDev = false;
+
+ if (isUnderConstruction && (!isDevelopment || forceRedirectInDev)) {
+ // Clone the current URL
+ const url = request.nextUrl.clone();
+
+ // Get route info to pass along as parameters
+ const routeInfo = getRouteInfo(path);
+
+ // Change the pathname to /coming-soon
+ url.pathname = '/coming-soon';
+
+ // Add the original path as a query parameter
+ url.searchParams.set('from', path);
+
+ // Generate and add a secure token to validate this is a legitimate redirect
+ url.searchParams.set('token', generateSimpleToken(path));
+
+ // Add notification preference if specified in route config
+ if (routeInfo?.showNotification !== undefined) {
+ url.searchParams.set('notify', routeInfo.showNotification.toString());
+ }
+
+ // Add launch date if specified in route config
+ if (routeInfo?.launchDate) {
+ url.searchParams.set('launchDate', routeInfo.launchDate.toISOString());
+ }
+
+ // Return a redirect response
+ return NextResponse.redirect(url);
+ }
+
+ // Otherwise, continue with the request
+ return NextResponse.next();
+}
+
+// Update the matcher to include the coming-soon page
+export const config = {
+ matcher: ['/docs/:path*', '/fivem/:path*', '/coming-soon'],
+};
diff --git a/src/posts/advanced-typescript.mdx b/src/posts/advanced-typescript.mdx
new file mode 100644
index 0000000..9eb9867
--- /dev/null
+++ b/src/posts/advanced-typescript.mdx
@@ -0,0 +1,137 @@
+---
+title: 'Advanced TypeScript Techniques'
+date: '2025-05-01'
+description: 'Explore advanced TypeScript techniques to improve code quality, maintainability, and developer experience.'
+tags: ['TypeScript', 'JavaScript', 'Web Development', 'Programming']
+---
+
+# Introduction
+
+TypeScript has become an essential tool for modern JavaScript development, providing static typing and enhanced tooling. While basic TypeScript usage is straightforward, mastering advanced techniques can significantly improve your code's quality, maintainability, and overall developer experience. In this blog post, I'll explore some advanced TypeScript techniques that can help you level up your skills.
+
+---
+
+## Conditional Types
+
+Conditional types allow you to define types that depend on other types. They are similar to ternary operators in JavaScript and are incredibly useful for creating flexible and reusable type definitions.
+
+### Example: Extracting Return Type
+
+```typescript
+type ReturnType any> = T extends (...args: any) => infer R ? R : any;
+
+function add(x: number, y: number): number {
+ return x + y;
+}
+
+type AddReturnType = ReturnType; // type AddReturnType = number
+```
+
+Here, `ReturnType` is a conditional type that extracts the return type of a function. If `T` is a function, it infers the return type `R` and returns it; otherwise, it returns `any`.
+
+---
+
+## Mapped Types
+
+Mapped types allow you to create new types by transforming existing ones. They are particularly useful for creating utility types that modify the properties of an object.
+
+### Example: Readonly Properties
+
+```typescript
+type Readonly = {
+ readonly [P in keyof T]: T[P];
+};
+
+interface User {
+ name: string;
+ age: number;
+}
+
+type ReadonlyUser = Readonly;
+```
+
+In this example, `Readonly` is a mapped type that makes all properties of the `User` interface read-only.
+
+---
+
+## Utility Types
+
+TypeScript provides several built-in utility types that can help you manipulate types in various ways. Some of the most useful utility types include `Partial`, `Required`, `Readonly`, `Pick`, and `Omit`.
+
+### Example: Partial Type
+
+```typescript
+interface Product {
+ id: string;
+ name: string;
+ price: number;
+ description: string;
+}
+
+type PartialProduct = Partial;
+
+const updateProduct = (id: string, updates: PartialProduct) => {
+ // Update product logic here
+};
+```
+
+`Partial` makes all properties of the `Product` interface optional, allowing you to update only specific fields.
+
+---
+
+## Type Inference
+
+TypeScript's type inference system can automatically determine the types of variables and expressions, reducing the need for explicit type annotations.
+
+### Example: Inferring Type from Value
+
+```typescript
+const user = {
+ name: 'John Doe',
+ age: 30,
+};
+
+// TypeScript infers the type of user as { name: string; age: number; }
+```
+
+Type inference simplifies your code and makes it more readable by reducing boilerplate.
+
+---
+
+## Discriminated Unions
+
+Discriminated unions, also known as tagged unions, are a powerful way to represent a value that can be one of several different types. They are particularly useful for modeling complex data structures and state machines.
+
+### Example: Result Type
+
+```typescript
+type SuccessResult = {
+ success: true;
+ data: any;
+};
+
+type ErrorResult = {
+ success: false;
+ error: string;
+};
+
+type Result = SuccessResult | ErrorResult;
+
+const handleResult = (result: Result) => {
+ if (result.success) {
+ console.log('Success:', result.data);
+ } else {
+ console.error('Error:', result.error);
+ }
+};
+```
+
+In this example, `Result` is a discriminated union that can be either a `SuccessResult` or an `ErrorResult`. The `success` property acts as a discriminator, allowing you to easily determine the type of the result.
+
+---
+
+## Conclusion
+
+Mastering advanced TypeScript techniques can significantly improve your code's quality, maintainability, and developer experience. By using conditional types, mapped types, utility types, type inference, and discriminated unions, you can create more flexible, robust, and type-safe code.
+
+I hope this exploration of advanced TypeScript techniques has been helpful. If you have any questions or comments, feel free to reach out. Happy coding!
diff --git a/src/posts/bun-experiments.mdx b/src/posts/bun-experiments.mdx
new file mode 100644
index 0000000..6cba8b2
--- /dev/null
+++ b/src/posts/bun-experiments.mdx
@@ -0,0 +1,70 @@
+---
+title: 'A Comprehensive Bun Breakdown'
+date: '2025-01-10'
+description: 'A detailed breakdown of my experiences with Bun, including its features, performance, and how it compares to other JavaScript runtimes.'
+tags: ['Bun', 'JavaScript', 'Deno', 'Web Development', 'Performance']
+---
+
+# Introduction
+
+In the ever-evolving landscape of JavaScript runtimes, Bun has emerged as a promising new player. Developed by Jarred Sumner, Bun aims to be a fast, modern, and efficient JavaScript runtime that can potentially outperform Node.js and Deno. In this blog post, I'll dive deep into what Bun is, its features, and my personal experiences with it.
+
+## What is Bun?
+
+Bun is a new JavaScript runtime that aims to provide a faster and more efficient alternative to Node.js and Deno. It is built from the ground up to optimize performance and developer experience. Here are some key features of Bun:
+
+- **Speed**: Bun is designed to be fast. It uses the JavaScriptCore engine, which is known for its performance.
+- **Bundling**: Bun includes a built-in bundler that can bundle JavaScript, TypeScript, and other assets.
+- **Transpiling**: Bun can transpile TypeScript and JSX out of the box.
+- **Native Modules**: Bun supports native modules, making it easier to use existing Node.js modules.
+- **Compatibility**: Bun aims to be compatible with Node.js, allowing developers to use existing Node.js libraries and tools.
+
+## Features of Bun
+
+### Performance
+
+One of the standout features of Bun is its performance. Bun's JavaScriptCore engine is known for its speed, and Bun leverages this to provide a runtime that is significantly faster than Node.js and Deno in many scenarios. In my tests, I found that Bun's performance was impressive, especially in tasks like HTTP server handling and file I/O operations.
+
+### Built-in Bundler
+
+Bun comes with a built-in bundler that can handle JavaScript, TypeScript, and other assets. This eliminates the need for additional tools like Webpack or Rollup, simplifying the development process. The bundler is fast and efficient, making it a great choice for modern web development.
+
+### Transpiling
+
+Bun can transpile TypeScript and JSX out of the box. This means you can write your code in TypeScript or JSX and Bun will handle the transpilation for you. This feature is particularly useful for developers who prefer using TypeScript for its type safety and developer experience.
+
+### Native Modules
+
+Bun supports native modules, making it easier to use existing Node.js modules. This compatibility with Node.js modules is a significant advantage, as it allows developers to leverage the vast ecosystem of Node.js libraries and tools.
+
+### Compatibility with Node.js
+
+Bun aims to be compatible with Node.js, allowing developers to use existing Node.js libraries and tools. This compatibility makes it easier for developers to transition to Bun without having to rewrite their existing codebase.
+
+## My Experiences with Bun
+
+### Setting Up Bun
+
+Setting up Bun was straightforward. The installation process was quick, and I was up and running in no time. The documentation provided clear instructions, making it easy to get started.
+
+### Performance Testing
+
+I conducted several performance tests to compare Bun with Node.js and Deno. In my tests, Bun consistently outperformed both Node.js and Deno in tasks like HTTP server handling, file I/O operations, and script execution. The performance gains were particularly noticeable in scenarios involving heavy computation and large file handling.
+
+### Development Experience
+
+The development experience with Bun was smooth and enjoyable. The built-in bundler and transpiler simplified the development process, and the compatibility with Node.js modules meant I could use my existing libraries and tools without any issues. The fast startup times and efficient performance made the development process more efficient.
+
+### Challenges and Limitations
+
+While my experience with Bun was largely positive, I did encounter some challenges and limitations. Bun is still a relatively new project, and there are some areas where it is not as mature as Node.js or Deno. For example, the ecosystem around Bun is still growing, and there are fewer libraries and tools available compared to Node.js. Additionally, while Bun aims to be compatible with Node.js, there are some edge cases where compatibility issues can arise.
+
+## Conclusion
+
+Overall, my experience with Bun has been positive. Bun offers impressive performance, a built-in bundler and transpiler, and compatibility with Node.js modules. While there are some challenges and limitations, Bun shows great promise as a modern JavaScript runtime. I look forward to seeing how Bun evolves and improves in the future.
+
+If you're looking for a fast and efficient JavaScript runtime, I highly recommend giving Bun a try. Its performance and developer experience make it a compelling choice for modern web development.
+
+---
+
+I hope you found this breakdown of my experiences with Bun helpful. If you have any questions or comments, feel free to reach out to me. Happy coding!
\ No newline at end of file
diff --git a/src/posts/chat-app.mdx b/src/posts/chat-app.mdx
new file mode 100644
index 0000000..5143af4
--- /dev/null
+++ b/src/posts/chat-app.mdx
@@ -0,0 +1,96 @@
+---
+title: 'Building a Real-Time Chat App'
+date: '2025-02-02'
+description: 'Learn how to build a real-time chat application using Socket.io and React, including setting up the server and client, components and more.'
+tags: ['React', 'Socket.io', 'JavaScript', 'Web Development']
+---
+
+# Introduction
+
+Real-time applications are becoming increasingly popular, and Socket.io is a powerful library for building real-time web applications. In this blog post, I'll show you how to build a real-time chat application using Socket.io and React.
+
+---
+
+## Setting Up the Server
+
+First, let's set up the server using Node.js and Socket.io.
+
+### Example: Server Setup
+
+```jsx
+const express = require('express');
+const http = require('http');
+const socketIo = require('socket.io');
+
+const app = express();
+const server = http.createServer(app);
+const io = socketIo(server);
+
+io.on('connection', (socket) => {
+ console.log('a user connected');
+ socket.on('disconnect', () => {
+ console.log('user disconnected');
+ });
+ socket.on('chat message', (msg) => {
+ io.emit('chat message', msg);
+ });
+});
+
+server.listen(3000, () => {
+ console.log('listening on *:3000');
+});
+```
+
+## Setting Up the Client
+Next, let's set up the client using React and Socket.io.
+
+### Example: Client Setup
+```jsx
+import React, { useState, useEffect } from 'react';
+import io from 'socket.io-client';
+
+const socket = io('http://localhost:3000');
+
+const Chat = () => {
+ const [messages, setMessages] = useState([]);
+ const [input, setInput] = useState('');
+
+ useEffect(() => {
+ socket.on('chat message', (msg) => {
+ setMessages((prevMessages) => [...prevMessages, msg]);
+ });
+ }, []);
+
+ const sendMessage = () => {
+ socket.emit('chat message', input);
+ setInput('');
+ };
+
+ return (
+
+
+ {messages.map((msg, index) => (
+ {msg}
+ ))}
+
+
setInput(e.target.value)}
+ onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
+ />
+
Send
+
+ );
+};
+
+export default Chat;
+```
+
+---
+
+## Conclusion
+Building a real-time chat application with Socket.io and React is a great way to learn about real-time web development. With Socket.io's powerful features and React's flexibility, you can create a responsive and interactive chat application.
+
+---
+
+I hope you found this tutorial on building a real-time chat application helpful. If you have any questions or comments, feel free to reach out to me. Happy coding!
\ No newline at end of file
diff --git a/src/posts/edge-ai-nextjs.mdx b/src/posts/edge-ai-nextjs.mdx
new file mode 100644
index 0000000..469ea39
--- /dev/null
+++ b/src/posts/edge-ai-nextjs.mdx
@@ -0,0 +1,87 @@
+---
+title: 'Running AI at the Edge with Next.js'
+date: '2025-06-06'
+description: 'A deep dive into deploying AI models at the edge using Next.js, Vercel Edge Functions, and ONNX.'
+tags: ['Next.js', 'AI', 'Edge Computing', 'Vercel', 'ONNX', 'Web Development']
+---
+
+# Introduction
+
+Edge computing is transforming how we deploy and scale AI-powered applications. By running inference close to the user, we can achieve lower latency, better privacy, and improved scalability. In this post, I’ll show you how to deploy a lightweight AI model at the edge using Next.js, Vercel Edge Functions, and ONNX.
+
+---
+
+## Why Edge AI?
+
+- **Low Latency:** Inference happens geographically close to the user.
+- **Privacy:** Data doesn’t need to leave the user’s region.
+- **Scalability:** Offload work from your origin servers.
+
+---
+
+## Setting Up Next.js with Edge Functions
+
+Next.js 13+ supports [Edge API Routes](https://nextjs.org/docs/app/building-your-application/routing/route-handlers#edge-and-nodejs-runtimes) for running code at the edge.
+
+```tsx
+// app/api/edge-inference/route.ts
+import { NextRequest } from 'next/server';
+import { InferenceSession } from 'onnxruntime-web';
+
+export const runtime = 'edge';
+
+export async function POST(req: NextRequest) {
+ const { input } = await req.json();
+ const session = await InferenceSession.create('/models/model.onnx');
+ const output = await session.run({ input });
+ return Response.json({ result: output });
+}
+```
+
+---
+
+## Deploying an ONNX Model
+
+1. Export your model to ONNX format.
+2. Place it in your `/public/models` directory.
+3. Use `onnxruntime-web` for inference in Edge Functions.
+
+---
+
+## Example: Sentiment Analysis at the Edge
+
+Suppose you have a small sentiment analysis model. You can expose an API route that takes user text and returns a sentiment score in milliseconds.
+
+```tsx
+// app/api/sentiment/route.ts
+import { NextRequest } from 'next/server';
+import { InferenceSession } from 'onnxruntime-web';
+
+export const runtime = 'edge';
+
+export async function POST(req: NextRequest) {
+ const { text } = await req.json();
+ // Preprocess text to tensor...
+ // Run inference...
+ // Return result
+ return Response.json({ sentiment: 'positive', confidence: 0.98 });
+}
+```
+
+---
+
+## Challenges
+
+- **Model Size:** Edge functions have strict size limits (e.g., 1MB on Vercel).
+- **Cold Starts:** Initial load may be slower due to model loading.
+- **WebAssembly:** ONNX models run via WASM, which is fast but not as fast as native.
+
+---
+
+## Conclusion
+
+Running AI at the edge with Next.js unlocks new possibilities for real-time, privacy-preserving, and scalable web applications. Experiment with small models and see how far you can push the edge!
+
+---
+
+If you have questions or want to see more edge AI demos, let me know!
diff --git a/src/posts/fun-with-react.mdx b/src/posts/fun-with-react.mdx
new file mode 100644
index 0000000..f87f80f
--- /dev/null
+++ b/src/posts/fun-with-react.mdx
@@ -0,0 +1,98 @@
+---
+title: 'Having fun with React'
+date: '2025-01-18'
+description: 'Explore some fun and interesting things you can do with React, including animations, hooks, and more.'
+tags: ['React', 'JavaScript', 'Web Development']
+---
+
+# Introduction
+
+React is a powerful and flexible JavaScript library for building user interfaces. In this blog post, I'll explore some fun and interesting things you can do with React, including animations, hooks, and more.
+
+## Animations with React Spring
+
+React Spring is a popular library for creating animations in React. It provides a simple and intuitive API for creating smooth and interactive animations.
+
+### Example: Bouncing Ball Animation
+
+```jsx
+import React from 'react';
+import { useSpring, animated } from 'react-spring';
+
+const BouncingBall = () => {
+ const props = useSpring({
+ from: { transform: 'translateY(0px)' },
+ to: { transform: 'translateY(-100px)' },
+ config: { tension: 180, friction: 12 },
+ loop: { reverse: true },
+ });
+
+ return ;
+};
+
+export default BouncingBall;
+```
+
+---
+
+## Custom Hooks
+Custom hooks are a powerful feature in React that allow you to encapsulate and reuse logic across your components.
+
+### Example: useWindowSize Hook
+```jsx
+import { useState, useEffect } from 'react';
+
+const useWindowSize = () => {
+ const [size, setSize] = useState([window.innerWidth, window.innerHeight]);
+
+ useEffect(() => {
+ const handleResize = () => {
+ setSize([window.innerWidth, window.innerHeight]);
+ };
+
+ window.addEventListener('resize', handleResize);
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ return size;
+};
+
+export default useWindowSize;
+```
+
+---
+
+## Context API for State Management
+The Context API is a powerful tool for managing state in React applications. It allows you to share state across your components without prop drilling.
+
+### Example: Theme Context
+```jsx
+import React, { createContext, useContext, useState } from 'react';
+
+const ThemeContext = createContext();
+
+export const useTheme = () => useContext(ThemeContext);
+
+export const ThemeProvider = ({ children }) => {
+ const [theme, setTheme] = useState('light');
+
+ const toggleTheme = () => {
+ setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+```
+
+---
+
+## Conclusion
+React offers a wide range of features and tools that make it a fun and powerful library for building user interfaces. Whether you're creating animations, custom hooks, or managing state with the Context API, there's always something new and exciting to explore in React.
+
+---
+
+I hope you found these fun things to do in React helpful. If you have any questions or comments, feel free to reach out to me. Happy coding!
\ No newline at end of file
diff --git a/src/posts/graphql-react.mdx b/src/posts/graphql-react.mdx
new file mode 100644
index 0000000..a00037a
--- /dev/null
+++ b/src/posts/graphql-react.mdx
@@ -0,0 +1,211 @@
+---
+title: 'GraphQL with React: A Practical Guide'
+date: '2025-05-15'
+description: 'Learn how to integrate GraphQL into your React applications for efficient data fetching and management.'
+tags: ['React', 'GraphQL', 'JavaScript', 'Web Development', 'API']
+---
+
+# Introduction
+
+GraphQL has emerged as a powerful alternative to REST for building APIs. Its flexibility and efficiency make it an excellent choice for modern React applications. In this guide, I'll walk you through integrating GraphQL into your React projects, covering everything from setting up your client to handling mutations and subscriptions.
+
+---
+
+## What is GraphQL?
+
+GraphQL is a query language for your API and a server-side runtime for executing those queries. Unlike REST, where the server determines the data returned, GraphQL allows the client to specify exactly what data it needs. This leads to more efficient data fetching and reduces over-fetching.
+
+---
+
+## Setting Up a GraphQL Client
+
+To use GraphQL in a React application, you'll need a GraphQL client. Apollo Client and urql are popular choices. Here, I'll demonstrate using Apollo Client.
+
+### Installation
+
+```bash
+npm install @apollo/client graphql
+```
+
+### Configuring Apollo Client
+
+```jsx
+import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
+
+const client = new ApolloClient({
+ uri: 'https://api.example.com/graphql', // Replace with your GraphQL API endpoint
+ cache: new InMemoryCache(),
+});
+
+const ApolloWrapper = ({ children }) => (
+
+ {children}
+
+);
+
+export default ApolloWrapper;
+```
+
+Wrap your application with `` to provide the client to your components.
+
+---
+
+## Fetching Data with GraphQL
+
+With Apollo Client set up, you can now fetch data using GraphQL queries.
+
+### Example: Fetching a List of Users
+
+```jsx
+import { useQuery, gql } from '@apollo/client';
+
+const GET_USERS = gql`
+ query GetUsers {
+ users {
+ id
+ name
+ email
+ }
+ }
+`;
+
+const Users = () => {
+ const { loading, error, data } = useQuery(GET_USERS);
+
+ if (loading) return Loading...
;
+ if (error) return Error : {error.message}
;
+
+ return (
+
+
Users
+
+ {data.users.map((user) => (
+
+ {user.name} ({user.email})
+
+ ))}
+
+
+ );
+};
+
+export default Users;
+```
+
+---
+
+## Performing Mutations
+
+Mutations are used to modify data on the server. Here's how to perform a mutation using Apollo Client.
+
+### Example: Adding a New User
+
+```jsx
+import { useMutation, gql } from '@apollo/client';
+
+const ADD_USER = gql`
+ mutation AddUser($name: String!, $email: String!) {
+ addUser(name: $name, email: $email) {
+ id
+ name
+ email
+ }
+ }
+`;
+
+const AddUserForm = () => {
+ const [name, setName] = useState('');
+ const [email, setEmail] = useState('');
+ const [addUser, { loading, error }] = useMutation(ADD_USER);
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ await addUser({ variables: { name, email } });
+ setName('');
+ setEmail('');
+ };
+
+ return (
+
+ setName(e.target.value)} placeholder="Name" />
+ setEmail(e.target.value)} placeholder="Email" />
+
+ {loading ? 'Adding...' : 'Add User'}
+
+ {error && Error : {error.message}
}
+
+ );
+};
+
+export default AddUserForm;
+```
+
+---
+
+## Real-Time Updates with Subscriptions
+
+GraphQL subscriptions allow you to receive real-time updates from the server. This is useful for building features like live chat or real-time dashboards.
+
+### Setting Up Subscriptions
+
+To use subscriptions, you'll need a WebSocket-based transport. Apollo Client provides a `SubscriptionClient` for this purpose.
+
+```jsx
+import { SubscriptionClient } from 'subscriptions-transport-ws';
+
+const wsClient = new SubscriptionClient('ws://api.example.com/graphql', {
+ reconnect: true,
+});
+
+const client = new ApolloClient({
+ uri: 'https://api.example.com/graphql',
+ cache: new InMemoryCache(),
+ link: wsClient,
+});
+```
+
+### Example: Subscribing to New Messages
+
+```jsx
+import { useSubscription, gql } from '@apollo/client';
+
+const NEW_MESSAGES = gql`
+ subscription NewMessages {
+ newMessage {
+ id
+ text
+ sender
+ }
+ }
+`;
+
+const Chat = () => {
+ const { loading, error, data } = useSubscription(NEW_MESSAGES);
+
+ if (loading) return Loading...
;
+ if (error) return Error : {error.message}
;
+
+ return (
+
+
Chat
+
+ {data.newMessage.map((message) => (
+
+ {message.sender}: {message.text}
+
+ ))}
+
+
+ );
+};
+
+export default Chat;
+```
+
+---
+
+## Conclusion
+
+GraphQL offers a powerful and efficient way to fetch and manage data in React applications. By using tools like Apollo Client, you can easily integrate GraphQL into your projects and take advantage of its benefits, including reduced over-fetching, strong typing, and real-time updates.
+
+I hope this guide has been helpful in getting you started with GraphQL in React. If you have any questions or comments, feel free to reach out. Happy coding!
diff --git a/src/posts/latest-experiments.mdx b/src/posts/latest-experiments.mdx
new file mode 100644
index 0000000..5e07b54
--- /dev/null
+++ b/src/posts/latest-experiments.mdx
@@ -0,0 +1,31 @@
+---
+title: 'Latest Experiments'
+date: '2024-11-08'
+description: 'Explore what i have been up to lately and join me on my development journey.'
+tags: ['Updates', 'NodeByte', 'Cordx', 'Development', 'Life']
+---
+
+# Building, Fixing, and Experimenting: NodeByte Updates, Infinity Docs, and the Move to OctoFlow
+
+It’s been a productive few weeks, with projects and experiments keeping me busy. Here’s a look at what I’ve been working on lately:
+
+## NodeByte LTD Website Launch
+One of my main focuses recently was getting a website live for NodeByte LTD. It’s now up as a dedicated space for everything NodeByte, giving visitors a clear look at our services and mission. It’s great seeing it come together, and I’m looking forward to expanding on it.
+
+## New Blog Site for NodeByte Hosting
+Alongside the main website, I created a blog specifically for NodeByte Hosting. The blog will serve as a platform for sharing tips, tutorials, and updates for the hosting community. It’s exciting to have a place to share knowledge directly with clients and anyone interested in VPS and Game Server Hosting.
+
+## Infinity Bot List Documentation Site
+I recently worked on a new documentation site for Infinity Bot List using Next.js. I chose Next.js for its speed, ease of use, and support for creating a clean, responsive user experience. This new site will make it easier for users to navigate our docs and find the information they need, adding a polished, more accessible experience for the Infinity Bot community.
+
+## Rebranding Git Logs to OctoFlow
+In a recent rebranding effort, I changed the name of our Git logging and notifications bot from “Git logs” to OctoFlow. The new name better fits the octo theme and reflects the bot’s purpose more clearly: it flows GitHub updates seamlessly into Discord channels. This rebranding is part of creating a more cohesive identity across our tools, making them more engaging and memorable.
+
+## Upgrades and Improvements to CordX
+I also made some improvements to my file-sharing service, working through several issues to enhance functionality and reliability. These recent fixes should provide a smoother experience for everyone using it.
+
+## Experimenting with Bun
+Another recent experiment has been with Bun, a new JavaScript runtime built for speed and efficiency. Bun combines the functionality of Node.js, npm, and bundlers into one package, with built-in TypeScript support and edge compatibility. I’m finding Bun to be an interesting tool, especially for high-performance tasks. I’ll keep testing and share more insights on how it might fit into future projects.
+
+## What’s Next?
+With these updates, I’m now looking forward to scaling and simplifying both personal projects and NodeByte’s ecosystem. Let me know if there’s anything specific you’d like to hear about—I’m always open to sharing insights from the journey!
\ No newline at end of file
diff --git a/src/posts/nextjs-caching.mdx b/src/posts/nextjs-caching.mdx
new file mode 100644
index 0000000..09080ad
--- /dev/null
+++ b/src/posts/nextjs-caching.mdx
@@ -0,0 +1,179 @@
+---
+title: 'Exploring the NextJS Cache API'
+date: '2025-02-13'
+description: 'A detailed breakdown of my experiments with the Next.js cache API, including caching strategies, optimizations, and potential pitfalls.'
+tags: ['React', 'Next.js', 'Caching', 'Performance', 'Web Development']
+---
+
+## Introduction
+Caching is one of the most critical aspects of web performance, ensuring fast response times while reducing unnecessary backend load. Next.js, with its flexible data-fetching strategies, offers a robust caching system that can be fine-tuned for different use cases.
+
+Recently, I experimented with Next.js’s caching mechanisms, particularly the `cache()` API, and explored how it interacts with data-fetching strategies like ISR, SSR, and streaming in server components.
+
+In this post, I’ll share a deep dive into how Next.js handles caching, how to optimize its behavior, and some of the pitfalls I encountered along the way.
+
+### Understanding Next.js Caching
+
+Next.js has a layered caching approach that determines whether a request should be served from cache or trigger new data fetching. These layers include:
+
+#### 1. Build-Time Caching (SSG & ISR)
+
+When using **Static Site Generation (SSG)**, Next.js pre-renders pages at build time and caches them indefinitely unless revalidated.
+
+• Pages built with `getStaticProps` in older Next.js versions behave this way.
+
+• In the App Router (`app/ directory`), static pages are cached automatically unless `fetch()` is explicitly configured otherwise.
+
+**Incremental Static Regeneration (ISR)** allows these static pages to update dynamically while still leveraging cache:
+
+```jsx
+export async function GET() {
+ const res = await fetch('https://api.example.com/data', {
+ next: { revalidate: 60 }, // Revalidate cache every 60 seconds
+ });
+
+ return Response.json(await res.json());
+}
+```
+
+Here, Next.js will serve cached data until 60 seconds have passed, after which it fetches fresh data.
+
+#### 2. Per-Request Caching (SSR & Server Components)
+
+For **Server-Side Rendering (SSR)**, pages are generated on every request unless explicitly cached.
+
+With the App Router, caching can be controlled at the fetch level:
+
+```jsx
+export async function GET() {
+ const res = await fetch('https://api.example.com/data', {
+ cache: 'no-store', // Disables caching
+ });
+
+ return Response.json(await res.json());
+}
+```
+
+This guarantees fresh data on every request but increases load on the backend.
+
+#### 3. Function-Level Caching (cache())
+
+The `cache()` API in Next.js provides an additional layer of control, allowing you to cache function results across multiple calls within the same request lifecycle:
+
+```jsx
+import { cache } from 'next/cache';
+
+const getData = cache(async (id: string) => {
+ const res = await fetch(`https://api.example.com/data/${id}`);
+ return res.json();
+});
+
+export async function GET(req: Request) {
+ const data = await getData('123'); // This will be cached for this request
+ return Response.json(data);
+}
+```
+
+By using cache(), we can avoid multiple redundant API calls for the same request.
+
+### Customizing Cache Behavior
+
+#### 1. Setting Up Fine-Grained Cache Control
+
+Next.js fetch requests support multiple caching strategies. Here’s how they differ:
+
+| Cache Mode | Description | Use Case |
+|-------------------|-----------------------------------------------|------------------------------------|
+| `force-cache` | Uses cached data even if stale | Static pages |
+| `no-store` | Always fetches fresh data | Live updates, authenticated requests |
+| `revalidate: X` | Stale-while-revalidate mechanism | News feeds, dashboards |
+| `cache: 'default'` | Uses Next.js defaults based on the rendering method | General use |
+
+Example: Using `force-cache` to store expensive API requests in cache:
+
+```jsx
+const response = await fetch('https://api.example.com/stats', {
+ cache: 'force-cache',
+});
+```
+
+This ensures that repeated API calls within a short period won’t hit the backend.
+
+#### 2. Revalidating Data Dynamically
+
+While `revalidate: X` automatically refreshes data at set intervals, sometimes you need manual cache invalidation. Next.js provides a way to do this via On-Demand Revalidation:
+
+```jsx
+import { revalidatePath } from 'next/cache';
+
+export async function POST(req: Request) {
+ const { path } = await req.json();
+ revalidatePath(path); // Clears cache for a specific path
+ return Response.json({ success: true });
+}
+```
+
+This is useful for CMS integrations where content updates need to be reflected instantly.
+
+### Caching in Server Components
+
+Server Components in Next.js naturally cache fetch requests unless cache: `'no-store'` is specified. However, when streaming large amounts of data, cache handling becomes crucial to avoid delays.
+
+**Example**: Fetching and streaming a large dataset efficiently with caching enabled:
+
+```jsx
+export default async function Page() {
+ const data = await fetch('https://api.example.com/streamed-data', {
+ next: { revalidate: 300 },
+ }).then((res) => res.json());
+
+ return (
+
+ {data.map((item) => (
+
{item.name}
+ ))}
+
+ );
+}
+```
+
+Since this is a Server Component, the data will be cached and only refreshed every 5 minutes.
+
+### Challenges and Pitfalls
+
+#### 1. Conflicts Between SSR & ISR
+
+When mixing no-store fetch requests inside an ISR page, caching can become inconsistent. If a page is statically generated but makes an SSR request inside a Server Component, it can cause hydration mismatches.
+
+- **Fix**: Use `revalidate: X` instead of `no-store` for better consistency.
+
+#### 2. Unexpected Cache Stale Issues
+
+When caching API calls, certain headers (like authentication tokens) can prevent cache reuse.
+
+- **Fix**: Always set `cache: 'no-store'` for private data.
+
+```jsx
+const response = await fetch('https://api.example.com/user', {
+ headers: { Authorization: `Bearer ${token}` },
+ cache: 'no-store',
+});
+```
+
+#### 3. Over-Caching Can Lead to Outdated Data
+
+If you overuse force-cache or long revalidation periods, users might see outdated information.
+
+- **Fix**: Use `revalidatePath()` in API routes to refresh data dynamically.
+
+### Final Thoughts
+
+After experimenting with Next.js’s cache API, I’ve found that:
+- ✅ Using cache() can optimize repeated function calls within the same request.
+- ✅ Revalidate strategies (revalidate: X) provide a balance between performance and fresh data.
+- ✅ Manual cache invalidation (revalidatePath) is crucial for real-time updates.
+- ✅ Avoid mixing SSR and ISR fetch behaviors to prevent caching conflicts.
+
+Caching in Next.js is incredibly powerful, but it requires careful tuning depending on your use case.
+
+Whether you’re building a statically optimized site or a real-time dashboard, understanding Next.js caching deeply can help you make better architectural decisions.
diff --git a/src/posts/nextjs-experiments.mdx b/src/posts/nextjs-experiments.mdx
new file mode 100644
index 0000000..b275e67
--- /dev/null
+++ b/src/posts/nextjs-experiments.mdx
@@ -0,0 +1,108 @@
+---
+title: 'Experimenting with Next.js'
+date: '2025-01-20'
+description: 'Discover the powerful features of Next.js and how you can use them to build modern web applications.'
+tags: ['React', 'Next.js', 'JavaScript', 'Web Development']
+---
+
+# Introduction
+
+Next.js is a powerful React framework that enables you to build modern web applications with ease. In this blog post, I'll explore some of the key features of Next.js and how you can use them to enhance your development experience.
+
+---
+
+## Static Site Generation (SSG)
+
+Next.js allows you to generate static pages at build time, providing fast and SEO-friendly web applications.
+
+### Example: Static Blog Page
+
+```jsx
+import { getAllPosts } from '@/lib/api';
+
+export async function getStaticProps() {
+ const posts = getAllPosts();
+ return {
+ props: { posts },
+ };
+}
+
+const Blog = ({ posts }) => (
+
+);
+
+export default Blog;
+```
+
+---
+
+## Server-Side Rendering (SSR)
+Next.js also supports server-side rendering, allowing you to render pages on the server for each request.
+
+### Example: Server-Side Rendered Page
+```jsx
+export async function getServerSideProps() {
+ const res = await fetch('https://api.example.com/data');
+ const data = await res.json();
+ return {
+ props: { data },
+ };
+}
+
+const DataPage = ({ data }) => (
+
+
Data
+
{JSON.stringify(data, null, 2)}
+
+);
+
+export default DataPage;
+```
+
+---
+
+## API Routes
+Next.js provides a built-in API routing system, allowing you to create API endpoints within your application.
+
+### Example: API Route
+```jsx
+export default function handler(req, res) {
+ res.status(200).json({ message: 'Hello from Next.js API!' });
+}
+```
+
+---
+
+## Image Optimization
+Next.js includes an Image component that optimizes images for better performance.
+
+### Example: Optimized Image
+```jsx
+import Image from 'next/image';
+
+const OptimizedImage = () => (
+
+
+
+);
+
+export default OptimizedImage;
+```
+
+---
+
+## Conclusion
+Next.js offers a wide range of features that make it a powerful framework for building modern web applications. Whether you're using static site generation, server-side rendering, API routes, or image optimization, Next.js provides the tools you need to create fast and efficient web applications.
+
+---
+
+I hope you found this exploration of Next.js helpful. If you have any questions or comments, feel free to reach out to me. Happy coding!
\ No newline at end of file
diff --git a/src/posts/typescript-patterns-2025.mdx b/src/posts/typescript-patterns-2025.mdx
new file mode 100644
index 0000000..d15a094
--- /dev/null
+++ b/src/posts/typescript-patterns-2025.mdx
@@ -0,0 +1,106 @@
+---
+title: 'Modern TypeScript Patterns for 2025'
+date: '2025-06-09'
+description: 'Explore the latest TypeScript patterns and best practices for scalable, maintainable codebases in 2025.'
+tags: ['TypeScript', 'Patterns', 'Best Practices', 'Web Development', 'JavaScript']
+---
+
+# Introduction
+
+TypeScript continues to evolve, and so do the patterns we use to build robust applications. In this post, I’ll share some of the most effective TypeScript patterns and best practices I’m using in 2025.
+
+---
+
+## 1. Type-Safe API Clients with Zod
+
+Zod is a TypeScript-first schema validation library. Use it to validate API responses and ensure type safety.
+
+```typescript
+import { z } from 'zod';
+
+const UserSchema = z.object({
+ id: z.string(),
+ name: z.string(),
+ email: z.string().email(),
+});
+
+type User = z.infer;
+
+async function fetchUser(id: string): Promise {
+ const res = await fetch(`/api/users/${id}`);
+ const data = await res.json();
+ return UserSchema.parse(data);
+}
+```
+
+---
+
+## 2. Discriminated Unions for State Machines
+
+Model complex state with discriminated unions.
+
+```typescript
+type Loading = { status: 'loading' };
+type Success = { status: 'success'; data: T };
+type Error = { status: 'error'; error: string };
+
+type FetchState = Loading | Success | Error;
+```
+
+---
+
+## 3. Exhaustive Switch Statements
+
+Use `never` to ensure all cases are handled.
+
+```typescript
+function handleState(state: FetchState) {
+ switch (state.status) {
+ case 'loading':
+ return 'Loading...';
+ case 'success':
+ return `Data: ${JSON.stringify(state.data)}`;
+ case 'error':
+ return `Error: ${state.error}`;
+ default:
+ const _exhaustive: never = state;
+ return _exhaustive;
+ }
+}
+```
+
+---
+
+## 4. Utility Types for Immutability
+
+Use `Readonly` and `as const` for safer code.
+
+```typescript
+const roles = ['admin', 'user', 'guest'] as const;
+type Role = typeof roles[number];
+
+const user: Readonly<{ name: string; role: Role }> = {
+ name: 'Alice',
+ role: 'admin',
+};
+```
+
+---
+
+## 5. Template Literal Types
+
+Leverage template literals for expressive types.
+
+```typescript
+type EventName = `user:${'created' | 'updated' | 'deleted'}`;
+```
+
+---
+
+## Conclusion
+
+TypeScript’s type system is more powerful than ever. By adopting these patterns, you’ll write safer, more maintainable code in 2025 and beyond.
+
+---
+
+Let me know if you want a deep dive on any of these patterns!
diff --git a/src/styles/docs.css b/src/styles/docs.css
new file mode 100644
index 0000000..9bfbd83
--- /dev/null
+++ b/src/styles/docs.css
@@ -0,0 +1,197 @@
+/* Documentation specific styles */
+
+/* Improve typography for documentation */
+.docs-content {
+ font-size: 1rem;
+ line-height: 1.7;
+}
+
+/* Override prose styles for docs */
+.docs-content .prose {
+ max-width: none;
+}
+
+.docs-content .prose h2 {
+ font-size: 1.75rem;
+ margin-top: 2.5rem;
+ margin-bottom: 1rem;
+ color: rgb(var(--color-primary-300));
+ scroll-margin-top: 100px;
+ font-weight: 600;
+}
+
+.docs-content .prose h3 {
+ font-size: 1.35rem;
+ margin-top: 2rem;
+ margin-bottom: 0.75rem;
+ color: rgb(var(--color-primary-400));
+ scroll-margin-top: 100px;
+ font-weight: 600;
+}
+
+.docs-content .prose h4 {
+ font-size: 1.15rem;
+ margin-top: 1.5rem;
+ margin-bottom: 0.5rem;
+ scroll-margin-top: 100px;
+ font-weight: 600;
+}
+
+.docs-content .prose a {
+ color: rgb(var(--color-primary-400));
+ text-decoration: none;
+ transition: color 0.2s;
+ font-weight: 500;
+}
+
+.docs-content .prose a:hover {
+ color: rgb(var(--color-primary-300));
+}
+
+/* Code blocks with improved styling */
+.docs-content .prose pre {
+ background-color: rgb(var(--color-card-alt));
+ border: 1px solid rgba(var(--color-primary-700), 0.2);
+ border-radius: 0.5rem;
+ margin: 1.5rem 0;
+ overflow-x: auto;
+}
+
+.docs-content .prose code {
+ font-size: 0.875em;
+ background-color: rgba(var(--color-code-bg), 0.4);
+ padding: 0.2em 0.4em;
+ border-radius: 0.25rem;
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+}
+
+.docs-content .prose pre code {
+ background-color: transparent;
+ padding: 1.25rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ color: rgb(235, 235, 235);
+}
+
+/* Tables with improved styling */
+.docs-content .prose table {
+ width: 100%;
+ margin: 1.5rem 0;
+ border-collapse: separate;
+ border-spacing: 0;
+ border-radius: 0.5rem;
+ overflow: hidden;
+ border: 1px solid rgba(var(--color-primary-800), 0.3);
+}
+
+.docs-content .prose th {
+ background-color: rgba(var(--color-primary-900), 0.4);
+ color: rgb(var(--color-primary-300));
+ font-weight: 600;
+ text-align: left;
+ padding: 0.75rem 1rem;
+ font-size: 0.875rem;
+}
+
+.docs-content .prose td {
+ padding: 0.75rem 1rem;
+ border-top: 1px solid rgba(var(--color-primary-800), 0.2);
+ font-size: 0.875rem;
+}
+
+.docs-content .prose tr:nth-child(even) {
+ background-color: rgba(var(--color-primary-900), 0.1);
+}
+
+/* Lists with improved styling */
+.docs-content .prose ul,
+.docs-content .prose ol {
+ margin: 1.25rem 0;
+ padding-left: 1.5rem;
+}
+
+.docs-content .prose li {
+ margin-bottom: 0.5rem;
+}
+
+/* Blockquotes */
+.docs-content .prose blockquote {
+ border-left: 4px solid rgb(var(--color-primary-500));
+ background-color: rgba(var(--color-primary-900), 0.2);
+ padding: 1rem 1.25rem;
+ margin: 1.5rem 0;
+ border-radius: 0 0.5rem 0.5rem 0;
+ color: rgb(var(--color-text-muted));
+ font-style: italic;
+}
+
+/* Custom docs components styling */
+.docs-section-card {
+ display: flex;
+ flex-direction: column;
+ border-radius: 0.5rem;
+ border: 1px solid rgba(var(--color-primary-700), 0.2);
+ background-color: rgb(var(--color-card));
+ padding: 1.5rem;
+ transition: border-color 0.2s, transform 0.2s;
+}
+
+.docs-section-card:hover {
+ border-color: rgba(var(--color-primary-600), 0.4);
+ transform: translateY(-2px);
+}
+
+.docs-section-card-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 2.5rem;
+ height: 2.5rem;
+ background-color: rgba(var(--color-primary-900), 0.3);
+ border-radius: 0.5rem;
+ color: rgb(var(--color-primary-400));
+ margin-bottom: 1rem;
+}
+
+/* Documentation search result styling */
+.doc-search-result {
+ padding: 0.75rem 1rem;
+ border-radius: 0.5rem;
+ transition: background-color 0.2s;
+}
+
+.doc-search-result:hover {
+ background-color: rgba(var(--color-primary-900), 0.2);
+}
+
+.doc-search-result-title {
+ font-weight: 500;
+ color: rgb(var(--color-text));
+}
+
+.doc-search-result-desc {
+ font-size: 0.875rem;
+ color: rgb(var(--color-text-muted));
+}
+
+.doc-search-result-section {
+ font-size: 0.75rem;
+ color: rgb(var(--color-primary-400));
+ margin-top: 0.25rem;
+}
+
+/* Add responsive styles for mobile */
+@media (max-width: 768px) {
+ .docs-content .prose h2 {
+ font-size: 1.5rem;
+ }
+
+ .docs-content .prose h3 {
+ font-size: 1.25rem;
+ }
+
+ .docs-content .prose pre {
+ max-width: 100%;
+ overflow-x: auto;
+ }
+}
diff --git a/src/styles/globals.css b/src/styles/globals.css
new file mode 100644
index 0000000..6b9184b
--- /dev/null
+++ b/src/styles/globals.css
@@ -0,0 +1,1375 @@
+@import '../styles/docs.css';
+
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root,
+ [data-theme="blue"] {
+ /* Primary colors - Vibrant blue */
+ --color-primary-50: 239, 246, 255;
+ --color-primary-100: 219, 234, 254;
+ --color-primary-200: 191, 219, 254;
+ --color-primary-300: 147, 197, 253;
+ --color-primary-400: 96, 165, 250;
+ --color-primary-500: 59, 130, 246;
+ --color-primary-600: 37, 99, 235;
+ --color-primary-700: 29, 78, 216;
+ --color-primary-800: 30, 64, 175;
+ --color-primary-900: 30, 58, 138;
+
+ /* Modern, sleek blue theme */
+ --color-bg: 15, 23, 42;
+ --color-bg-alt: 30, 41, 59;
+ --color-bg-accent: 17, 25, 45;
+ --color-card: 22, 31, 49;
+ --color-card-alt: 25, 39, 67;
+ --color-text: 255, 255, 255;
+ --color-text-muted: 203, 213, 225;
+ --color-border: 51, 65, 85;
+ --color-glow: 37, 99, 235;
+
+ /* Code block background colors */
+ --color-code-bg: 15, 35, 70;
+ }
+
+ [data-theme="purple"] {
+ /* Primary colors - Vibrant purple */
+ --color-primary-50: 250, 245, 255;
+ --color-primary-100: 243, 232, 255;
+ --color-primary-200: 233, 213, 255;
+ --color-primary-300: 216, 180, 254;
+ --color-primary-400: 192, 132, 252;
+ --color-primary-500: 168, 85, 247;
+ --color-primary-600: 147, 51, 234;
+ --color-primary-700: 126, 34, 206;
+ --color-primary-800: 107, 33, 168;
+ --color-primary-900: 88, 28, 135;
+
+ /* Modern, sleek purple theme */
+ --color-bg: 30, 20, 50;
+ --color-bg-alt: 45, 30, 75;
+ --color-bg-accent: 35, 25, 60;
+ --color-card: 40, 30, 65;
+ --color-card-alt: 55, 40, 85;
+ --color-text: 255, 255, 255;
+ --color-text-muted: 214, 203, 225;
+ --color-border: 70, 55, 95;
+ --color-glow: 147, 51, 234;
+
+ /* Code block background colors */
+ --color-code-bg: 30, 15, 60;
+ }
+
+ [data-theme="teal"] {
+ /* Primary colors - Vibrant teal */
+ --color-primary-50: 240, 253, 250;
+ --color-primary-100: 204, 251, 241;
+ --color-primary-200: 153, 246, 228;
+ --color-primary-300: 94, 234, 212;
+ --color-primary-400: 45, 212, 191;
+ --color-primary-500: 20, 184, 166;
+ --color-primary-600: 13, 148, 136;
+ --color-primary-700: 15, 118, 110;
+ --color-primary-800: 17, 94, 89;
+ --color-primary-900: 19, 78, 74;
+
+ /* Modern, sleek teal theme */
+ --color-bg: 15, 32, 32;
+ --color-bg-alt: 22, 48, 48;
+ --color-bg-accent: 17, 40, 40;
+ --color-card: 24, 45, 45;
+ --color-card-alt: 30, 58, 58;
+ --color-text: 255, 255, 255;
+ --color-text-muted: 203, 225, 220;
+ --color-border: 45, 85, 85;
+ --color-glow: 13, 148, 136;
+
+ /* Code block background colors */
+ --color-code-bg: 10, 40, 40;
+ }
+
+ [data-theme="rose"] {
+ /* Primary colors - Vibrant rose */
+ --color-primary-50: 255, 241, 242;
+ --color-primary-100: 255, 228, 230;
+ --color-primary-200: 254, 205, 211;
+ --color-primary-300: 253, 164, 175;
+ --color-primary-400: 251, 113, 133;
+ --color-primary-500: 244, 63, 94;
+ --color-primary-600: 225, 29, 72;
+ --color-primary-700: 190, 18, 60;
+ --color-primary-800: 159, 18, 57;
+ --color-primary-900: 136, 19, 55;
+
+ /* Modern, sleek rose theme */
+ --color-bg: 28, 15, 20;
+ --color-bg-alt: 47, 23, 32;
+ --color-bg-accent: 38, 17, 24;
+ --color-card: 43, 22, 29;
+ --color-card-alt: 62, 30, 42;
+ --color-text: 255, 255, 255;
+ --color-text-muted: 225, 205, 210;
+ --color-border: 85, 45, 60;
+ --color-glow: 225, 29, 72;
+
+ /* Code block background colors */
+ --color-code-bg: 40, 15, 25;
+ }
+
+ [data-theme="amber"] {
+ /* Primary colors - Vibrant amber */
+ --color-primary-50: 255, 251, 235;
+ --color-primary-100: 254, 243, 199;
+ --color-primary-200: 253, 230, 138;
+ --color-primary-300: 252, 211, 77;
+ --color-primary-400: 251, 191, 36;
+ --color-primary-500: 245, 158, 11;
+ --color-primary-600: 217, 119, 6;
+ --color-primary-700: 180, 83, 9;
+ --color-primary-800: 146, 64, 14;
+ --color-primary-900: 120, 53, 15;
+
+ /* Modern, sleek amber theme */
+ --color-bg: 28, 25, 15;
+ --color-bg-alt: 44, 38, 20;
+ --color-bg-accent: 35, 30, 15;
+ --color-card: 42, 35, 18;
+ --color-card-alt: 58, 45, 22;
+ --color-text: 255, 255, 255;
+ --color-text-muted: 225, 218, 200;
+ --color-border: 85, 70, 40;
+ --color-glow: 217, 119, 6;
+
+ /* Code block background colors */
+ --color-code-bg: 40, 30, 10;
+ }
+
+ [data-theme="sunset"] {
+ /* Primary colors - Vibrant sunset orange */
+ --color-primary-50: 255, 247, 237;
+ --color-primary-100: 255, 237, 213;
+ --color-primary-200: 254, 215, 170;
+ --color-primary-300: 253, 186, 116;
+ --color-primary-400: 251, 146, 60;
+ --color-primary-500: 249, 115, 22;
+ --color-primary-600: 234, 88, 12;
+ --color-primary-700: 194, 65, 12;
+ --color-primary-800: 154, 52, 18;
+ --color-primary-900: 124, 45, 18;
+
+ /* Modern, sleek sunset theme */
+ --color-bg: 30, 20, 25;
+ --color-bg-alt: 48, 30, 35;
+ --color-bg-accent: 40, 25, 30;
+ --color-card: 45, 28, 32;
+ --color-card-alt: 60, 38, 42;
+ --color-text: 255, 255, 255;
+ --color-text-muted: 225, 213, 205;
+ --color-border: 85, 55, 55;
+ --color-glow: 234, 88, 12;
+
+ /* Code block background colors */
+ --color-code-bg: 40, 20, 15;
+ }
+
+ [data-theme="emerald"] {
+ /* Primary colors - Rich emerald */
+ --color-primary-50: 236, 253, 245;
+ --color-primary-100: 209, 250, 229;
+ --color-primary-200: 167, 243, 208;
+ --color-primary-300: 110, 231, 183;
+ --color-primary-400: 52, 211, 153;
+ --color-primary-500: 16, 185, 129;
+ --color-primary-600: 5, 150, 105;
+ --color-primary-700: 4, 120, 87;
+ --color-primary-800: 6, 95, 70;
+ --color-primary-900: 6, 78, 59;
+
+ /* Modern, sleek emerald theme */
+ --color-bg: 15, 30, 25;
+ --color-bg-alt: 22, 45, 38;
+ --color-bg-accent: 18, 38, 32;
+ --color-card: 20, 42, 35;
+ --color-card-alt: 25, 55, 45;
+ --color-text: 255, 255, 255;
+ --color-text-muted: 200, 225, 215;
+ --color-border: 40, 85, 70;
+ --color-glow: 5, 150, 105;
+
+ /* Code block background colors */
+ --color-code-bg: 12, 40, 30;
+ }
+
+ [data-theme="crimson"] {
+ /* Primary colors - Deep crimson */
+ --color-primary-50: 254, 242, 242;
+ --color-primary-100: 254, 226, 226;
+ --color-primary-200: 254, 202, 202;
+ --color-primary-300: 252, 165, 165;
+ --color-primary-400: 248, 113, 113;
+ --color-primary-500: 239, 68, 68;
+ --color-primary-600: 220, 38, 38;
+ --color-primary-700: 185, 28, 28;
+ --color-primary-800: 153, 27, 27;
+ --color-primary-900: 127, 29, 29;
+
+ /* Modern, sleek crimson theme */
+ --color-bg: 32, 15, 20;
+ --color-bg-alt: 48, 22, 30;
+ --color-bg-accent: 40, 18, 25;
+ --color-card: 45, 20, 28;
+ --color-card-alt: 60, 25, 35;
+ --color-text: 255, 255, 255;
+ --color-text-muted: 225, 205, 205;
+ --color-border: 85, 40, 50;
+ --color-glow: 220, 38, 38;
+
+ /* Code block background colors */
+ --color-code-bg: 45, 15, 20;
+ }
+
+ [data-theme="nord"] {
+ /* Primary colors - Nordic blue */
+ --color-primary-50: 236, 245, 255;
+ --color-primary-100: 219, 235, 254;
+ --color-primary-200: 191, 219, 254;
+ --color-primary-300: 147, 197, 253;
+ --color-primary-400: 94, 165, 233;
+ --color-primary-500: 66, 136, 206;
+ --color-primary-600: 49, 112, 179;
+ --color-primary-700: 42, 90, 142;
+ --color-primary-800: 37, 73, 109;
+ --color-primary-900: 31, 58, 82;
+
+ /* Nord-inspired theme */
+ --color-bg: 46, 52, 64;
+ --color-bg-alt: 59, 66, 82;
+ --color-bg-accent: 52, 58, 70;
+ --color-card: 67, 76, 94;
+ --color-card-alt: 76, 86, 106;
+ --color-text: 236, 239, 244;
+ --color-text-muted: 216, 222, 233;
+ --color-border: 76, 86, 106;
+ --color-glow: 94, 129, 172;
+
+ /* Code block background colors */
+ --color-code-bg: 37, 42, 54;
+ }
+
+ [data-theme="cyberpunk"] {
+ /* Primary colors - Neon yellow */
+ --color-primary-50: 255, 255, 230;
+ --color-primary-100: 255, 255, 204;
+ --color-primary-200: 255, 255, 153;
+ --color-primary-300: 255, 255, 102;
+ --color-primary-400: 255, 255, 25;
+ --color-primary-500: 236, 236, 0;
+ --color-primary-600: 204, 204, 0;
+ --color-primary-700: 170, 170, 0;
+ --color-primary-800: 120, 120, 0;
+ --color-primary-900: 85, 85, 0;
+
+ /* Cyberpunk inspired theme */
+ --color-bg: 17, 10, 40;
+ --color-bg-alt: 38, 16, 71;
+ --color-bg-accent: 25, 12, 55;
+ --color-card: 45, 15, 70;
+ --color-card-alt: 60, 20, 90;
+ --color-text: 255, 255, 255;
+ --color-text-muted: 200, 200, 255;
+ --color-border: 100, 40, 150;
+ --color-glow: 255, 0, 255;
+
+ /* Code block background colors */
+ --color-code-bg: 17, 5, 35;
+ }
+
+ [data-theme="mint"] {
+ /* Primary colors - Soft mint */
+ --color-primary-50: 240, 253, 244;
+ --color-primary-100: 220, 252, 231;
+ --color-primary-200: 187, 247, 208;
+ --color-primary-300: 134, 239, 172;
+ --color-primary-400: 74, 222, 128;
+ --color-primary-500: 34, 197, 94;
+ --color-primary-600: 22, 163, 74;
+ --color-primary-700: 21, 128, 61;
+ --color-primary-800: 22, 101, 52;
+ --color-primary-900: 20, 83, 45;
+
+ /* Minty fresh theme */
+ --color-bg: 25, 40, 35;
+ --color-bg-alt: 35, 55, 48;
+ --color-bg-accent: 30, 48, 42;
+ --color-card: 38, 58, 50;
+ --color-card-alt: 45, 68, 60;
+ --color-text: 255, 255, 255;
+ --color-text-muted: 210, 230, 220;
+ --color-border: 50, 80, 65;
+ --color-glow: 34, 197, 94;
+
+ /* Code block background colors */
+ --color-code-bg: 15, 35, 25;
+ }
+}
+
+/* Apply modern, sleek background styles */
+html, body {
+ color: rgb(var(--color-text));
+ background-color: rgb(var(--color-bg));
+ min-height: 100vh;
+ scroll-behavior: smooth;
+}
+
+/* Enhanced Pixelated background effect with glitch animations */
+.pixel-grid-layer-1 {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-image:
+ linear-gradient(90deg, rgba(var(--color-primary-400), 0.15) 1px, transparent 1px),
+ linear-gradient(rgba(var(--color-primary-400), 0.15) 1px, transparent 1px);
+ background-size: 12px 12px;
+ background-position: center;
+ opacity: 0.2;
+ z-index: 0;
+ pointer-events: none;
+ animation: pixelGrid 8s linear infinite;
+}
+
+.pixel-grid-layer-2 {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-image:
+ linear-gradient(90deg, rgba(var(--color-primary-300), 0.08) 1px, transparent 1px),
+ linear-gradient(rgba(var(--color-primary-300), 0.08) 1px, transparent 1px);
+ background-size: 24px 24px;
+ background-position: 6px 6px;
+ opacity: 0.15;
+ z-index: 0;
+ pointer-events: none;
+ animation: pixelGrid 12s linear infinite reverse;
+}
+
+/* Grid background effects */
+/* .pixel-grid-layer-1 {
+ background-image: radial-gradient(rgba(var(--color-primary-500), 0.025) 1px, transparent 1px);
+ background-size: 20px 20px;
+}
+
+.pixel-grid-layer-2 {
+ background-image: radial-gradient(rgba(var(--color-primary-400), 0.015) 1px, transparent 1px);
+ background-size: 40px 40px;
+ background-position: 10px 10px;
+} */
+
+/* Glitch effect animations */
+@keyframes pixelGridGlitch {
+ 0%, 100% {
+ background-position: 0 0;
+ opacity: 0.2;
+ }
+ 25% {
+ background-position: -5px 0;
+ opacity: 0.25;
+ }
+ 26% {
+ background-position: 2px -4px;
+ opacity: 0.15;
+ }
+ 50% {
+ background-position: 0 0;
+ opacity: 0.2;
+ }
+ 62% {
+ background-position: 3px 2px;
+ opacity: 0.3;
+ }
+ 63% {
+ background-position: -3px -1px;
+ opacity: 0.15;
+ }
+ 80% {
+ background-position: 0 0;
+ opacity: 0.2;
+ }
+}
+
+.glitch-active .pixel-grid-layer-1 {
+ animation: pixelGridGlitch 0.5s steps(2) 2;
+}
+
+.glitch-active .pixel-grid-layer-2 {
+ animation: pixelGridGlitch 0.5s steps(2) reverse 2;
+}
+
+/* Glitch slices */
+.glitch-slice-1 {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 5px;
+ background-color: rgba(var(--color-primary-500), 0.2);
+ z-index: 2;
+ transform: translateY(30vh);
+ opacity: 0.7;
+ animation: glitchSlice 0.5s ease-out 2;
+}
+
+.glitch-slice-2 {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 3px;
+ background-color: rgba(var(--color-primary-300), 0.3);
+ z-index: 2;
+ transform: translateY(70vh);
+ opacity: 0.5;
+ animation: glitchSlice 0.5s ease-out 2 0.1s;
+}
+
+@keyframes glitchSlice {
+ 0% { transform: translateY(-100vh); }
+ 100% { transform: translateY(100vh); }
+}
+
+/* Glitch noise overlay */
+.glitch-noise {
+ position: absolute;
+ inset: 0;
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.05'/%3E%3C/svg%3E");
+ background-size: 200px;
+ opacity: 0;
+ mix-blend-mode: screen;
+ pointer-events: none;
+ animation: noiseAnimation 0.5s steps(2) 2;
+}
+
+@keyframes noiseAnimation {
+ 0%, 100% { opacity: 0; }
+ 10%, 30%, 50%, 70%, 90% { opacity: 0.1; }
+ 20%, 40%, 60%, 80% { opacity: 0.05; }
+}
+
+/* Glitch text effect */
+.glitch-text {
+ display: inline-block;
+ position: absolute;
+ left: 0;
+ top: 0;
+ color: rgb(var(--color-primary-400));
+ clip-path: polygon(0 0, 100% 0, 100% 45%, 0 45%);
+ transform: translateX(-1px);
+ animation: glitchText 0.5s infinite linear alternate-reverse;
+}
+
+@keyframes glitchText {
+ 0% {
+ clip-path: polygon(0 0, 100% 0, 100% 45%, 0 45%);
+ transform: translateX(-1px);
+ }
+ 20% {
+ clip-path: polygon(0 20%, 100% 20%, 100% 55%, 0 55%);
+ transform: translateX(1px);
+ }
+ 40% {
+ clip-path: polygon(0 40%, 100% 40%, 100% 75%, 0 75%);
+ transform: translateX(-1px);
+ }
+ 60% {
+ clip-path: polygon(0 60%, 100% 60%, 100% 95%, 0 95%);
+ transform: translateX(1px);
+ }
+ 80% {
+ clip-path: polygon(0 75%, 100% 75%, 100% 100%, 0 100%);
+ transform: translateX(-1px);
+ }
+ 100% {
+ clip-path: polygon(0 85%, 100% 85%, 100% 100%, 0 100%);
+ transform: translateX(1px);
+ }
+}
+
+/* Custom animations */
+@keyframes float {
+ 0% { transform: translateY(0px); }
+ 50% { transform: translateY(-10px); }
+ 100% { transform: translateY(0px); }
+}
+
+@keyframes pulse-glow {
+ 0%, 100% { box-shadow: 0 0 15px 2px rgba(var(--color-glow), 0.3); }
+ 50% { box-shadow: 0 0 25px 8px rgba(var(--color-glow), 0.5); }
+}
+
+@keyframes gradient-shift {
+ 0% { background-position: 0% 50%; }
+ 50% { background-position: 100% 50%; }
+ 100% { background-position: 0% 50%; }
+}
+
+@keyframes fade-up {
+ 0% { opacity: 0; transform: translateY(20px); }
+ 100% { opacity: 1; transform: translateY(0); }
+}
+
+.animate-float {
+ animation: float 3s ease-in-out infinite;
+}
+
+.animate-pulse-glow {
+ animation: pulse-glow 3s ease-in-out infinite;
+}
+
+.animate-gradient {
+ animation: gradient-shift 5s ease infinite;
+ background-size: 200% 200%;
+}
+
+.animate-fade-up {
+ animation: fade-up 0.6s ease-out forwards;
+}
+
+.animate-pixelGrid {
+ animation: pixelGrid 4s linear infinite;
+}
+
+/* Additional animation utilities */
+@keyframes pulse-slow {
+ 0%, 100% { opacity: 0.4; }
+ 50% { opacity: 0.7; }
+}
+
+.animate-pulse-slow {
+ animation: pulse-slow 8s ease-in-out infinite;
+}
+
+.delay-1000 {
+ animation-delay: 1s;
+}
+
+.delay-2000 {
+ animation-delay: 2s;
+}
+
+.animate-scroll-indicator {
+ animation: scrollIndicator 2s ease-in-out infinite;
+}
+
+@keyframes scrollIndicator {
+ 0% { transform: translateY(0); opacity: 0.8; }
+ 50% { transform: translateY(10px); opacity: 1; }
+ 100% { transform: translateY(0); opacity: 0.8; }
+}
+
+/* Animation utilities for dropdowns and other elements */
+.animate-in {
+ animation-duration: 150ms;
+ animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
+ will-change: transform, opacity;
+}
+
+.fade-in-0 {
+ opacity: 0;
+}
+
+.fade-in-100 {
+ opacity: 1;
+}
+
+.fade-in-80 {
+ animation-name: fadeIn;
+}
+
+.slide-in-from-top-5 {
+ animation-name: slideInFromTop;
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+@keyframes slideInFromTop {
+ from { transform: translateY(-5%); }
+ to { transform: translateY(0); }
+}
+
+/* Blog card enhancements */
+.blog-card-animation-wrapper {
+ isolation: isolate;
+}
+
+.blog-card {
+ backdrop-filter: blur(8px);
+ transform-style: preserve-3d;
+ will-change: transform;
+}
+
+.blog-card-glow {
+ pointer-events: none;
+ position: absolute;
+ inset: 0;
+ opacity: 0;
+ background: radial-gradient(
+ circle at var(--x, 50%) var(--y, 50%),
+ rgba(var(--color-primary-400), 0.1) 0%,
+ transparent 50%
+ );
+ border-radius: inherit;
+ z-index: 1;
+ transition: opacity 0.3s ease;
+}
+
+.blog-card:hover .blog-card-glow {
+ opacity: 1;
+}
+
+/* Enhanced blog card styles */
+.blog-card-footer {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+@media (max-width: 480px) {
+ .blog-card-footer {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .blog-card-footer > div:last-child {
+ align-self: flex-end;
+ margin-top: 8px;
+ }
+}
+
+/* Updated gradient border animation */
+@keyframes borderGradientShift {
+ 0% {
+ background-position: 0% 50%;
+ }
+ 50% {
+ background-position: 100% 50%;
+ }
+ 100% {
+ background-position: 0% 50%;
+ }
+}
+
+.animated-border {
+ position: relative;
+ isolation: isolate;
+ transition: border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
+}
+
+.animated-border::before {
+ content: '';
+ position: absolute;
+ inset: -1px;
+ z-index: -1;
+ border-radius: inherit;
+ background: linear-gradient(
+ 90deg,
+ rgba(var(--color-primary-500), 0) 0%,
+ rgba(var(--color-primary-500), 0.3) 50%,
+ rgba(var(--color-primary-500), 0) 100%
+ );
+ mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
+ -webkit-mask-composite: xor;
+ mask-composite: exclude;
+ opacity: 0;
+ transition: opacity 0.5s ease;
+}
+
+.animated-border:hover::before {
+ opacity: 1;
+ animation: borderBeam 2s linear infinite;
+}
+
+@keyframes borderBeam {
+ from {
+ background-position: -300% 0;
+ }
+ to {
+ background-position: 300% 0;
+ }
+}
+
+/* Card hover state - glow effect */
+.card-glow {
+ position: relative;
+}
+
+.card-glow::after {
+ content: '';
+ position: absolute;
+ inset: -1px;
+ z-index: -2;
+ border-radius: inherit;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ background: radial-gradient(
+ circle at center,
+ rgba(var(--color-primary-400), 0.3),
+ transparent 70%
+ );
+ filter: blur(15px);
+}
+
+.card-glow:hover::after {
+ opacity: 1;
+}
+
+/* Article card badge styles */
+.new-badge {
+ position: relative;
+ overflow: hidden;
+}
+
+.new-badge::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(
+ 135deg,
+ rgba(var(--color-primary-400), 0.3) 0%,
+ transparent 50%
+ );
+ clip-path: polygon(100% 0, 100% 100%, 70% 100%, 100% 0);
+ z-index: -1;
+}
+
+/* Add a gradient border animation */
+@keyframes borderGradientShift {
+ 0%, 100% {
+ border-image-source: linear-gradient(
+ 45deg,
+ rgba(var(--color-primary-400), 0.5),
+ transparent,
+ rgba(var(--color-primary-500), 0.3)
+ );
+ }
+ 50% {
+ border-image-source: linear-gradient(
+ 225deg,
+ rgba(var(--color-primary-400), 0.5),
+ transparent,
+ rgba(var(--color-primary-500), 0.3)
+ );
+ }
+}
+
+.animated-border {
+ border: 1px solid transparent;
+ border-radius: 0.75rem;
+ background-clip: padding-box;
+ position: relative;
+}
+
+.animated-border::before {
+ content: '';
+ position: absolute;
+ inset: -1px;
+ z-index: -1;
+ border-radius: inherit;
+ background: linear-gradient(
+ 45deg,
+ rgba(var(--color-primary-500), 0.3),
+ transparent,
+ rgba(var(--color-primary-300), 0.3)
+ );
+ opacity: 0;
+ transition: opacity 0.3s ease;
+}
+
+.animated-border:hover::before {
+ opacity: 1;
+ animation: borderGradientShift 3s linear infinite;
+}
+
+/* Mobile filter dropdown */
+.mobile-filter {
+ position: fixed;
+ bottom: 20px;
+ right: 20px;
+ z-index: 40;
+}
+
+.mobile-filter-button {
+ width: 50px;
+ height: 50px;
+ border-radius: 25px;
+ background-color: rgb(var(--color-primary-600));
+ color: white;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 4px 12px rgba(var(--color-primary-900), 0.3);
+ border: none;
+ transition: all 0.3s ease;
+}
+
+.mobile-filter-button:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 16px rgba(var(--color-primary-900), 0.4);
+}
+
+/* Custom components */
+@layer components {
+ .btn-primary {
+ @apply px-6 py-3 rounded-lg bg-primary-600 text-white font-medium transition-all
+ relative overflow-hidden hover:bg-primary-700
+ shadow-[0_0_15px_rgba(var(--color-glow),0.3)]
+ hover:shadow-[0_0_25px_rgba(var(--color-glow),0.5)]
+ focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 focus:ring-offset-bg;
+ }
+
+ .btn-primary {
+ @apply relative overflow-hidden;
+ }
+
+ .btn-primary::before {
+ @apply content-[''] absolute inset-0 bg-primary-700 -z-10 opacity-0 transition-opacity;
+ }
+
+ .btn-primary:hover::before {
+ @apply opacity-100;
+ }
+
+ .btn-secondary {
+ @apply px-6 py-3 rounded-lg text-primary-400 font-medium
+ border border-primary-800/50 hover:border-primary-600
+ bg-bg-alt/50 backdrop-blur-sm
+ hover:bg-bg-alt transition-all
+ focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 focus:ring-offset-bg;
+ }
+
+ .btn-icon {
+ @apply p-2 rounded-full bg-bg-alt/80 backdrop-blur-sm hover:bg-bg-alt
+ text-primary-400 hover:text-primary-300
+ transition-all border border-primary-800/30 hover:border-primary-700/50
+ focus:outline-none focus:ring-2 focus:ring-primary-500;
+ }
+
+ .container-section {
+ @apply max-w-6xl mx-auto px-4 sm:px-8 py-24 relative z-10;
+ }
+
+ .card {
+ @apply p-6 rounded-xl bg-card border border-color-border shadow-lg
+ backdrop-blur-sm relative z-10
+ hover:shadow-xl hover:shadow-primary-900/5 transition-all;
+ }
+
+ .card-highlight {
+ @apply rounded-xl bg-card-alt/50 border border-primary-500/50
+ shadow-lg shadow-primary-900/50 relative z-10
+ hover:shadow-xl hover:shadow-primary-800/20 transition-all
+ backdrop-blur-sm w-72 h-72 md:w-96 md:h-96;
+ }
+
+ .tag {
+ @apply px-3 py-1 rounded-full text-sm font-medium
+ bg-primary-900/40 text-primary-300
+ border border-primary-700/50 backdrop-blur-sm;
+ }
+
+ .navbar {
+ @apply fixed top-0 left-0 right-0 z-50 transition-all duration-300
+ backdrop-blur-md;
+ }
+
+ .navbar-scrolled {
+ @apply bg-bg/80 backdrop-blur-xl border-b border-color-border;
+ }
+
+ .navbar-container {
+ @apply max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between;
+ }
+
+ .navbar-link {
+ @apply relative px-1 py-2 text-color-text-muted hover:text-primary-400
+ transition-colors font-medium;
+ }
+
+ .navbar-link-active {
+ @apply text-primary-400;
+ }
+
+ .navbar-link::after {
+ @apply content-[''] absolute -bottom-1 left-0 w-0 h-[2px]
+ bg-gradient-to-r from-primary-600 to-primary-400
+ transition-all duration-300 rounded-full;
+ }
+
+ .navbar-link:hover::after, .navbar-link-active::after {
+ @apply w-full;
+ }
+
+ .section-bg-accent {
+ @apply bg-[rgb(var(--color-bg-accent))];
+ }
+
+ .heading-primary {
+ @apply text-4xl sm:text-5xl font-bold mb-8
+ bg-gradient-to-r from-primary-400 to-primary-300
+ bg-clip-text text-transparent;
+ }
+
+ .heading-secondary {
+ @apply text-2xl sm:text-3xl font-bold mb-4 text-primary-300;
+ }
+
+ .gradient-text {
+ @apply bg-gradient-to-r from-primary-400 to-primary-300 bg-clip-text text-transparent;
+ }
+
+ .gradient-border {
+ @apply relative;
+ }
+
+ .gradient-border::before {
+ @apply content-[''] absolute inset-0 rounded-xl p-[1.5px] -z-10
+ bg-gradient-to-r from-primary-600 to-primary-400
+ mask-gradient-border opacity-50;
+ }
+
+ .mask-gradient-border {
+ mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
+ -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
+ -webkit-mask-composite: xor;
+ mask-composite: exclude;
+ }
+
+ .glass-effect {
+ @apply backdrop-blur-lg bg-opacity-20 border border-white/10 shadow-lg;
+ }
+
+ .neon-glow {
+ box-shadow: 0 0 15px rgba(var(--color-glow), 0.5);
+ }
+
+ .text-balance {
+ text-wrap: balance;
+ }
+}
+
+/* Custom utilities */
+@layer utilities {
+ .grid-auto-fit {
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ }
+
+ .bg-mesh-gradient {
+ background-image:
+ radial-gradient(at 40% 20%, rgba(var(--color-primary-600), 0.15) 0px, transparent 50%),
+ radial-gradient(at 80% 70%, rgba(var(--color-primary-700), 0.2) 0px, transparent 50%),
+ radial-gradient(at 10% 80%, rgba(var(--color-primary-800), 0.1) 0px, transparent 50%);
+ background-attachment: fixed;
+ }
+
+ .text-shadow-sm {
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ }
+
+ .blur-behind {
+ backdrop-filter: blur(8px);
+ }
+
+ .text-gradient {
+ background-clip: text;
+ -webkit-background-clip: text;
+ color: transparent;
+ }
+
+ .scrollbar-hidden {
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+ }
+
+ .scrollbar-hidden::-webkit-scrollbar {
+ display: none;
+ }
+}
+
+.loader-content {
+ opacity: 0;
+ transition: opacity 0.3s ease-in-out;
+}
+
+.loader-content.loaded {
+ opacity: 1;
+}
+
+/* MDX Content Styling */
+.mdx-content {
+ font-size: 1.125rem;
+ line-height: 1.75;
+ color: rgb(var(--color-text-muted));
+}
+
+.mdx-content h1,
+.mdx-content h2,
+.mdx-content h3,
+.mdx-content h4,
+.mdx-content h5,
+.mdx-content h6 {
+ margin-top: 2em;
+ margin-bottom: 0.75em;
+ color: rgb(var(--color-text));
+ font-weight: 700;
+ line-height: 1.2;
+}
+
+.mdx-content h1 {
+ font-size: 2.25rem;
+ margin-top: 0;
+ background: linear-gradient(to right, rgb(var(--color-primary-400)), rgb(var(--color-primary-300)));
+ -webkit-background-clip: text;
+ background-clip: text;
+ color: transparent;
+}
+
+.mdx-content h2 {
+ font-size: 1.875rem;
+ padding-bottom: 0.5rem;
+ border-bottom: 1px solid rgba(var(--color-primary-700), 0.3);
+ color: rgb(var(--color-primary-300));
+}
+
+.mdx-content h3 {
+ font-size: 1.5rem;
+ color: rgb(var(--color-primary-400));
+}
+
+.mdx-content h4 {
+ font-size: 1.25rem;
+}
+
+.mdx-content p {
+ margin-bottom: 1.5rem;
+}
+
+.mdx-content a {
+ color: rgb(var(--color-primary-400));
+ text-decoration: none;
+ transition: color 0.2s;
+}
+
+.mdx-content a:hover {
+ color: rgb(var(--color-primary-300));
+}
+
+.mdx-content ul,
+.mdx-content ol {
+ margin-bottom: 1.5rem;
+ padding-left: 1.5rem;
+}
+
+.mdx-content ul {
+ list-style-type: disc;
+}
+
+.mdx-content ol {
+ list-style-type: decimal;
+}
+
+.mdx-content li {
+ margin-bottom: 0.5rem;
+}
+
+.mdx-content img {
+ max-width: 100%;
+ height: auto;
+ border-radius: 0.5rem;
+ margin: 2rem 0;
+ border: 1px solid rgba(var(--color-primary-800), 0.3);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
+}
+
+.mdx-content hr {
+ margin: 3rem 0;
+ border: 0;
+ height: 1px;
+ background: linear-gradient(
+ to right,
+ transparent,
+ rgba(var(--color-primary-500), 0.5),
+ transparent
+ );
+}
+
+.mdx-content code:not([class*="language-"]) {
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ color: rgb(var(--color-primary-300));
+ background-color: rgba(var(--color-code-bg), 0.4);
+ padding: 0.2em 0.4em;
+ border-radius: 0.25rem;
+ font-size: 0.875em;
+ border: 1px solid rgba(var(--color-primary-700), 0.2);
+}
+
+/* MDX Content Styling - updated code blocks with line numbers */
+.mdx-content pre {
+ margin: 1.5rem 0;
+ border-radius: 0.5rem;
+ overflow: hidden;
+ background-color: rgb(var(--color-code-bg)) !important;
+ border: 1px solid rgba(var(--color-primary-700), 0.3);
+ padding: 0 !important;
+ position: relative;
+}
+
+.mdx-content pre code {
+ display: block;
+ border-radius: 0;
+ /* Increase left padding to make room for line numbers */
+ margin-left: 3.5rem;
+ padding: 1.25rem 1rem 1.25rem 4.5rem !important;
+ font-size: 0.9rem !important;
+ line-height: 1.5 !important;
+ tab-size: 2;
+ background-color: transparent !important; /* Keep background transparent to show themed background */
+}
+
+/* Line numbers styles - adjusted width and positioning */
+.with-line-numbers {
+ counter-reset: line;
+ position: relative;
+}
+
+.line-numbers-rows {
+ position: absolute;
+ top: 1.25rem;
+ left: 0;
+ bottom: 1.25rem;
+ /* Increase width to prevent overlap */
+ width: 3.5rem;
+ counter-reset: line;
+ user-select: none;
+ pointer-events: none;
+ /* Add right border for visual separation */
+ border-right: 1px solid rgba(var(--color-primary-700), 0.2);
+ padding-right: 0.5rem;
+ text-align: right;
+}
+
+.line-numbers-rows > span {
+ display: block;
+ counter-increment: line;
+ height: 1.5rem;
+ line-height: 1.5rem;
+ color: rgba(var(--color-primary-600), 0.6);
+ font-size: 0.85rem;
+ padding-right: 1rem;
+}
+
+.line-numbers-rows > span::before {
+ content: counter(line);
+}
+
+/* Hover effect for line numbers */
+.mdx-content pre:hover .line-numbers-rows > span {
+ color: rgba(var(--color-primary-400), 0.7);
+}
+
+/* Code block container enhancements */
+.code-content {
+ position: relative;
+ overflow: auto;
+ /* Add gutter area background */
+ background-image: linear-gradient(
+ to right,
+ rgba(var(--color-primary-900), 0.2) 0,
+ rgba(var(--color-primary-900), 0.2) 3.5rem,
+ transparent 3.5rem
+ );
+}
+
+/* Code block headers */
+.code-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.75rem 1rem;
+ background-color: rgba(var(--color-primary-900), 0.6);
+ border-bottom: 1px solid rgba(var(--color-primary-700), 0.4);
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+}
+
+.code-dots {
+ display: flex;
+ gap: 6px;
+}
+
+.code-dot {
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ background-color: rgba(255, 255, 255, 0.2);
+}
+
+.code-dot:nth-child(1) {
+ background-color: rgba(239, 68, 68, 0.7);
+}
+
+.code-dot:nth-child(2) {
+ background-color: rgba(245, 158, 11, 0.7);
+}
+
+.code-dot:nth-child(3) {
+ background-color: rgba(34, 197, 94, 0.7);
+}
+
+.code-language {
+ font-size: 0.75rem;
+ font-weight: 600;
+ color: rgb(var(--color-primary-300));
+ margin-left: auto;
+ margin-right: 1rem;
+ padding: 0.25rem 0.5rem;
+ background-color: rgba(var(--color-primary-800), 0.5);
+ border-radius: 0.25rem;
+ border: 1px solid rgba(var(--color-primary-600), 0.3);
+}
+
+.copy-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0.35rem 0.5rem;
+ border-radius: 0.25rem;
+ background-color: rgba(var(--color-primary-800), 0.5);
+ color: rgb(var(--color-primary-300));
+ border: 1px solid rgba(var(--color-primary-600), 0.3);
+ font-size: 0.75rem;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.copy-button:hover {
+ background-color: rgba(var(--color-primary-700), 0.5);
+}
+
+.copy-button.copied {
+ background-color: rgba(22, 163, 74, 0.5);
+ color: rgb(134, 239, 172);
+ border-color: rgba(34, 197, 94, 0.5);
+}
+
+/* Code block content */
+.code-content {
+ position: relative;
+ overflow-x: auto;
+}
+
+/* Line highlight effect */
+.mdx-content code .line-highlight {
+ background-color: rgba(var(--color-primary-800), 0.3);
+ display: block;
+ margin: 0 -1.25rem;
+ padding: 0 1.25rem;
+}
+
+/* Code block container with enhanced styling */
+.code-block-container {
+ margin: 1.5rem 0;
+ border-radius: 0.5rem;
+ overflow: hidden;
+ background-color: rgb(var(--color-code-bg));
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
+ transform: translateZ(0);
+ transition: all 0.3s ease;
+}
+
+.code-block-container:hover {
+ box-shadow: 0 8px 30px rgba(var(--color-primary-900), 0.3);
+ transform: translateY(-2px);
+}
+
+.mdx-content pre {
+ margin: 1.5rem 0;
+ border-radius: 0.5rem;
+ overflow-x: auto;
+ background-color: rgb(var(--color-code-bg)) !important;
+ border: 1px solid rgba(var(--color-primary-700), 0.3);
+}
+
+.mdx-content pre code {
+ border-radius: 0.375rem;
+ padding: 1.25rem !important;
+ font-size: 0.9rem !important;
+ line-height: 1.5 !important;
+ tab-size: 2;
+ background-color: transparent !important;
+ color: rgb(235, 235, 235) !important;
+}
+
+/* Update the code block wrapper styles */
+.code-block-container {
+ margin: 1.5rem 0;
+ border-radius: 0.5rem;
+ overflow: hidden;
+ background-color: rgb(var(--color-code-bg));
+ border: 1px solid rgba(var(--color-primary-700), 0.3);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
+}
+
+.code-block-container pre {
+ margin: 0 !important;
+ border: none !important;
+}
+
+/* Custom code block wrapper styling */
+.code-block-wrapper {
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
+ border: 1px solid rgba(var(--color-primary-700), 0.4);
+ border-radius: 0.5rem;
+}
+
+.code-header {
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+}
+
+/* Custom styles for specific elements */
+.mdx-content .note {
+ background-color: rgba(var(--color-primary-900), 0.15);
+ border-left: 4px solid rgb(var(--color-primary-400));
+ padding: 1rem;
+ margin: 1.5rem 0;
+ border-radius: 0 0.5rem 0.5rem 0;
+}
+
+.mdx-content .warning {
+ background-color: rgba(255, 170, 0, 0.15);
+ border-left: 4px solid rgb(255, 170, 0);
+ padding: 1rem;
+ margin: 1.5rem 0;
+ border-radius: 0 0.5rem 0.5rem 0;
+}
+
+/* Show error messages properly */
+.mdx-content .error-message {
+ padding: 1rem;
+ margin: 1rem 0;
+ background-color: rgba(220, 38, 38, 0.1);
+ border-left: 4px solid rgba(220, 38, 38, 0.8);
+ color: rgb(var(--color-text));
+ border-radius: 0 0.5rem 0.5rem 0;
+}
diff --git a/src/types/blog.ts b/src/types/blog.ts
new file mode 100644
index 0000000..0c41a9c
--- /dev/null
+++ b/src/types/blog.ts
@@ -0,0 +1,25 @@
+export interface BlogPostMetadata {
+ title: string;
+ date: string;
+ slug: string;
+ description?: string;
+ content?: string;
+ image?: string;
+ author?: string;
+ readingTime?: string;
+ tags?: string[];
+ categories?: string[];
+ featured?: boolean;
+}
+
+export interface BlogPost {
+ content: string;
+ metadata: BlogPostMetadata;
+ slug: string; // Make slug required and consistent
+}
+
+export interface BlogCategory {
+ name: string;
+ count: number;
+ slug: string;
+}
diff --git a/src/types/fivem.ts b/src/types/fivem.ts
new file mode 100644
index 0000000..9a23ef8
--- /dev/null
+++ b/src/types/fivem.ts
@@ -0,0 +1,24 @@
+export interface FivemScript {
+ id: string;
+ title: string;
+ description: string;
+ longDescription: string;
+ price: string;
+ framework: "ESX" | "QBCore" | "Both" | "Standalone";
+ status: "Released" | "In Development" | "Coming Soon";
+ version: string;
+ lastUpdated: string;
+ features: string[];
+ images: string[];
+ video?: string;
+ tags: string[];
+ links: {
+ demo?: string;
+ purchase?: string;
+ github?: string;
+ documentation?: string;
+ slug: string;
+ };
+ requirements?: string[];
+ installation?: string;
+}
diff --git a/src/types/links.ts b/src/types/links.ts
new file mode 100644
index 0000000..302eb29
--- /dev/null
+++ b/src/types/links.ts
@@ -0,0 +1,72 @@
+export interface LinkItem {
+ id: string;
+ title: string;
+ url: string;
+ icon?: string; // Icon name for React Icons
+ description?: string;
+ color?: string; // Tailwind color class
+ featured?: boolean;
+ category?: string;
+ new?: boolean; // Flag to highlight new links
+}
+
+export interface SocialLink extends LinkItem {
+ username?: string;
+ followers?: number; // Optional follower count for social platforms
+}
+
+export interface LinkCategory {
+ id: string;
+ name: string;
+ description?: string;
+ icon?: string;
+ color?: string; // Tailwind color class
+ links: LinkItem[];
+}
+
+export interface DiscordStatus {
+ id: string;
+ username: string;
+ discriminator: string;
+ avatar?: string;
+ status: 'online' | 'idle' | 'dnd' | 'offline';
+ activity?: {
+ name: string;
+ type: string;
+ url?: string;
+ details?: string;
+ state?: string;
+ applicationId?: string;
+ timestamps?: {
+ start?: number;
+ end?: number;
+ };
+ };
+}
+
+export interface LinkHubProfile {
+ name: string;
+ avatar: string;
+ title: string;
+ bio: string;
+ discord?: DiscordStatus;
+ featuredLinks: LinkItem[];
+ socialLinks: SocialLink[];
+ categories: LinkCategory[];
+}
+
+export interface Track {
+ id: string;
+ title: string;
+ artist: string;
+ albumCover: string;
+ audioFile?: string;
+ spotifyUrl?: string;
+ genre?: string; // Add genre field
+}
+
+export interface Playlist {
+ title: string;
+ description: string;
+ tracks: Track[];
+}
diff --git a/src/types/project.ts b/src/types/project.ts
index 73b23b0..f71f607 100644
--- a/src/types/project.ts
+++ b/src/types/project.ts
@@ -2,11 +2,34 @@ export interface Project {
id: string;
title: string;
description: string;
+ longDescription?: string;
images: string[];
tags: string[];
links: {
demo?: string;
github?: string;
+ docs?: string;
+ slug?: string;
};
- featured: boolean;
-}
\ No newline at end of file
+ featured?: boolean;
+ technologies?: {
+ name: string;
+ description?: string;
+ }[];
+ challenges?: string[];
+ solutions?: string[];
+ keyFeatures?: string[];
+ date?: string;
+ role?: string;
+ teamSize?: number;
+ testimonials?: Testimonial[];
+ testimonial?: Testimonial;
+}
+
+export interface Testimonial {
+ quote: string;
+ author: string;
+ position?: string;
+ company?: string;
+ avatar?: string;
+}
\ No newline at end of file
diff --git a/src/types/referrals.ts b/src/types/referrals.ts
new file mode 100644
index 0000000..f7259dc
--- /dev/null
+++ b/src/types/referrals.ts
@@ -0,0 +1,25 @@
+export interface Referral {
+ id: string;
+ title: string;
+ description: string;
+ company: string;
+ bannerImage?: string;
+ color?: string; // Tailwind color class
+ url: string;
+ code?: string; // Promo/referral code
+ discount?: string; // Description of the discount (e.g., "20% off first month")
+ benefits?: string[]; // List of benefits for using this referral
+ featured?: boolean;
+ category: string;
+ categoryName: string; // Display name for the category
+ expiryDate?: string; // ISO date string for when the referral expires
+ new?: boolean; // Flag for highlighting new referrals
+}
+
+export interface ReferralCategory {
+ id: string;
+ name: string;
+ description?: string;
+ icon?: string;
+ color?: string; // Tailwind color class
+}
diff --git a/src/types/skills.ts b/src/types/skills.ts
new file mode 100644
index 0000000..5af9c44
--- /dev/null
+++ b/src/types/skills.ts
@@ -0,0 +1,15 @@
+export interface Skill {
+ name: string;
+ icon?: string; // Icon name for the TechIcon component
+ level?: number; // 1-5 scale
+ description?: string;
+ url?: string; // URL to the technology's documentation
+}
+
+export interface SkillCategory {
+ name: string;
+ description: string;
+ skills: Skill[];
+ icon: string; // React Icons name
+ color: string; // Tailwind color class for the category
+}