Full Code of dostonnabotov/quicksnip for AI

main 40b4fc216b46 cached
119 files
152.8 KB
45.0k tokens
152 symbols
1 requests
Download .txt
Repository: dostonnabotov/quicksnip
Branch: main
Commit: 40b4fc216b46
Files: 119
Total size: 152.8 KB

Directory structure:
gitextract_nak_pvkj/

├── .gitignore
├── .source/
│   └── index.ts
├── CONTRIBUTING.md
├── README.md
├── components.json
├── content/
│   └── docs/
│       ├── comparison.mdx
│       ├── contributing/
│       │   ├── adding-snippets.mdx
│       │   ├── how-to-contribute.mdx
│       │   ├── meta.json
│       │   ├── modifying-snippets.mdx
│       │   └── third-party-apps.mdx
│       ├── extensions/
│       │   ├── meta.json
│       │   ├── quicksnip-cli.mdx
│       │   ├── quicksnip-raycast.mdx
│       │   ├── quicksnip-vscode.mdx
│       │   └── quicksnip.mdx
│       ├── index.mdx
│       ├── installation.mdx
│       └── meta.json
├── eslint.config.mjs
├── mdx-components.tsx
├── next.config.ts
├── package.json
├── postcss.config.mjs
├── public/
│   └── data/
│       └── snippets/
│           ├── README.md
│           ├── all.json
│           ├── array-manipulation/
│           │   └── shuffle-array.json
│           ├── data-handling/
│           │   └── add-localstorage.json
│           ├── number-formatting/
│           │   └── number-to-currency.json
│           └── string-manipulation/
│               ├── reverse-string.json
│               ├── string-to-camel-case.json
│               ├── string-to-param-case.json
│               ├── string-to-pascal-case.json
│               ├── string-to-snake-case.json
│               ├── string-to-title-case.json
│               └── truncate-sring.json
├── scripts/
│   └── merge-snippets.ts
├── snippets/
│   ├── README.md
│   ├── array-manipulation/
│   │   ├── shuffle-array.json
│   │   └── shuffle-array.md
│   ├── data-handling/
│   │   ├── add-localstorage.json
│   │   └── add-localstorage.md
│   ├── number-formatting/
│   │   ├── number-to-currency.json
│   │   └── number-to-currency.md
│   └── string-manipulation/
│       ├── reverse-string.json
│       ├── reverse-string.md
│       ├── string-to-camel-case.json
│       ├── string-to-camel-case.md
│       ├── string-to-param-case.json
│       ├── string-to-param-case.md
│       ├── string-to-pascal-case.json
│       ├── string-to-pascal-case.md
│       ├── string-to-snake-case.json
│       ├── string-to-snake-case.md
│       ├── string-to-title-case.json
│       ├── string-to-title-case.md
│       ├── truncate-sring.json
│       └── truncate-sring.md
├── source.config.ts
├── src/
│   ├── app/
│   │   ├── api/
│   │   │   └── search/
│   │   │       └── route.ts
│   │   ├── community/
│   │   │   └── page.tsx
│   │   ├── contributing/
│   │   │   └── page.tsx
│   │   ├── extensions/
│   │   │   └── page.tsx
│   │   ├── globals.css
│   │   ├── guide/
│   │   │   ├── [[...slug]]/
│   │   │   │   └── page.tsx
│   │   │   └── layout.tsx
│   │   ├── layout.config.tsx
│   │   ├── layout.tsx
│   │   ├── page.tsx
│   │   └── snippets/
│   │       ├── [category]/
│   │       │   ├── [snippet]/
│   │       │   │   └── page.tsx
│   │       │   └── page.tsx
│   │       ├── layout.tsx
│   │       └── page.tsx
│   ├── components/
│   │   ├── layouts/
│   │   │   ├── available-for.tsx
│   │   │   ├── code-preview.tsx
│   │   │   ├── features.tsx
│   │   │   ├── footer.tsx
│   │   │   ├── header.tsx
│   │   │   ├── hero.tsx
│   │   │   ├── snippet-header.tsx
│   │   │   ├── snippet-list.tsx
│   │   │   ├── snippet-sidebar.tsx
│   │   │   └── sponsor.tsx
│   │   └── ui/
│   │       ├── accordion.tsx
│   │       ├── aspect-ratio.tsx
│   │       ├── avatar.tsx
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       ├── dark-mode-switch.tsx
│   │       ├── extension-item.tsx
│   │       ├── input-group.tsx
│   │       ├── input.tsx
│   │       ├── label.tsx
│   │       ├── language-icons.tsx
│   │       ├── logo.tsx
│   │       ├── navigation-menu.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── sidebar.tsx
│   │       ├── skeleton.tsx
│   │       ├── snippet-detail.tsx
│   │       ├── snippet-item.tsx
│   │       ├── snippet-search.tsx
│   │       ├── switch.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       └── tooltip.tsx
│   ├── data/
│   │   ├── extensions.ts
│   │   ├── meta.ts
│   │   └── yt-videos.ts
│   ├── hooks/
│   │   ├── use-fetch.ts
│   │   └── use-mobile.ts
│   ├── lib/
│   │   ├── source.ts
│   │   └── utils.ts
│   ├── store/
│   │   └── useSnippetsStore.ts
│   └── types/
│       └── index.ts
├── tailwind.config.mjs
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

_pagefind/

================================================
FILE: .source/index.ts
================================================
// @ts-nocheck -- skip type checking
import * as docs_10 from "../content/docs/extensions/quicksnip.mdx?collection=docs&hash=1765841507830"
import * as docs_9 from "../content/docs/extensions/quicksnip-vscode.mdx?collection=docs&hash=1765841507830"
import * as docs_8 from "../content/docs/extensions/quicksnip-raycast.mdx?collection=docs&hash=1765841507830"
import * as docs_7 from "../content/docs/extensions/quicksnip-cli.mdx?collection=docs&hash=1765841507830"
import * as docs_6 from "../content/docs/contributing/third-party-apps.mdx?collection=docs&hash=1765841507830"
import * as docs_5 from "../content/docs/contributing/modifying-snippets.mdx?collection=docs&hash=1765841507830"
import * as docs_4 from "../content/docs/contributing/how-to-contribute.mdx?collection=docs&hash=1765841507830"
import * as docs_3 from "../content/docs/contributing/adding-snippets.mdx?collection=docs&hash=1765841507830"
import * as docs_2 from "../content/docs/installation.mdx?collection=docs&hash=1765841507830"
import * as docs_1 from "../content/docs/index.mdx?collection=docs&hash=1765841507830"
import * as docs_0 from "../content/docs/comparison.mdx?collection=docs&hash=1765841507830"
import { _runtime } from "fumadocs-mdx/runtime/next"
import * as _source from "../source.config"
export const docs = _runtime.docs<typeof _source.docs>([{ info: {"path":"comparison.mdx","fullPath":"content/docs/comparison.mdx"}, data: docs_0 }, { info: {"path":"index.mdx","fullPath":"content/docs/index.mdx"}, data: docs_1 }, { info: {"path":"installation.mdx","fullPath":"content/docs/installation.mdx"}, data: docs_2 }, { info: {"path":"contributing/adding-snippets.mdx","fullPath":"content/docs/contributing/adding-snippets.mdx"}, data: docs_3 }, { info: {"path":"contributing/how-to-contribute.mdx","fullPath":"content/docs/contributing/how-to-contribute.mdx"}, data: docs_4 }, { info: {"path":"contributing/modifying-snippets.mdx","fullPath":"content/docs/contributing/modifying-snippets.mdx"}, data: docs_5 }, { info: {"path":"contributing/third-party-apps.mdx","fullPath":"content/docs/contributing/third-party-apps.mdx"}, data: docs_6 }, { info: {"path":"extensions/quicksnip-cli.mdx","fullPath":"content/docs/extensions/quicksnip-cli.mdx"}, data: docs_7 }, { info: {"path":"extensions/quicksnip-raycast.mdx","fullPath":"content/docs/extensions/quicksnip-raycast.mdx"}, data: docs_8 }, { info: {"path":"extensions/quicksnip-vscode.mdx","fullPath":"content/docs/extensions/quicksnip-vscode.mdx"}, data: docs_9 }, { info: {"path":"extensions/quicksnip.mdx","fullPath":"content/docs/extensions/quicksnip.mdx"}, data: docs_10 }], [{"info":{"path":"meta.json","fullPath":"content/docs/meta.json"},"data":{"title":"Docs","pages":["---Introduction---","index.mdx","installation.mdx","comparison.mdx","---Extensions---","...extensions","---Contributing---","...contributing"],"root":true}}, {"info":{"path":"contributing/meta.json","fullPath":"content/docs/contributing/meta.json"},"data":{"title":"Contributing","pages":["how-to-contribute","adding-snippets","modifying-snippets","third-party-apps"]}}, {"info":{"path":"extensions/meta.json","fullPath":"content/docs/extensions/meta.json"},"data":{"title":"Extensions","pages":["quicksnip","quicksnip-cli","quicksnip-vscode","quicksnip-raycast"]}}])

================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to QuickSnip

> THIS FILE NEEDS TO BE REWORKED. IT'S IMPORTED FROM PREVIOUS VERSION FOR TESTING.

Hey there! 👋 First off, thanks for taking the time to contribute! ❤️

You can contribute in two main ways:

- **Improving the code** (like fixing bugs or adding cool new features)
- **Adding new code snippets** (or improving the existing ones!)

---

## Improving the code

### How to report bugs

