Lil bit of cleanup
This commit is contained in:
parent
722ace7c74
commit
1a713fe1b6
18 changed files with 304 additions and 205 deletions
|
|
@ -16,6 +16,13 @@ export async function generateMetadata({ params }: CategoryPageProps): Promise<M
|
|||
return {
|
||||
title: `${category} | ${metadata.title!.toString()}`,
|
||||
description: 'List of posts in this category',
|
||||
openGraph: {
|
||||
type: 'website',
|
||||
title: `${category} | ${metadata.title!.toString()}`,
|
||||
description: 'List of posts in this category',
|
||||
url: process.env.SITE_URL + `/categories/${categorySlug}`,
|
||||
siteName: metadata.title!.toString(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
77
app/(withRecent)/categories/page.tsx
Normal file
77
app/(withRecent)/categories/page.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import { getAllCategories } from '@/lib/categories'
|
||||
import { getAllPostsMeta } from '@/lib/posts'
|
||||
import { slugify } from '@/utils/slug'
|
||||
import type { Metadata } from 'next'
|
||||
import Link from 'next/link'
|
||||
import { metadata } from '@/app/metadata'
|
||||
import { dateFormatter } from '@/utils/time'
|
||||
import { RecentlyUpdatedPosts } from '@/components/common/RecentlyUpdatedPosts'
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
return {
|
||||
title: `Categories | ${metadata.title!.toString()}`,
|
||||
description: 'List of categories',
|
||||
openGraph: {
|
||||
type: 'website',
|
||||
title: `Categories | ${metadata.title!.toString()}`,
|
||||
description: 'List of categories',
|
||||
url: process.env.SITE_URL + '/categories',
|
||||
siteName: metadata.title!.toString(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default function CategoriesPage() {
|
||||
const categories = getAllCategories()
|
||||
const posts = getAllPostsMeta()
|
||||
const categoryNames = Object.keys(categories)
|
||||
|
||||
return (
|
||||
<main className="max-w-3xl mx-auto px-4 py-8 lg:py-16 w-full">
|
||||
<div className="text-sm text-fg-dim mb-2">~/categories</div>
|
||||
|
||||
<ul className="border-l border-neutral-800 ml-1">
|
||||
{categoryNames.map((category) => {
|
||||
const categoryPosts = posts.filter((post) => post.category === category)
|
||||
|
||||
return (
|
||||
<li key={category} className="pl-5">
|
||||
<Link
|
||||
href={`/categories/${slugify(category)}`}
|
||||
className="group flex items-baseline gap-2 py-2 px-2 -ml-2 hover:bg-accent/10 transition-colors"
|
||||
>
|
||||
<span className="font-bold group-hover:text-accent transition-colors">
|
||||
{category}
|
||||
</span>
|
||||
<span className="font-mono text-xs text-neutral-600">({categoryPosts.length})</span>
|
||||
</Link>
|
||||
|
||||
<ul className="border-l border-neutral-800 ml-3 mb-3">
|
||||
{categoryPosts.map((post) => (
|
||||
<li key={post.slug} className="pl-5">
|
||||
<Link
|
||||
href={`/posts/${post.slug}`}
|
||||
className="group flex items-baseline justify-between gap-3 py-1.5 px-2 -ml-2 border-l-2 border-transparent hover:bg-accent/10 hover:border-accent transition-colors"
|
||||
>
|
||||
<span className="group-hover:text-accent transition-colors truncate">
|
||||
{post.title}
|
||||
</span>
|
||||
{post.date && (
|
||||
<time
|
||||
dateTime={new Date(post.date).toISOString()}
|
||||
className="text-sm whitespace-nowrap"
|
||||
>
|
||||
{dateFormatter.format(new Date(post.date))}
|
||||
</time>
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
10
app/(withRecent)/layout.tsx
Normal file
10
app/(withRecent)/layout.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { RecentlyUpdatedPosts } from '@/components/common/RecentlyUpdatedPosts'
|
||||
|
||||
export default function WithRecentLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<RecentlyUpdatedPosts />
|
||||
</>
|
||||
)
|
||||
}
|
||||
41
app/(withRecent)/page.tsx
Normal file
41
app/(withRecent)/page.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { getAllPostsMeta } from '@/lib/posts'
|
||||
import { Post } from '@/components/common/Post'
|
||||
|
||||
export default function BlogIndex() {
|
||||
const posts = getAllPostsMeta()
|
||||
|
||||
const rest = posts.slice(1)
|
||||
const rows = []
|
||||
for (let i = 0; i < rest.length; i += 2) {
|
||||
rows.push(rest.slice(i, i + 2))
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="max-w-3xl mx-auto px-4 py-8 lg:py-16">
|
||||
<section>
|
||||
{posts.length === 0 ? (
|
||||
<div className="rounded-xl border border-border p-6 text-fg-dim">
|
||||
No published posts yet.
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Post post={posts[0]} isLatest />
|
||||
<div className="border-t border-border my-6" aria-hidden="true"></div>
|
||||
<div className="flex flex-col gap-8 mt-4">
|
||||
{rows.map((row) => {
|
||||
const rowHasImage = row.some((p) => p.coverImage)
|
||||
return (
|
||||
<div key={row[0].slug} className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
{row.map((post) => (
|
||||
<Post post={post} key={post.slug} showImageSlot={rowHasImage} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
import { getAllCategories } from '@/lib/categories'
|
||||
import { getAllPostsMeta } from '@/lib/posts'
|
||||
import { slugify } from '@/utils/slug'
|
||||
import type { Metadata } from 'next'
|
||||
import Link from 'next/link'
|
||||
import { metadata } from '@/app/metadata'
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
return { title: `Categories | ${metadata.title!.toString()}`, description: 'List of categories' }
|
||||
}
|
||||
|
||||
export default function CategoriesPage() {
|
||||
const categories = getAllCategories()
|
||||
const posts = getAllPostsMeta()
|
||||
|
||||
return (
|
||||
<div className="flex w-full">
|
||||
<main className="max-w-3xl mx-auto px-4 py-8 lg:py-16 w-full">
|
||||
<h1 className="text-3xl font-bold">Categories</h1>
|
||||
<ul>
|
||||
{Object.keys(categories).map((category) => (
|
||||
<li key={category}>
|
||||
<Link href={`/categories/${slugify(category)}`}>
|
||||
{category} ({categories[category]})
|
||||
</Link>
|
||||
|
||||
<ul>
|
||||
{posts
|
||||
.filter((post) => post.category === category)
|
||||
.map((post) => (
|
||||
<li key={post.slug}>
|
||||
<Link href={`/posts/${post.slug}`}>{post.title}</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</main>
|
||||
<div className="w-80" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -43,7 +43,8 @@ body {
|
|||
@apply list-decimal;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
ul,
|
||||
ol {
|
||||
@apply list-outside;
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +152,7 @@ dd {
|
|||
}
|
||||
|
||||
.prose a {
|
||||
@apply text-accent;
|
||||
@apply text-accent hover:underline focus:underline cursor-pointer;
|
||||
}
|
||||
|
||||
.prose ul:not(.task-list-container) {
|
||||
|
|
|
|||
14
app/metadata.ts
Normal file
14
app/metadata.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Ephemeris",
|
||||
description: "A blog about me and my interests",
|
||||
creator: "hangerthem",
|
||||
openGraph: {
|
||||
type: "website",
|
||||
title: "Ephemeris",
|
||||
description: "A blog about me and my interests",
|
||||
url: process.env.SITE_URL,
|
||||
siteName: "Ephemeris",
|
||||
},
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Ephemeris",
|
||||
description: "A blog about me and my interests",
|
||||
creator: "hangerthem",
|
||||
}
|
||||
63
app/page.tsx
63
app/page.tsx
|
|
@ -1,63 +0,0 @@
|
|||
import { getAllPostsMeta } from '@/lib/posts'
|
||||
import { Post } from '@/components/common/Post'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function BlogIndex() {
|
||||
const posts = getAllPostsMeta()
|
||||
|
||||
const rest = posts.slice(1)
|
||||
const rows = []
|
||||
for (let i = 0; i < rest.length; i += 2) {
|
||||
rows.push(rest.slice(i, i + 2))
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<main className="max-w-3xl mx-auto px-4 py-8 lg:py-16">
|
||||
<section>
|
||||
{posts.length === 0 ? (
|
||||
<div className="rounded-xl border border-border p-6 text-fg-dim">
|
||||
No published posts yet.
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Post post={posts[0]} isLatest />
|
||||
<div className="border-t border-border my-6" aria-hidden="true"></div>
|
||||
<div className="flex flex-col gap-8 mt-4">
|
||||
{rows.map((row) => {
|
||||
const rowHasImage = row.some((p) => p.coverImage)
|
||||
return (
|
||||
<div key={row[0].slug} className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
{row.map((post) => (
|
||||
<Post post={post} key={post.slug} showImageSlot={rowHasImage} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
</main>
|
||||
<aside className="w-80 hidden lg:block px-4 py-8 lg:py-16">
|
||||
<div className="sticky top-8">
|
||||
<h2 className="text-xl font-bold mb-4">Recently updated</h2>
|
||||
<ul className="flex flex-col gap-2 ml-2">
|
||||
{posts
|
||||
.toSorted(
|
||||
(a, b) =>
|
||||
new Date(b.updated ?? b.date).getTime() - new Date(a.updated ?? a.date).getTime(),
|
||||
)
|
||||
.map((post) => (
|
||||
<li key={post.slug} className="text-sm text-fg-dim">
|
||||
<Link href={`/posts/${post.slug}`} className="hover:underline">
|
||||
{post.title}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -2,7 +2,8 @@ import { metadata } from '@/app/metadata'
|
|||
import { ImageWrapper } from '@/components/common/ImageWrapper'
|
||||
import { ShareLinks } from '@/components/common/ShareLinks'
|
||||
import { TableOfContents } from '@/components/common/TableOfContents'
|
||||
import { getAllPostSlugs, getPostBySlug } from '@/lib/posts'
|
||||
import { getAllPostSlugs, getNextAndPreviousPosts, getPostBySlug } from '@/lib/posts'
|
||||
import { slugify } from '@/utils/slug'
|
||||
import type { Metadata } from 'next'
|
||||
import Link from 'next/link'
|
||||
|
||||
|
|
@ -19,19 +20,33 @@ export async function generateMetadata({
|
|||
}): Promise<Metadata> {
|
||||
const { slug } = await params
|
||||
const { meta } = await getPostBySlug(slug)
|
||||
return { title: `${meta.title} | ${metadata.title!.toString()}`, description: meta.description }
|
||||
return {
|
||||
title: `${meta.title} | ${metadata.title!.toString()}`,
|
||||
description: meta.description,
|
||||
openGraph: {
|
||||
type: 'article',
|
||||
title: `${meta.title} | ${metadata.title!.toString()}`,
|
||||
description: meta.description,
|
||||
url: process.env.SITE_URL + `/posts/${slug}`,
|
||||
images: meta.coverImage ? [meta.coverImage] : undefined,
|
||||
tags: meta.tags,
|
||||
...(meta.updated && { modifiedTime: new Date(meta.updated).toISOString() }),
|
||||
siteName: metadata.title!.toString(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default async function Post({ params }: { params: Promise<{ slug: string }> }) {
|
||||
const { slug } = await params
|
||||
const { meta, content, tableOfContents } = await getPostBySlug(slug)
|
||||
const { next, previous } = getNextAndPreviousPosts(slug)
|
||||
|
||||
return (
|
||||
<main className="flex w-full">
|
||||
<article className="max-w-3xl mx-auto px-4 py-8 lg:py-16">
|
||||
{meta.category && (
|
||||
<Link
|
||||
href={`/categories/${meta.category.toLowerCase()}`}
|
||||
href={`/categories/${slugify(meta.category)}`}
|
||||
className="text-accent hover:underline font-sm mb-2 block"
|
||||
>
|
||||
{meta.category}
|
||||
|
|
@ -99,6 +114,29 @@ export default async function Post({ params }: { params: Promise<{ slug: string
|
|||
description={meta.description}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex justify-between">
|
||||
{previous ? (
|
||||
<Link
|
||||
href={`/posts/${previous.slug}`}
|
||||
className="text-accent hover:underline font-sm mb-2 block"
|
||||
>
|
||||
← {previous.title}
|
||||
</Link>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
{next ? (
|
||||
<Link
|
||||
href={`/posts/${next.slug}`}
|
||||
className="text-accent hover:underline font-sm mb-2 block"
|
||||
>
|
||||
{next.title} →
|
||||
</Link>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<aside className="hidden lg:block w-80">
|
||||
|
|
|
|||
|
|
@ -1,22 +1,11 @@
|
|||
'use client'
|
||||
|
||||
import { isValidElement, type ReactNode } from 'react'
|
||||
import { CopyButton } from './CopyButton'
|
||||
import { getTextContent } from '@/lib/react-node-text'
|
||||
|
||||
type CodeWrapperProps = {
|
||||
language?: string
|
||||
children?: ReactNode
|
||||
}
|
||||
|
||||
function getTextContent(node: ReactNode): string {
|
||||
if (node === null || typeof node === 'boolean') return ''
|
||||
if (typeof node === 'string' || typeof node === 'number') return String(node)
|
||||
if (Array.isArray(node)) return node.map(getTextContent).join('')
|
||||
if (isValidElement(node)) {
|
||||
const props = node.props as { children?: ReactNode }
|
||||
return getTextContent(props.children)
|
||||
}
|
||||
return ''
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export function CodeWrapper({ language, children }: CodeWrapperProps) {
|
||||
|
|
|
|||
|
|
@ -1,20 +1,12 @@
|
|||
import type { AnchorHTMLAttributes } from 'react';
|
||||
import { cn } from '@/utils/cn';
|
||||
import type { AnchorHTMLAttributes } from 'react'
|
||||
|
||||
type ExternalLinkProps = Omit<
|
||||
AnchorHTMLAttributes<HTMLAnchorElement>,
|
||||
'href' | 'rel' | 'target'
|
||||
> & {
|
||||
href: string;
|
||||
};
|
||||
|
||||
export function ExternalLink({ className, ...props }: ExternalLinkProps) {
|
||||
return (
|
||||
<a
|
||||
{...props}
|
||||
className={cn(className, 'hover:underline focus:underline')}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
/>
|
||||
);
|
||||
href: string
|
||||
}
|
||||
|
||||
export function ExternalLink(props: ExternalLinkProps) {
|
||||
return <a {...props} target="_blank" rel="noopener noreferrer" />
|
||||
}
|
||||
|
|
|
|||
27
components/common/RecentlyUpdatedPosts.tsx
Normal file
27
components/common/RecentlyUpdatedPosts.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { getAllPostsMeta } from '@/lib/posts'
|
||||
import Link from 'next/link'
|
||||
|
||||
export function RecentlyUpdatedPosts() {
|
||||
const posts = getAllPostsMeta()
|
||||
return (
|
||||
<aside className="w-80 hidden lg:block py-4 lg:py-12">
|
||||
<div className="sticky top-8 border-l border-border h-fit pl-4 py-4">
|
||||
<h2 className="text-xl font-bold mb-4">Recently updated</h2>
|
||||
<ul className="flex flex-col gap-2 ml-1">
|
||||
{posts
|
||||
.toSorted(
|
||||
(a, b) =>
|
||||
new Date(b.updated ?? b.date).getTime() - new Date(a.updated ?? a.date).getTime(),
|
||||
)
|
||||
.map((post) => (
|
||||
<li key={post.slug} className="text-sm text-fg-dim">
|
||||
<Link href={`/posts/${post.slug}`} className="hover:underline">
|
||||
{post.title}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
'use client'
|
||||
|
||||
import { cn } from '@/utils/cn'
|
||||
import { Folder, Home, Info, Rss, Tags } from 'lucide-react'
|
||||
import { Folder, GitBranch, Home, Info, Rss, Tags } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { ExternalLink } from '../common/ExternalLink'
|
||||
|
||||
type NavItem = {
|
||||
href: string
|
||||
label: string
|
||||
icon: React.ReactNode
|
||||
target?: string
|
||||
rel?: string
|
||||
isExternal?: boolean
|
||||
}
|
||||
|
||||
const navItems: NavItem[] = [
|
||||
|
|
@ -22,8 +22,13 @@ const navItems: NavItem[] = [
|
|||
href: '/feed.xml',
|
||||
label: 'RSS Feed',
|
||||
icon: <Rss className="w-4 h-4" />,
|
||||
target: '_blank',
|
||||
rel: 'noopener noreferrer',
|
||||
isExternal: true,
|
||||
},
|
||||
{
|
||||
href: 'https://git.hangerthem.com/hangerthem/blog.hangerthem.com',
|
||||
label: 'Source Code',
|
||||
icon: <GitBranch className="w-4 h-4" />,
|
||||
isExternal: true,
|
||||
},
|
||||
]
|
||||
|
||||
|
|
@ -45,15 +50,20 @@ export function NavLinks() {
|
|||
isActive(item.href) ? 'text-fg bg-fg/25' : 'text-fg-dim hover:text-fg hover:bg-fg/10',
|
||||
)}
|
||||
>
|
||||
<Link
|
||||
{item.isExternal ? (
|
||||
<ExternalLink
|
||||
href={item.href}
|
||||
className="flex items-center gap-2 px-2 py-1"
|
||||
target={item.target}
|
||||
rel={item.rel}
|
||||
className="flex items-center gap-2 px-2 py-1 hover:no-underline focus:no-underline"
|
||||
>
|
||||
{item.icon}
|
||||
{item.label}
|
||||
</ExternalLink>
|
||||
) : (
|
||||
<Link href={item.href} className="flex items-center gap-2 px-2 py-1">
|
||||
{item.icon}
|
||||
{item.label}
|
||||
</Link>
|
||||
)}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ title: Self-Development and Gamification of Life
|
|||
description: In this post, I explore the concept of self-development and how gamification can be used to enhance productivity and personal growth. Learn how to turn your life into a game and level up your skills!
|
||||
author: hangerthem
|
||||
date: 2024-08-05 22:00:00 +0200
|
||||
category: "Personal Development"
|
||||
category: Personal Development
|
||||
tags: [self-development, gamification, productivity, growth]
|
||||
math: true
|
||||
mermaid: true
|
||||
|
|
|
|||
|
|
@ -72,8 +72,9 @@ $$
|
|||
|
||||
---
|
||||
|
||||
[this is a link](https://example.com)
|
||||
this is an [external link](https://example.com)
|
||||
|
||||
and this is an [internal link](/about)
|
||||
|
||||
```mermaid
|
||||
graph TD;
|
||||
|
|
|
|||
|
|
@ -18,27 +18,18 @@ import { ExternalLink } from '@/components/common/ExternalLink'
|
|||
import readingTime from 'reading-time'
|
||||
import { rehypeUnwrapImages } from './rehype-unwrap-images'
|
||||
import { rehypeMermaidSsg } from './rehype-mermaid-ssg'
|
||||
import { stripLeadingMatch } from './react-node-text'
|
||||
import { getTextContent, stripLeadingMatch } from './react-node-text'
|
||||
import { BlockquoteWrapper } from '@/components/common/BlockquoteWrapper'
|
||||
import type { NoteType } from '@/types/Note.type'
|
||||
import { rehypeMark } from './rehype-mark'
|
||||
import { rehypeDefinition } from './rehype-definition'
|
||||
import { slugify } from '@/utils/slug'
|
||||
import { PostMeta } from '@/types/Post.type'
|
||||
import Link from 'next/link'
|
||||
import { ExternalLinkIcon } from 'lucide-react'
|
||||
|
||||
const postsDirectory = path.join(process.cwd(), 'content/posts')
|
||||
|
||||
function getTextContent(node: ReactNode): string {
|
||||
if (typeof node === 'string') return node
|
||||
if (typeof node === 'number') return String(node)
|
||||
if (Array.isArray(node)) return node.map(getTextContent).join('')
|
||||
if (node && typeof node === 'object' && 'props' in node) {
|
||||
const props = node.props as { children?: ReactNode }
|
||||
return getTextContent(props.children)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
export function getAllPostSlugs(): string[] {
|
||||
return fs
|
||||
.readdirSync(postsDirectory)
|
||||
|
|
@ -60,6 +51,17 @@ export function getAllPostsMeta(): PostMeta[] {
|
|||
.toSorted((a, b) => (a.date < b.date ? 1 : -1))
|
||||
}
|
||||
|
||||
export function getNextAndPreviousPosts(slug: string): {
|
||||
next: PostMeta | null
|
||||
previous: PostMeta | null
|
||||
} {
|
||||
const posts = getAllPostsMeta()
|
||||
const index = posts.findIndex((post) => post.slug === slug)
|
||||
const next = index < posts.length - 1 ? posts[index + 1] : null
|
||||
const previous = index > 0 ? posts[index - 1] : null
|
||||
return { next, previous }
|
||||
}
|
||||
|
||||
export async function getPostBySlug(slug: string) {
|
||||
const fullPath = path.join(postsDirectory, `${slug}.md`)
|
||||
const { data, content } = matter(fs.readFileSync(fullPath, 'utf8'))
|
||||
|
|
@ -98,9 +100,12 @@ export async function getPostBySlug(slug: string) {
|
|||
img: ({ src, alt }: { src: string; alt: string }) => <ImageWrapper src={src} alt={alt} />,
|
||||
a: ({ href, children }: { href: string; children: ReactNode }) =>
|
||||
href?.startsWith('http') ? (
|
||||
<ExternalLink href={href}>{children}</ExternalLink>
|
||||
<ExternalLink href={href}>
|
||||
{children}
|
||||
<ExternalLinkIcon className="inline-block w-3 h-3 ml-1" />
|
||||
</ExternalLink>
|
||||
) : (
|
||||
<a href={href}>{children}</a>
|
||||
<Link href={href}>{children}</Link>
|
||||
),
|
||||
blockquote: ({ children }: { children: ReactNode }) => {
|
||||
const textContent = getTextContent(children).trimStart()
|
||||
|
|
|
|||
Loading…
Reference in a new issue