{post.title}
250 |{post.desc}
251 | 252 |├── app
├── not-found.tsx
├── favicon.ico
├── components
│ ├── Oneko.tsx
│ ├── Age.tsx
│ ├── Footer.tsx
│ ├── Skills.tsx
│ ├── Activity.tsx
│ ├── Intro.tsx
│ ├── Socials.tsx
│ ├── Projects.tsx
│ └── Blogs.tsx
├── robots.ts
├── page.tsx
├── manifest.ts
├── sitemap.ts
├── resume
│ └── page.tsx
├── globals.css
├── layout.tsx
└── blog
│ ├── page.tsx
│ └── [url]
│ └── page.tsx
├── public
├── googlecb5b318981d52a32.html
├── oneko.gif
├── og
│ └── card.png
├── priyansh.jpg
├── PriyanshResume.pdf
├── favicon
│ ├── favicon.ico
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── apple-touch-icon.png
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ └── site.webmanifest
├── blog
│ ├── devprofiles-commit.png
│ ├── devprofiles-fork.jpeg
│ ├── devprofiles-open-pr.png
│ ├── devprofiles-preview.png
│ ├── devprofiles-submit-pr.png
│ └── how-to-get-discord-token.png
└── oneko.js
├── postcss.config.mjs
├── README.md
├── next.config.ts
├── .gitignore
├── eslint.config.mjs
├── lib
├── fonts.ts
├── posts.ts
└── blog.ts
├── DB
└── projects.json
├── tsconfig.json
├── tailwind.config.ts
├── package.json
├── posts
├── how-to-get-discord-account-token.mdx
└── contribute-your-first-code.mdx
└── .github
└── workflows
└── nextjs.yml
/app/not-found.tsx:
--------------------------------------------------------------------------------
1 | export default function NotFound() {
2 | return <>>
3 | }
--------------------------------------------------------------------------------
/public/googlecb5b318981d52a32.html:
--------------------------------------------------------------------------------
1 | google-site-verification: googlecb5b318981d52a32.html
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/app/favicon.ico
--------------------------------------------------------------------------------
/public/oneko.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/oneko.gif
--------------------------------------------------------------------------------
/public/og/card.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/og/card.png
--------------------------------------------------------------------------------
/public/priyansh.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/priyansh.jpg
--------------------------------------------------------------------------------
/public/PriyanshResume.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/PriyanshResume.pdf
--------------------------------------------------------------------------------
/public/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/favicon/favicon.ico
--------------------------------------------------------------------------------
/public/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/public/blog/devprofiles-commit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/blog/devprofiles-commit.png
--------------------------------------------------------------------------------
/public/blog/devprofiles-fork.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/blog/devprofiles-fork.jpeg
--------------------------------------------------------------------------------
/public/blog/devprofiles-open-pr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/blog/devprofiles-open-pr.png
--------------------------------------------------------------------------------
/public/blog/devprofiles-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/blog/devprofiles-preview.png
--------------------------------------------------------------------------------
/public/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/blog/devprofiles-submit-pr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/blog/devprofiles-submit-pr.png
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | const config = {
2 | plugins: {
3 | "@tailwindcss/postcss": {},
4 | },
5 | };
6 |
7 | export default config;
8 |
--------------------------------------------------------------------------------
/public/blog/how-to-get-discord-token.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/blog/how-to-get-discord-token.png
--------------------------------------------------------------------------------
/public/favicon/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/favicon/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/favicon/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/priyazsh/priyazsh.github.io/HEAD/public/favicon/android-chrome-512x512.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
34 | {total !== null ? total : "…"} contributions
35 | @priyazsh
30 | I'm Priyansh, a
33 | I build things with{" "}
34 |
35 | {project.description} {project.tech} {post.desc} {post.date} {post.readTime} {post.desc} {post.date} {post.readTime}
2 |
--------------------------------------------------------------------------------
/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | output: 'export',
5 | trailingSlash: true,
6 | reactCompiler: true,
7 | };
8 |
9 | export default nextConfig;
10 |
--------------------------------------------------------------------------------
/public/favicon/site.webmanifest:
--------------------------------------------------------------------------------
1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /.pnp
3 | .pnp.*
4 | .yarn/*
5 | !.yarn/patches
6 | !.yarn/plugins
7 | !.yarn/releases
8 | !.yarn/versions
9 | /coverage
10 | /.next/
11 | /out/
12 | /build
13 | .DS_Store
14 | *.pem
15 | npm-debug.log*
16 | yarn-debug.log*
17 | yarn-error.log*
18 | .pnpm-debug.log*
19 | .env*
20 | .vercel
21 | *.tsbuildinfo
22 | next-env.d.ts
23 |
--------------------------------------------------------------------------------
/app/components/Oneko.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useEffect } from "react";
4 | export default function Oneko() {
5 | useEffect(() => {
6 | const script = document.createElement("script");
7 | script.src = "/oneko.js";
8 | document.body.append(script);
9 | return () => {
10 | script.remove();
11 | };
12 | }, []);
13 | return null;
14 | }
15 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineConfig, globalIgnores } from "eslint/config";
2 | import nextVitals from "eslint-config-next/core-web-vitals";
3 | import nextTs from "eslint-config-next/typescript";
4 |
5 | const eslintConfig = defineConfig([
6 | ...nextVitals,
7 | ...nextTs,
8 | globalIgnores([
9 | ".next/**",
10 | "out/**",
11 | "build/**",
12 | "next-env.d.ts",
13 | ]),
14 | ]);
15 |
16 | export default eslintConfig;
17 |
--------------------------------------------------------------------------------
/lib/fonts.ts:
--------------------------------------------------------------------------------
1 | import { Inter, JetBrains_Mono, Geist } from 'next/font/google'
2 |
3 | export const inter = Inter({
4 | subsets: ['latin'],
5 | display: 'swap',
6 | variable: '--font-inter',
7 | })
8 |
9 | export const jetbrainsMono = JetBrains_Mono({
10 | subsets: ['latin'],
11 | display: 'swap',
12 | variable: '--font-jetbrains-mono',
13 | })
14 |
15 | export const geist = Geist({
16 | subsets: ['latin'],
17 | display: 'swap',
18 | variable: '--font-geist',
19 | })
--------------------------------------------------------------------------------
/app/robots.ts:
--------------------------------------------------------------------------------
1 | import { MetadataRoute } from 'next'
2 |
3 | export const dynamic = 'force-static'
4 |
5 | export default function robots(): MetadataRoute.Robots {
6 | const baseUrl = 'https://priyazsh.github.io'
7 |
8 | return {
9 | rules: {
10 | userAgent: '*',
11 | allow: '/',
12 | disallow: [
13 | '/private/',
14 | '/api/',
15 | '/_next/',
16 | ],
17 | },
18 | sitemap: `${baseUrl}/sitemap.xml`,
19 | host: baseUrl,
20 | }
21 | }
--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Socials from "./components/Socials";
2 | import Projects from "./components/Projects";
3 | import Intro from "./components/Intro";
4 | import Activity from "./components/Activity";
5 | import Blogs from "./components/Blogs";
6 | import Footer from "./components/Footer";
7 |
8 | export const dynamic = 'force-static';
9 |
10 | export default function Home() {
11 | return (
12 | <>
13 |
My Resume
30 |
31 |
31 | ~/ GitHub Activity
32 |
33 | Priyansh Prajapat
22 |
21 | ~/ Projects
22 |
23 |
24 | {projects.map((project: Project) => (
25 |
31 |
34 | {project.title}
35 |
36 |
37 |
42 | {project.status ? "Active" : "Building"}
43 |
44 |
24 |
25 | ### Now, What is DevProfiles?
26 |
27 | DevProfiles is a website where developer list their profiles, showcase their skills, so it will be easier to connect with each other
28 |
29 | Now we will add your profile on the website
30 |
31 | ### Forking the repository
32 |
33 |
34 |
35 | Visit the GitHub repository of DevProfiles and fork it
36 | Link: github.com/priyazsh/DevProfiles
37 |
38 | ### Making changes
39 |
40 | Now go to 'data.json' file in your forked repository and add the following code
41 |
42 | ```json
43 | {
44 | "name": "YOUR_NAME",
45 | "image": "IMAGE_URL",
46 | "github": "YOUR GITHUB URL",
47 | "twitter": "YOUR X/TWITTER URL",
48 | "linkedin": "YOUR LINKEDIN URL",
49 | "skills": ["SKILL-1", "SKILL-2", "SKILL-3"]
50 | }
51 | ```
52 |
53 | Change/Replace the placeholders with your name, skills, image and profiles url.
54 |
55 | Now commit your changes!
56 |
57 |
58 |
59 | ### Submit a Pull request
60 |
61 | After commiting changes, you can create a pull request
62 |
63 |
64 |
65 |
66 |
67 | Add a good title to the pull request, like 'data: Profile addition by <Your Name>'
68 |
69 | Now soon a maintainer will review and merge your pull request after that the changes you've made will be live on the website
70 |
71 | Congratulations, you've contributed your first code to open source!
--------------------------------------------------------------------------------
/.github/workflows/nextjs.yml:
--------------------------------------------------------------------------------
1 | # Sample workflow for building and deploying a Next.js site to GitHub Pages
2 | #
3 | # To get started with Next.js see: https://nextjs.org/docs/getting-started
4 | #
5 | name: Deploy Next.js site to Pages
6 |
7 | on:
8 | # Runs on pushes targeting the default branch
9 | push:
10 | branches: ["main"]
11 |
12 | # Allows you to run this workflow manually from the Actions tab
13 | workflow_dispatch:
14 |
15 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
16 | permissions:
17 | contents: read
18 | pages: write
19 | id-token: write
20 |
21 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
22 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
23 | concurrency:
24 | group: "pages"
25 | cancel-in-progress: false
26 |
27 | jobs:
28 | # Build job
29 | build:
30 | runs-on: ubuntu-latest
31 | steps:
32 | - name: Checkout
33 | uses: actions/checkout@v4
34 | - name: Detect package manager
35 | id: detect-package-manager
36 | run: |
37 | if [ -f "${{ github.workspace }}/yarn.lock" ]; then
38 | echo "manager=yarn" >> $GITHUB_OUTPUT
39 | echo "command=install" >> $GITHUB_OUTPUT
40 | echo "runner=yarn" >> $GITHUB_OUTPUT
41 | exit 0
42 | elif [ -f "${{ github.workspace }}/package.json" ]; then
43 | echo "manager=npm" >> $GITHUB_OUTPUT
44 | echo "command=ci" >> $GITHUB_OUTPUT
45 | echo "runner=npx --no-install" >> $GITHUB_OUTPUT
46 | exit 0
47 | else
48 | echo "Unable to determine package manager"
49 | exit 1
50 | fi
51 | - name: Setup Node
52 | uses: actions/setup-node@v4
53 | with:
54 | node-version: "20"
55 | cache: ${{ steps.detect-package-manager.outputs.manager }}
56 | - name: Setup Pages
57 | uses: actions/configure-pages@v5
58 | with:
59 | # Automatically inject basePath in your Next.js configuration file and disable
60 | # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized).
61 | #
62 | # You may remove this line if you want to manage the configuration yourself.
63 | static_site_generator: next
64 | - name: Restore cache
65 | uses: actions/cache@v4
66 | with:
67 | path: |
68 | .next/cache
69 | # Generate a new cache whenever packages or source files change.
70 | key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
71 | # If source files changed but packages didn't, rebuild from a prior cache.
72 | restore-keys: |
73 | ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-
74 | - name: Install dependencies
75 | run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
76 | - name: Build with Next.js
77 | env:
78 | GOOGLE_ANALYTICS: ${{ secrets.GOOGLE_ANALYTICS }}
79 | run: ${{ steps.detect-package-manager.outputs.runner }} next build
80 | - name: Upload artifact
81 | uses: actions/upload-pages-artifact@v3
82 | with:
83 | path: ./out
84 |
85 | # Deployment job
86 | deploy:
87 | environment:
88 | name: github-pages
89 | url: ${{ steps.deployment.outputs.page_url }}
90 | runs-on: ubuntu-latest
91 | needs: build
92 | steps:
93 | - name: Deploy to GitHub Pages
94 | id: deployment
95 | uses: actions/deploy-pages@v4
96 |
--------------------------------------------------------------------------------
/public/oneko.js:
--------------------------------------------------------------------------------
1 | (function oneko() {
2 | const nekoEl = document.createElement("div");
3 | let nekoPosX = 32;
4 | let nekoPosY = 32;
5 | let mousePosX = 0;
6 | let mousePosY = 0;
7 | let frameCount = 0;
8 | let idleTime = 0;
9 | let idleAnimation = null;
10 | let idleAnimationFrame = 0;
11 | const nekoSpeed = 10;
12 | const spriteSets = {
13 | idle: [[-3, -3]],
14 | alert: [[-7, -3]],
15 | scratch: [
16 | [-5, 0],
17 | [-6, 0],
18 | [-7, 0],
19 | ],
20 | tired: [[-3, -2]],
21 | sleeping: [
22 | [-2, 0],
23 | [-2, -1],
24 | ],
25 | N: [
26 | [-1, -2],
27 | [-1, -3],
28 | ],
29 | NE: [
30 | [0, -2],
31 | [0, -3],
32 | ],
33 | E: [
34 | [-3, 0],
35 | [-3, -1],
36 | ],
37 | SE: [
38 | [-5, -1],
39 | [-5, -2],
40 | ],
41 | S: [
42 | [-6, -3],
43 | [-7, -2],
44 | ],
45 | SW: [
46 | [-5, -3],
47 | [-6, -1],
48 | ],
49 | W: [
50 | [-4, -2],
51 | [-4, -3],
52 | ],
53 | NW: [
54 | [-1, 0],
55 | [-1, -1],
56 | ],
57 | };
58 | function create() {
59 | nekoEl.id = "oneko";
60 | nekoEl.style.width = "32px";
61 | nekoEl.style.height = "32px";
62 | nekoEl.style.position = "fixed";
63 | nekoEl.style.backgroundImage = "url('./oneko.gif')";
64 | nekoEl.style.imageRendering = "pixelated";
65 | nekoEl.style.left = "16px";
66 | nekoEl.style.top = "16px";
67 |
68 | document.body.appendChild(nekoEl);
69 |
70 | document.onmousemove = (event) => {
71 | mousePosX = event.clientX;
72 | mousePosY = event.clientY;
73 | };
74 |
75 | window.onekoInterval = setInterval(frame, 100);
76 | }
77 |
78 | function setSprite(name, frame) {
79 | const sprite = spriteSets[name][frame % spriteSets[name].length];
80 | nekoEl.style.backgroundPosition = `${sprite[0] * 32}px ${
81 | sprite[1] * 32
82 | }px`;
83 | }
84 |
85 | function resetIdleAnimation() {
86 | idleAnimation = null;
87 | idleAnimationFrame = 0;
88 | }
89 |
90 | function idle() {
91 | idleTime += 1;
92 |
93 | // every ~ 20 seconds
94 | if (
95 | idleTime > 10 &&
96 | Math.floor(Math.random() * 200) == 0 &&
97 | idleAnimation == null
98 | ) {
99 | idleAnimation = ["sleeping", "scratch"][
100 | Math.floor(Math.random() * 2)
101 | ];
102 | }
103 |
104 | switch (idleAnimation) {
105 | case "sleeping":
106 | if (idleAnimationFrame < 8) {
107 | setSprite("tired", 0);
108 | break;
109 | }
110 | setSprite("sleeping", Math.floor(idleAnimationFrame / 4));
111 | if (idleAnimationFrame > 192) {
112 | resetIdleAnimation();
113 | }
114 | break;
115 | case "scratch":
116 | setSprite("scratch", idleAnimationFrame);
117 | if (idleAnimationFrame > 9) {
118 | resetIdleAnimation();
119 | }
120 | break;
121 | default:
122 | setSprite("idle", 0);
123 | return;
124 | }
125 | idleAnimationFrame += 1;
126 | }
127 |
128 | function frame() {
129 | frameCount += 1;
130 | const diffX = nekoPosX - mousePosX;
131 | const diffY = nekoPosY - mousePosY;
132 | const distance = Math.sqrt(diffX ** 2 + diffY ** 2);
133 |
134 | if (distance < nekoSpeed || distance < 48) {
135 | idle();
136 | return;
137 | }
138 |
139 | idleAnimation = null;
140 | idleAnimationFrame = 0;
141 |
142 | if (idleTime > 1) {
143 | setSprite("alert", 0);
144 | // count down after being alerted before moving
145 | idleTime = Math.min(idleTime, 7);
146 | idleTime -= 1;
147 | return;
148 | }
149 |
150 | direction = diffY / distance > 0.5 ? "N" : "";
151 | direction += diffY / distance < -0.5 ? "S" : "";
152 | direction += diffX / distance > 0.5 ? "W" : "";
153 | direction += diffX / distance < -0.5 ? "E" : "";
154 | setSprite(direction, frameCount);
155 |
156 | nekoPosX -= (diffX / distance) * nekoSpeed;
157 | nekoPosY -= (diffY / distance) * nekoSpeed;
158 |
159 | nekoEl.style.left = `${nekoPosX - 16}px`;
160 | nekoEl.style.top = `${nekoPosY - 16}px`;
161 | }
162 |
163 | create();
164 | })();
165 |
--------------------------------------------------------------------------------
/app/components/Blogs.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import { LuCalendar, LuClock, LuTag } from "react-icons/lu";
3 | import fs from "fs";
4 | import path from "path";
5 |
6 | interface BlogPost {
7 | title: string;
8 | desc: string;
9 | date: string;
10 | tags: string[];
11 | slug: string;
12 | readTime?: string;
13 | }
14 |
15 | function parseFrontmatter(content: string) {
16 | const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
17 | if (!frontmatterMatch) {
18 | return null;
19 | }
20 |
21 | const [, frontmatterText, postContent] = frontmatterMatch;
22 |
23 | const frontmatter: any = {};
24 | frontmatterText.split("\n").forEach((line) => {
25 | const [key, ...valueParts] = line.split(":");
26 | if (key && valueParts.length > 0) {
27 | const value = valueParts.join(":").trim();
28 | if (key.trim() === "tags") {
29 | try {
30 | frontmatter[key.trim()] = JSON.parse(value);
31 | } catch {
32 | frontmatter[key.trim()] = [];
33 | }
34 | } else {
35 | frontmatter[key.trim()] = value.replace(/^["']|["']$/g, "");
36 | }
37 | }
38 | });
39 |
40 | return { frontmatter, content: postContent };
41 | }
42 |
43 | function estimateReadTime(content: string): string {
44 | const wordsPerMinute = 200;
45 | const words = content.split(/\s+/).length;
46 | const minutes = Math.ceil(words / wordsPerMinute);
47 | return `${minutes} min read`;
48 | }
49 |
50 | function getTopBlogPosts(): BlogPost[] {
51 | try {
52 | const postsDirectory = path.join(process.cwd(), "posts");
53 |
54 | if (!fs.existsSync(postsDirectory)) {
55 | return [];
56 | }
57 |
58 | const filenames = fs.readdirSync(postsDirectory);
59 | const posts: BlogPost[] = [];
60 |
61 | for (const filename of filenames) {
62 | if (filename.endsWith(".mdx")) {
63 | const filePath = path.join(postsDirectory, filename);
64 | const fileContent = fs.readFileSync(filePath, "utf8");
65 |
66 | const parsed = parseFrontmatter(fileContent);
67 | if (parsed) {
68 | const slug = filename.replace(".mdx", "");
69 | posts.push({
70 | title: parsed.frontmatter.title || "Untitled",
71 | desc: parsed.frontmatter.desc || "",
72 | date: parsed.frontmatter.date || "",
73 | tags: parsed.frontmatter.tags || [],
74 | slug: slug,
75 | readTime: estimateReadTime(parsed.content),
76 | });
77 | }
78 | }
79 | }
80 |
81 | return posts
82 | .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
83 | .slice(0, 2);
84 | } catch (error) {
85 | console.error("Error reading blog posts:", error);
86 | return [];
87 | }
88 | }
89 |
90 | export default function Blogs() {
91 | const blogPosts = getTopBlogPosts();
92 |
93 | return (
94 |
96 | ~/ Blogs
97 |
98 |
99 | {blogPosts.map((post) => (
100 |
105 |
108 | {post.title}
109 |
110 |
111 |
112 | {post.readTime}
113 |
114 |
119 | ~/ Blog
120 |
121 |
122 | {blogPosts.map((post) => (
123 |
128 |
131 | {post.title}
132 |
133 |
134 |
135 | {post.readTime}
136 |
137 | $1
');
63 | html = html.replace(/^## (.*$)/gim, '$1
');
64 | html = html.replace(/^# (.*$)/gim, '$1
');
65 |
66 | // Text formatting
67 | html = html.replace(/\*\*(.*?)\*\*/g, '$1');
68 | html = html.replace(/\*(.*?)\*/g, '$1');
69 |
70 | // Linked images (must come before regular links and images)
71 | html = html.replace(
72 | /\[!\[([^\]]*)\]\(([^)]*)\)\]\(([^)]*)\)/g,
73 | ''
74 | );
75 |
76 | // Images (must come before regular links)
77 | html = html.replace(/!\[([^\]]*)\]\(([^)]*)\)/g, '
');
78 |
79 | // Links
80 | html = html.replace(
81 | /\[([^\]]*)\]\(([^)]*)\)/g,
82 | '$1'
83 | );
84 |
85 | // Code blocks
86 | html = html.replace(
87 | /```(\w+)?\n([\s\S]*?)```/g,
88 | '
'
89 | );
90 |
91 | // Inline code
92 | html = html.replace(/`([^`]*)`/g, '$2$1');
93 |
94 | // Lists
95 | html = html.replace(/^\* (.*$)/gim, '$1
');
97 |
98 | // Paragraphs
99 | html = html.replace(/\n\n/g, '
'); 100 | html = '
' + html + '
'; 101 | 102 | // Clean up empty paragraphs 103 | html = html.replace(/<\/p>/g, '');
104 |
105 | return html;
106 | }
107 |
108 | async function getBlogPost(slug: string) {
109 | try {
110 | const filePath = path.join(process.cwd(), "posts", `${slug}.mdx`);
111 |
112 | if (!fs.existsSync(filePath)) {
113 | return null;
114 | }
115 |
116 | const fileContent = fs.readFileSync(filePath, "utf8");
117 | const parsed = parseFrontmatter(fileContent);
118 |
119 | if (!parsed) {
120 | return null;
121 | }
122 |
123 | return {
124 | ...parsed.frontmatter,
125 | content: parsed.content,
126 | readTime: estimateReadTime(parsed.content),
127 | };
128 | } catch (error) {
129 | console.error("Error reading blog post:", error);
130 | return null;
131 | }
132 | }
133 |
134 | // Generate static params for all blog posts
135 | export async function generateStaticParams() {
136 | try {
137 | const postsDirectory = path.join(process.cwd(), "posts");
138 | const filenames = fs.readdirSync(postsDirectory);
139 |
140 | return filenames
141 | .filter(name => name.endsWith('.mdx'))
142 | .map(name => ({
143 | url: name.replace('.mdx', '')
144 | }));
145 | } catch (error) {
146 | console.error('Error generating static params:', error);
147 | return [];
148 | }
149 | }
150 |
151 | function extractFirstImage(content: string): string | null {
152 | const imagePatterns = [
153 | /!\[([^\]]*)\]\(([^)]*)\)/, // 
154 | /\[!\[([^\]]*)\]\(([^)]*)\)\]\(([^)]*)\)/ // [](link)
155 | ];
156 |
157 | for (const pattern of imagePatterns) {
158 | const match = content.match(pattern);
159 | if (match) {
160 | return match[2];
161 | }
162 | }
163 |
164 | return null;
165 | }
166 |
167 | export async function generateMetadata({
168 | params,
169 | }: BlogPostProps): Promise The blog post you're looking for doesn't exist. {post.desc}Post Not Found
230 | {post.title}
250 |