If you spot a bug in the codebase or issues with the documentation, please open up a [GitHub issue](https://github.com/technoph1le/quicksnip/issues) detailing the problem before creating a PR. Once confirmed with maintainers, you can then create a PR.

### How to propose new features

If you are interested in proposing new features, please open up a new [GitHub discussion](https://github.com/technoph1le/quicksnip/discussions) with details for the proposed feature.

Please do not create a PR for a new feature without first discussing it with the maintainers. If you create a PR for a new feature without discussing it first, then your PR will be closed.

---

## Snippets Guidelines

### Snippet Tags

- Tags must describe the snippet with simple word.

Here's an example:

```md
---
title: Convert Number to Currency
description: Converts a number to a currency format with a specific locale.
author: axorax
tags: number,currency
---
```

**Do not use generic keywords or the language itself as a tag `utility` or `javascript`!**

### Snippet Format

**All** snippets should follow the following structure:

- A `code` segment, containing a function with the actual snippet functionnality
- An `example` segement, containing one or more examples of use

Example in javascript:

```js
function example(x) {
  return x * 2;
}

// Usage:
example(5); // Returns: 10
```

If your function doesn't return anything just show how to use it. If the result of your function is too complicated to be expressed in a single comment, your snippet is probably too complex to begin with.

### Snippet boundaries

To ensure your snippet isn’t refused, consider these questions:

- **Does the standard library of my language provide an easy way of doing this ?**
- **Does that snippet not have a real, and practical use case ?**
- **Could it be split into separate parts to be better understood ?**

If any answer is yes, then your snippet will most likely get rejected.

---

## Adding Snippets

### Adding a New Snippet

1. **Ensure your snippet match [guidelines](#snippets-guidelines)**

2. **Navigate to the relevant folder:**

   - Go to the `/snippets` folder in the root directory.
   - Locate the folder for the programming language of your snippet, such as `javascript` or `python`.

3. **Choose the correct category:**

   - Within the language folder, find the relevant category folder for your snippet.
   - If no suitable category exists, refer to [Adding a New Category](#adding-a-new-category).

4. **Create a markdown file:**

   - Create a new file with a `.md` extension.
   - Name the file appropriately, keeping it descriptive and concise.

5. **Add your snippet:**

   - Use the following format to structure your snippet:

````md
---
title: Name of the snippet
description: A short explanation of what the snippet does
tags: tag1, tag2, tag3
author: your-github-username
---

```lang
// Your code here
```
````

Here’s an example for JavaScript:

````md
---
title: Format Date
description: Formats a date in 'YYYY-MM-DD' format.
author: technoph1le
tags: date,format
---

```js
const formatDate = (date) => date.toISOString().split("T")[0];

// Usage:
console.log(formatDate(new Date())); // Output: '2024-12-10'
```
````

6. **Use syntax highlighting:**

   - Enclose your code with triple backticks (```).
   - Specify the language after the first set of backticks for syntax highlighting.

7. **Test your snippet:**

   - Ensure your code runs as expected. \
      To test that your snippets are formatted correctly use the `snippets:check` script:

     ```
     $ npm run snippets:check
     ```

     It will return nothing if they are well formatted, otherwise it will tell you what the error is.

     ***

     To preview the snippets, start the vite server using:

     ```
     $ npm run dev
     ```

     It will use HMR to update the snippets in the `/public` folder, making them available to the frontend.

Expected file structure:

```md
/snippets
|- language
|- category-name
|- your-snippet-here.md
```

> Please do **NOT** add or edit anything in `/public` folder. It will be used for consolidating snippets.

### Editing a Existing Snippet

If you’d like to refine or improve an existing snippet:

1. **Add a `contributors` field:**

   - Include your GitHub username under the `contributors` field in the metadata section.

````md
---
title: Name of the snippet
description: A short explanation of what the snippet does
tags: tag1, tag2, tag3
author: original-author
contributors: your-github-username
---

```
Updated code here
```
````

2. **Credit all contributors:**

   - If contributors already exist, add your username separated by a comma

```md
contributors: contributor1, contributor2, your-github-username
```

3. **Document changes:**

   - Clearly indicate what you updated and why in your pull request description.

> We want to make sure that original author and contributor(s) are credited for their work.

### Adding a New Category

If your snippet doesn’t fit into any existing category, you can create a new one! Just make sure it’s unique and doesn’t overlap with others (e.g., don’t create separate categories for “Date” and “Time” when “Date and Time” works).

1. **Create a new category folder:**

   - In the relevant language directory, add a new folder.
   - Use a lowercase name with hyphens for separation (e.g., `file-handling`).

2. **Add snippets:**

   - Follow the [Adding a New Snippet](#adding-a-new-snippet) instructions.

Example structure:

```md
/snippets
|- python
|- file-handling
|- list-manipulation
|- ....
```

### Adding a New Language

If you want to introduce a new programming language, here's how to do it:

1. **Create a language folder:**

   - Add a new folder under the `snippets` directory.
   - Name it after the language in lowercase (e.g., `go`, `ruby`).

2. **Add categories and snippets:**

   - Follow the [Adding a New Snippet](#adding-a-new-snippet) and [Adding a New Category](#adding-a-new-category) guidelines.

3. **Include an icon:**

   - Add an `icon.svg` file (50x50px) in the same language folder.
   - Use tools like [Resize SVG](https://www.iloveimg.com/resize-image/resize-svg) to ensure the correct size.

4. **Double-check your work:**

   - Verify that everything is structured correctly and displays as intended.

---

## Testing Snippets

To test that your snippets are formatted correctly use the following script:

```
$ npm run snippets:check
```

It will return nothing if they are well formatted, otherwise it will tell you what the error is.

---

To preview the snippets, you need to consolidate them, use the following script:

```
$ npm run snippets:consolidate
```

It will update the snippets in the `/public` folder, making them available to the frontend.

## Final Notes

Whether you’re fixing a tiny typo, writing a new snippet, or dreaming up big features, every bit counts! 🛠️

If you have any questions or need help, feel free to open a new [GitHub discussion](https://github.com/technoph1le/quicksnip/discussions).

Happy coding! 💻✨


================================================
FILE: README.md
================================================
# QuickSnip

QuickSnip is an open-source tool designed for developers who want to organize, search, and share code snippets across various programming languages. It provides a centralized platform for managing handy snippets. Built with love and powered by an awesome community. 🚀

<div>
<a href="https://youtu.be/BhRi7fJzPgk?si=z1sVXU7uRS0bkSEt" target="_blank">
  <img src="https://img.shields.io/static/v1?label=&message=Watch%20on%20YouTube&labelColor=FFFFFF&color=FF0000&style=for-the-badge&logo=youtube&logoColor=FF0000" alt="Watch on YouTube">
</a>
<div>

<br>

![Website preview](/preview.png)

## How to Contribute

Want to help make QuickSnip even better? You can contribute by:

- **Improving the Code**: Fix bugs, suggest new features, or optimize the project.
- **Adding New Snippets**: Share your favorite snippets to grow the database.

Check out the [CONTRIBUTING.md](/CONTRIBUTING.md) file for detailed contribution guidelines.

## General Rules

To keep everything smooth and consistent, please:

- [x] Follow the project’s style and contribution rules.
- [x] Be kind and respectful to others.
- [x] If you’re unsure, ask questions.

Following these rules helps us build an awesome community. 🚀

## Third-Party

We love to see the creativity of our community! If you build something using QuickSnip, let us know. Here are some featured examples:

- ⚡️ [**Raycast Extension**](https://www.raycast.com/anders_morille/quicksnip): An extension to browse and use QuickSnip snippets directly from Raycast. (Thanks to the creator: [@lille-morille](https://github.com/lille-morille))

If you’d like to create your own extension or tool:

- [x] Please ensure it adheres to our project’s licensing and attribution requirements.
- [x] Please link to the [original QuickSnip repository](https://github.com/technoph1le/quicksnip) in your extension/tool to attribute the source.
- [x] Share your work with us — we’d be thrilled to feature it!

## Project Vision

For a detailed look into our goals, future direction, and aspirations, see the [VISION.md](/VISION.md) file.

## License

QuickSnip is licensed under the [MIT License](/LICENSE). Feel free to use and share it as you like.

<a href="https://www.producthunt.com/products/quicksnip" target="_blank" style="text-decoration: none;">
  <img src="https://img.shields.io/static/v1?label=&message=Leave%20a%20Review&labelColor=FFFFFF&color=DA552F&style=for-the-badge&logo=product-hunt&logoColor=DA552F" alt="Leave a Review">
</a>


================================================
FILE: components.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "",
    "css": "src/app/globals.css",
    "baseColor": "neutral",
    "cssVariables": true,
    "prefix": ""
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  },
  "iconLibrary": "lucide"
}

================================================
FILE: content/docs/comparison.mdx
================================================
---
title: Comparison
description: Hello World
---

## This is title

This is description


================================================
FILE: content/docs/contributing/adding-snippets.mdx
================================================
---
title: Adding snippets
description: Hello World
---

## This is title

This is description


================================================
FILE: content/docs/contributing/how-to-contribute.mdx
================================================
---
title: How to contribute
description: Hello World
---

## This is title

This is description


================================================
FILE: content/docs/contributing/meta.json
================================================
{
  "title": "Contributing",
  "pages": [
    "how-to-contribute",
    "adding-snippets",
    "modifying-snippets",
    "third-party-apps"
  ]
}


================================================
FILE: content/docs/contributing/modifying-snippets.mdx
================================================
---
title: Modifying snippets
description: Hello World
---

## This is title

This is description


================================================
FILE: content/docs/contributing/third-party-apps.mdx
================================================
---
title: Third-party apps
description: Hello World
---

## How to add an extension

This is description


================================================
FILE: content/docs/extensions/meta.json
================================================
{
  "title": "Extensions",
  "pages": [
    "quicksnip",
    "quicksnip-cli",
    "quicksnip-vscode",
    "quicksnip-raycast"
  ]
}


================================================
FILE: content/docs/extensions/quicksnip-cli.mdx
================================================
---
title: QuickSnip CLI
description: Hello World
---

## This is title

This is description


================================================
FILE: content/docs/extensions/quicksnip-raycast.mdx
================================================
---
title: QuickSnip Raycast
description: Hello World
---

## This is title

This is description


================================================
FILE: content/docs/extensions/quicksnip-vscode.mdx
================================================
---
title: QuickSnip VS Code
description: Hello World
---

## This is title

This is description


================================================
FILE: content/docs/extensions/quicksnip.mdx
================================================
---
title: QuickSnip
description: Hello World
---

## This is title

This is description


================================================
FILE: content/docs/index.mdx
================================================
---
title: Get Started
description: Hello World
---

## This is title

This is description


================================================
FILE: content/docs/installation.mdx
================================================
---
title: Installation
description: Hello World
---

## This is title

This is description


================================================
FILE: content/docs/meta.json
================================================
{
  "title": "Docs",
  "root": true,
  "pages": [
    "---Introduction---",
    "index.mdx",
    "installation.mdx",
    "comparison.mdx",
    "---Extensions---",
    "...extensions",
    "---Contributing---",
    "...contributing"
  ]
}


================================================
FILE: eslint.config.mjs
================================================
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
  baseDirectory: __dirname,
});

const eslintConfig = [
  ...compat.extends("next/core-web-vitals", "next/typescript"),
];

export default eslintConfig;


================================================
FILE: mdx-components.tsx
================================================
import defaultMdxComponents from "fumadocs-ui/mdx";
import type { MDXComponents } from "mdx/types";

export function getMDXComponents(components?: MDXComponents): MDXComponents {
  return {
    ...defaultMdxComponents,
    ...components,
  };
}


================================================
FILE: next.config.ts
================================================
import { createMDX } from "fumadocs-mdx/next";

const withMDX = createMDX();

/** @type {import('next').NextConfig} */
const config = {
  reactStrictMode: true,
  images: {
    remotePatterns: [
      new URL("https://img.youtube.com/vi/**"),
      new URL("https://storage.ko-fi.com/cdn/**"),
    ],
  },
};

export default withMDX(config);


================================================
FILE: package.json
================================================
{
  "name": "quicksnip",
  "version": "2.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "prebuild": "tsx scripts/merge-snippets.ts",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "postinstall": "fumadocs-mdx"
  },
  "dependencies": {
    "@radix-ui/react-accordion": "^1.2.11",
    "@radix-ui/react-aspect-ratio": "^1.1.7",
    "@radix-ui/react-avatar": "^1.1.10",
    "@radix-ui/react-dialog": "^1.1.15",
    "@radix-ui/react-label": "^2.1.7",
    "@radix-ui/react-navigation-menu": "^1.2.13",
    "@radix-ui/react-select": "^2.2.6",
    "@radix-ui/react-separator": "^1.1.7",
    "@radix-ui/react-slot": "^1.2.3",
    "@radix-ui/react-switch": "^1.2.5",
    "@radix-ui/react-tabs": "^1.1.13",
    "@radix-ui/react-tooltip": "^1.2.8",
    "@types/mdx": "^2.0.13",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "framer-motion": "^12.23.12",
    "fumadocs-core": "^15.6.9",
    "fumadocs-mdx": "^12.0.3",
    "fumadocs-ui": "^15.6.9",
    "lucide-react": "^0.535.0",
    "next": "15.4.10",
    "radix-ui": "^1.4.3",
    "react": "^19.1.0",
    "react-dom": "^19.1.0",
    "react-icons": "^5.5.0",
    "react-markdown": "^10.1.0",
    "react-syntax-highlighter": "^15.6.6",
    "remark-gfm": "^4.0.1",
    "tailwind-merge": "^3.3.1",
    "zustand": "^5.0.8"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@tailwindcss/postcss": "^4",
    "@tailwindcss/typography": "^0.5.16",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "@types/react-syntax-highlighter": "^15.5.13",
    "eslint": "^9",
    "eslint-config-next": "15.4.5",
    "pagefind": "^1.3.0",
    "tailwindcss": "^4",
    "tsx": "^4.20.5",
    "tw-animate-css": "^1.3.6",
    "typescript": "^5"
  }
}


================================================
FILE: postcss.config.mjs
================================================
const config = {
  plugins: ["@tailwindcss/postcss"],
};

export default config;


================================================
FILE: public/data/snippets/README.md
================================================
# IMPORTANT

### This folder is read-only. Do NOT modify this folder!

================================================
FILE: public/data/snippets/all.json
================================================
[
  {
    "id": "shuffle-array",
    "category": "array-manipulation",
    "title": "Shuffle Array",
    "description": "Shuffles the items in an array.",
    "languages": [
      "js"
    ],
    "contributors": [
      "loxt-nixo"
    ],
    "tags": [
      "array",
      "shuffle",
      "random"
    ]
  },
  {
    "id": "add-localstorage",
    "category": "data-handling",
    "title": "Add Item to localStorage",
    "description": "Stores a value in localStorage under the given key.",
    "languages": [
      "js"
    ],
    "contributors": [
      "technoph1le"
    ],
    "tags": [
      "localStorage",
      "storage",
      "memory"
    ]
  },
  {
    "id": "number-to-currency",
    "category": "number-formatting",
    "title": "Convert Number to Currency",
    "description": "Converts a number to a currency format with a specific locale.",
    "languages": [
      "js"
    ],
    "contributors": [
      "axorax"
    ],
    "tags": [
      "number",
      "currency"
    ]
  },
  {
    "id": "reverse-string",
    "category": "string-manipulation",
    "title": "Reverse String",
    "description": "Reverses the characters in a string.",
    "languages": [
      "js",
      "cpp",
      "py"
    ],
    "contributors": [
      "technoph1le",
      "Vaibhav-kesarwani"
    ],
    "tags": [
      "string",
      "reverse"
    ]
  },
  {
    "id": "string-to-camel-case",
    "category": "string-manipulation",
    "title": "Convert String to Camel Case",
    "description": "Converts a given string into camelCase.",
    "languages": [
      "js",
      "java"
    ],
    "contributors": [
      "aumirza",
      "Mcbencrafter"
    ],
    "tags": [
      "string",
      "case"
    ]
  },
  {
    "id": "string-to-param-case",
    "category": "string-manipulation",
    "title": "Convert String to Param Case",
    "description": "Converts a given string into param-case.",
    "languages": [
      "js",
      "java"
    ],
    "contributors": [
      "aumirza",
      "Mcbencrafter"
    ],
    "tags": [
      "string",
      "case"
    ]
  },
  {
    "id": "string-to-pascal-case",
    "category": "string-manipulation",
    "title": "Convert String to Pascal Case",
    "description": "Converts a given string into PascalCase.",
    "languages": [
      "js",
      "java"
    ],
    "contributors": [
      "aumirza",
      "Mcbencrafter"
    ],
    "tags": [
      "string",
      "case"
    ]
  },
  {
    "id": "string-to-snake-case",
    "category": "string-manipulation",
    "title": "Convert String to Snake Case",
    "description": "Converts a given string into snake_case.",
    "languages": [
      "js",
      "java"
    ],
    "contributors": [
      "axorax",
      "Mcbencrafter"
    ],
    "tags": [
      "string",
      "case"
    ]
  },
  {
    "id": "string-to-title-case",
    "category": "string-manipulation",
    "title": "Convert String to Title Case",
    "description": "Converts a given string into Title Case.",
    "languages": [
      "js"
    ],
    "contributors": [
      "aumirza"
    ],
    "tags": [
      "string",
      "case"
    ]
  },
  {
    "id": "truncate-sring",
    "category": "string-manipulation",
    "title": "Truncate String",
    "description": "Truncates a string after a specified length.",
    "languages": [
      "js",
      "java",
      "py"
    ],
    "contributors": [
      "realvishalrana",
      "Mcbencrafter",
      "axorax",
      "MinerMinerMods"
    ],
    "tags": [
      "string",
      "truncate",
      "mask",
      "hide"
    ]
  }
]

================================================
FILE: public/data/snippets/array-manipulation/shuffle-array.json
================================================
{
  "id": "shuffle-array",
  "category": "array-manipulation",
  "title": "Shuffle Array",
  "description": "Shuffles the items in an array.",
  "languages": [
    "js"
  ],
  "contributors": [
    "loxt-nixo"
  ],
  "tags": [
    "array",
    "shuffle",
    "random"
  ],
  "snippets": {
    "js": "function shuffleArray(array) {\r\n    for (let i = array.length - 1; i >= 0; i--) {\r\n        const j = Math.floor(Math.random() * (i + 1));\r\n        [array[i], array[j]] = [array[j], array[i]];\r\n    }\r\n}\r\n\r\n// Usage:\r\nconst array = [1, 2, 3, 4, 5];\r\nshuffleArray(array); // Shuffles `array` in place"
  }
}

================================================
FILE: public/data/snippets/data-handling/add-localstorage.json
================================================
{
  "id": "add-localstorage",
  "category": "data-handling",
  "title": "Add Item to localStorage",
  "description": "Stores a value in localStorage under the given key.",
  "languages": [
    "js"
  ],
  "contributors": [
    "technoph1le"
  ],
  "tags": [
    "localStorage",
    "storage",
    "memory"
  ],
  "snippets": {
    "js": "const addToLocalStorage = (key, value) => {\r\n  localStorage.setItem(key, JSON.stringify(value));\r\n};\r\n\r\n// Usage:\r\naddToLocalStorage('user', { name: 'John', age: 30 });"
  }
}

================================================
FILE: public/data/snippets/number-formatting/number-to-currency.json
================================================
{
  "id": "number-to-currency",
  "category": "number-formatting",
  "title": "Convert Number to Currency",
  "description": "Converts a number to a currency format with a specific locale.",
  "languages": [
    "js"
  ],
  "contributors": [
    "axorax"
  ],
  "tags": [
    "number",
    "currency"
  ],
  "snippets": {
    "js": "const convertToCurrency = (num, locale = 'en-US', currency = 'USD') => {\r\n  return new Intl.NumberFormat(locale, {\r\n    style: 'currency',\r\n    currency: currency\r\n  }).format(num);\r\n};\r\n\r\n// Usage:\r\nconvertToCurrency(1234567.89); // Returns: '$1,234,567.89'\r\nconvertToCurrency(987654.32, 'de-DE', 'EUR'); // Returns: '987.654,32 €'"
  }
}

================================================
FILE: public/data/snippets/string-manipulation/reverse-string.json
================================================
{
  "id": "reverse-string",
  "category": "string-manipulation",
  "title": "Reverse String",
  "description": "Reverses the characters in a string.",
  "languages": [
    "js",
    "cpp",
    "py"
  ],
  "contributors": [
    "technoph1le",
    "Vaibhav-kesarwani"
  ],
  "tags": [
    "string",
    "reverse"
  ],
  "snippets": {
    "js": "const reverseString = (str) => str.split('').reverse().join('');\r\n\r\n// Usage:\r\nreverseString('hello'); // Returns: 'olleh'",
    "cpp": "#include <string>\r\n#include <algorithm>\r\n\r\nstd::string reverseString(const std::string& input) {\r\n    std::string reversed = input;\r\n    std::reverse(reversed.begin(), reversed.end());\r\n    return reversed;\r\n}\r\n\r\n// Usage:\r\nreverseString(\"quicksnip\"); // Returns: \"pinskciuq\"",
    "py": "def reverse_string(s:str) -> str:\r\n    return s[::-1]\r\n\r\n# Usage:\r\nreverse_string('hello') # Returns: 'olleh'"
  }
}

================================================
FILE: public/data/snippets/string-manipulation/string-to-camel-case.json
================================================
{
  "id": "string-to-camel-case",
  "category": "string-manipulation",
  "title": "Convert String to Camel Case",
  "description": "Converts a given string into camelCase.",
  "languages": [
    "js",
    "java"
  ],
  "contributors": [
    "aumirza",
    "Mcbencrafter"
  ],
  "tags": [
    "string",
    "case"
  ],
  "snippets": {
    "js": "function toCamelCase(str) {\r\n  return str.replace(/\\W+(.)/g, (match, chr) => chr.toUpperCase());\r\n}\r\n\r\n// Usage:\r\ntoCamelCase('hello world test'); // Returns: 'helloWorldTest'",
    "java": "public static String stringToCamelCase(String text) {\r\n    String[] words = text.split(\"\\\\s+\");\r\n    StringBuilder camelCase = new StringBuilder(\r\n        words[0].substring(0, 1).toLowerCase() + words[0].substring(1)\r\n    );\r\n\r\n    for (int i = 1; i < words.length; i++) {\r\n        camelCase.append(words[i].substring(0, 1).toUpperCase());\r\n        camelCase.append(words[i].substring(1));\r\n    }\r\n\r\n    return camelCase.toString();\r\n}\r\n\r\n// Usage:\r\nSystem.out.println(stringToCamelCase(\"Hello world test\")); // \"helloWorldTest\""
  }
}

================================================
FILE: public/data/snippets/string-manipulation/string-to-param-case.json
================================================
{
  "id": "string-to-param-case",
  "category": "string-manipulation",
  "title": "Convert String to Param Case",
  "description": "Converts a given string into param-case.",
  "languages": [
    "js",
    "java"
  ],
  "contributors": [
    "aumirza",
    "Mcbencrafter"
  ],
  "tags": [
    "string",
    "case"
  ],
  "snippets": {
    "js": "function toParamCase(str) {\r\n  return str.toLowerCase().replace(/\\s+/g, '-');\r\n}\r\n\r\n// Usage:\r\ntoParamCase('Hello World Test'); // Returns: 'hello-world-test'",
    "java": "public static String stringToParamCase(String text) {\r\n    return text.toLowerCase().replaceAll(\"\\\\s+\", \"-\");\r\n}\r\n\r\n// Usage:\r\nSystem.out.println(stringToParamCase(\"Hello World 123\")); // \"hello-world-123\""
  }
}

================================================
FILE: public/data/snippets/string-manipulation/string-to-pascal-case.json
================================================
{
  "id": "string-to-pascal-case",
  "category": "string-manipulation",
  "title": "Convert String to Pascal Case",
  "description": "Converts a given string into PascalCase.",
  "languages": [
    "js",
    "java"
  ],
  "contributors": [
    "aumirza",
    "Mcbencrafter"
  ],
  "tags": [
    "string",
    "case"
  ],
  "snippets": {
    "js": "function toPascalCase(str) {\r\n  return str.replace(/\\b\\w/g, (s) => s.toUpperCase()).replace(/\\W+(.)/g, (match, chr) => chr.toUpperCase());\r\n}\r\n\r\n// Usage:\r\ntoPascalCase('hello world test'); // Returns: 'HelloWorldTest'",
    "java": "public static String stringToPascalCase(String text) {\r\n    String[] words = text.split(\"\\\\s+\");\r\n    StringBuilder pascalCase = new StringBuilder();\r\n\r\n    for (String word : words) {\r\n        pascalCase.append(word.substring(0, 1).toUpperCase());\r\n        pascalCase.append(word.substring(1).toLowerCase());\r\n    }\r\n\r\n    return pascalCase.toString();\r\n}\r\n\r\n// Usage:\r\nSystem.out.println(stringToPascalCase(\"hello world\")); // \"HelloWorld\""
  }
}

================================================
FILE: public/data/snippets/string-manipulation/string-to-snake-case.json
================================================
{
  "id": "string-to-snake-case",
  "category": "string-manipulation",
  "title": "Convert String to Snake Case",
  "description": "Converts a given string into snake_case.",
  "languages": [
    "js",
    "java"
  ],
  "contributors": [
    "axorax",
    "Mcbencrafter"
  ],
  "tags": [
    "string",
    "case"
  ],
  "snippets": {
    "js": "function toSnakeCase(str) {\r\n  return str.replace(/([a-z])([A-Z])/g, '$1_$2')\r\n            .replace(/\\s+/g, '_')\r\n            .toLowerCase();\r\n}\r\n\r\n// Usage:\r\ntoSnakeCase('Hello World Test'); // Returns: 'hello_world_test'",
    "java": "public static String stringToSnakeCase(String text) {\r\n    return text.toLowerCase().replaceAll(\"\\\\s+\", \"_\");\r\n}\r\n\r\n// Usage:\r\nSystem.out.println(stringToSnakeCase(\"Hello World 123\")); // \"hello_world_123\""
  }
}

================================================
FILE: public/data/snippets/string-manipulation/string-to-title-case.json
================================================
{
  "id": "string-to-title-case",
  "category": "string-manipulation",
  "title": "Convert String to Title Case",
  "description": "Converts a given string into Title Case.",
  "languages": [
    "js"
  ],
  "contributors": [
    "aumirza"
  ],
  "tags": [
    "string",
    "case"
  ],
  "snippets": {
    "js": "function toTitleCase(str) {\r\n  return str.toLowerCase().replace(/\\b\\w/g, (s) => s.toUpperCase());\r\n}\r\n\r\n// Usage:\r\ntoTitleCase('hello world test'); // Returns: 'Hello World Test'"
  }
}

================================================
FILE: public/data/snippets/string-manipulation/truncate-sring.json
================================================
{
  "id": "truncate-sring",
  "category": "string-manipulation",
  "title": "Truncate String",
  "description": "Truncates a string after a specified length.",
  "languages": [
    "js",
    "java",
    "py"
  ],
  "contributors": [
    "realvishalrana",
    "Mcbencrafter",
    "axorax",
    "MinerMinerMods"
  ],
  "tags": [
    "string",
    "truncate",
    "mask",
    "hide"
  ],
  "snippets": {
    "js": "const truncateText = (text = '', maxLength = 50) => {\r\n  return `${text.slice(0, maxLength)}${text.length >= maxLength ? '...' : ''}`;\r\n};\r\n\r\n// Usage:\r\nconst title = \"Hello, World! This is a Test.\";\r\ntruncateText(title, 10); // Returns: 'Hello, Wor...'",
    "java": "public static String truncate(String text, int length, String suffix) {\r\n    if (text.length() <= length)\r\n        return text;\r\n    \r\n    return text.substring(0, length).trim() + (suffix != null ? suffix : \"\");\r\n}\r\n\r\n// Usage:\r\nSystem.out.println(truncate(\"hello world\", 5, \"...\")); // \"hello...\"",
    "py": "def truncate(s:str, length:int, suffix:bool = True) -> str :\r\n    return (s[:length] + (\"...\" if suffix else \"\")) if len(s) > length else s\r\n\r\n# Usage:\r\ntruncate('This is a long string', 10) # Returns: 'This is a ...'\r\ntruncate('This is a long string', 10, False) # Returns: 'This is a '"
  }
}

================================================
FILE: scripts/merge-snippets.ts
================================================
import fs from "fs";
import path from "path";

const SNIPPETS_DIR = path.join(process.cwd(), "snippets");
const OUTPUT_DIR = path.join(process.cwd(), "public", "data", "snippets");

/**
 * Extracts the code from markdown into a `{ language: code }` object.
 */
function parseMarkdown(mdContent: string) {
  const regex = /```([a-zA-Z0-9+#-]*)\s*([\s\S]*?)```/g;
  const snippets: { [lang: string]: string } = {};
  let match;

  while ((match = regex.exec(mdContent)) !== null) {
    const lang = match[1];
    const code = match[2].trim();
    snippets[lang] = code;
  }

  return snippets;
}

/**
 * Loads the `.json` and `.md` file of each snippet, and combines them into one object.
 */
function mergeSnippet(category: string, name: string) {
  const basePath = path.join(SNIPPETS_DIR, category, name);
  const mdFile = basePath + ".md";
  const jsonFile = basePath + ".json";

  if (!fs.existsSync(mdFile) || !fs.existsSync(jsonFile)) return null;

  const mdContent = fs.readFileSync(mdFile, "utf-8");
  const meta = JSON.parse(fs.readFileSync(jsonFile, "utf-8"));
  const snippets = parseMarkdown(mdContent);

  return {
    id: name,
    category,
    ...meta,
    snippets,
  };
}

/**
 * Reads all categories inside `/snippets` and merge their snippets.
 */
function scanSnippets(dir: string) {
  const items = [];

  for (const category of fs.readdirSync(dir)) {
    const categoryPath = path.join(dir, category);
    if (!fs.statSync(categoryPath).isDirectory()) continue;

    for (const file of fs.readdirSync(categoryPath)) {
      if (file.endsWith(".json")) {
        const name = file.replace(".json", "");
        const snippet = mergeSnippet(category, name);
        if (snippet) items.push(snippet);
      }
    }
  }

  return items;
}

/**
 * Generates JSON output for each snippet and a global all.json.
 */
function buildSnippets() {
  const allSnippets = scanSnippets(SNIPPETS_DIR);

  // write individual files
  for (const snippet of allSnippets) {
    const outDir = path.join(OUTPUT_DIR, snippet.category);
    if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });

    const outFile = path.join(outDir, `${snippet.id}.json`);
    fs.writeFileSync(outFile, JSON.stringify(snippet, null, 2));
  }

  // write all.json (metadata only)
  const allMeta = allSnippets.map(({ snippets, ...meta }) => meta);
  fs.writeFileSync(
    path.join(OUTPUT_DIR, "all.json"),
    JSON.stringify(allMeta, null, 2)
  );

  console.log("✅ Snippets built:", allSnippets.length);
}

buildSnippets();


================================================
FILE: snippets/README.md
================================================


## Markdown Format

````
```js
// code here
```

---

```py
# code here
```
````


## JSON Format

```json
{
  "title": "",
  "description": "",
  "languages": [""],
  "contributors": [""],
  "tags": ["", ""]
}
```

================================================
FILE: snippets/array-manipulation/shuffle-array.json
================================================
{
  "title": "Shuffle Array",
  "description": "Shuffles the items in an array.",
  "languages": ["js"],
  "contributors": ["loxt-nixo"],
  "tags": ["array", "shuffle", "random"]
}


================================================
FILE: snippets/array-manipulation/shuffle-array.md
================================================
```js
function shuffleArray(array) {
    for (let i = array.length - 1; i >= 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
}

// Usage:
const array = [1, 2, 3, 4, 5];
shuffleArray(array); // Shuffles `array` in place
```

================================================
FILE: snippets/data-handling/add-localstorage.json
================================================
{
  "title": "Add Item to localStorage",
  "description": "Stores a value in localStorage under the given key.",
  "languages": ["js"],
  "contributors": ["technoph1le"],
  "tags": ["localStorage", "storage", "memory"]
}


================================================
FILE: snippets/data-handling/add-localstorage.md
================================================
```js
const addToLocalStorage = (key, value) => {
  localStorage.setItem(key, JSON.stringify(value));
};

// Usage:
addToLocalStorage('user', { name: 'John', age: 30 });
```

================================================
FILE: snippets/number-formatting/number-to-currency.json
================================================
{
  "title": "Convert Number to Currency",
  "description": "Converts a number to a currency format with a specific locale.",
  "languages": ["js"],
  "contributors": ["axorax"],
  "tags": ["number", "currency"]
}


================================================
FILE: snippets/number-formatting/number-to-currency.md
================================================
```js
const convertToCurrency = (num, locale = 'en-US', currency = 'USD') => {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: currency
  }).format(num);
};

// Usage:
convertToCurrency(1234567.89); // Returns: '$1,234,567.89'
convertToCurrency(987654.32, 'de-DE', 'EUR'); // Returns: '987.654,32 €'
```

================================================
FILE: snippets/string-manipulation/reverse-string.json
================================================
{
  "title": "Reverse String",
  "description": "Reverses the characters in a string.",
  "languages": ["js", "cpp", "py"],
  "contributors": ["technoph1le", "Vaibhav-kesarwani"],
  "tags": ["string", "reverse"]
}


================================================
FILE: snippets/string-manipulation/reverse-string.md
================================================
```js
const reverseString = (str) => str.split('').reverse().join('');

// Usage:
reverseString('hello'); // Returns: 'olleh'
```

---

```cpp
#include <string>
#include <algorithm>

std::string reverseString(const std::string& input) {
    std::string reversed = input;
    std::reverse(reversed.begin(), reversed.end());
    return reversed;
}

// Usage:
reverseString("quicksnip"); // Returns: "pinskciuq"
```

---

```py
def reverse_string(s:str) -> str:
    return s[::-1]

# Usage:
reverse_string('hello') # Returns: 'olleh'
```

================================================
FILE: snippets/string-manipulation/string-to-camel-case.json
================================================
{
  "title": "Convert String to Camel Case",
  "description": "Converts a given string into camelCase.",
  "languages": ["js", "java"],
  "contributors": ["aumirza", "Mcbencrafter"],
  "tags": ["string", "case"]
}


================================================
FILE: snippets/string-manipulation/string-to-camel-case.md
================================================
```js
function toCamelCase(str) {
  return str.replace(/\W+(.)/g, (match, chr) => chr.toUpperCase());
}

// Usage:
toCamelCase('hello world test'); // Returns: 'helloWorldTest'
```

---

```java
public static String stringToCamelCase(String text) {
    String[] words = text.split("\\s+");
    StringBuilder camelCase = new StringBuilder(
        words[0].substring(0, 1).toLowerCase() + words[0].substring(1)
    );

    for (int i = 1; i < words.length; i++) {
        camelCase.append(words[i].substring(0, 1).toUpperCase());
        camelCase.append(words[i].substring(1));
    }

    return camelCase.toString();
}

// Usage:
System.out.println(stringToCamelCase("Hello world test")); // "helloWorldTest"
```

================================================
FILE: snippets/string-manipulation/string-to-param-case.json
================================================
{
  "title": "Convert String to Param Case",
  "description": "Converts a given string into param-case.",
  "languages": ["js", "java"],
  "contributors": ["aumirza", "Mcbencrafter"],
  "tags": ["string", "case"]
}


================================================
FILE: snippets/string-manipulation/string-to-param-case.md
================================================
```js
function toParamCase(str) {
  return str.toLowerCase().replace(/\s+/g, '-');
}

// Usage:
toParamCase('Hello World Test'); // Returns: 'hello-world-test'
```

---

```java
public static String stringToParamCase(String text) {
    return text.toLowerCase().replaceAll("\\s+", "-");
}

// Usage:
System.out.println(stringToParamCase("Hello World 123")); // "hello-world-123"
```

================================================
FILE: snippets/string-manipulation/string-to-pascal-case.json
================================================
{
  "title": "Convert String to Pascal Case",
  "description": "Converts a given string into PascalCase.",
  "languages": ["js", "java"],
  "contributors": ["aumirza", "Mcbencrafter"],
  "tags": ["string", "case"]
}


================================================
FILE: snippets/string-manipulation/string-to-pascal-case.md
================================================
```js
function toPascalCase(str) {
  return str.replace(/\b\w/g, (s) => s.toUpperCase()).replace(/\W+(.)/g, (match, chr) => chr.toUpperCase());
}

// Usage:
toPascalCase('hello world test'); // Returns: 'HelloWorldTest'
```

---

```java
public static String stringToPascalCase(String text) {
    String[] words = text.split("\\s+");
    StringBuilder pascalCase = new StringBuilder();

    for (String word : words) {
        pascalCase.append(word.substring(0, 1).toUpperCase());
        pascalCase.append(word.substring(1).toLowerCase());
    }

    return pascalCase.toString();
}

// Usage:
System.out.println(stringToPascalCase("hello world")); // "HelloWorld"
```

================================================
FILE: snippets/string-manipulation/string-to-snake-case.json
================================================
{
  "title": "Convert String to Snake Case",
  "description": "Converts a given string into snake_case.",
  "languages": ["js", "java"],
  "contributors": ["axorax", "Mcbencrafter"],
  "tags": ["string", "case"]
}


================================================
FILE: snippets/string-manipulation/string-to-snake-case.md
================================================
```js
function toSnakeCase(str) {
  return str.replace(/([a-z])([A-Z])/g, '$1_$2')
            .replace(/\s+/g, '_')
            .toLowerCase();
}

// Usage:
toSnakeCase('Hello World Test'); // Returns: 'hello_world_test'
```

---

```java
public static String stringToSnakeCase(String text) {
    return text.toLowerCase().replaceAll("\\s+", "_");
}

// Usage:
System.out.println(stringToSnakeCase("Hello World 123")); // "hello_world_123"
```

================================================
FILE: snippets/string-manipulation/string-to-title-case.json
================================================
{
  "title": "Convert String to Title Case",
  "description": "Converts a given string into Title Case.",
  "languages": ["js"],
  "contributors": ["aumirza"],
  "tags": ["string", "case"]
}


================================================
FILE: snippets/string-manipulation/string-to-title-case.md
================================================
```js
function toTitleCase(str) {
  return str.toLowerCase().replace(/\b\w/g, (s) => s.toUpperCase());
}

// Usage:
toTitleCase('hello world test'); // Returns: 'Hello World Test'
```

================================================
FILE: snippets/string-manipulation/truncate-sring.json
================================================
{
  "title": "Truncate String",
  "description": "Truncates a string after a specified length.",
  "languages": ["js", "java", "py"],
  "contributors": [
    "realvishalrana",
    "Mcbencrafter",
    "axorax",
    "MinerMinerMods"
  ],
  "tags": ["string", "truncate", "mask", "hide"]
}


================================================
FILE: snippets/string-manipulation/truncate-sring.md
================================================
```js
const truncateText = (text = '', maxLength = 50) => {
  return `${text.slice(0, maxLength)}${text.length >= maxLength ? '...' : ''}`;
};

// Usage:
const title = "Hello, World! This is a Test.";
truncateText(title, 10); // Returns: 'Hello, Wor...'
```

---

```java
public static String truncate(String text, int length, String suffix) {
    if (text.length() <= length)
        return text;
    
    return text.substring(0, length).trim() + (suffix != null ? suffix : "");
}

// Usage:
System.out.println(truncate("hello world", 5, "...")); // "hello..."
```

---

```py
def truncate(s:str, length:int, suffix:bool = True) -> str :
    return (s[:length] + ("..." if suffix else "")) if len(s) > length else s

# Usage:
truncate('This is a long string', 10) # Returns: 'This is a ...'
truncate('This is a long string', 10, False) # Returns: 'This is a '
```

================================================
FILE: source.config.ts
================================================
import {
  defineConfig,
  defineDocs,
  frontmatterSchema,
  metaSchema,
} from "fumadocs-mdx/config";

// You can customise Zod schemas for frontmatter and `meta.json` here
// see https://fumadocs.vercel.app/docs/mdx/collections#define-docs
export const docs = defineDocs({
  docs: {
    schema: frontmatterSchema,
  },
  meta: {
    schema: metaSchema,
  },
});

export default defineConfig({
  mdxOptions: {
    // MDX options
  },
});


================================================
FILE: src/app/api/search/route.ts
================================================
import { source } from "@/lib/source";
import { createFromSource } from "fumadocs-core/search/server";

export const { GET } = createFromSource(source, {
  // https://docs.orama.com/open-source/supported-languages
  language: "english",
});


================================================
FILE: src/app/community/page.tsx
================================================
"use client";

import { AspectRatio } from "@/components/ui/aspect-ratio";
import { Button } from "@/components/ui/button";
import { ExternalLink } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { motion } from "framer-motion";
import { DISCORD_URL, GITHUB_URL } from "@/data/meta";
import { YOUTUBE_VIDEOS } from "@/data/yt-videos";

export default function Communitypage() {
  return (
    <>
      <section>
        <div className="wrapper grid py-20 gap-8 lg:grid-cols-2">
          <div className="space-y-8 grid justify-items-center">
            <motion.div
              whileHover={{ rotate: "10deg", scale: 1.1 }}
              whileTap={{ scale: 0.9 }}
              transition={{ type: "spring" }}
            >
              <Image
                src="/assets/icons/github.svg"
                alt="GitHub"
                width={150}
                height={150}
                className="shadow-[0_0_5rem_1rem_hsl(225,22%,15%)] rounded-[3rem]"
              />
            </motion.div>
            <h2 className="text-3xl font-bold text-center">
              QuickSnip is Open-Source
            </h2>
            <Button size="lg" asChild>
              <Link href={GITHUB_URL} target="_blank" rel="noopener noreferrer">
                See the code <ExternalLink />
              </Link>
            </Button>
          </div>
          <div className="space-y-8 grid justify-items-center">
            <motion.div
              whileHover={{ rotate: "-10deg", scale: 1.1 }}
              whileTap={{ scale: 0.9 }}
              transition={{ type: "spring" }}
            >
              <Image
                src="/assets/icons/discord.svg"
                alt="Discord"
                width={150}
                height={150}
                className="shadow-[0_0_5rem_1rem_hsl(235,86%,65%,0.5)] rounded-[3rem]"
              />
            </motion.div>
            <h2 className="text-3xl font-bold text-center">
              Join our Discord Community
            </h2>
            <Button size="lg" asChild>
              <Link
                href={DISCORD_URL}
                target="_blank"
                rel="noopener noreferrer"
              >
                Take me there <ExternalLink />
              </Link>
            </Button>
          </div>
        </div>
      </section>

      <section>
        <div className="wrapper pt-8 pb-16 space-y-8">
          <h2 className="text-xl text-muted-foreground font-bold text-center">
            Videos from Technophile
          </h2>
          <ul className="grid gap-4 sm:grid-cols-2 auto-rows-fr lg:grid-cols-3">
            {YOUTUBE_VIDEOS.map((video) => (
              <li key={video.id}>
                <Link
                  href={`https://www.youtube.com/watch?v=${video.id}`}
                  className="block space-y-2"
                >
                  <AspectRatio
                    ratio={16 / 9}
                    className="bg-muted rounded-lg border-4 border-secondary overflow-hidden"
                  >
                    <Image
                      src={`https://img.youtube.com/vi/${video.id}/maxresdefault.jpg`}
                      alt={video.title}
                      fill
                      className="h-full w-full object-cover"
                    />
                  </AspectRatio>
                  <h3 className="text-lg font-semibold">{video.title}</h3>
                </Link>
              </li>
            ))}
          </ul>
          <Button
            size="lg"
            variant="secondary"
            className="flex w-fit mx-auto"
            asChild
          >
            <Link
              href="https://www.youtube.com/@technoph1le"
              target="_blank"
              rel="noopener noreferrer"
            >
              Let me subscribe <ExternalLink />
            </Link>
          </Button>
        </div>
      </section>
    </>
  );
}

/**
 * Fetch `CONTRIBUTING.md`
 */


================================================
FILE: src/app/contributing/page.tsx
================================================
import fs from "fs";
import path from "path";
import remarkGfm from "remark-gfm";
import Markdown from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism";

export default function ContributingPage() {
  const filePath = path.join(process.cwd(), "CONTRIBUTING.md");
  const fileContent = fs.readFileSync(filePath, "utf-8");

  return (
    <article className="wrapper-xs py-8 lg:py-16 prose dark:prose-invert">
      <Markdown
        remarkPlugins={[remarkGfm]}
        components={{
          code(props) {
            const { children, className, ...rest } = props;
            const match = /language-(\w+)/.exec(className || "");
            return match ? (
              <SyntaxHighlighter language={match[1]} style={oneDark}>
                {String(children).replace(/\n$/, "")}
              </SyntaxHighlighter>
            ) : (
              <code {...rest} className={className}>
                {children}
              </code>
            );
          },
        }}
      >
        {fileContent}
      </Markdown>
    </article>
  );
}


================================================
FILE: src/app/extensions/page.tsx
================================================
import ExtensionItem, {
  NewExtensionItem,
} from "@/components/ui/extension-item";
import { EXTENSIONS } from "@/data/extensions";

export default function ExtensionsPage() {
  return (
    <section>
      <div className="wrapper py-8 space-y-8">
        <h2 className="text-3xl font-bold text-center">Extensions</h2>
        <ul className="grid gap-4 sm:grid-cols-2 auto-rows-fr lg:grid-cols-3">
          {EXTENSIONS.map((extension) => (
            <ExtensionItem key={extension.name} extension={extension} />
          ))}
          <NewExtensionItem />
        </ul>
      </div>
    </section>
  );
}

/**
 * 📦 Official Extensions

    VS Code Extension (coming soon)

    QuickSnip CLI (optional later)

🔌 Community Extensions

    ✅ Raycast Extension

    🔜 Vim Plugin

    ⌨️ Obsidian Snippet Sync

    💡 (Form to submit your own tool)

📚 How to Build One

    Short guide: “Build your own extension using QuickSnip API”

    Link to API docs or SDK

    Example template repo
 */


================================================
FILE: src/app/globals.css
================================================
@import "tailwindcss";
@import "tw-animate-css";
@import "fumadocs-ui/css/neutral.css";
@import "fumadocs-ui/css/preset.css";

@custom-variant dark (&:is(.dark *));

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --font-sans: var(--font-geist-sans);
  --font-mono: var(--font-geist-mono);
  --color-sidebar-ring: var(--sidebar-ring);
  --color-sidebar-border: var(--sidebar-border);
  --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
  --color-sidebar-accent: var(--sidebar-accent);
  --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
  --color-sidebar-primary: var(--sidebar-primary);
  --color-sidebar-foreground: var(--sidebar-foreground);
  --color-sidebar: var(--sidebar);
  --color-chart-5: var(--chart-5);
  --color-chart-4: var(--chart-4);
  --color-chart-3: var(--chart-3);
  --color-chart-2: var(--chart-2);
  --color-chart-1: var(--chart-1);
  --color-ring: var(--ring);
  --color-input: var(--input);
  --color-border: var(--border);
  --color-destructive: var(--destructive);
  --color-accent-foreground: var(--accent-foreground);
  --color-accent: var(--accent);
  --color-muted-foreground: var(--muted-foreground);
  --color-muted: var(--muted);
  --color-secondary-foreground: var(--secondary-foreground);
  --color-secondary: var(--secondary);
  --color-primary-foreground: var(--primary-foreground);
  --color-primary: var(--primary);
  --color-popover-foreground: var(--popover-foreground);
  --color-popover: var(--popover);
  --color-card-foreground: var(--card-foreground);
  --color-card: var(--card);
  --radius-sm: calc(var(--radius) - 4px);
  --radius-md: calc(var(--radius) - 2px);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) + 4px);
}

:root {
  --radius: 0.625rem;
  --background: hsl(0, 0%, 95%);
  --foreground: hsl(0, 0%, 16%);
  --card: hsl(0, 0%, 90%);
  --card-foreground: hsl(0, 0%, 24%);
  --popover: hsl(0, 0%, 90%);
  --popover-foreground: hsl(0, 0%, 24%);
  --primary: hsl(0, 0%, 95%);
  --primary-foreground: hsl(0, 0%, 11%);
  --secondary: hsl(0, 0%, 90%);
  --secondary-foreground: hsl(0, 0%, 16%);
  --muted: hsl(0, 0%, 80%);
  --muted-foreground: hsl(0, 0%, 24%);
  --accent: hsl(181, 100%, 22%);
  --accent-foreground: hsl(0, 0%, 11%);
  --destructive: oklch(0.577 0.245 27.325);
  --border: oklch(0.922 0 0);
  --input: oklch(0.922 0 0);
  --ring: oklch(0.708 0 0);
  --chart-1: oklch(0.646 0.222 41.116);
  --chart-2: oklch(0.6 0.118 184.704);
  --chart-3: oklch(0.398 0.07 227.392);
  --chart-4: oklch(0.828 0.189 84.429);
  --chart-5: oklch(0.769 0.188 70.08);
  --sidebar: oklch(0.985 0 0);
  --sidebar-foreground: oklch(0.145 0 0);
  --sidebar-primary: oklch(0.205 0 0);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-accent: oklch(0.97 0 0);
  --sidebar-accent-foreground: oklch(0.205 0 0);
  --sidebar-border: oklch(0.922 0 0);
  --sidebar-ring: oklch(0.708 0 0);

  --fd-layout-width: 90rem;
  --fd-sidebar-margin: 0 2rem;
  --fd-banner-height: 4rem;
  /* --fd-nav-height: 4rem; */

  --header-height: 4rem;
  --footer-height: 4rem;
}

.dark {
  --background: hsl(0, 0%, 11%);
  --foreground: hsl(0, 0%, 90%);
  --card: hsl(0, 0%, 16%);
  --card-foreground: hsl(0, 0%, 80%);
  --popover: hsl(0, 0%, 16%);
  --popover-foreground: hsl(0, 0%, 80%);
  --primary: hsl(0, 0%, 11%);
  --primary-foreground: hsl(0, 0%, 90%);
  --secondary: hsl(0, 0%, 16%);
  --secondary-foreground: hsl(0, 0%, 80%);
  --muted: hsl(0, 0%, 24%);
  --muted-foreground: hsl(0, 0%, 70%);
  --accent: hsl(181, 100%, 36%);
  --accent-foreground: hsl(0, 0%, 11%);
  --destructive: oklch(0.704 0.191 22.216);
  --border: oklch(1 0 0 / 10%);
  --input: oklch(1 0 0 / 15%);
  --ring: oklch(0.556 0 0);
  --chart-1: oklch(0.488 0.243 264.376);
  --chart-2: oklch(0.696 0.17 162.48);
  --chart-3: oklch(0.769 0.188 70.08);
  --chart-4: oklch(0.627 0.265 303.9);
  --chart-5: oklch(0.645 0.246 16.439);
  --sidebar: hsl(0, 0%, 11%);
  --sidebar-foreground: hsl(0, 0%, 90%);
  --sidebar-primary: oklch(0.488 0.243 264.376);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-accent: oklch(0.269 0 0);
  --sidebar-accent-foreground: oklch(0.985 0 0);
  --sidebar-border: oklch(1 0 0 / 10%);
  --sidebar-ring: oklch(0.556 0 0);
}

@layer base {
  * {
    @apply border-border outline-ring/50;
  }
  body {
    @apply bg-background text-foreground;
  }
}

@layer utilities {
  .wrapper {
    @apply max-w-7xl w-full mx-auto px-4;
  }

  .wrapper-sm {
    @apply max-w-6xl w-full mx-auto px-4;
  }

  .wrapper-xs {
    @apply max-w-4xl w-full mx-auto px-4;
  }

  .wrapper-2xs {
    @apply max-w-2xl w-full mx-auto px-4;
  }

  .wrapper-lg {
    @apply max-w-[90rem] w-full mx-auto px-4;
  }
}

/* #nd-docs-layout {
  padding: 0 !important;
}

#nd-page {
  border: 1px solid green;
} */

#nd-sidebar {
  background: var(--background);
  bottom: var(--footer-height);
  position: absolute;
}


