-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 17b0e57
Showing
79 changed files
with
14,461 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# Sample workflow for building and deploying a Next.js site to GitHub Pages | ||
# | ||
# To get started with Next.js see: https://nextjs.org/docs/getting-started | ||
# | ||
name: Deploy Next.js site to Pages | ||
|
||
on: | ||
# Runs on pushes targeting the default branch | ||
push: | ||
branches: ["main"] | ||
|
||
# Allows you to run this workflow manually from the Actions tab | ||
workflow_dispatch: | ||
|
||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages | ||
permissions: | ||
contents: read | ||
pages: write | ||
id-token: write | ||
|
||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. | ||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. | ||
concurrency: | ||
group: "pages" | ||
cancel-in-progress: false | ||
|
||
jobs: | ||
# Build job | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v4 | ||
|
||
- name: Detect package manager | ||
id: detect-package-manager | ||
run: | | ||
if [ -f "${{ github.workspace }}/yarn.lock" ]; then | ||
echo "manager=yarn" >> $GITHUB_OUTPUT | ||
echo "command=install" >> $GITHUB_OUTPUT | ||
echo "runner=yarn" >> $GITHUB_OUTPUT | ||
exit 0 | ||
elif [ -f "${{ github.workspace }}/package.json" ]; then | ||
echo "manager=npm" >> $GITHUB_OUTPUT | ||
echo "command=ci" >> $GITHUB_OUTPUT | ||
echo "runner=npx --no-install" >> $GITHUB_OUTPUT | ||
exit 0 | ||
else | ||
echo "Unable to determine package manager" | ||
exit 1 | ||
fi | ||
- name: Setup Node | ||
uses: actions/setup-node@v4 | ||
with: | ||
node-version: "lts/*" | ||
cache: ${{ steps.detect-package-manager.outputs.manager }} | ||
|
||
- name: Setup Pages | ||
uses: actions/configure-pages@v4 | ||
|
||
- name: Restore cache | ||
uses: actions/cache@v4 | ||
with: | ||
path: | | ||
.next/cache | ||
# Generate a new cache whenever packages or source files change. | ||
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} | ||
# If source files changed but packages didn't, rebuild from a prior cache. | ||
restore-keys: | | ||
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- | ||
- name: Install dependencies | ||
run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} --force | ||
|
||
- name: Build with Next.js | ||
run: ${{ steps.detect-package-manager.outputs.runner }} next build | ||
|
||
- name: Upload artifact | ||
uses: actions/upload-pages-artifact@v3 | ||
with: | ||
path: ./out | ||
|
||
# Deployment job | ||
deploy: | ||
environment: | ||
name: github-pages | ||
url: ${{ steps.deployment.outputs.page_url }} | ||
runs-on: ubuntu-latest | ||
needs: build | ||
steps: | ||
- name: Deploy to GitHub Pages | ||
id: deployment | ||
uses: actions/deploy-pages@v4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# 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* | ||
|
||
# env files (can opt-in for commiting if needed) | ||
.env* | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). | ||
|
||
## Getting Started | ||
|
||
First, run the development server: | ||
|
||
```bash | ||
npm run dev | ||
# or | ||
yarn dev | ||
# or | ||
pnpm dev | ||
# or | ||
bun dev | ||
``` | ||
|
||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. | ||
|
||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. | ||
|
||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. | ||
|
||
## Learn More | ||
|
||
To learn more about Next.js, take a look at the following resources: | ||
|
||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. | ||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. | ||
|
||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! | ||
|
||
## Deploy on Vercel | ||
|
||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. | ||
|
||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
"use client" | ||
|
||
import { useEffect, useState } from 'react' | ||
import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote' | ||
import { serialize } from 'next-mdx-remote/serialize' | ||
import rehypePrism from 'rehype-prism-plus' | ||
import remarkGfm from 'remark-gfm' | ||
import { notFound } from 'next/navigation' | ||
import { Clock, User } from 'lucide-react' | ||
import { type BlogPost } from '@/lib/blog-data' | ||
|
||
interface BlogContentProps { | ||
initialPost: BlogPost | ||
slug: string | ||
} | ||
|
||
export default function BlogContent({ initialPost, slug }: BlogContentProps) { | ||
const [mdxSource, setMdxSource] = useState<MDXRemoteSerializeResult | null>(null) | ||
const [post] = useState<BlogPost | null>(initialPost) | ||
const [isLoading, setIsLoading] = useState(true) | ||
|
||
useEffect(() => { | ||
async function loadPost() { | ||
try { | ||
const response = await fetch(`/blogs/${slug}.md`) | ||
if (!response.ok) { | ||
throw new Error('Blog post not found') | ||
} | ||
|
||
const text = await response.text() | ||
const mdxSource = await serialize(text, { | ||
mdxOptions: { | ||
remarkPlugins: [remarkGfm], | ||
rehypePlugins: [rehypePrism], | ||
} | ||
}) | ||
|
||
setMdxSource(mdxSource) | ||
} catch (error) { | ||
console.error('Failed to load blog post:', error) | ||
notFound() | ||
} finally { | ||
setIsLoading(false) | ||
} | ||
} | ||
|
||
loadPost() | ||
}, [slug]) | ||
|
||
if (isLoading) { | ||
return <BlogSkeleton /> | ||
} | ||
|
||
if (!post || !mdxSource) { | ||
return notFound() | ||
} | ||
|
||
const components = { | ||
pre: ({ className, ...props }: React.HTMLAttributes<HTMLPreElement>) => ( | ||
<pre className={`${className} p-4 rounded-lg bg-neutral-950 overflow-x-auto`} {...props} /> | ||
), | ||
code: ({ className, ...props }: React.HTMLAttributes<HTMLElement>) => ( | ||
<code className={`${className} px-1 py-0.5 rounded-md text-orange-400 bg-neutral-950`} {...props} /> | ||
), | ||
h1: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => ( | ||
<h1 className={`${className} text-3xl font-bold mt-8 mb-4`} {...props} /> | ||
), | ||
h2: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => ( | ||
<h2 className={`${className} text-2xl font-semibold mt-6 mb-3`} {...props} /> | ||
), | ||
h3: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => ( | ||
<h3 className={`${className} text-xl font-semibold mt-4 mb-2`} {...props} /> | ||
), | ||
p: ({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => ( | ||
<p className={`${className} my-4 leading-7`} {...props} /> | ||
), | ||
ul: ({ className, ...props }: React.HTMLAttributes<HTMLUListElement>) => ( | ||
<ul className={`${className} list-disc list-inside my-4 space-y-2`} {...props} /> | ||
), | ||
ol: ({ className, ...props }: React.HTMLAttributes<HTMLOListElement>) => ( | ||
<ol className={`${className} list-decimal list-inside my-4 space-y-2`} {...props} /> | ||
), | ||
li: ({ className, ...props }: React.HTMLAttributes<HTMLLIElement>) => ( | ||
<li className={`${className} ml-4`} {...props} /> | ||
), | ||
a: ({ className, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement>) => ( | ||
<a className={`${className} text-blue-400 hover:text-blue-300 underline`} {...props} /> | ||
), | ||
blockquote: ({ className, ...props }: React.HTMLAttributes<HTMLQuoteElement>) => ( | ||
<blockquote className={`${className} border-l-4 border-neutral-700 pl-4 my-4 italic`} {...props} /> | ||
), | ||
table: ({ className, ...props }: React.TableHTMLAttributes<HTMLTableElement>) => ( | ||
<div className="overflow-x-auto my-4"> | ||
<table className={`${className} min-w-full border-collapse`} {...props} /> | ||
</div> | ||
), | ||
th: ({ className, ...props }: React.ThHTMLAttributes<HTMLTableHeaderCellElement>) => ( | ||
<th className={`${className} border border-neutral-800 px-4 py-2 bg-neutral-900`} {...props} /> | ||
), | ||
td: ({ className, ...props }: React.TdHTMLAttributes<HTMLTableDataCellElement>) => ( | ||
<td className={`${className} border border-neutral-800 px-4 py-2`} {...props} /> | ||
), | ||
} | ||
|
||
return ( | ||
<div className="min-h-screen bg-black text-white"> | ||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16"> | ||
<div className="mb-8"> | ||
<h1 className="text-4xl font-bold text-neutral-100 mb-4"> | ||
{post.title} | ||
</h1> | ||
|
||
<div className="flex items-center gap-6 mb-6 text-neutral-400"> | ||
<div className="flex items-center gap-2"> | ||
<User className="w-4 h-4" /> | ||
<span>{post.author}</span> | ||
</div> | ||
<div className="flex items-center gap-2"> | ||
<Clock className="w-4 h-4" /> | ||
<span>{post.readingTime} min read</span> | ||
</div> | ||
<time> | ||
{new Date(post.date).toLocaleDateString('en-US', { | ||
year: 'numeric', | ||
month: 'long', | ||
day: 'numeric' | ||
})} | ||
</time> | ||
</div> | ||
|
||
<div className="flex gap-2 flex-wrap mb-4"> | ||
{post.categories.map(category => ( | ||
<span | ||
key={category} | ||
className="px-3 py-1 text-sm rounded-full bg-blue-500/10 text-blue-400" | ||
> | ||
{category} | ||
</span> | ||
))} | ||
</div> | ||
|
||
<p className="text-lg text-neutral-400"> | ||
{post.excerpt} | ||
</p> | ||
</div> | ||
|
||
<div className="prose prose-invert prose-neutral max-w-none"> | ||
<MDXRemote {...mdxSource} components={components} /> | ||
</div> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
function BlogSkeleton() { | ||
return ( | ||
<div className="min-h-screen bg-black text-white"> | ||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16"> | ||
<div className="animate-pulse"> | ||
<div className="h-10 w-2/3 bg-neutral-800 rounded mb-4" /> | ||
<div className="flex gap-4 mb-6"> | ||
<div className="h-5 w-32 bg-neutral-800 rounded" /> | ||
<div className="h-5 w-24 bg-neutral-800 rounded" /> | ||
<div className="h-5 w-32 bg-neutral-800 rounded" /> | ||
</div> | ||
<div className="flex gap-2 mb-4"> | ||
<div className="h-6 w-20 bg-neutral-800 rounded-full" /> | ||
<div className="h-6 w-24 bg-neutral-800 rounded-full" /> | ||
</div> | ||
<div className="h-6 w-2/3 bg-neutral-800 rounded mb-8" /> | ||
<div className="space-y-4"> | ||
<div className="h-4 bg-neutral-800 rounded w-full" /> | ||
<div className="h-4 bg-neutral-800 rounded w-5/6" /> | ||
<div className="h-4 bg-neutral-800 rounded w-4/6" /> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { blogData } from '@/lib/blog-data' | ||
import BlogContent from './blog-content' | ||
|
||
export default function BlogPage({ | ||
params, | ||
}: { | ||
params: { slug: string } | ||
searchParams: Record<string, string | string[] | undefined> | ||
}) { | ||
const post = blogData.find((p) => p.slug === params.slug) | ||
|
||
if (!post) { | ||
return null | ||
} | ||
|
||
return <BlogContent initialPost={post} slug={params.slug} /> | ||
} | ||
|
||
export const generateStaticParams = () => | ||
blogData.map((post) => ({ | ||
slug: post.slug, | ||
})) |
Oops, something went wrong.