================================================
FILE: src/app/guide/[[...slug]]/page.tsx
================================================
import { source } from "@/lib/source";
import {
  DocsBody,
  DocsDescription,
  DocsPage,
  DocsTitle,
} from "fumadocs-ui/page";
import { getGithubLastEdit } from "fumadocs-core/server";
import { notFound } from "next/navigation";
import { getMDXComponents } from "../../../../mdx-components";

export default async function Page(props: {
  params: Promise<{ slug?: string[] }>;
}) {
  const params = await props.params;

  const page = source.getPage(params.slug);
  if (!page) notFound();

  const MDX = page.data.body;
  const time = await getGithubLastEdit({
    owner: "fuma-nama",
    repo: "fumadocs",
    path: `content/guide/${page.path}`,
  });

  return (
    <DocsPage
      editOnGithub={{
        owner: "quicksnip-dev",
        repo: "quicksnip",
        path: `content/guide/${page.path}`,
      }}
      lastUpdate={time ? new Date(time) : undefined}
      full={page.data.full}
    >
      <DocsTitle>{page.data.title}</DocsTitle>
      <DocsDescription>{page.data.description}</DocsDescription>
      <DocsBody>
        <MDX components={getMDXComponents()} />
      </DocsBody>
    </DocsPage>
  );
}

export async function generateStaticParams() {
  return source.generateParams();
}

export async function generateMetadata(props: {
  params: Promise<{ slug?: string[] }>;
}) {
  const params = await props.params;
  const page = source.getPage(params.slug);
  if (!page) notFound();

  return {
    title: page.data.title,
    description: page.data.description,
  };
}


================================================
FILE: src/app/guide/layout.tsx
================================================
import { source } from "@/lib/source";
import { DocsLayout } from "fumadocs-ui/layouts/docs";
import type { ReactNode } from "react";
import { baseOptions } from "@/app/layout.config";

export default function Layout({ children }: { children: ReactNode }) {
  return (
    <DocsLayout
      tree={source.pageTree}
      {...baseOptions}
      themeSwitch={{ enabled: false }}
    >
      {children}
    </DocsLayout>
  );
}


================================================
FILE: src/app/layout.config.tsx
================================================
import { BaseLayoutProps } from "fumadocs-ui/layouts/shared";

export const baseOptions: BaseLayoutProps = {
  // githubUrl: "https://github.com/quicksnip-dev/quicksnip",
  nav: {
    enabled: false,
  },
};


================================================
FILE: src/app/layout.tsx
================================================
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import { RootProvider } from "fumadocs-ui/provider";
import "./globals.css";
import Header from "@/components/layouts/header";
import Footer from "@/components/layouts/footer";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "QuickSnip",
  description: "Find code snippets in seconds.",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en" dir="ltr" suppressHydrationWarning>
      <body
        className={`${geistSans.variable} ${geistMono.variable} grid grid-rows-[auto_1fr_auto] min-h-screen antialiased bg-background text-foreground dark`}
      >
        <Header />
        <RootProvider>{children}</RootProvider>
        <Footer />
      </body>
    </html>
  );
}


================================================
FILE: src/app/page.tsx
================================================
import Features from "@/components/layouts/features";
import Hero from "@/components/layouts/hero";
import AvailableFor from "@/components/layouts/available-for";
import Sponsor from "@/components/layouts/sponsor";

export default function Home() {
  return (
    <>
      <Hero />
      <Features />
      <AvailableFor />
      <Sponsor />
    </>
  );
}


================================================
FILE: src/app/snippets/[category]/[snippet]/page.tsx
================================================
"use client";

import { use } from "react";

import { useRouter } from "next/navigation";
import Link from "next/link";

import { Loader, XIcon } from "lucide-react";

import { FullSnippet } from "@/types";
import { useFetch } from "@/hooks/use-fetch";
import { Button } from "@/components/ui/button";
import CodePreview from "@/components/layouts/code-preview";
import {
  Card,
  CardContent,
  CardFooter,
  CardHeader,
} from "@/components/ui/card";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";

interface Props {
  params: Promise<{ category: string; snippet: string }>;
}

export default function SnippetPage({ params }: Props) {
  const router = useRouter();
  const { category, snippet } = use(params);
  const { data, loading } = useFetch<FullSnippet>(
    `/data/snippets/${category}/${snippet}.json`
  );

  const handleCloseModal = () => {
    /**
     * If a snippet is visited directly from URL,
     * it won't have history to go back to.
     * Thus, fallback to `/snippets/` page.
     */
    if (window.history.length > 2) router.back();
    else router.push("/snippets");
  };

  if (loading) return <Loader />;
  if (!data) return null;

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
      <Card className="wrapper-xs">
        <CardHeader className="flex items-center justify-between gap-4">
          <h2 className="text-2xl font-bold">{data.title}</h2>
          <Button onClick={handleCloseModal} size="icon">
            <XIcon />
          </Button>
        </CardHeader>
        <CardContent className="space-y-4">
          <p>{data.description}</p>
          <CodePreview languages={data.languages} snippets={data.snippets} />
        </CardContent>
        <CardFooter className="grid gap-4">
          <div className="flex items-center gap-4">
            <p className="font-bold">Contributors: </p>
            <div className="*:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale">
              {data.contributors.map((contributor) => (
                <Avatar
                  key={contributor}
                  className="w-8 h-8"
                  title={`@${contributor}`}
                >
                  <AvatarImage
                    src={`https://github.com/${contributor}.png`}
                    alt={`@${contributor}`}
                  />
                  <AvatarFallback>{contributor.slice(0, 2)}</AvatarFallback>
                </Avatar>
              ))}
            </div>
          </div>
          <ul className="flex items-center flex-wrap gap-2">
            {data.tags.map((tag) => (
              <li key={tag}>
                <Link
                  className="border border-border font-semibold pt-1 pb-2 px-3 rounded-md leading-tight"
                  href={`/snippets/tags/${tag}`}
                >
                  {tag}
                </Link>
              </li>
            ))}
          </ul>
        </CardFooter>
      </Card>
    </div>
  );
}


================================================
FILE: src/app/snippets/[category]/page.tsx
================================================
"use client";

import SnippetList from "@/components/layouts/snippet-list";
import { unslugify } from "@/lib/utils";
import { useSnippetsStore } from "@/store/useSnippetsStore";
import { use } from "react";

interface Props {
  params: Promise<{ category: string }>;
}

export default function Categories({ params }: Props) {
  const { snippets } = useSnippetsStore();
  const { category } = use(params);

  const filteredSnippets = snippets.filter(
    (snippet) => snippet.category === category
  );

  return (
    <section className="space-y-4">
      <h2 className="text-2xl font-bold">{unslugify(category)}</h2>
      <SnippetList snippets={filteredSnippets} />
    </section>
  );
}


================================================
FILE: src/app/snippets/layout.tsx
================================================
import SnippetHeader from "@/components/layouts/snippet-header";
import SnippetSidebar from "@/components/layouts/snippet-sidebar";

import { SidebarProvider } from "@/components/ui/sidebar";

export default function SnippetsLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <SidebarProvider>
      <SnippetSidebar />
      <main className="py-4 pl-4 space-y-4 w-full">
        <SnippetHeader />
        {children}
      </main>
    </SidebarProvider>
  );
}


================================================
FILE: src/app/snippets/page.tsx
================================================
"use client";

import SnippetList from "@/components/layouts/snippet-list";
import { useSnippetsStore } from "@/store/useSnippetsStore";
import { useEffect } from "react";

export default function SnippetsPage() {
  const { setSnippets, snippets } = useSnippetsStore();

  useEffect(() => {
    fetch("/data/snippets/all.json")
      .then((res) => res.json())
      .then((data) => setSnippets(data));
  }, [setSnippets]);

  // This is for showing all the snippets
  return <SnippetList snippets={snippets} />;
}


================================================
FILE: src/components/layouts/available-for.tsx
================================================
import Link from "next/link";

import { EXTENSIONS } from "@/data/extensions";

import { Button } from "@/components/ui/button";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";

export default function AvailableFor() {
  return (
    <section>
      <div className="wrapper py-8 grid justify-items-center gap-6">
        <h2 className="text-center text-2xl">Available for:</h2>
        <div className="flex gap-4 flex-wrap">
          {EXTENSIONS.map((extension) => (
            <Link key={extension.name} href={extension.guide_url}>
              <Avatar className="size-14 bg-secondary p-3 hover:bg-muted">
                <AvatarImage src={extension.icon} alt={extension.name} />
                <AvatarFallback>{extension.name.slice(0, 2)}</AvatarFallback>
              </Avatar>
            </Link>
          ))}
        </div>
        <Button variant="secondary" size="lg" asChild>
          <Link href="/extensions">View more</Link>
        </Button>
      </div>
    </section>
  );
}


================================================
FILE: src/components/layouts/code-preview.tsx
================================================
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism";

import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";

// import CopyToClipboard from "./CopyToClipboard";
// import CopyURLButton from "./CopyURLButton";

type Props = {
  languages: string[];
  snippets: Record<string, string>;
};

const CodePreview = ({ languages, snippets }: Props) => {
  return (
    <div className="w-full">
      <Tabs defaultValue={languages[0]}>
        <TabsList>
          {languages.map((language) => (
            <TabsTrigger key={language} value={language}>
              {language}
            </TabsTrigger>
          ))}
        </TabsList>
        {Object.keys(snippets).map((language) => {
          const code = snippets[language as keyof typeof snippets];

          return (
            <TabsContent value={language} key={language}>
              <SyntaxHighlighter
                language={language}
                style={oneDark}
                wrapLines={true}
                customStyle={{ margin: "0", maxHeight: "22rem" }}
              >
                {code}
              </SyntaxHighlighter>
            </TabsContent>
          );
        })}
      </Tabs>
    </div>
  );
};

export default CodePreview;


================================================
FILE: src/components/layouts/features.tsx
================================================
import {
  Code,
  Terminal,
  Paintbrush,
  Rocket,
  Book,
  PlusCircle,
} from "lucide-react";
import { Card, CardContent, CardHeader } from "../ui/card";

const features = [
  {
    icon: <Code className="h-6 w-6" />,
    title: "Developer-Friendly",
    desc: "Tailored for developers to create and iterate fast, with minimal overhead and maximum flexibility.",
  },
  {
    icon: <Terminal className="h-6 w-6" />,
    title: "CLI Support",
    desc: "Command-line interface support for seamless development and workflow integration.",
  },
  {
    icon: <Paintbrush className="h-6 w-6" />,
    title: "Easily Customizable",
    desc: "Every block is built to be editable. From layout to logic, style to structure—make it your own.",
  },
  {
    icon: <Rocket className="h-6 w-6" />,
    title: "v0 Support",
    desc: "Launch fast with confidence. Perfect for MVPs, prototypes, and weekend projects.",
  },
  {
    icon: <Book className="h-6 w-6" />,
    title: "Full Documentation",
    desc: "Comprehensive documentation to understand every feature and maximize your development experience.",
  },
  {
    icon: <PlusCircle className="h-6 w-6" />,
    title: "Contribute Yours",
    desc: "Add your own blocks to the library and become part of the MVPBlocks community.",
  },
];
export default function Features() {
  return (
    <section className="relative py-4 my-6">
      <div className="wrapper">
        <ul className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
          {features.map((item, idx) => (
            <Card key={idx}>
              <CardHeader>
                <div className="text-accent w-fit transform-gpu rounded-full bg-background p-4">
                  {item.icon}
                </div>
              </CardHeader>
              <CardContent>
                <h4 className="text-lg font-bold">{item.title}</h4>
                <p className="text-muted-foreground">{item.desc}</p>
              </CardContent>
            </Card>
          ))}
        </ul>
      </div>
    </section>
  );
}


================================================
FILE: src/components/layouts/footer.tsx
================================================
import { ExternalLink } from "lucide-react";
import Link from "next/link";

const NAV_ITEMS = [
  { name: "Contribute", url: "/contributing", external: false },
  {
    name: "Changelog",
    url: "https://github.com/quicksnip-dev/quicksnip/releases",
    external: true,
  },
  {
    name: "Sponsor",
    url: "https://ko-fi.com/technoph1le",
    external: true,
  },
  {
    name: "GitHub",
    url: "https://github.com/quicksnip-dev/quicksnip",
    external: true,
  },
  {
    name: "Discord",
    url: "https://discord.com/invite/Nm5K46yUy5",
    external: true,
  },
];

const Footer = () => {
  return (
    <footer className="h-(--footer-height) border-t border-secondary text-secondary-foreground">
      <div className="wrapper-lg py-8 grid gap-8 md:grid-cols-[1fr_auto]">
        <p>
          Released under the{" "}
          <Link
            href="https://github.com/quicksnip-dev/quicksnip/LICENSE"
            className="text-primary-foreground font-semibold"
          >
            MIT License.
          </Link>
          <br />
          Copyright © 2025
        </p>
        <ul className="flex md:items-center gap-4 md:gap-8 flex-col md:flex-row flex-wrap">
          {NAV_ITEMS.map((item) => (
            <li key={item.name}>
              <Link
                href={item.url}
                target={item.external ? "_blank" : "_self"}
                className="flex items-center gap-1 font-semibold"
              >
                {item.name}
                {item.external ? <ExternalLink size={14} /> : null}
              </Link>
            </li>
          ))}
        </ul>
      </div>
    </footer>
  );
};

export default Footer;


================================================
FILE: src/components/layouts/header.tsx
================================================
"use client";

import React, { useState, useEffect } from "react";
import { motion, AnimatePresence, easeInOut } from "framer-motion";
import { Menu, X } from "lucide-react";

import Link from "next/link";
import Image from "next/image";

import Logo from "@/components/ui/logo";
import DarkModeSwitch from "@/components/ui/dark-mode-switch";

import { DISCORD_URL, GITHUB_URL } from "@/data/meta";

const NAV_ITEMS = [
  { name: "Snippets", url: "/snippets" },
  { name: "Guide", url: "/guide" },
  { name: "Extensions", url: "/extensions" },
  { name: "Community", url: "/community" },
  { name: "Contributing", url: "/contributing" },
];

const SOCIAL_ITEMS = [
  {
    icon: "/assets/icons/github.svg",
    name: "GitHub",
    url: GITHUB_URL,
  },
  {
    icon: "/assets/icons/discord.svg",
    name: "Discord",
    url: DISCORD_URL,
  },
];

export default function Header() {
  const [isScrolled, setIsScrolled] = useState(false);
  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);

  useEffect(() => {
    const handleScroll = () => {
      setIsScrolled(window.scrollY > 10);
    };
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, []);

  const itemVariants = {
    hidden: { opacity: 0, y: -10 },
    visible: { opacity: 1, y: 0 },
  };

  const mobileMenuVariants = {
    closed: {
      opacity: 0,
      x: "100%",
      transition: {
        duration: 0.3,
        ease: easeInOut,
      },
    },
    open: {
      opacity: 1,
      x: 0,
      transition: {
        duration: 0.3,
        ease: easeInOut,
        staggerChildren: 0.1,
      },
    },
  };

  const mobileItemVariants = {
    closed: { opacity: 0, x: 20 },
    open: { opacity: 1, x: 0 },
  };

  return (
    <>
      <div className="h-16 w-screen" />
      <motion.header
        className={`fixed top-0 right-0 left-0 z-50 transition-all border-b border-border/50 duration-500 ${
          isScrolled
            ? "bg-background/80 shadow-sm backdrop-blur-md"
            : "bg-transparent"
        }`}
      >
        <div className="wrapper-lg">
          <div className="flex h-16 items-center justify-between">
            <Logo />

            <div className="flex gap-4 items-center justify-between">
              <nav className="hidden items-center gap-8 space-x-1 lg:flex">
                {NAV_ITEMS.map((item) => (
                  <motion.div
                    key={item.name}
                    variants={itemVariants}
                    className="relative"
                  >
                    <Link
                      href={item.url}
                      className="text-foreground/80 hover:text-accent font-medium transition-colors duration-200"
                    >
                      <span>{item.name}</span>
                    </Link>
                  </motion.div>
                ))}
              </nav>

              <hr className="border-0 w-0.5 h-8 bg-secondary" />

              <DarkModeSwitch />

              <hr className="border-0 w-0.5 h-8 bg-secondary" />

              <motion.div
                className="hidden items-center space-x-3 lg:flex"
                variants={itemVariants}
              >
                {SOCIAL_ITEMS.map((item) => (
                  <Link
                    key={item.name}
                    href={item.url}
                    title={item.name}
                    className=""
                  >
                    <Image
                      src={item.icon}
                      alt={item.name}
                      width={24}
                      height={24}
                    />
                  </Link>
                ))}
              </motion.div>

              <motion.button
                className="text-foreground hover:bg-muted rounded-lg p-2 transition-colors duration-200 lg:hidden"
                onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
                variants={itemVariants}
                whileTap={{ scale: 0.95 }}
              >
                {isMobileMenuOpen ? (
                  <X className="h-6 w-6" />
                ) : (
                  <Menu className="h-6 w-6" />
                )}
              </motion.button>
            </div>
          </div>
        </div>
      </motion.header>

      {/* Mobile menu */}
      <AnimatePresence>
        {isMobileMenuOpen && (
          <>
            <motion.div
              className="fixed inset-0 z-40 bg-black/20 backdrop-blur-sm lg:hidden"
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              onClick={() => setIsMobileMenuOpen(false)}
            />
            <motion.div
              className="border-border bg-background fixed top-16 right-4 z-50 w-80 overflow-hidden rounded-2xl border shadow-2xl lg:hidden"
              variants={mobileMenuVariants}
              initial="closed"
              animate="open"
              exit="closed"
            >
              <div className="space-y-6 p-6">
                <div className="space-y-1">
                  {NAV_ITEMS.map((item) => (
                    <motion.div key={item.name} variants={mobileItemVariants}>
                      <Link
                        href={item.url}
                        className="text-foreground hover:bg-muted block rounded-lg px-4 py-3 font-medium transition-colors duration-200"
                        onClick={() => setIsMobileMenuOpen(false)}
                      >
                        {item.name}
                      </Link>
                    </motion.div>
                  ))}
                </div>

                <motion.div
                  className="border-border space-y-3 border-t pt-6"
                  variants={mobileItemVariants}
                >
                  <Link
                    href="/login"
                    className="text-foreground hover:bg-muted block w-full rounded-lg py-3 text-center font-medium transition-colors duration-200"
                    onClick={() => setIsMobileMenuOpen(false)}
                  >
                    Sign In
                  </Link>
                  <Link
                    href="/signup"
                    className="bg-foreground text-background hover:bg-foreground/90 block w-full rounded-lg py-3 text-center font-medium transition-all duration-200"
                    onClick={() => setIsMobileMenuOpen(false)}
                  >
                    Get Started
                  </Link>
                </motion.div>
              </div>
            </motion.div>
          </>
        )}
      </AnimatePresence>
    </>
  );
}


================================================
FILE: src/components/layouts/hero.tsx
================================================
import Link from "next/link";
import { Button } from "../ui/button";
import { GITHUB_URL } from "@/data/meta";

const Hero = () => {
  return (
    <section>
      <div className="wrapper grid md:grid-cols-2">
        <article className="space-y-6 py-12">
          <Link
            className="block bg-secondary px-4 py-2 rounded-md w-fit text-lg font-semibold hover:bg-muted transition-colors duration-200 leading-none"
            href="https://github.com/quicksnip-dev/quicksnip"
          >
            v2.3.1
          </Link>
          <h1 className="text-4xl lg:text-7xl font-bold">
            A collection of <span className="text-accent">&lt;code&gt;</span>{" "}
            snippets
          </h1>
          <p className="text-2xl font-semibold text-muted-foreground">
            Made by the community.
          </p>
          <div className="flex flex-wrap gap-2">
            <Button asChild size="lg" className=" rounded-full">
              <Link href="/snippets">View all snippets</Link>
            </Button>
            <Button
              asChild
              size="lg"
              variant="secondary"
              className=" rounded-full"
            >
              <Link href="/snippets">Get Started</Link>
            </Button>
            <Button
              asChild
              size="lg"
              variant="secondary"
              className=" rounded-full"
            >
              <Link href={GITHUB_URL} target="_blank" rel="noopener noreferrer">
                GitHub
              </Link>
            </Button>
          </div>
        </article>
        <div className="p-8">
          <div className="w-full h-full bg-secondary rounded-2xl border-2 border-muted shadow-xl shadow-neutral-900"></div>
        </div>
      </div>
    </section>
  );
};

export default Hero;


================================================
FILE: src/components/layouts/snippet-header.tsx
================================================
import { Search } from "lucide-react";

import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { SidebarTrigger } from "@/components/ui/sidebar";
import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from "@/components/ui/input-group";

export default function SnippetHeader() {
  return (
    <section className="grid gap-4 items-center md:grid-cols-[auto_1fr_auto_auto]">
      <SidebarTrigger />
      <InputGroup>
        <InputGroupInput placeholder="Search 875 snippets..." />
        <InputGroupAddon>
          <Search size={16} />
        </InputGroupAddon>
      </InputGroup>
      <Select>
        <SelectTrigger className="w-[10rem]">
          <SelectValue placeholder="Sort by" />
        </SelectTrigger>
        <SelectContent>
          <SelectItem value="relevance">Relevance</SelectItem>
          <SelectItem value="popularity">Popularity</SelectItem>
          <SelectItem value="recent">Most recent</SelectItem>
          <SelectItem value="oldest">The oldest</SelectItem>
        </SelectContent>
      </Select>
      <Select>
        <SelectTrigger className="w-[15rem]">
          <SelectValue placeholder="Filter by language" />
        </SelectTrigger>
        <SelectContent>
          <SelectItem value="js">JavaScript</SelectItem>
          <SelectItem value="py">Python</SelectItem>
          <SelectItem value="cplusplus">C++</SelectItem>
        </SelectContent>
      </Select>
    </section>
  );
}


================================================
FILE: src/components/layouts/snippet-list.tsx
================================================
import SnippetItem from "../ui/snippet-item";

import type { SnippetType } from "@/types";

interface SnippetListProps {
  snippets: SnippetType[];
}

export default function SnippetList({ snippets }: SnippetListProps) {
  return (
    <ul className="grid gap-4 sm:grid-cols-3 md:grid-cols-3">
      {snippets.map((snippet) => (
        <SnippetItem key={snippet.id} snippet={snippet} />
      ))}
    </ul>
  );
}


================================================
FILE: src/components/layouts/snippet-sidebar.tsx
================================================
"use client";

import {
  Sidebar,
  SidebarContent,
  SidebarGroup,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
  SidebarMenuSub,
  SidebarMenuSubButton,
  SidebarMenuSubItem,
  SidebarRail,
} from "@/components/ui/sidebar";
import { unslugify } from "@/lib/utils";
import { useSnippetsStore } from "@/store/useSnippetsStore";
import Link from "next/link";

// const categories = [
//   {
//     title: "Routing",
//     url: "/routing",
//     count: 23,
//   },
//   {
//     title: "Data Fetching",
//     url: "#",
//     isActive: true,
//     count: 86,
//   },
//   {
//     title: "Rendering",
//     url: "#",
//     count: 34,
//   },
//   {
//     title: "Caching",
//     url: "#",
//     count: 64,
//   },
//   {
//     title: "Styling",
//     url: "#",
//     count: 98,
//   },
//   {
//     title: "Optimizing",
//     url: "#",
//     count: 45,
//   },
//   {
//     title: "Configuring",
//     url: "#",
//     count: 123,
//   },
//   {
//     title: "Testing",
//     url: "#",
//     count: 56,
//   },
//   {
//     title: "Authentication",
//     url: "#",
//     count: 87,
//   },
//   {
//     title: "Deploying",
//     url: "#",
//     count: 234,
//   },
//   {
//     title: "Upgrading",
//     url: "#",
//     count: 12,
//   },
//   {
//     title: "Examples",
//     url: "#",
//     count: 56,
//   },
// ];

export default function SnippetSidebar() {
  const { setCategory, categories } = useSnippetsStore();
  const cats = categories();

  return (
    <Sidebar className="z-40 border-r border-border">
      <SidebarContent>
        <SidebarGroup>
          <SidebarMenu>
            <SidebarMenuItem>
              <SidebarMenuButton asChild>
                <Link href="/snippets" onClick={() => setCategory("All")}>
                  All
                </Link>
              </SidebarMenuButton>
            </SidebarMenuItem>
            <SidebarMenuItem>
              <SidebarMenuButton asChild>
                <Link href="/snippets/categories">Categories</Link>
              </SidebarMenuButton>
              {cats.length ? (
                <SidebarMenuSub>
                  {cats.map((item) => (
                    <SidebarMenuSubItem key={item.name}>
                      <SidebarMenuSubButton
                        className="py-4"
                        asChild
                        isActive={item.name === "current"}
                      >
                        <Link
                          href={`/snippets/${item.name}`}
                          onClick={() => setCategory(item.name)}
                          className="flex items-center justify-between"
                        >
                          <span>{unslugify(item.name)}</span>
                          <span className="text-muted-foreground">
                            {item.count}
                          </span>
                        </Link>
                      </SidebarMenuSubButton>
                    </SidebarMenuSubItem>
                  ))}
                </SidebarMenuSub>
              ) : null}
            </SidebarMenuItem>
          </SidebarMenu>
        </SidebarGroup>
      </SidebarContent>
      <SidebarRail />
    </Sidebar>
  );
}


================================================
FILE: src/components/layouts/sponsor.tsx
================================================
import Image from "next/image";
import Link from "next/link";
import {
  Card,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";
import { SPONSOR_URL } from "@/data/meta";

export default function Sponsor() {
  return (
    <div className="wrapper-xs pt-4 pb-12">
      <Card className="grid md:grid-cols-[1fr_auto]">
        <CardHeader>
          <CardTitle className="text-xl font-bold">
            Support the project
          </CardTitle>
          <CardDescription>
            And get a special <strong>💖 Prime Supporter</strong> role in our
            Discord server.
          </CardDescription>
        </CardHeader>
        <CardFooter>
          <Link href={SPONSOR_URL} target="_blank">
            <Image
              width={200}
              height={80}
              className="mx-auto"
              src="/assets/images/kofi-button.png"
              alt="Buy Me a Coffee at ko-fi.com"
            />
          </Link>
        </CardFooter>
      </Card>
    </div>
  );
}


================================================
FILE: src/components/ui/accordion.tsx
================================================
"use client"

import * as React from "react"
import * as AccordionPrimitive from "@radix-ui/react-accordion"
import { ChevronDownIcon } from "lucide-react"

import { cn } from "@/lib/utils"

function Accordion({
  ...props
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
  return <AccordionPrimitive.Root data-slot="accordion" {...props} />
}

function AccordionItem({
  className,
  ...props
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
  return (
    <AccordionPrimitive.Item
      data-slot="accordion-item"
      className={cn("border-b last:border-b-0", className)}
      {...props}
    />
  )
}

function AccordionTrigger({
  className,
  children,
  ...props
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
  return (
    <AccordionPrimitive.Header className="flex">
      <AccordionPrimitive.Trigger
        data-slot="accordion-trigger"
        className={cn(
          "focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
          className
        )}
        {...props}
      >
        {children}
        <ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
      </AccordionPrimitive.Trigger>
    </AccordionPrimitive.Header>
  )
}

function AccordionContent({
  className,
  children,
  ...props
}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
  return (
    <AccordionPrimitive.Content
      data-slot="accordion-content"
      className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
      {...props}
    >
      <div className={cn("pt-0 pb-4", className)}>{children}</div>
    </AccordionPrimitive.Content>
  )
}

export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }


================================================
FILE: src/components/ui/aspect-ratio.tsx
================================================
"use client"

import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"

function AspectRatio({
  ...props
}: React.ComponentProps<typeof AspectRatioPrimitive.Root>) {
  return <AspectRatioPrimitive.Root data-slot="aspect-ratio" {...props} />
}

export { AspectRatio }


================================================
FILE: src/components/ui/avatar.tsx
================================================
"use client";

import * as React from "react";
import * as AvatarPrimitive from "@radix-ui/react-avatar";

import { cn } from "@/lib/utils";

function Avatar({
  className,
  ...props
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
  return (
    <AvatarPrimitive.Root
      data-slot="avatar"
      className={cn(
        "relative flex shrink-0 overflow-hidden rounded-full",
        className
      )}
      {...props}
    />
  );
}

function AvatarImage({
  className,
  ...props
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
  return (
    <AvatarPrimitive.Image
      data-slot="avatar-image"
      className={cn("aspect-square size-full", className)}
      {...props}
    />
  );
}

function AvatarFallback({
  className,
  ...props
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
  return (
    <AvatarPrimitive.Fallback
      data-slot="avatar-fallback"
      className={cn(
        "bg-muted flex size-full items-center justify-center rounded-full",
        className
      )}
      {...props}
    />
  );
}

export { Avatar, AvatarImage, AvatarFallback };


================================================
FILE: src/components/ui/button.tsx
================================================
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";

import { cn } from "@/lib/utils";

const buttonVariants = cva(
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
  {
    variants: {
      variant: {
        default:
          "bg-accent rounded-full font-semibold text-accent-foreground shadow-xs hover:bg-accent/70",
        destructive:
          "bg-destructive rounded-full text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
        outline:
          "border font-semibold rounded-full bg-background shadow-xs hover:bg-accent dark:bg-input/30 dark:border-input dark:hover:bg-secondary/50",
        secondary:
          "bg-secondary font-semibold rounded-full text-secondary-foreground shadow-xs hover:bg-secondary/80",
        ghost:
          "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
        link: "text-primary font-semibold underline-offset-4 hover:underline",
      },
      size: {
        default: "h-9 px-4 py-2 has-[>svg]:px-3",
        sm: "h-8 rounded-full gap-1.5 px-3 has-[>svg]:px-2.5",
        lg: "h-10 rounded-full px-6 has-[>svg]:px-4",
        icon: "size-9",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
);

function Button({
  className,
  variant,
  size,
  asChild = false,
  ...props
}: React.ComponentProps<"button"> &
  VariantProps<typeof buttonVariants> & {
    asChild?: boolean;
  }) {
  const Comp = asChild ? Slot : "button";

  return (
    <Comp
      data-slot="button"
      className={cn(buttonVariants({ variant, size, className }))}
      {...props}
    />
  );
}

export { Button, buttonVariants };


================================================
FILE: src/components/ui/card.tsx
================================================
import * as React from "react"

import { cn } from "@/lib/utils"

function Card({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card"
      className={cn(
        "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
        className
      )}
      {...props}
    />
  )
}

function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-header"
      className={cn(
        "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
        className
      )}
      {...props}
    />
  )
}

function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-title"
      className={cn("leading-none font-semibold", className)}
      {...props}
    />
  )
}

function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-description"
      className={cn("text-muted-foreground text-sm", className)}
      {...props}
    />
  )
}

function CardAction({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-action"
      className={cn(
        "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
        className
      )}
      {...props}
    />
  )
}

function CardContent({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-content"
      className={cn("px-6", className)}
      {...props}
    />
  )
}

function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-footer"
      className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
      {...props}
    />
  )
}

export {
  Card,
  CardHeader,
  CardFooter,
  CardTitle,
  CardAction,
  CardDescription,
  CardContent,
}


================================================
FILE: src/components/ui/dark-mode-switch.tsx
================================================
"use client";

import { useId, useState } from "react";
import { MoonIcon, SunIcon } from "lucide-react";

import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";

export default function DarkModeSwitch() {
  const id = useId();
  const [checked, setChecked] = useState<boolean>(true);

  return (
    <div>
      <div className="relative inline-grid h-7 grid-cols-[1fr_1fr] items-center text-sm font-medium">
        <Switch
          id={id}
          checked={checked}
          onCheckedChange={setChecked}
          className="peer data-[state=checked]:bg-input/50 data-[state=unchecked]:bg-input/50 absolute inset-0 h-[inherit] w-auto [&_span]:h-full [&_span]:w-1/2 [&_span]:transition-transform [&_span]:duration-300 [&_span]:ease-[cubic-bezier(0.16,1,0.3,1)] [&_span]:data-[state=checked]:translate-x-full [&_span]:data-[state=checked]:rtl:-translate-x-full"
        />
        <span className="peer-data-[state=checked]:text-muted-foreground/70 pointer-events-none relative ms-0.5 flex min-w-7 items-center justify-center text-center">
          <MoonIcon size={16} aria-hidden="true" />
        </span>
        <span className="peer-data-[state=unchecked]:text-muted-foreground/70 pointer-events-none relative me-0.5 flex min-w-7 items-center justify-center text-center">
          <SunIcon size={16} aria-hidden="true" />
        </span>
      </div>
      <Label htmlFor={id} className="sr-only">
        Dark mode toggle switch
      </Label>
    </div>
  );
}


================================================
FILE: src/components/ui/extension-item.tsx
================================================
import Link from "next/link";
import Image from "next/image";
import type { ExtensionType } from "@/types";
import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "./card";
import { Button } from "./button";
import { FUTURE_EXTENSIONS } from "@/data/extensions";
import { Avatar, AvatarFallback, AvatarImage } from "./avatar";

export default function ExtensionItem({
  extension,
}: {
  extension: ExtensionType;
}) {
  return (
    <Card className="justify-start gap-4">
      <CardHeader className="grid grid-cols-[auto_1fr] items-center gap-4">
        <div className="bg-primary p-4 rounded-lg">
          <Image
            src={extension.icon}
            alt=""
            aria-hidden="true"
            width={40}
            height={40}
          />
        </div>
        <div className="space-y-1">
          <CardTitle className="text-2xl font-bold">{extension.name}</CardTitle>
          <p className="text-muted-foreground">
            <strong>Downloads: </strong>
            {new Intl.NumberFormat().format(extension.downloads)}
          </p>
        </div>
      </CardHeader>
      <CardContent className="flex-1">
        <CardDescription className="text-base">
          {extension.description}
        </CardDescription>
      </CardContent>
      <CardFooter>
        <CardAction className="flex flex-wrap gap-4">
          <Button asChild>
            <Link href={extension.guide_url}>Guide</Link>
          </Button>
          <Button asChild variant="outline">
            <Link
              href={extension.source_url}
              target="_blank"
              rel="noopener noreferrer"
            >
              Source
            </Link>
          </Button>
        </CardAction>
      </CardFooter>
    </Card>
  );
}

export function NewExtensionItem() {
  return (
    <Link
      href="/guide/extensions"
      className="bg-transparent h-full outline-2 outline-secondary transition-colors duration-200 hover:outline-muted space-y-4 p-6 rounded-lg grid gap-4 place-content-center justify-items-center text-secondary-foreground"
    >
      <ExtensionsAvatarGroup />
      <span className="text-lg font-semibold">Add your extension here...</span>
    </Link>
  );
}

function ExtensionsAvatarGroup() {
  return (
    <div className="flex -space-x-4">
      {FUTURE_EXTENSIONS.map((extension) => (
        <Avatar key={extension.name} className="size-14 bg-secondary p-3">
          <AvatarImage src={extension.icon} alt={extension.name} />
          <AvatarFallback>{extension.shortcut_name}</AvatarFallback>
        </Avatar>
      ))}
    </div>
  );
}


================================================
FILE: src/components/ui/input-group.tsx
================================================
"use client"

import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"

function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="input-group"
      role="group"
      className={cn(
        "group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none",
        "h-9 min-w-0 has-[>textarea]:h-auto",

        // Variants based on alignment.
        "has-[>[data-align=inline-start]]:[&>input]:pl-2",
        "has-[>[data-align=inline-end]]:[&>input]:pr-2",
        "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
        "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",

        // Focus state.
        "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]",

        // Error state.
        "has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",

        className
      )}
      {...props}
    />
  )
}

const inputGroupAddonVariants = cva(
  "text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
  {
    variants: {
      align: {
        "inline-start":
          "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
        "inline-end":
          "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
        "block-start":
          "order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5",
        "block-end":
          "order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5",
      },
    },
    defaultVariants: {
      align: "inline-start",
    },
  }
)

function InputGroupAddon({
  className,
  align = "inline-start",
  ...props
}: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
  return (
    <div
      role="group"
      data-slot="input-group-addon"
      data-align={align}
      className={cn(inputGroupAddonVariants({ align }), className)}
      onClick={(e) => {
        if ((e.target as HTMLElement).closest("button")) {
          return
        }
        e.currentTarget.parentElement?.querySelector("input")?.focus()
      }}
      {...props}
    />
  )
}

const inputGroupButtonVariants = cva(
  "text-sm shadow-none flex gap-2 items-center",
  {
    variants: {
      size: {
        xs: "h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2",
        sm: "h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5",
        "icon-xs":
          "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
        "icon-sm": "size-8 p-0 has-[>svg]:p-0",
      },
    },
    defaultVariants: {
      size: "xs",
    },
  }
)

function InputGroupButton({
  className,
  type = "button",
  variant = "ghost",
  size = "xs",
  ...props
}: Omit<React.ComponentProps<typeof Button>, "size"> &
  VariantProps<typeof inputGroupButtonVariants>) {
  return (
    <Button
      type={type}
      data-size={size}
      variant={variant}
      className={cn(inputGroupButtonVariants({ size }), className)}
      {...props}
    />
  )
}

function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
  return (
    <span
      className={cn(
        "text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      {...props}
    />
  )
}

function InputGroupInput({
  className,
  ...props
}: React.ComponentProps<"input">) {
  return (
    <Input
      data-slot="input-group-control"
      className={cn(
        "flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
        className
      )}
      {...props}
    />
  )
}

function InputGroupTextarea({
  className,
  ...props
}: React.ComponentProps<"textarea">) {
  return (
    <Textarea
      data-slot="input-group-control"
      className={cn(
        "flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",
        className
      )}
      {...props}
    />
  )
}

export {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupText,
  InputGroupInput,
  InputGroupTextarea,
}


================================================
FILE: src/components/ui/input.tsx
================================================
import * as React from "react"

import { cn } from "@/lib/utils"

function Input({ className, type, ...props }: React.ComponentProps<"input">) {
  return (
    <input
      type={type}
      data-slot="input"
      className={cn(
        "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
        "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
        "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
        className
      )}
      {...props}
    />
  )
}

export { Input }


================================================
FILE: src/components/ui/label.tsx
================================================
"use client"

import * as React from "react"
import { Label as LabelPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"

function Label({
  className,
  ...props
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
  return (
    <LabelPrimitive.Root
      data-slot="label"
      className={cn(
        "text-foreground text-sm leading-4 font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
        className
      )}
      {...props}
    />
  )
}

export { Label }


================================================
FILE: src/components/ui/language-icons.tsx
================================================
import { FaJs, FaPython, FaJava } from "react-icons/fa";
import { SiTypescript, SiNextdotjs, SiReact } from "react-icons/si";

export const languageIcons: Record<
  string,
  React.ComponentType<{ size?: number }>
> = {
  js: FaJs,
  ts: SiTypescript,
  py: FaPython,
  java: FaJava,
  nextjs: SiNextdotjs,
  react: SiReact,
};


================================================
FILE: src/components/ui/logo.tsx
================================================
import Link from "next/link";

export const LogoIcon = () => (
  <svg
    width="43"
    height="30"
    viewBox="0 0 43 30"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
    role="img"
    aria-labelledby="qs-logo-title qs-logo-description"
  >
    <title id="qs-logo-title">QuickSnip Logo</title>
    <desc id="qs-logo-description">
      A light blue logo with a thunderbolt in brackets
    </desc>
    <path
      d="M11.0123 3.81725C8.81785 3.81725 7.7866 4.47726 7.9186 5.79726L8.4136 10.871C8.4301 11.0195 8.43835 11.234 8.43835 11.5145C8.43835 12.3065 8.2486 12.917 7.8691 13.346C7.4896 13.7585 6.90385 14.039 6.11185 14.1875C6.92035 14.336 7.5061 14.6165 7.8691 15.029C8.2486 15.425 8.43835 16.0273 8.43835 16.8358C8.43835 17.1163 8.4301 17.339 8.4136 17.504L7.9186 22.5778C7.8526 23.3038 8.1001 23.8153 8.6611 24.1123C9.2221 24.4093 10.0058 24.5578 11.0123 24.5578V26.216C9.52735 26.216 8.3146 25.9603 7.3741 25.4488C6.4336 24.9373 5.96335 24.071 5.96335 22.85C5.96335 22.6355 5.9716 22.4705 5.9881 22.355L6.50785 17.6525C6.52435 17.537 6.5326 17.372 6.5326 17.1575C6.5326 16.4315 6.26035 15.8953 5.71585 15.5488C5.17135 15.2023 4.18135 15.029 2.74585 15.029V13.346C4.18135 13.346 5.17135 13.181 5.71585 12.851C6.26035 12.5045 6.5326 11.9683 6.5326 11.2423C6.5326 11.0443 6.52435 10.8875 6.50785 10.772L5.9881 6.02001C5.9716 5.90451 5.96335 5.73951 5.96335 5.525C5.96335 4.304 6.4336 3.43775 7.3741 2.92625C8.3146 2.41476 9.52735 2.15901 11.0123 2.15901V3.81725Z"
      fill="#00b5b8"
    />
    <path
      d="M30.6782 13.2235L18.41 25.8383C18.0691 26.1878 17.5037 25.7829 17.7253 25.3482L22.0922 16.7309C22.1252 16.6659 22.1411 16.5935 22.1381 16.5206C22.1352 16.4477 22.1136 16.3767 22.0754 16.3146C22.0372 16.2524 21.9836 16.2011 21.9199 16.1656C21.8562 16.13 21.7844 16.1114 21.7114 16.1116H12.5259C12.442 16.1115 12.36 16.0867 12.2902 16.0402C12.2203 15.9937 12.1658 15.9277 12.1333 15.8503C12.1008 15.7729 12.0919 15.6877 12.1077 15.6053C12.1235 15.5229 12.1632 15.447 12.2219 15.3871L23.6008 3.85901C23.9304 3.52517 24.4787 3.89169 24.2955 4.32496L21.0324 12.0174C21.0048 12.0827 20.9938 12.1537 21.0006 12.2242C21.0073 12.2947 21.0315 12.3625 21.071 12.4213C21.1105 12.4801 21.164 12.5281 21.2267 12.561C21.2894 12.5939 21.3593 12.6107 21.4302 12.6098L30.3671 12.499C30.4514 12.4977 30.5343 12.5214 30.6051 12.5671C30.676 12.6129 30.7317 12.6786 30.7651 12.7561C30.7986 12.8335 30.8083 12.9191 30.7931 13.0021C30.7779 13.085 30.7384 13.1616 30.6796 13.2221L30.6782 13.2235Z"
      stroke="#00b5b8"
      strokeWidth="1.65"
    />
    <path
      d="M31.7378 2.15901C33.2228 2.15901 34.4355 2.41476 35.376 2.92625C36.3165 3.43775 36.7868 4.304 36.7868 5.525C36.7868 5.73951 36.7785 5.90451 36.762 6.02001L36.2423 10.772C36.2258 10.8545 36.2175 10.9783 36.2175 11.1433C36.2175 11.8858 36.5063 12.4385 37.0838 12.8015C37.6778 13.1645 38.6513 13.346 40.0043 13.346V15.029C38.6348 15.029 37.6613 15.2105 37.0838 15.5735C36.5063 15.9365 36.2175 16.4975 36.2175 17.2565C36.2175 17.4215 36.2258 17.5535 36.2423 17.6525L36.762 22.355C36.7785 22.4705 36.7868 22.6355 36.7868 22.85C36.7868 24.071 36.3165 24.9373 35.376 25.4488C34.4355 25.9603 33.2228 26.216 31.7378 26.216V24.5578C32.7443 24.5578 33.528 24.4093 34.089 24.1123C34.65 23.8153 34.8975 23.3038 34.8315 22.5778L34.3365 17.504C34.3035 17.174 34.287 16.9513 34.287 16.8358C34.287 16.0108 34.4933 15.4003 34.9058 15.0043C35.3183 14.6083 35.9865 14.3443 36.9105 14.2123C36.0195 14.0473 35.3595 13.7585 34.9305 13.346C34.518 12.917 34.3118 12.2983 34.3118 11.4898C34.3118 11.2258 34.32 11.0195 34.3365 10.871L34.8315 5.79726C34.9635 4.47726 33.9323 3.81725 31.7378 3.81725V2.15901Z"
      fill="#00b5b8"
    />
  </svg>
);

const Logo = () => {
  return (
    <Link href="/" className="flex items-center gap-2">
      <LogoIcon />
      <span className="font-bold text-lg">QuickSnip</span>
    </Link>
  );
};

export default Logo;


================================================
FILE: src/components/ui/navigation-menu.tsx
================================================
import * as React from "react"
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
import { cva } from "class-variance-authority"
import { ChevronDownIcon } from "lucide-react"

import { cn } from "@/lib/utils"

function NavigationMenu({
  className,
  children,
  viewport = true,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
  viewport?: boolean
}) {
  return (
    <NavigationMenuPrimitive.Root
      data-slot="navigation-menu"
      data-viewport={viewport}
      className={cn(
        "group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
        className
      )}
      {...props}
    >
      {children}
      {viewport && <NavigationMenuViewport />}
    </NavigationMenuPrimitive.Root>
  )
}

function NavigationMenuList({
  className,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
  return (
    <NavigationMenuPrimitive.List
      data-slot="navigation-menu-list"
      className={cn(
        "group flex flex-1 list-none items-center justify-center gap-1",
        className
      )}
      {...props}
    />
  )
}

function NavigationMenuItem({
  className,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
  return (
    <NavigationMenuPrimitive.Item
      data-slot="navigation-menu-item"
      className={cn("relative", className)}
      {...props}
    />
  )
}

const navigationMenuTriggerStyle = cva(
  "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1"
)

function NavigationMenuTrigger({
  className,
  children,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
  return (
    <NavigationMenuPrimitive.Trigger
      data-slot="navigation-menu-trigger"
      className={cn(navigationMenuTriggerStyle(), "group", className)}
      {...props}
    >
      {children}{" "}
      <ChevronDownIcon
        className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
        aria-hidden="true"
      />
    </NavigationMenuPrimitive.Trigger>
  )
}

function NavigationMenuContent({
  className,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
  return (
    <NavigationMenuPrimitive.Content
      data-slot="navigation-menu-content"
      className={cn(
        "data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto",
        "group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
        className
      )}
      {...props}
    />
  )
}

function NavigationMenuViewport({
  className,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
  return (
    <div
      className={cn(
        "absolute top-full left-0 isolate z-50 flex justify-center"
      )}
    >
      <NavigationMenuPrimitive.Viewport
        data-slot="navigation-menu-viewport"
        className={cn(
          "origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]",
          className
        )}
        {...props}
      />
    </div>
  )
}

function NavigationMenuLink({
  className,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
  return (
    <NavigationMenuPrimitive.Link
      data-slot="navigation-menu-link"
      className={cn(
        "data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      {...props}
    />
  )
}

function NavigationMenuIndicator({
  className,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
  return (
    <NavigationMenuPrimitive.Indicator
      data-slot="navigation-menu-indicator"
      className={cn(
        "data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden",
        className
      )}
      {...props}
    >
      <div className="bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" />
    </NavigationMenuPrimitive.Indicator>
  )
}

export {
  NavigationMenu,
  NavigationMenuList,
  NavigationMenuItem,
  NavigationMenuContent,
  NavigationMenuTrigger,
  NavigationMenuLink,
  NavigationMenuIndicator,
  NavigationMenuViewport,
  navigationMenuTriggerStyle,
}


================================================
FILE: src/components/ui/select.tsx
================================================
"use client"

import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"

import { cn } from "@/lib/utils"

function Select({
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
  return <SelectPrimitive.Root data-slot="select" {...props} />
}

function SelectGroup({
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
  return <SelectPrimitive.Group data-slot="select-group" {...props} />
}

function SelectValue({
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
  return <SelectPrimitive.Value data-slot="select-value" {...props} />
}

function SelectTrigger({
  className,
  size = "default",
  children,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
  size?: "sm" | "default"
}) {
  return (
    <SelectPrimitive.Trigger
      data-slot="select-trigger"
      data-size={size}
      className={cn(
        "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      {...props}
    >
      {children}
      <SelectPrimitive.Icon asChild>
        <ChevronDownIcon className="size-4 opacity-50" />
      </SelectPrimitive.Icon>
    </SelectPrimitive.Trigger>
  )
}

function SelectContent({
  className,
  children,
  position = "popper",
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
  return (
    <SelectPrimitive.Portal>
      <SelectPrimitive.Content
        data-slot="select-content"
        className={cn(
          "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
          position === "popper" &&
            "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
          className
        )}
        position={position}
        {...props}
      >
        <SelectScrollUpButton />
        <SelectPrimitive.Viewport
          className={cn(
            "p-1",
            position === "popper" &&
              "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
          )}
        >
          {children}
        </SelectPrimitive.Viewport>
        <SelectScrollDownButton />
      </SelectPrimitive.Content>
    </SelectPrimitive.Portal>
  )
}

function SelectLabel({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
  return (
    <SelectPrimitive.Label
      data-slot="select-label"
      className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
      {...props}
    />
  )
}

function SelectItem({
  className,
  children,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
  return (
    <SelectPrimitive.Item
      data-slot="select-item"
      className={cn(
        "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
        className
      )}
      {...props}
    >
      <span className="absolute right-2 flex size-3.5 items-center justify-center">
        <SelectPrimitive.ItemIndicator>
          <CheckIcon className="size-4" />
        </SelectPrimitive.ItemIndicator>
      </span>
      <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
    </SelectPrimitive.Item>
  )
}

function SelectSeparator({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
  return (
    <SelectPrimitive.Separator
      data-slot="select-separator"
      className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
      {...props}
    />
  )
}

function SelectScrollUpButton({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
  return (
    <SelectPrimitive.ScrollUpButton
      data-slot="select-scroll-up-button"
      className={cn(
        "flex cursor-default items-center justify-center py-1",
        className
      )}
      {...props}
    >
      <ChevronUpIcon className="size-4" />
    </SelectPrimitive.ScrollUpButton>
  )
}

function SelectScrollDownButton({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
  return (
    <SelectPrimitive.ScrollDownButton
      data-slot="select-scroll-down-button"
      className={cn(
        "flex cursor-default items-center justify-center py-1",
        className
      )}
      {...props}
    >
      <ChevronDownIcon className="size-4" />
    </SelectPrimitive.ScrollDownButton>
  )
}

export {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectScrollDownButton,
  SelectScrollUpButton,
  SelectSeparator,
  SelectTrigger,
  SelectValue,
}


================================================
FILE: src/components/ui/separator.tsx
================================================
"use client"

import * as React from "react"
import * as SeparatorPrimitive from "@radix-ui/react-separator"

import { cn } from "@/lib/utils"

function Separator({
  className,
  orientation = "horizontal",
  decorative = true,
  ...props
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
  return (
    <SeparatorPrimitive.Root
      data-slot="separator"
      decorative={decorative}
      orientation={orientation}
      className={cn(
        "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
        className
      )}
      {...props}
    />
  )
}

export { Separator }


================================================
FILE: src/components/ui/sheet.tsx
================================================
"use client"

import * as React from "react"
import * as SheetPrimitive from "@radix-ui/react-dialog"
import { XIcon } from "lucide-react"

import { cn } from "@/lib/utils"

function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
  return <SheetPrimitive.Root data-slot="sheet" {...props} />
}

function SheetTrigger({
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
  return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
}

function SheetClose({
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
  return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
}

function SheetPortal({
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
  return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
}

function SheetOverlay({
  className,
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
  return (
    <SheetPrimitive.Overlay
      data-slot="sheet-overlay"
      className={cn(
        "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
        className
      )}
      {...props}
    />
  )
}

function SheetContent({
  className,
  children,
  side = "right",
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
  side?: "top" | "right" | "bottom" | "left"
}) {
  return (
    <SheetPortal>
      <SheetOverlay />
      <SheetPrimitive.Content
        data-slot="sheet-content"
        className={cn(
          "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
          side === "right" &&
            "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
          side === "left" &&
            "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
          side === "top" &&
            "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
          side === "bottom" &&
            "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
          className
        )}
        {...props}
      >
        {children}
        <SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
          <XIcon className="size-4" />
          <span className="sr-only">Close</span>
        </SheetPrimitive.Close>
      </SheetPrimitive.Content>
    </SheetPortal>
  )
}

function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="sheet-header"
      className={cn("flex flex-col gap-1.5 p-4", className)}
      {...props}
    />
  )
}

function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="sheet-footer"
      className={cn("mt-auto flex flex-col gap-2 p-4", className)}
      {...props}
    />
  )
}

function SheetTitle({
  className,
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
  return (
    <SheetPrimitive.Title
      data-slot="sheet-title"
      className={cn("text-foreground font-semibold", className)}
      {...props}
    />
  )
}

function SheetDescription({
  className,
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
  return (
    <SheetPrimitive.Description
      data-slot="sheet-description"
      className={cn("text-muted-foreground text-sm", className)}
      {...props}
    />
  )
}

export {
  Sheet,
  SheetTrigger,
  SheetClose,
  SheetContent,
  SheetHeader,
  SheetFooter,
  SheetTitle,
  SheetDescription,
}


================================================
FILE: src/components/ui/sidebar.tsx
================================================
"use client";

import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, VariantProps } from "class-variance-authority";
import { PanelLeftIcon } from "lucide-react";

import { useIsMobile } from "@/hooks/use-mobile";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
} from "@/components/ui/sheet";
import { Skeleton } from "@/components/ui/skeleton";
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip";

const SIDEBAR_COOKIE_NAME = "sidebar_state";
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
const SIDEBAR_WIDTH = "16rem";
const SIDEBAR_WIDTH_MOBILE = "18rem";
const SIDEBAR_WIDTH_ICON = "3rem";
const SIDEBAR_KEYBOARD_SHORTCUT = "b";

type SidebarContextProps = {
  state: "expanded" | "collapsed";
  open: boolean;
  setOpen: (open: boolean) => void;
  openMobile: boolean;
  setOpenMobile: (open: boolean) => void;
  isMobile: boolean;
  toggleSidebar: () => void;
};

const SidebarContext = React.createContext<SidebarContextProps | null>(null);

function useSidebar() {
  const context = React.useContext(SidebarContext);
  if (!context) {
    throw new Error("useSidebar must be used within a SidebarProvider.");
  }

  return context;
}

function SidebarProvider({
  defaultOpen = true,
  open: openProp,
  onOpenChange: setOpenProp,
  className,
  style,
  children,
  ...props
}: React.ComponentProps<"div"> & {
  defaultOpen?: boolean;
  open?: boolean;
  onOpenChange?: (open: boolean) => void;
}) {
  const isMobile = useIsMobile();
  const [openMobile, setOpenMobile] = React.useState(false);

  // This is the internal state of the sidebar.
  // We use openProp and setOpenProp for control from outside the component.
  const [_open, _setOpen] = React.useState(defaultOpen);
  const open = openProp ?? _open;
  const setOpen = React.useCallback(
    (value: boolean | ((value: boolean) => boolean)) => {
      const openState = typeof value === "function" ? value(open) : value;
      if (setOpenProp) {
        setOpenProp(openState);
      } else {
        _setOpen(openState);
      }

      // This sets the cookie to keep the sidebar state.
      document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
    },
    [setOpenProp, open]
  );

  // Helper to toggle the sidebar.
  const toggleSidebar = React.useCallback(() => {
    return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
  }, [isMobile, setOpen, setOpenMobile]);

  // Adds a keyboard shortcut to toggle the sidebar.
  React.useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      if (
        event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
        (event.metaKey || event.ctrlKey)
      ) {
        event.preventDefault();
        toggleSidebar();
      }
    };

    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, [toggleSidebar]);

  // We add a state so that we can do data-state="expanded" or "collapsed".
  // This makes it easier to style the sidebar with Tailwind classes.
  const state = open ? "expanded" : "collapsed";

  const contextValue = React.useMemo<SidebarContextProps>(
    () => ({
      state,
      open,
      setOpen,
      isMobile,
      openMobile,
      setOpenMobile,
      toggleSidebar,
    }),
    [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
  );

  return (
    <SidebarContext.Provider value={contextValue}>
      <TooltipProvider delayDuration={0}>
        <div
          data-slot="sidebar-wrapper"
          style={
            {
              "--sidebar-width": SIDEBAR_WIDTH,
              "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
              ...style,
            } as React.CSSProperties
          }
          className={cn(
            "group/sidebar-wrapper overflow-x-hidden has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full wrapper-lg",
            className
          )}
          {...props}
        >
          {children}
        </div>
      </TooltipProvider>
    </SidebarContext.Provider>
  );
}

function Sidebar({
  side = "left",
  variant = "sidebar",
  collapsible = "offcanvas",
  className,
  children,
  ...props
}: React.ComponentProps<"div"> & {
  side?: "left" | "right";
  variant?: "sidebar" | "floating" | "inset";
  collapsible?: "offcanvas" | "icon" | "none";
}) {
  const { isMobile, state, openMobile, setOpenMobile } = useSidebar();

  if (collapsible === "none") {
    return (
      <div
        data-slot="sidebar"
        className={cn(
          "bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col",
          className
        )}
        {...props}
      >
        {children}
      </div>
    );
  }

  if (isMobile) {
    return (
      <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
        <SheetContent
          data-sidebar="sidebar"
          data-slot="sidebar"
          data-mobile="true"
          className="bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
          style={
            {
              "--sidebar-width": SIDEBAR_WIDTH_MOBILE,
            } as React.CSSProperties
          }
          side={side}
        >
          <SheetHeader className="sr-only">
            <SheetTitle>Sidebar</SheetTitle>
            <SheetDescription>Displays the mobile sidebar.</SheetDescription>
          </SheetHeader>
          <div className="flex h-full w-full flex-col">{children}</div>
        </SheetContent>
      </Sheet>
    );
  }

  return (
    <div
      className="group relative peer text-sidebar-foreground hidden md:block"
      data-state={state}
      data-collapsible={state === "collapsed" ? collapsible : ""}
      data-variant={variant}
      data-side={side}
      data-slot="sidebar"
    >
      {/* This is what handles the sidebar gap on desktop */}
      <div
        data-slot="sidebar-gap"
        className={cn(
          "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
          "group-data-[collapsible=offcanvas]:w-0",
          "group-data-[side=right]:rotate-180",
          variant === "floating" || variant === "inset"
            ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"
            : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
        )}
      />
      <div
        data-slot="sidebar-container"
        className={cn(
          "absolute inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
          side === "left"
            ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
            : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
          // Adjust the padding for floating and inset variants.
          variant === "floating" || variant === "inset"
            ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
            : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
          className
        )}
        {...props}
      >
        <div
          data-sidebar="sidebar"
          data-slot="sidebar-inner"
          className="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm"
        >
          {children}
        </div>
      </div>
    </div>
  );
}

function SidebarTrigger({
  className,
  onClick,
  ...props
}: React.ComponentProps<typeof Button>) {
  const { toggleSidebar } = useSidebar();

  return (
    <Button
      data-sidebar="trigger"
      data-slot="sidebar-trigger"
      variant="ghost"
      size="icon"
      className={cn("size-7", className)}
      onClick={(event) => {
        onClick?.(event);
        toggleSidebar();
      }}
      {...props}
    >
      <PanelLeftIcon />
      <span className="sr-only">Toggle Sidebar</span>
    </Button>
  );
}

function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
  const { toggleSidebar } = useSidebar();

  return (
    <button
      data-sidebar="rail"
      data-slot="sidebar-rail"
      aria-label="Toggle Sidebar"
      tabIndex={-1}
      onClick={toggleSidebar}
      title="Toggle Sidebar"
      className={cn(
        "hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex",
        "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
        "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
        "hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
        "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
        "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
        className
      )}
      {...props}
    />
  );
}

function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
  return (
    <main
      data-slot="sidebar-inset"
      className={cn(
        "bg-background relative flex w-full flex-1 flex-col",
        "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
        className
      )}
      {...props}
    />
  );
}

function SidebarInput({
  className,
  ...props
}: React.ComponentProps<typeof Input>) {
  return (
    <Input
      data-slot="sidebar-input"
      data-sidebar="input"
      className={cn("bg-background h-8 w-full shadow-none", className)}
      {...props}
    />
  );
}

function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="sidebar-header"
      data-sidebar="header"
      className={cn("flex flex-col gap-2 p-2", className)}
      {...props}
    />
  );
}

function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="sidebar-footer"
      data-sidebar="footer"
      className={cn("flex flex-col gap-2 p-2", className)}
      {...props}
    />
  );
}

function SidebarSeparator({
  className,
  ...props
}: React.ComponentProps<typeof Separator>) {
  return (
    <Separator
      data-slot="sidebar-separator"
      data-sidebar="separator"
      className={cn("bg-sidebar-border mx-2 w-auto", className)}
      {...props}
    />
  );
}

function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="sidebar-content"
      data-sidebar="content"
      className={cn(
        "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
        className
      )}
      {...props}
    />
  );
}

function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="sidebar-group"
      data-sidebar="group"
      className={cn("relative flex w-full min-w-0 flex-col p-2", className)}
      {...props}
    />
  );
}

function SidebarGroupLabel({
  className,
  asChild = false,
  ...props
}: React.ComponentProps<"div"> & { asChild?: boolean }) {
  const Comp = asChild ? Slot : "div";

  return (
    <Comp
      data-slot="sidebar-group-label"
      data-sidebar="group-label"
      className={cn(
        "text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
        "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
        className
      )}
      {...props}
    />
  );
}

function SidebarGroupAction({
  className,
  asChild = false,
  ...props
}: React.ComponentProps<"button"> & { asChild?: boolean }) {
  const Comp = asChild ? Slot : "button";

  return (
    <Comp
      data-slot="sidebar-group-action"
      data-sidebar="group-action"
      className={cn(
        "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
        // Increases the hit area of the button on mobile.
        "after:absolute after:-inset-2 md:after:hidden",
        "group-data-[collapsible=icon]:hidden",
        className
      )}
      {...props}
    />
  );
}

function SidebarGroupContent({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="sidebar-group-content"
      data-sidebar="group-content"
      className={cn("w-full text-sm", className)}
      {...props}
    />
  );
}

function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
  return (
    <ul
      data-slot="sidebar-menu"
      data-sidebar="menu"
      className={cn("flex w-full min-w-0 flex-col gap-1", className)}
      {...props}
    />
  );
}

function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
  return (
    <li
      data-slot="sidebar-menu-item"
      data-sidebar="menu-item"
      className={cn("group/menu-item relative", className)}
      {...props}
    />
  );
}

const sidebarMenuButtonVariants = cva(
  "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
  {
    variants: {
      variant: {
        default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
        outline:
          "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]",
      },
      size: {
        default: "h-8 text-sm",
        sm: "h-7 text-xs",
        lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
);

function SidebarMenuButton({
  asChild = false,
  isActive = false,
  variant = "default",
  size = "default",
  tooltip,
  className,
  ...props
}: React.ComponentProps<"button"> & {
  asChild?: boolean;
  isActive?: boolean;
  tooltip?: string | React.ComponentProps<typeof TooltipContent>;
} & VariantProps<typeof sidebarMenuButtonVariants>) {
  const Comp = asChild ? Slot : "button";
  const { isMobile, state } = useSidebar();

  const button = (
    <Comp
      data-slot="sidebar-menu-button"
      data-sidebar="menu-button"
      data-size={size}
      data-active={isActive}
      className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
      {...props}
    />
  );

  if (!tooltip) {
    return button;
  }

  if (typeof tooltip === "string") {
    tooltip = {
      children: tooltip,
    };
  }

  return (
    <Tooltip>
      <TooltipTrigger asChild>{button}</TooltipTrigger>
      <TooltipContent
        side="right"
        align="center"
        hidden={state !== "collapsed" || isMobile}
        {...tooltip}
      />
    </Tooltip>
  );
}

function SidebarMenuAction({
  className,
  asChild = false,
  showOnHover = false,
  ...props
}: React.ComponentProps<"button"> & {
  asChild?: boolean;
  showOnHover?: boolean;
}) {
  const Comp = asChild ? Slot : "button";

  return (
    <Comp
      data-slot="sidebar-menu-action"
      data-sidebar="menu-action"
      className={cn(
        "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
        // Increases the hit area of the button on mobile.
        "after:absolute after:-inset-2 md:after:hidden",
        "peer-data-[size=sm]/menu-button:top-1",
        "peer-data-[size=default]/menu-button:top-1.5",
        "peer-data-[size=lg]/menu-button:top-2.5",
        "group-data-[collapsible=icon]:hidden",
        showOnHover &&
          "peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0",
        className
      )}
      {...props}
    />
  );
}

function SidebarMenuBadge({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="sidebar-menu-badge"
      data-sidebar="menu-badge"
      className={cn(
        "text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none",
        "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground",
        "peer-data-[size=sm]/menu-button:top-1",
        "peer-data-[size=default]/menu-button:top-1.5",
        "peer-data-[size=lg]/menu-button:top-2.5",
        "group-data-[collapsible=icon]:hidden",
        className
      )}
      {...props}
    />
  );
}

function SidebarMenuSkeleton({
  className,
  showIcon = false,
  ...props
}: React.ComponentProps<"div"> & {
  showIcon?: boolean;
}) {
  // Random width between 50 to 90%.
  const width = React.useMemo(() => {
    return `${Math.floor(Math.random() * 40) + 50}%`;
  }, []);

  return (
    <div
      data-slot="sidebar-menu-skeleton"
      data-sidebar="menu-skeleton"
      className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
      {...props}
    >
      {showIcon && (
        <Skeleton
          className="size-4 rounded-md"
          data-sidebar="menu-skeleton-icon"
        />
      )}
      <Skeleton
        className="h-4 max-w-(--skeleton-width) flex-1"
        data-sidebar="menu-skeleton-text"
        style={
          {
            "--skeleton-width": width,
          } as React.CSSProperties
        }
      />
    </div>
  );
}

function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
  return (
    <ul
      data-slot="sidebar-menu-sub"
      data-sidebar="menu-sub"
      className={cn(
        "border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5",
        "group-data-[collapsible=icon]:hidden",
        className
      )}
      {...props}
    />
  );
}

function SidebarMenuSubItem({
  className,
  ...props
}: React.ComponentProps<"li">) {
  return (
    <li
      data-slot="sidebar-menu-sub-item"
      data-sidebar="menu-sub-item"
      className={cn("group/menu-sub-item relative", className)}
      {...props}
    />
  );
}

function SidebarMenuSubButton({
  asChild = false,
  size = "md",
  isActive = false,
  className,
  ...props
}: React.ComponentProps<"a"> & {
  asChild?: boolean;
  size?: "sm" | "md";
  isActive?: boolean;
}) {
  const Comp = asChild ? Slot : "a";

  return (
    <Comp
      data-slot="sidebar-menu-sub-button"
      data-sidebar="menu-sub-button"
      data-size={size}
      data-active={isActive}
      className={cn(
        "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
        "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
        size === "sm" && "text-xs",
        size === "md" && "text-sm",
        "group-data-[collapsible=icon]:hidden",
        className
      )}
      {...props}
    />
  );
}

export {
  Sidebar,
  SidebarContent,
  SidebarFooter,
  SidebarGroup,
  SidebarGroupAction,
  SidebarGroupContent,
  SidebarGroupLabel,
  SidebarHeader,
  SidebarInput,
  SidebarInset,
  SidebarMenu,
  SidebarMenuAction,
  SidebarMenuBadge,
  SidebarMenuButton,
  SidebarMenuItem,
  SidebarMenuSkeleton,
  SidebarMenuSub,
  SidebarMenuSubButton,
  SidebarMenuSubItem,
  SidebarProvider,
  SidebarRail,
  SidebarSeparator,
  SidebarTrigger,
  useSidebar,
};


================================================
FILE: src/components/ui/skeleton.tsx
================================================
import { cn } from "@/lib/utils"

function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="skeleton"
      className={cn("bg-accent animate-pulse rounded-md", className)}
      {...props}
    />
  )
}

export { Skeleton }


================================================
FILE: src/components/ui/snippet-detail.tsx
================================================
export default function SnippetDetail() {
  return (
    <div>
      <p>Snippet Detail</p>
    </div>
  );
}


================================================
FILE: src/components/ui/snippet-item.tsx
================================================
import { Card, CardContent, CardTitle } from "./card";
import type { SnippetType } from "@/types";
import Link from "next/link";
import { languageIcons } from "./language-icons";

interface SnippetItemProps {
  snippet: SnippetType;
}

export default function SnippetItem({ snippet }: SnippetItemProps) {
  const { title, languages } = snippet;

  return (
    <Link href={`/snippets/${snippet.category}/${snippet.id}`}>
      <Card className="py-2 hover:outline-2 hover:outline-border">
        <CardContent className="space-y-4 px-4">
          <CardTitle className="text-lg font-bold">{title}</CardTitle>
          <ul className="flex gap-2 ml-auto w-fit flex-wrap">
            {languages.map((language) => {
              const Icon = languageIcons[language.toLowerCase()];
              return <li key={language}>{Icon ? <Icon size={24} /> : null}</li>;
            })}
          </ul>
        </CardContent>
      </Card>
    </Link>
  );
}


================================================
FILE: src/components/ui/snippet-search.tsx
================================================
export default function SnippetSearch() {
  return (
    <div>
      <p>Snippet Search</p>
    </div>
  );
}


================================================
FILE: src/components/ui/switch.tsx
================================================
"use client"

import * as React from "react"
import { Switch as SwitchPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"

function Switch({
  className,
  ...props
}: React.ComponentProps<typeof SwitchPrimitive.Root>) {
  return (
    <SwitchPrimitive.Root
      data-slot="switch"
      className={cn(
        "peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:ring-ring/50 inline-flex h-6 w-10 shrink-0 items-center rounded-full border-2 border-transparent transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
        className
      )}
      {...props}
    >
      <SwitchPrimitive.Thumb
        data-slot="switch-thumb"
        className={cn(
          "bg-background pointer-events-none block size-5 rounded-full shadow-xs ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0 data-[state=checked]:rtl:-translate-x-4"
        )}
      />
    </SwitchPrimitive.Root>
  )
}

export { Switch }


================================================
FILE: src/components/ui/tabs.tsx
================================================
"use client"

import * as React from "react"
import * as TabsPrimitive from "@radix-ui/react-tabs"

import { cn } from "@/lib/utils"

function Tabs({
  className,
  ...props
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
  return (
    <TabsPrimitive.Root
      data-slot="tabs"
      className={cn("flex flex-col gap-2", className)}
      {...props}
    />
  )
}

function TabsList({
  className,
  ...props
}: React.ComponentProps<typeof TabsPrimitive.List>) {
  return (
    <TabsPrimitive.List
      data-slot="tabs-list"
      className={cn(
        "bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
        className
      )}
      {...props}
    />
  )
}

function TabsTrigger({
  className,
  ...props
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
  return (
    <TabsPrimitive.Trigger
      data-slot="tabs-trigger"
      className={cn(
        "data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      {...props}
    />
  )
}

function TabsContent({
  className,
  ...props
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
  return (
    <TabsPrimitive.Content
      data-slot="tabs-content"
      className={cn("flex-1 outline-none", className)}
      {...props}
    />
  )
}

export { Tabs, TabsList, TabsTrigger, TabsContent }


================================================
FILE: src/components/ui/textarea.tsx
================================================
import * as React from "react"

import { cn } from "@/lib/utils"

function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
  return (
    <textarea
      data-slot="textarea"
      className={cn(
        "border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
        className
      )}
      {...props}
    />
  )
}

export { Textarea }


================================================
FILE: src/components/ui/tooltip.tsx
================================================
"use client"

import * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"

import { cn } from "@/lib/utils"

function TooltipProvider({
  delayDuration = 0,
  ...props
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
  return (
    <TooltipPrimitive.Provider
      data-slot="tooltip-provider"
      delayDuration={delayDuration}
      {...props}
    />
  )
}

function Tooltip({
  ...props
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
  return (
    <TooltipProvider>
      <TooltipPrimitive.Root data-slot="tooltip" {...props} />
    </TooltipProvider>
  )
}

function TooltipTrigger({
  ...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
  return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
}

function TooltipContent({
  className,
  sideOffset = 0,
  children,
  ...props
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
  return (
    <TooltipPrimitive.Portal>
      <TooltipPrimitive.Content
        data-slot="tooltip-content"
        sideOffset={sideOffset}
        className={cn(
          "bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
          className
        )}
        {...props}
      >
        {children}
        <TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
      </TooltipPrimitive.Content>
    </TooltipPrimitive.Portal>
  )
}

export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }


================================================
FILE: src/data/extensions.ts
================================================
import type { ExtensionType } from "@/types";

export const EXTENSIONS: ExtensionType[] = [
  {
    icon: "/assets/extensions/cli.svg",
    name: "CLI",
    description: "CLI extension for QuickSnip.",
    guide_url: "/guide/extensions/quicksnip-cli",
    source_url: "https://github.com",
    downloads: 9865,
  },
  {
    icon: "/assets/extensions/raycast.svg",
    name: "Raycast",
    description: "Raycast extension for QuickSnip.",
    guide_url: "/guide/extensions/quicksnip-raycast",
    source_url: "https://github.com",
    downloads: 3580,
  },
  {
    icon: "/assets/extensions/vscode.svg",
    name: "VS Code",
    description: "VS Code extension for QuickSnip.",
    guide_url: "/guide/extensions/quicksnip-vscode",
    source_url: "https://github.com",
    downloads: 8400,
  },
];

export const FUTURE_EXTENSIONS = [
  {
    name: "JetBrains",
    icon: "/assets/extensions/jetbrains.svg",
    shortcut_name: "JB",
  },
  {
    name: "NeoVim",
    icon: "/assets/extensions/neovim.svg",
    shortcut_name: "NV",
  },
  {
    name: "Vim",
    icon: "/assets/extensions/vim.svg",
    shortcut_name: "VM",
  },
];


================================================
FILE: src/data/meta.ts
================================================
export const GITHUB_URL = "https://github.com/quicksnip-dev/quicksnip";
export const DISCORD_URL = "https://discord.com/invite/Nm5K46yUy5";
export const SPONSOR_URL = "https://ko-fi.com/D1D217QALD";


================================================
FILE: src/data/yt-videos.ts
================================================
export const YOUTUBE_VIDEOS = [
  {
    id: "2_AfhnMVuSY",
    title: "We made QuickSnip even better (v1)",
  },
  {
    id: "BhRi7fJzPgk",
    title: "I built a library of code snippets for developers",
  },
];


================================================
FILE: src/hooks/use-fetch.ts
================================================
import { useEffect, useState } from "react";

export const useFetch = <T>(url: string) => {
  const [data, setData] = useState<T | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [loading, setLoading] = useState<boolean>(true);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const res = await fetch(url);
        if (!res.ok) {
          throw new Error(`Failed to fetch data from ${url}`);
        }
        const result: T = await res.json();
        setData(result);
      } catch (err) {
        setError((err as Error).message);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};


================================================
FILE: src/hooks/use-mobile.ts
================================================
import * as React from "react"

const MOBILE_BREAKPOINT = 768

export function useIsMobile() {
  const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)

  React.useEffect(() => {
    const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
    const onChange = () => {
      setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
    }
    mql.addEventListener("change", onChange)
    setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
    return () => mql.removeEventListener("change", onChange)
  }, [])

  return !!isMobile
}


================================================
FILE: src/lib/source.ts
================================================
// .source folder will be generated when you run `next dev`
import { docs } from "../../.source";
import { loader } from "fumadocs-core/source";

export const source = loader({
  baseUrl: "/guide",
  source: docs.toFumadocsSource(),
});


================================================
FILE: src/lib/utils.ts
================================================
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

export function slugify(string: string, separator = "-") {
  return string
    .toString() // Cast to string (optional)
    .toLowerCase() // Convert the string to lowercase letters
    .trim() // Remove whitespace from both sides of a string (optional)
    .replace(/\s+/g, separator) // Replace spaces with {separator}
    .replace(/[^\w\-]+/g, "") // Remove all non-word chars
    .replace(/\_/g, separator) // Replace _ with {separator}
    .replace(/\-\-+/g, separator) // Replace multiple - with single {separator}
    .replace(/\-$/g, ""); // Remove trailing -
}

export function unslugify(string: string) {
  return string
    .trim()
    .replace(/\-/g, " ")
    .split(" ")
    .map((word) => word[0].toUpperCase() + word.slice(1))
    .join(" "); // Replace - with " "
}


================================================
FILE: src/store/useSnippetsStore.ts
================================================
import { SnippetType } from "@/types";
import { create } from "zustand";

interface SnippetsStore {
  snippets: SnippetType[];
  selectedCategory: string;

  setSnippets: (snippets: SnippetType[]) => void;
  setCategory: (category: string) => void;

  categories: () => { name: string; count: number }[];
}

export const useSnippetsStore = create<SnippetsStore>((set, get) => ({
  snippets: [],
  selectedLanguage: "",
  selectedCategory: "all",

  setSnippets: (snippets) => set({ snippets: snippets }),
  setCategory: (category) => set({ selectedCategory: category }),

  categories: () => {
    const { snippets } = get();
    const counts: Record<string, number> = {};

    snippets.forEach((snippet) => {
      if (counts[snippet.category]) {
        counts[snippet.category]++;
      } else {
        counts[snippet.category] = 1;
      }
    });

    return Object.entries(counts).map(([name, count]) => ({ name, count }));
  },
}));


================================================
FILE: src/types/index.ts
================================================
export type ExtensionType = {
  icon: string;
  name: string;
  description: string;
  guide_url: string;
  source_url: string;
  downloads: number;
};

export type SnippetType = {
  id: string;
  category: string;
  title: string;
  description: string;
  languages: string[];
  contributors: string[];
  tags: string[];
};

export type FullSnippet = {
  id: string;
  category: string;
  title: string;
  description: string;
  languages: string[];
  contributors: string[];
  tags: string[];
  snippets: Record<string, string>;
};


================================================
FILE: tailwind.config.mjs
================================================
const config = {
  // content: ["./app/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"],
  theme: { extend: {} },
  plugins: [require("@tailwindcss/typography")],
};

export default config;


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}
Download .txt
gitextract_nak_pvkj/

├── .gitignore
├── .source/
│   └── index.ts
├── CONTRIBUTING.md
├── README.md
├── components.json
├── content/
│   └── docs/
│       ├── comparison.mdx
│       ├── contributing/
│       │   ├── adding-snippets.mdx
│       │   ├── how-to-contribute.mdx
│       │   ├── meta.json
│       │   ├── modifying-snippets.mdx
│       │   └── third-party-apps.mdx
│       ├── extensions/
│       │   ├── meta.json
│       │   ├── quicksnip-cli.mdx
│       │   ├── quicksnip-raycast.mdx
│       │   ├── quicksnip-vscode.mdx
│       │   └── quicksnip.mdx
│       ├── index.mdx
│       ├── installation.mdx
│       └── meta.json
├── eslint.config.mjs
├── mdx-components.tsx
├── next.config.ts
├── package.json
├── postcss.config.mjs
├── public/
│   └── data/
│       └── snippets/
│           ├── README.md
│           ├── all.json
│           ├── array-manipulation/
│           │   └── shuffle-array.json
│           ├── data-handling/
│           │   └── add-localstorage.json
│           ├── number-formatting/
│           │   └── number-to-currency.json
│           └── string-manipulation/
│               ├── reverse-string.json
│               ├── string-to-camel-case.json
│               ├── string-to-param-case.json
│               ├── string-to-pascal-case.json
│               ├── string-to-snake-case.json
│               ├── string-to-title-case.json
│               └── truncate-sring.json
├── scripts/
│   └── merge-snippets.ts
├── snippets/
│   ├── README.md
│   ├── array-manipulation/
│   │   ├── shuffle-array.json
│   │   └── shuffle-array.md
│   ├── data-handling/
│   │   ├── add-localstorage.json
│   │   └── add-localstorage.md
│   ├── number-formatting/
│   │   ├── number-to-currency.json
│   │   └── number-to-currency.md
│   └── string-manipulation/
│       ├── reverse-string.json
│       ├── reverse-string.md
│       ├── string-to-camel-case.json
│       ├── string-to-camel-case.md
│       ├── string-to-param-case.json
│       ├── string-to-param-case.md
│       ├── string-to-pascal-case.json
│       ├── string-to-pascal-case.md
│       ├── string-to-snake-case.json
│       ├── string-to-snake-case.md
│       ├── string-to-title-case.json
│       ├── string-to-title-case.md
│       ├── truncate-sring.json
│       └── truncate-sring.md
├── source.config.ts
├── src/
│   ├── app/
│   │   ├── api/
│   │   │   └── search/
│   │   │       └── route.ts
│   │   ├── community/
│   │   │   └── page.tsx
│   │   ├── contributing/
│   │   │   └── page.tsx
│   │   ├── extensions/
│   │   │   └── page.tsx
│   │   ├── globals.css
│   │   ├── guide/
│   │   │   ├── [[...slug]]/
│   │   │   │   └── page.tsx
│   │   │   └── layout.tsx
│   │   ├── layout.config.tsx
│   │   ├── layout.tsx
│   │   ├── page.tsx
│   │   └── snippets/
│   │       ├── [category]/
│   │       │   ├── [snippet]/
│   │       │   │   └── page.tsx
│   │       │   └── page.tsx
│   │       ├── layout.tsx
│   │       └── page.tsx
│   ├── components/
│   │   ├── layouts/
│   │   │   ├── available-for.tsx
│   │   │   ├── code-preview.tsx
│   │   │   ├── features.tsx
│   │   │   ├── footer.tsx
│   │   │   ├── header.tsx
│   │   │   ├── hero.tsx
│   │   │   ├── snippet-header.tsx
│   │   │   ├── snippet-list.tsx
│   │   │   ├── snippet-sidebar.tsx
│   │   │   └── sponsor.tsx
│   │   └── ui/
│   │       ├── accordion.tsx
│   │       ├── aspect-ratio.tsx
│   │       ├── avatar.tsx
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       ├── dark-mode-switch.tsx
│   │       ├── extension-item.tsx
│   │       ├── input-group.tsx
│   │       ├── input.tsx
│   │       ├── label.tsx
│   │       ├── language-icons.tsx
│   │       ├── logo.tsx
│   │       ├── navigation-menu.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── sidebar.tsx
│   │       ├── skeleton.tsx
│   │       ├── snippet-detail.tsx
│   │       ├── snippet-item.tsx
│   │       ├── snippet-search.tsx
│   │       ├── switch.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       └── tooltip.tsx
│   ├── data/
│   │   ├── extensions.ts
│   │   ├── meta.ts
│   │   └── yt-videos.ts
│   ├── hooks/
│   │   ├── use-fetch.ts
│   │   └── use-mobile.ts
│   ├── lib/
│   │   ├── source.ts
│   │   └── utils.ts
│   ├── store/
│   │   └── useSnippetsStore.ts
│   └── types/
│       └── index.ts
├── tailwind.config.mjs
└── tsconfig.json
Download .txt
SYMBOL INDEX (152 symbols across 52 files)

FILE: mdx-components.tsx
  function getMDXComponents (line 4) | function getMDXComponents(components?: MDXComponents): MDXComponents {

FILE: scripts/merge-snippets.ts
  constant SNIPPETS_DIR (line 4) | const SNIPPETS_DIR = path.join(process.cwd(), "snippets");
  constant OUTPUT_DIR (line 5) | const OUTPUT_DIR = path.join(process.cwd(), "public", "data", "snippets");
  function parseMarkdown (line 10) | function parseMarkdown(mdContent: string) {
  function mergeSnippet (line 27) | function mergeSnippet(category: string, name: string) {
  function scanSnippets (line 49) | function scanSnippets(dir: string) {
  function buildSnippets (line 71) | function buildSnippets() {

FILE: src/app/community/page.tsx
  function Communitypage (line 12) | function Communitypage() {

FILE: src/app/contributing/page.tsx
  function ContributingPage (line 8) | function ContributingPage() {

FILE: src/app/extensions/page.tsx
  function ExtensionsPage (line 6) | function ExtensionsPage() {

FILE: src/app/guide/[[...slug]]/page.tsx
  function Page (line 12) | async function Page(props: {
  function generateStaticParams (line 46) | async function generateStaticParams() {
  function generateMetadata (line 50) | async function generateMetadata(props: {

FILE: src/app/guide/layout.tsx
  function Layout (line 6) | function Layout({ children }: { children: ReactNode }) {

FILE: src/app/layout.tsx
  function RootLayout (line 23) | function RootLayout({

FILE: src/app/page.tsx
  function Home (line 6) | function Home() {

FILE: src/app/snippets/[category]/[snippet]/page.tsx
  type Props (line 22) | interface Props {
  function SnippetPage (line 26) | function SnippetPage({ params }: Props) {

FILE: src/app/snippets/[category]/page.tsx
  type Props (line 8) | interface Props {
  function Categories (line 12) | function Categories({ params }: Props) {

FILE: src/app/snippets/layout.tsx
  function SnippetsLayout (line 6) | function SnippetsLayout({

FILE: src/app/snippets/page.tsx
  function SnippetsPage (line 7) | function SnippetsPage() {

FILE: src/components/layouts/available-for.tsx
  function AvailableFor (line 8) | function AvailableFor() {

FILE: src/components/layouts/code-preview.tsx
  type Props (line 9) | type Props = {

FILE: src/components/layouts/features.tsx
  function Features (line 43) | function Features() {

FILE: src/components/layouts/footer.tsx
  constant NAV_ITEMS (line 4) | const NAV_ITEMS = [

FILE: src/components/layouts/header.tsx
  constant NAV_ITEMS (line 15) | const NAV_ITEMS = [
  constant SOCIAL_ITEMS (line 23) | const SOCIAL_ITEMS = [
  function Header (line 36) | function Header() {

FILE: src/components/layouts/snippet-header.tsx
  function SnippetHeader (line 17) | function SnippetHeader() {

FILE: src/components/layouts/snippet-list.tsx
  type SnippetListProps (line 5) | interface SnippetListProps {
  function SnippetList (line 9) | function SnippetList({ snippets }: SnippetListProps) {

FILE: src/components/layouts/snippet-sidebar.tsx
  function SnippetSidebar (line 83) | function SnippetSidebar() {

FILE: src/components/layouts/sponsor.tsx
  function Sponsor (line 12) | function Sponsor() {

FILE: src/components/ui/accordion.tsx
  function Accordion (line 9) | function Accordion({
  function AccordionItem (line 15) | function AccordionItem({
  function AccordionTrigger (line 28) | function AccordionTrigger({
  function AccordionContent (line 50) | function AccordionContent({

FILE: src/components/ui/aspect-ratio.tsx
  function AspectRatio (line 5) | function AspectRatio({

FILE: src/components/ui/avatar.tsx
  function Avatar (line 8) | function Avatar({
  function AvatarImage (line 24) | function AvatarImage({
  function AvatarFallback (line 37) | function AvatarFallback({

FILE: src/components/ui/button.tsx
  function Button (line 38) | function Button({

FILE: src/components/ui/card.tsx
  function Card (line 5) | function Card({ className, ...props }: React.ComponentProps<"div">) {
  function CardHeader (line 18) | function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
  function CardTitle (line 31) | function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
  function CardDescription (line 41) | function CardDescription({ className, ...props }: React.ComponentProps<"...
  function CardAction (line 51) | function CardAction({ className, ...props }: React.ComponentProps<"div">) {
  function CardContent (line 64) | function CardContent({ className, ...props }: React.ComponentProps<"div"...
  function CardFooter (line 74) | function CardFooter({ className, ...props }: React.ComponentProps<"div">) {

FILE: src/components/ui/dark-mode-switch.tsx
  function DarkModeSwitch (line 9) | function DarkModeSwitch() {

FILE: src/components/ui/extension-item.tsx
  function ExtensionItem (line 17) | function ExtensionItem({
  function NewExtensionItem (line 67) | function NewExtensionItem() {
  function ExtensionsAvatarGroup (line 79) | function ExtensionsAvatarGroup() {

FILE: src/components/ui/input-group.tsx
  function InputGroup (line 11) | function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
  function InputGroupAddon (line 60) | function InputGroupAddon({
  function InputGroupButton (line 100) | function InputGroupButton({
  function InputGroupText (line 119) | function InputGroupText({ className, ...props }: React.ComponentProps<"s...
  function InputGroupInput (line 131) | function InputGroupInput({
  function InputGroupTextarea (line 147) | function InputGroupTextarea({

FILE: src/components/ui/input.tsx
  function Input (line 5) | function Input({ className, type, ...props }: React.ComponentProps<"inpu...

FILE: src/components/ui/label.tsx
  function Label (line 8) | function Label({

FILE: src/components/ui/navigation-menu.tsx
  function NavigationMenu (line 8) | function NavigationMenu({
  function NavigationMenuList (line 32) | function NavigationMenuList({
  function NavigationMenuItem (line 48) | function NavigationMenuItem({
  function NavigationMenuTrigger (line 65) | function NavigationMenuTrigger({
  function NavigationMenuContent (line 85) | function NavigationMenuContent({
  function NavigationMenuViewport (line 102) | function NavigationMenuViewport({
  function NavigationMenuLink (line 124) | function NavigationMenuLink({
  function NavigationMenuIndicator (line 140) | function NavigationMenuIndicator({

FILE: src/components/ui/select.tsx
  function Select (line 9) | function Select({
  function SelectGroup (line 15) | function SelectGroup({
  function SelectValue (line 21) | function SelectValue({
  function SelectTrigger (line 27) | function SelectTrigger({
  function SelectContent (line 53) | function SelectContent({
  function SelectLabel (line 88) | function SelectLabel({
  function SelectItem (line 101) | function SelectItem({
  function SelectSeparator (line 125) | function SelectSeparator({
  function SelectScrollUpButton (line 138) | function SelectScrollUpButton({
  function SelectScrollDownButton (line 156) | function SelectScrollDownButton({

FILE: src/components/ui/separator.tsx
  function Separator (line 8) | function Separator({

FILE: src/components/ui/sheet.tsx
  function Sheet (line 9) | function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive....
  function SheetTrigger (line 13) | function SheetTrigger({
  function SheetClose (line 19) | function SheetClose({
  function SheetPortal (line 25) | function SheetPortal({
  function SheetOverlay (line 31) | function SheetOverlay({
  function SheetContent (line 47) | function SheetContent({
  function SheetHeader (line 84) | function SheetHeader({ className, ...props }: React.ComponentProps<"div"...
  function SheetFooter (line 94) | function SheetFooter({ className, ...props }: React.ComponentProps<"div"...
  function SheetTitle (line 104) | function SheetTitle({
  function SheetDescription (line 117) | function SheetDescription({

FILE: src/components/ui/sidebar.tsx
  constant SIDEBAR_COOKIE_NAME (line 28) | const SIDEBAR_COOKIE_NAME = "sidebar_state";
  constant SIDEBAR_COOKIE_MAX_AGE (line 29) | const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
  constant SIDEBAR_WIDTH (line 30) | const SIDEBAR_WIDTH = "16rem";
  constant SIDEBAR_WIDTH_MOBILE (line 31) | const SIDEBAR_WIDTH_MOBILE = "18rem";
  constant SIDEBAR_WIDTH_ICON (line 32) | const SIDEBAR_WIDTH_ICON = "3rem";
  constant SIDEBAR_KEYBOARD_SHORTCUT (line 33) | const SIDEBAR_KEYBOARD_SHORTCUT = "b";
  type SidebarContextProps (line 35) | type SidebarContextProps = {
  function useSidebar (line 47) | function useSidebar() {
  function SidebarProvider (line 56) | function SidebarProvider({
  function Sidebar (line 154) | function Sidebar({
  function SidebarTrigger (line 256) | function SidebarTrigger({
  function SidebarRail (line 282) | function SidebarRail({ className, ...props }: React.ComponentProps<"butt...
  function SidebarInset (line 307) | function SidebarInset({ className, ...props }: React.ComponentProps<"mai...
  function SidebarInput (line 321) | function SidebarInput({
  function SidebarHeader (line 335) | function SidebarHeader({ className, ...props }: React.ComponentProps<"di...
  function SidebarFooter (line 346) | function SidebarFooter({ className, ...props }: React.ComponentProps<"di...
  function SidebarSeparator (line 357) | function SidebarSeparator({
  function SidebarContent (line 371) | function SidebarContent({ className, ...props }: React.ComponentProps<"d...
  function SidebarGroup (line 385) | function SidebarGroup({ className, ...props }: React.ComponentProps<"div...
  function SidebarGroupLabel (line 396) | function SidebarGroupLabel({
  function SidebarGroupAction (line 417) | function SidebarGroupAction({
  function SidebarGroupContent (line 440) | function SidebarGroupContent({
  function SidebarMenu (line 454) | function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
  function SidebarMenuItem (line 465) | function SidebarMenuItem({ className, ...props }: React.ComponentProps<"...
  function SidebarMenuButton (line 498) | function SidebarMenuButton({
  function SidebarMenuAction (line 548) | function SidebarMenuAction({
  function SidebarMenuBadge (line 580) | function SidebarMenuBadge({
  function SidebarMenuSkeleton (line 602) | function SidebarMenuSkeleton({
  function SidebarMenuSub (line 640) | function SidebarMenuSub({ className, ...props }: React.ComponentProps<"u...
  function SidebarMenuSubItem (line 655) | function SidebarMenuSubItem({
  function SidebarMenuSubButton (line 669) | function SidebarMenuSubButton({

FILE: src/components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({ className, ...props }: React.ComponentProps<"div">) {

FILE: src/components/ui/snippet-detail.tsx
  function SnippetDetail (line 1) | function SnippetDetail() {

FILE: src/components/ui/snippet-item.tsx
  type SnippetItemProps (line 6) | interface SnippetItemProps {
  function SnippetItem (line 10) | function SnippetItem({ snippet }: SnippetItemProps) {

FILE: src/components/ui/snippet-search.tsx
  function SnippetSearch (line 1) | function SnippetSearch() {

FILE: src/components/ui/switch.tsx
  function Switch (line 8) | function Switch({

FILE: src/components/ui/tabs.tsx
  function Tabs (line 8) | function Tabs({
  function TabsList (line 21) | function TabsList({
  function TabsTrigger (line 37) | function TabsTrigger({
  function TabsContent (line 53) | function TabsContent({

FILE: src/components/ui/textarea.tsx
  function Textarea (line 5) | function Textarea({ className, ...props }: React.ComponentProps<"textare...

FILE: src/components/ui/tooltip.tsx
  function TooltipProvider (line 8) | function TooltipProvider({
  function Tooltip (line 21) | function Tooltip({
  function TooltipTrigger (line 31) | function TooltipTrigger({
  function TooltipContent (line 37) | function TooltipContent({

FILE: src/data/extensions.ts
  constant EXTENSIONS (line 3) | const EXTENSIONS: ExtensionType[] = [
  constant FUTURE_EXTENSIONS (line 30) | const FUTURE_EXTENSIONS = [

FILE: src/data/meta.ts
  constant GITHUB_URL (line 1) | const GITHUB_URL = "https://github.com/quicksnip-dev/quicksnip";
  constant DISCORD_URL (line 2) | const DISCORD_URL = "https://discord.com/invite/Nm5K46yUy5";
  constant SPONSOR_URL (line 3) | const SPONSOR_URL = "https://ko-fi.com/D1D217QALD";

FILE: src/data/yt-videos.ts
  constant YOUTUBE_VIDEOS (line 1) | const YOUTUBE_VIDEOS = [

FILE: src/hooks/use-mobile.ts
  constant MOBILE_BREAKPOINT (line 3) | const MOBILE_BREAKPOINT = 768
  function useIsMobile (line 5) | function useIsMobile() {

FILE: src/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {
  function slugify (line 8) | function slugify(string: string, separator = "-") {
  function unslugify (line 20) | function unslugify(string: string) {

FILE: src/store/useSnippetsStore.ts
  type SnippetsStore (line 4) | interface SnippetsStore {

FILE: src/types/index.ts
  type ExtensionType (line 1) | type ExtensionType = {
  type SnippetType (line 10) | type SnippetType = {
  type FullSnippet (line 20) | type FullSnippet = {
Condensed preview — 119 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (173K chars).
[
  {
    "path": ".gitignore",
    "chars": 491,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
  },
  {
    "path": ".source/index.ts",
    "chars": 3285,
    "preview": "// @ts-nocheck -- skip type checking\nimport * as docs_10 from \"../content/docs/extensions/quicksnip.mdx?collection=docs&"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 7474,
    "preview": "# Contributing to QuickSnip\n\n> THIS FILE NEEDS TO BE REWORKED. IT'S IMPORTED FROM PREVIOUS VERSION FOR TESTING.\n\nHey the"
  },
  {
    "path": "README.md",
    "chars": 2488,
    "preview": "# QuickSnip\n\nQuickSnip is an open-source tool designed for developers who want to organize, search, and share code snipp"
  },
  {
    "path": "components.json",
    "chars": 430,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"style\": \"new-york\",\n  \"rsc\": true,\n  \"tsx\": true,\n  \"tailwind\": {"
  },
  {
    "path": "content/docs/comparison.mdx",
    "chars": 90,
    "preview": "---\ntitle: Comparison\ndescription: Hello World\n---\n\n## This is title\n\nThis is description\n"
  },
  {
    "path": "content/docs/contributing/adding-snippets.mdx",
    "chars": 95,
    "preview": "---\ntitle: Adding snippets\ndescription: Hello World\n---\n\n## This is title\n\nThis is description\n"
  },
  {
    "path": "content/docs/contributing/how-to-contribute.mdx",
    "chars": 97,
    "preview": "---\ntitle: How to contribute\ndescription: Hello World\n---\n\n## This is title\n\nThis is description\n"
  },
  {
    "path": "content/docs/contributing/meta.json",
    "chars": 145,
    "preview": "{\n  \"title\": \"Contributing\",\n  \"pages\": [\n    \"how-to-contribute\",\n    \"adding-snippets\",\n    \"modifying-snippets\",\n    "
  },
  {
    "path": "content/docs/contributing/modifying-snippets.mdx",
    "chars": 98,
    "preview": "---\ntitle: Modifying snippets\ndescription: Hello World\n---\n\n## This is title\n\nThis is description\n"
  },
  {
    "path": "content/docs/contributing/third-party-apps.mdx",
    "chars": 106,
    "preview": "---\ntitle: Third-party apps\ndescription: Hello World\n---\n\n## How to add an extension\n\nThis is description\n"
  },
  {
    "path": "content/docs/extensions/meta.json",
    "chars": 132,
    "preview": "{\n  \"title\": \"Extensions\",\n  \"pages\": [\n    \"quicksnip\",\n    \"quicksnip-cli\",\n    \"quicksnip-vscode\",\n    \"quicksnip-ray"
  },
  {
    "path": "content/docs/extensions/quicksnip-cli.mdx",
    "chars": 93,
    "preview": "---\ntitle: QuickSnip CLI\ndescription: Hello World\n---\n\n## This is title\n\nThis is description\n"
  },
  {
    "path": "content/docs/extensions/quicksnip-raycast.mdx",
    "chars": 97,
    "preview": "---\ntitle: QuickSnip Raycast\ndescription: Hello World\n---\n\n## This is title\n\nThis is description\n"
  },
  {
    "path": "content/docs/extensions/quicksnip-vscode.mdx",
    "chars": 97,
    "preview": "---\ntitle: QuickSnip VS Code\ndescription: Hello World\n---\n\n## This is title\n\nThis is description\n"
  },
  {
    "path": "content/docs/extensions/quicksnip.mdx",
    "chars": 89,
    "preview": "---\ntitle: QuickSnip\ndescription: Hello World\n---\n\n## This is title\n\nThis is description\n"
  },
  {
    "path": "content/docs/index.mdx",
    "chars": 91,
    "preview": "---\ntitle: Get Started\ndescription: Hello World\n---\n\n## This is title\n\nThis is description\n"
  },
  {
    "path": "content/docs/installation.mdx",
    "chars": 92,
    "preview": "---\ntitle: Installation\ndescription: Hello World\n---\n\n## This is title\n\nThis is description\n"
  },
  {
    "path": "content/docs/meta.json",
    "chars": 238,
    "preview": "{\n  \"title\": \"Docs\",\n  \"root\": true,\n  \"pages\": [\n    \"---Introduction---\",\n    \"index.mdx\",\n    \"installation.mdx\",\n   "
  },
  {
    "path": "eslint.config.mjs",
    "chars": 393,
    "preview": "import { dirname } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { FlatCompat } from \"@eslint/eslintrc\";\n\ncon"
  },
  {
    "path": "mdx-components.tsx",
    "chars": 245,
    "preview": "import defaultMdxComponents from \"fumadocs-ui/mdx\";\nimport type { MDXComponents } from \"mdx/types\";\n\nexport function get"
  },
  {
    "path": "next.config.ts",
    "chars": 342,
    "preview": "import { createMDX } from \"fumadocs-mdx/next\";\n\nconst withMDX = createMDX();\n\n/** @type {import('next').NextConfig} */\nc"
  },
  {
    "path": "package.json",
    "chars": 1794,
    "preview": "{\n  \"name\": \"quicksnip\",\n  \"version\": \"2.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"prebuild\": "
  },
  {
    "path": "postcss.config.mjs",
    "chars": 81,
    "preview": "const config = {\n  plugins: [\"@tailwindcss/postcss\"],\n};\n\nexport default config;\n"
  },
  {
    "path": "public/data/snippets/README.md",
    "chars": 69,
    "preview": "# IMPORTANT\n\n### This folder is read-only. Do NOT modify this folder!"
  },
  {
    "path": "public/data/snippets/all.json",
    "chars": 3536,
    "preview": "[\n  {\n    \"id\": \"shuffle-array\",\n    \"category\": \"array-manipulation\",\n    \"title\": \"Shuffle Array\",\n    \"description\": "
  },
  {
    "path": "public/data/snippets/array-manipulation/shuffle-array.json",
    "chars": 622,
    "preview": "{\n  \"id\": \"shuffle-array\",\n  \"category\": \"array-manipulation\",\n  \"title\": \"Shuffle Array\",\n  \"description\": \"Shuffles th"
  },
  {
    "path": "public/data/snippets/data-handling/add-localstorage.json",
    "chars": 523,
    "preview": "{\n  \"id\": \"add-localstorage\",\n  \"category\": \"data-handling\",\n  \"title\": \"Add Item to localStorage\",\n  \"description\": \"St"
  },
  {
    "path": "public/data/snippets/number-formatting/number-to-currency.json",
    "chars": 690,
    "preview": "{\n  \"id\": \"number-to-currency\",\n  \"category\": \"number-formatting\",\n  \"title\": \"Convert Number to Currency\",\n  \"descripti"
  },
  {
    "path": "public/data/snippets/string-manipulation/reverse-string.json",
    "chars": 923,
    "preview": "{\n  \"id\": \"reverse-string\",\n  \"category\": \"string-manipulation\",\n  \"title\": \"Reverse String\",\n  \"description\": \"Reverses"
  },
  {
    "path": "public/data/snippets/string-manipulation/string-to-camel-case.json",
    "chars": 1121,
    "preview": "{\n  \"id\": \"string-to-camel-case\",\n  \"category\": \"string-manipulation\",\n  \"title\": \"Convert String to Camel Case\",\n  \"des"
  },
  {
    "path": "public/data/snippets/string-manipulation/string-to-param-case.json",
    "chars": 763,
    "preview": "{\n  \"id\": \"string-to-param-case\",\n  \"category\": \"string-manipulation\",\n  \"title\": \"Convert String to Param Case\",\n  \"des"
  },
  {
    "path": "public/data/snippets/string-manipulation/string-to-pascal-case.json",
    "chars": 1077,
    "preview": "{\n  \"id\": \"string-to-pascal-case\",\n  \"category\": \"string-manipulation\",\n  \"title\": \"Convert String to Pascal Case\",\n  \"d"
  },
  {
    "path": "public/data/snippets/string-manipulation/string-to-snake-case.json",
    "chars": 830,
    "preview": "{\n  \"id\": \"string-to-snake-case\",\n  \"category\": \"string-manipulation\",\n  \"title\": \"Convert String to Snake Case\",\n  \"des"
  },
  {
    "path": "public/data/snippets/string-manipulation/string-to-title-case.json",
    "chars": 511,
    "preview": "{\n  \"id\": \"string-to-title-case\",\n  \"category\": \"string-manipulation\",\n  \"title\": \"Convert String to Title Case\",\n  \"des"
  },
  {
    "path": "public/data/snippets/string-manipulation/truncate-sring.json",
    "chars": 1339,
    "preview": "{\n  \"id\": \"truncate-sring\",\n  \"category\": \"string-manipulation\",\n  \"title\": \"Truncate String\",\n  \"description\": \"Truncat"
  },
  {
    "path": "scripts/merge-snippets.ts",
    "chars": 2526,
    "preview": "import fs from \"fs\";\nimport path from \"path\";\n\nconst SNIPPETS_DIR = path.join(process.cwd(), \"snippets\");\nconst OUTPUT_D"
  },
  {
    "path": "snippets/README.md",
    "chars": 216,
    "preview": "\n\n## Markdown Format\n\n````\n```js\n// code here\n```\n\n---\n\n```py\n# code here\n```\n````\n\n\n## JSON Format\n\n```json\n{\n  \"title\""
  },
  {
    "path": "snippets/array-manipulation/shuffle-array.json",
    "chars": 181,
    "preview": "{\n  \"title\": \"Shuffle Array\",\n  \"description\": \"Shuffles the items in an array.\",\n  \"languages\": [\"js\"],\n  \"contributors"
  },
  {
    "path": "snippets/array-manipulation/shuffle-array.md",
    "chars": 298,
    "preview": "```js\nfunction shuffleArray(array) {\n    for (let i = array.length - 1; i >= 0; i--) {\n        const j = Math.floor(Math"
  },
  {
    "path": "snippets/data-handling/add-localstorage.json",
    "chars": 221,
    "preview": "{\n  \"title\": \"Add Item to localStorage\",\n  \"description\": \"Stores a value in localStorage under the given key.\",\n  \"lang"
  },
  {
    "path": "snippets/data-handling/add-localstorage.md",
    "chars": 173,
    "preview": "```js\nconst addToLocalStorage = (key, value) => {\n  localStorage.setItem(key, JSON.stringify(value));\n};\n\n// Usage:\naddT"
  },
  {
    "path": "snippets/number-formatting/number-to-currency.json",
    "chars": 214,
    "preview": "{\n  \"title\": \"Convert Number to Currency\",\n  \"description\": \"Converts a number to a currency format with a specific loca"
  },
  {
    "path": "snippets/number-formatting/number-to-currency.md",
    "chars": 333,
    "preview": "```js\nconst convertToCurrency = (num, locale = 'en-US', currency = 'USD') => {\n  return new Intl.NumberFormat(locale, {\n"
  },
  {
    "path": "snippets/string-manipulation/reverse-string.json",
    "chars": 214,
    "preview": "{\n  \"title\": \"Reverse String\",\n  \"description\": \"Reverses the characters in a string.\",\n  \"languages\": [\"js\", \"cpp\", \"py"
  },
  {
    "path": "snippets/string-manipulation/reverse-string.md",
    "chars": 534,
    "preview": "```js\nconst reverseString = (str) => str.split('').reverse().join('');\n\n// Usage:\nreverseString('hello'); // Returns: 'o"
  },
  {
    "path": "snippets/string-manipulation/string-to-camel-case.json",
    "chars": 214,
    "preview": "{\n  \"title\": \"Convert String to Camel Case\",\n  \"description\": \"Converts a given string into camelCase.\",\n  \"languages\": "
  },
  {
    "path": "snippets/string-manipulation/string-to-camel-case.md",
    "chars": 713,
    "preview": "```js\nfunction toCamelCase(str) {\n  return str.replace(/\\W+(.)/g, (match, chr) => chr.toUpperCase());\n}\n\n// Usage:\ntoCam"
  },
  {
    "path": "snippets/string-manipulation/string-to-param-case.json",
    "chars": 215,
    "preview": "{\n  \"title\": \"Convert String to Param Case\",\n  \"description\": \"Converts a given string into param-case.\",\n  \"languages\":"
  },
  {
    "path": "snippets/string-manipulation/string-to-param-case.md",
    "chars": 382,
    "preview": "```js\nfunction toParamCase(str) {\n  return str.toLowerCase().replace(/\\s+/g, '-');\n}\n\n// Usage:\ntoParamCase('Hello World"
  },
  {
    "path": "snippets/string-manipulation/string-to-pascal-case.json",
    "chars": 216,
    "preview": "{\n  \"title\": \"Convert String to Pascal Case\",\n  \"description\": \"Converts a given string into PascalCase.\",\n  \"languages\""
  },
  {
    "path": "snippets/string-manipulation/string-to-pascal-case.md",
    "chars": 670,
    "preview": "```js\nfunction toPascalCase(str) {\n  return str.replace(/\\b\\w/g, (s) => s.toUpperCase()).replace(/\\W+(.)/g, (match, chr)"
  },
  {
    "path": "snippets/string-manipulation/string-to-snake-case.json",
    "chars": 214,
    "preview": "{\n  \"title\": \"Convert String to Snake Case\",\n  \"description\": \"Converts a given string into snake_case.\",\n  \"languages\":"
  },
  {
    "path": "snippets/string-manipulation/string-to-snake-case.md",
    "chars": 444,
    "preview": "```js\nfunction toSnakeCase(str) {\n  return str.replace(/([a-z])([A-Z])/g, '$1_$2')\n            .replace(/\\s+/g, '_')\n   "
  },
  {
    "path": "snippets/string-manipulation/string-to-title-case.json",
    "chars": 191,
    "preview": "{\n  \"title\": \"Convert String to Title Case\",\n  \"description\": \"Converts a given string into Title Case.\",\n  \"languages\":"
  },
  {
    "path": "snippets/string-manipulation/string-to-title-case.md",
    "chars": 183,
    "preview": "```js\nfunction toTitleCase(str) {\n  return str.toLowerCase().replace(/\\b\\w/g, (s) => s.toUpperCase());\n}\n\n// Usage:\ntoTi"
  },
  {
    "path": "snippets/string-manipulation/truncate-sring.json",
    "chars": 287,
    "preview": "{\n  \"title\": \"Truncate String\",\n  \"description\": \"Truncates a string after a specified length.\",\n  \"languages\": [\"js\", \""
  },
  {
    "path": "snippets/string-manipulation/truncate-sring.md",
    "chars": 865,
    "preview": "```js\nconst truncateText = (text = '', maxLength = 50) => {\n  return `${text.slice(0, maxLength)}${text.length >= maxLen"
  },
  {
    "path": "source.config.ts",
    "chars": 440,
    "preview": "import {\n  defineConfig,\n  defineDocs,\n  frontmatterSchema,\n  metaSchema,\n} from \"fumadocs-mdx/config\";\n\n// You can cust"
  },
  {
    "path": "src/app/api/search/route.ts",
    "chars": 241,
    "preview": "import { source } from \"@/lib/source\";\nimport { createFromSource } from \"fumadocs-core/search/server\";\n\nexport const { G"
  },
  {
    "path": "src/app/community/page.tsx",
    "chars": 3995,
    "preview": "\"use client\";\n\nimport { AspectRatio } from \"@/components/ui/aspect-ratio\";\nimport { Button } from \"@/components/ui/butto"
  },
  {
    "path": "src/app/contributing/page.tsx",
    "chars": 1162,
    "preview": "import fs from \"fs\";\nimport path from \"path\";\nimport remarkGfm from \"remark-gfm\";\nimport Markdown from \"react-markdown\";"
  },
  {
    "path": "src/app/extensions/page.tsx",
    "chars": 992,
    "preview": "import ExtensionItem, {\n  NewExtensionItem,\n} from \"@/components/ui/extension-item\";\nimport { EXTENSIONS } from \"@/data/"
  },
  {
    "path": "src/app/globals.css",
    "chars": 4943,
    "preview": "@import \"tailwindcss\";\n@import \"tw-animate-css\";\n@import \"fumadocs-ui/css/neutral.css\";\n@import \"fumadocs-ui/css/preset."
  },
  {
    "path": "src/app/guide/[[...slug]]/page.tsx",
    "chars": 1493,
    "preview": "import { source } from \"@/lib/source\";\nimport {\n  DocsBody,\n  DocsDescription,\n  DocsPage,\n  DocsTitle,\n} from \"fumadocs"
  },
  {
    "path": "src/app/guide/layout.tsx",
    "chars": 424,
    "preview": "import { source } from \"@/lib/source\";\nimport { DocsLayout } from \"fumadocs-ui/layouts/docs\";\nimport type { ReactNode } "
  },
  {
    "path": "src/app/layout.config.tsx",
    "chars": 208,
    "preview": "import { BaseLayoutProps } from \"fumadocs-ui/layouts/shared\";\n\nexport const baseOptions: BaseLayoutProps = {\n  // github"
  },
  {
    "path": "src/app/layout.tsx",
    "chars": 1019,
    "preview": "import type { Metadata } from \"next\";\nimport { Geist, Geist_Mono } from \"next/font/google\";\nimport { RootProvider } from"
  },
  {
    "path": "src/app/page.tsx",
    "chars": 357,
    "preview": "import Features from \"@/components/layouts/features\";\nimport Hero from \"@/components/layouts/hero\";\nimport AvailableFor "
  },
  {
    "path": "src/app/snippets/[category]/[snippet]/page.tsx",
    "chars": 3068,
    "preview": "\"use client\";\n\nimport { use } from \"react\";\n\nimport { useRouter } from \"next/navigation\";\nimport Link from \"next/link\";\n"
  },
  {
    "path": "src/app/snippets/[category]/page.tsx",
    "chars": 690,
    "preview": "\"use client\";\n\nimport SnippetList from \"@/components/layouts/snippet-list\";\nimport { unslugify } from \"@/lib/utils\";\nimp"
  },
  {
    "path": "src/app/snippets/layout.tsx",
    "chars": 484,
    "preview": "import SnippetHeader from \"@/components/layouts/snippet-header\";\nimport SnippetSidebar from \"@/components/layouts/snippe"
  },
  {
    "path": "src/app/snippets/page.tsx",
    "chars": 515,
    "preview": "\"use client\";\n\nimport SnippetList from \"@/components/layouts/snippet-list\";\nimport { useSnippetsStore } from \"@/store/us"
  },
  {
    "path": "src/components/layouts/available-for.tsx",
    "chars": 1025,
    "preview": "import Link from \"next/link\";\n\nimport { EXTENSIONS } from \"@/data/extensions\";\n\nimport { Button } from \"@/components/ui/"
  },
  {
    "path": "src/components/layouts/code-preview.tsx",
    "chars": 1335,
    "preview": "import { Prism as SyntaxHighlighter } from \"react-syntax-highlighter\";\nimport { oneDark } from \"react-syntax-highlighter"
  },
  {
    "path": "src/components/layouts/features.tsx",
    "chars": 2031,
    "preview": "import {\n  Code,\n  Terminal,\n  Paintbrush,\n  Rocket,\n  Book,\n  PlusCircle,\n} from \"lucide-react\";\nimport { Card, CardCon"
  },
  {
    "path": "src/components/layouts/footer.tsx",
    "chars": 1668,
    "preview": "import { ExternalLink } from \"lucide-react\";\nimport Link from \"next/link\";\n\nconst NAV_ITEMS = [\n  { name: \"Contribute\", "
  },
  {
    "path": "src/components/layouts/header.tsx",
    "chars": 6700,
    "preview": "\"use client\";\n\nimport React, { useState, useEffect } from \"react\";\nimport { motion, AnimatePresence, easeInOut } from \"f"
  },
  {
    "path": "src/components/layouts/hero.tsx",
    "chars": 1828,
    "preview": "import Link from \"next/link\";\nimport { Button } from \"../ui/button\";\nimport { GITHUB_URL } from \"@/data/meta\";\n\nconst He"
  },
  {
    "path": "src/components/layouts/snippet-header.tsx",
    "chars": 1512,
    "preview": "import { Search } from \"lucide-react\";\n\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue"
  },
  {
    "path": "src/components/layouts/snippet-list.tsx",
    "chars": 415,
    "preview": "import SnippetItem from \"../ui/snippet-item\";\n\nimport type { SnippetType } from \"@/types\";\n\ninterface SnippetListProps {"
  },
  {
    "path": "src/components/layouts/snippet-sidebar.tsx",
    "chars": 3224,
    "preview": "\"use client\";\n\nimport {\n  Sidebar,\n  SidebarContent,\n  SidebarGroup,\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuIt"
  },
  {
    "path": "src/components/layouts/sponsor.tsx",
    "chars": 1036,
    "preview": "import Image from \"next/image\";\nimport Link from \"next/link\";\nimport {\n  Card,\n  CardDescription,\n  CardFooter,\n  CardHe"
  },
  {
    "path": "src/components/ui/accordion.tsx",
    "chars": 2053,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AccordionPrimitive from \"@radix-ui/react-accordion\"\nimport { Ch"
  },
  {
    "path": "src/components/ui/aspect-ratio.tsx",
    "chars": 280,
    "preview": "\"use client\"\n\nimport * as AspectRatioPrimitive from \"@radix-ui/react-aspect-ratio\"\n\nfunction AspectRatio({\n  ...props\n}:"
  },
  {
    "path": "src/components/ui/avatar.tsx",
    "chars": 1098,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport * as AvatarPrimitive from \"@radix-ui/react-avatar\";\n\nimport { cn }"
  },
  {
    "path": "src/components/ui/button.tsx",
    "chars": 2216,
    "preview": "import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"cla"
  },
  {
    "path": "src/components/ui/card.tsx",
    "chars": 1989,
    "preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Card({ className, ...props }: React.Component"
  },
  {
    "path": "src/components/ui/dark-mode-switch.tsx",
    "chars": 1514,
    "preview": "\"use client\";\n\nimport { useId, useState } from \"react\";\nimport { MoonIcon, SunIcon } from \"lucide-react\";\n\nimport { Labe"
  },
  {
    "path": "src/components/ui/extension-item.tsx",
    "chars": 2653,
    "preview": "import Link from \"next/link\";\nimport Image from \"next/image\";\nimport type { ExtensionType } from \"@/types\";\nimport {\n  C"
  },
  {
    "path": "src/components/ui/input-group.tsx",
    "chars": 5065,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport {"
  },
  {
    "path": "src/components/ui/input.tsx",
    "chars": 967,
    "preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Input({ className, type, ...props }: React.Co"
  },
  {
    "path": "src/components/ui/label.tsx",
    "chars": 595,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Label as LabelPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/l"
  },
  {
    "path": "src/components/ui/language-icons.tsx",
    "chars": 328,
    "preview": "import { FaJs, FaPython, FaJava } from \"react-icons/fa\";\nimport { SiTypescript, SiNextdotjs, SiReact } from \"react-icons"
  },
  {
    "path": "src/components/ui/logo.tsx",
    "chars": 3907,
    "preview": "import Link from \"next/link\";\n\nexport const LogoIcon = () => (\n  <svg\n    width=\"43\"\n    height=\"30\"\n    viewBox=\"0 0 43"
  },
  {
    "path": "src/components/ui/navigation-menu.tsx",
    "chars": 6664,
    "preview": "import * as React from \"react\"\nimport * as NavigationMenuPrimitive from \"@radix-ui/react-navigation-menu\"\nimport { cva }"
  },
  {
    "path": "src/components/ui/select.tsx",
    "chars": 6253,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SelectPrimitive from \"@radix-ui/react-select\"\nimport { CheckIco"
  },
  {
    "path": "src/components/ui/separator.tsx",
    "chars": 699,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SeparatorPrimitive from \"@radix-ui/react-separator\"\n\nimport { c"
  },
  {
    "path": "src/components/ui/sheet.tsx",
    "chars": 4090,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\"\nimport { XIcon } f"
  },
  {
    "path": "src/components/ui/sidebar.tsx",
    "chars": 21782,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, VariantProps }"
  },
  {
    "path": "src/components/ui/skeleton.tsx",
    "chars": 276,
    "preview": "import { cn } from \"@/lib/utils\"\n\nfunction Skeleton({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n "
  },
  {
    "path": "src/components/ui/snippet-detail.tsx",
    "chars": 109,
    "preview": "export default function SnippetDetail() {\n  return (\n    <div>\n      <p>Snippet Detail</p>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/ui/snippet-item.tsx",
    "chars": 948,
    "preview": "import { Card, CardContent, CardTitle } from \"./card\";\nimport type { SnippetType } from \"@/types\";\nimport Link from \"nex"
  },
  {
    "path": "src/components/ui/snippet-search.tsx",
    "chars": 109,
    "preview": "export default function SnippetSearch() {\n  return (\n    <div>\n      <p>Snippet Search</p>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/ui/switch.tsx",
    "chars": 1036,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Switch as SwitchPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@"
  },
  {
    "path": "src/components/ui/tabs.tsx",
    "chars": 1969,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\"\n\nimport { cn } from \""
  },
  {
    "path": "src/components/ui/textarea.tsx",
    "chars": 759,
    "preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Textarea({ className, ...props }: React.Compo"
  },
  {
    "path": "src/components/ui/tooltip.tsx",
    "chars": 1891,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\"\n\nimport { cn } "
  },
  {
    "path": "src/data/extensions.ts",
    "chars": 1127,
    "preview": "import type { ExtensionType } from \"@/types\";\n\nexport const EXTENSIONS: ExtensionType[] = [\n  {\n    icon: \"/assets/exten"
  },
  {
    "path": "src/data/meta.ts",
    "chars": 199,
    "preview": "export const GITHUB_URL = \"https://github.com/quicksnip-dev/quicksnip\";\nexport const DISCORD_URL = \"https://discord.com/"
  },
  {
    "path": "src/data/yt-videos.ts",
    "chars": 212,
    "preview": "export const YOUTUBE_VIDEOS = [\n  {\n    id: \"2_AfhnMVuSY\",\n    title: \"We made QuickSnip even better (v1)\",\n  },\n  {\n   "
  },
  {
    "path": "src/hooks/use-fetch.ts",
    "chars": 726,
    "preview": "import { useEffect, useState } from \"react\";\n\nexport const useFetch = <T>(url: string) => {\n  const [data, setData] = us"
  },
  {
    "path": "src/hooks/use-mobile.ts",
    "chars": 565,
    "preview": "import * as React from \"react\"\n\nconst MOBILE_BREAKPOINT = 768\n\nexport function useIsMobile() {\n  const [isMobile, setIsM"
  },
  {
    "path": "src/lib/source.ts",
    "chars": 237,
    "preview": "// .source folder will be generated when you run `next dev`\nimport { docs } from \"../../.source\";\nimport { loader } from"
  },
  {
    "path": "src/lib/utils.ts",
    "chars": 952,
    "preview": "import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: C"
  },
  {
    "path": "src/store/useSnippetsStore.ts",
    "chars": 941,
    "preview": "import { SnippetType } from \"@/types\";\nimport { create } from \"zustand\";\n\ninterface SnippetsStore {\n  snippets: SnippetT"
  },
  {
    "path": "src/types/index.ts",
    "chars": 534,
    "preview": "export type ExtensionType = {\n  icon: string;\n  name: string;\n  description: string;\n  guide_url: string;\n  source_url: "
  },
  {
    "path": "tailwind.config.mjs",
    "chars": 201,
    "preview": "const config = {\n  // content: [\"./app/**/*.{js,ts,jsx,tsx}\", \"./components/**/*.{js,ts,jsx,tsx}\"],\n  theme: { extend: {"
  },
  {
    "path": "tsconfig.json",
    "chars": 602,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2017\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    "
  }
]

About this extraction

This page contains the full source code of the dostonnabotov/quicksnip GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 119 files (152.8 KB), approximately 45.0k tokens, and a symbol index with 152 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!