refactor(shell): strip global chrome and retire /posts routes
This commit is contained in:
@@ -3,9 +3,6 @@ import { Fraunces, IBM_Plex_Mono, JetBrains_Mono, Space_Grotesk } from 'next/fon
|
|||||||
import { ThemeProvider } from '@wrksz/themes/next'
|
import { ThemeProvider } from '@wrksz/themes/next'
|
||||||
import './globals.css'
|
import './globals.css'
|
||||||
import 'katex/dist/katex.min.css'
|
import 'katex/dist/katex.min.css'
|
||||||
import { Header } from '@/components/layout/Header'
|
|
||||||
import { Footer } from '@/components/layout/Footer'
|
|
||||||
import { ScrollProgress } from '@/components/ui/ScrollProgress'
|
|
||||||
import { Providers } from './providers'
|
import { Providers } from './providers'
|
||||||
|
|
||||||
const fraunces = Fraunces({
|
const fraunces = Fraunces({
|
||||||
@@ -63,12 +60,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|||||||
forcedTheme="dark"
|
forcedTheme="dark"
|
||||||
disableTransitionOnChange
|
disableTransitionOnChange
|
||||||
>
|
>
|
||||||
<Providers>
|
<Providers>{children}</Providers>
|
||||||
<ScrollProgress />
|
|
||||||
<Header />
|
|
||||||
{children}
|
|
||||||
<Footer />
|
|
||||||
</Providers>
|
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
36
app/page.tsx
36
app/page.tsx
@@ -1,30 +1,18 @@
|
|||||||
import { getPosts } from '@/lib/posts'
|
import Link from 'next/link'
|
||||||
import { PostList } from '@/components/posts/PostList'
|
|
||||||
|
|
||||||
export default async function HomePage() {
|
|
||||||
const posts = await getPosts()
|
|
||||||
|
|
||||||
|
export default function HomePage() {
|
||||||
return (
|
return (
|
||||||
<main className="mx-auto max-w-5xl px-6 py-10 sm:py-14">
|
<div className="flex min-h-screen flex-col items-center justify-center gap-6 bg-blacksite-bg px-6 text-center text-blacksite-text">
|
||||||
<section aria-labelledby="recent-heading" className="space-y-7">
|
{/* Temporary placeholder until the portfolio landing is implemented in P5. */}
|
||||||
<div className="border-b border-border pb-5">
|
<h1 className="font-display text-4xl font-semibold tracking-tight sm:text-6xl">
|
||||||
<h1 id="recent-heading" className="heading-xl m-0 text-ink">
|
Krishna Ayyalasomayajula
|
||||||
Recent
|
|
||||||
</h1>
|
</h1>
|
||||||
|
<Link
|
||||||
|
href="/blog/"
|
||||||
|
className="font-mono text-xs uppercase tracking-[0.22em] text-signal-cyan underline-offset-4 transition hover:text-signal-orange hover:underline"
|
||||||
|
>
|
||||||
|
Field Notes
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{posts.length > 0 ? (
|
|
||||||
<PostList posts={posts} />
|
|
||||||
) : (
|
|
||||||
<div className="empty-state">
|
|
||||||
<p className="font-mono text-xs uppercase tracking-[0.22em] text-ink-soft">No posts yet</p>
|
|
||||||
<h3 className="heading-sm m-0 text-ink">The notebook is ready.</h3>
|
|
||||||
<p className="m-0 max-w-xl text-sm leading-6 text-ink-soft">
|
|
||||||
Add your first MDX post and it will show up here with the same polished card treatment.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
import { notFound } from 'next/navigation'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import { getPosts, getPost } from '@/lib/posts'
|
|
||||||
import { tagToSlug } from '@/lib/tags'
|
|
||||||
import { TableOfContents } from '@/components/posts/TableOfContents'
|
|
||||||
import { ScrollToTop } from '@/components/ui/ScrollToTop'
|
|
||||||
import { ReadingProgress } from '@/components/ui/ReadingProgress'
|
|
||||||
|
|
||||||
export const dynamicParams = false
|
|
||||||
export const dynamic = 'force-static'
|
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
|
||||||
const posts = await getPosts()
|
|
||||||
if (posts.length === 0) {
|
|
||||||
return [{ slug: '__no-posts__' }]
|
|
||||||
}
|
|
||||||
return posts.map((post) => ({ slug: post.slug }))
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
|
|
||||||
const slug = (await params).slug
|
|
||||||
const post = await getPost(slug)
|
|
||||||
if (!post) return { title: 'Not Found' }
|
|
||||||
return { title: post.title }
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
|
|
||||||
const slug = (await params).slug
|
|
||||||
|
|
||||||
if (slug === '__no-posts__') notFound()
|
|
||||||
|
|
||||||
const post = await getPost(slug)
|
|
||||||
|
|
||||||
if (!post) notFound()
|
|
||||||
|
|
||||||
const { default: PostContent } = await import(
|
|
||||||
/* turbopackOptional: true */
|
|
||||||
`@/content/posts/${slug}.mdx`
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ScrollToTop />
|
|
||||||
<ReadingProgress />
|
|
||||||
<div className="mx-auto max-w-6xl px-6 py-16">
|
|
||||||
<div className="grid grid-cols-1 gap-8 lg:grid-cols-[minmax(0,1fr)_14rem] lg:gap-12">
|
|
||||||
<article className="min-w-0">
|
|
||||||
<header className="mb-12">
|
|
||||||
<time className="font-mono text-sm text-ink-soft" dateTime={post.date}>{new Date(post.date).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })}</time>
|
|
||||||
<h1 className="heading-xl text-ink mt-3 mb-2">
|
|
||||||
{post.title}
|
|
||||||
</h1>
|
|
||||||
<div className="flex items-center gap-4 font-mono text-xs text-ink-soft">
|
|
||||||
{post.author && <span>by {post.author}</span>}
|
|
||||||
<span>{post.readingTime} min read</span>
|
|
||||||
</div>
|
|
||||||
{post.coverImage && (
|
|
||||||
<img
|
|
||||||
src={post.coverImage}
|
|
||||||
alt={`Featured image for article: ${post.title}`}
|
|
||||||
className="my-8 aspect-[16/9] w-full rounded-2xl border border-border bg-surface object-cover shadow-card"
|
|
||||||
loading="eager"
|
|
||||||
decoding="async"
|
|
||||||
fetchPriority="high"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{post.tags.length > 0 && (
|
|
||||||
<div className="flex flex-wrap gap-2 mt-4">
|
|
||||||
{post.tags.map((tag) => (
|
|
||||||
<Link key={tag} href={`/tags/${tagToSlug(tag)}/`} className="rounded-full bg-surface px-3 py-1 text-xs font-medium text-ink-soft border border-border transition-colors hover:border-accent/50 hover:text-accent focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent">
|
|
||||||
{tag}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</header>
|
|
||||||
<div className="prose prose-lg article-prose max-w-none min-w-0">
|
|
||||||
<PostContent />
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<aside className="hidden lg:block">
|
|
||||||
<TableOfContents />
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { getPosts } from '@/lib/posts'
|
|
||||||
import { PostSearch } from '@/components/posts/PostSearch'
|
|
||||||
|
|
||||||
export const metadata = { title: 'Posts' }
|
|
||||||
|
|
||||||
export default async function PostsPage() {
|
|
||||||
const posts = await getPosts()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="mx-auto max-w-5xl px-6 py-12 sm:py-16">
|
|
||||||
<header className="mb-10 border-b border-border pb-8">
|
|
||||||
<h1 className="heading-xl m-0 text-ink">
|
|
||||||
Posts
|
|
||||||
</h1>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
{posts.length > 0 ? (
|
|
||||||
<PostSearch posts={posts} />
|
|
||||||
) : (
|
|
||||||
<section className="empty-state" aria-label="No posts published">
|
|
||||||
<p className="font-mono text-xs uppercase tracking-[0.22em] text-ink-soft">No posts yet</p>
|
|
||||||
<h2 className="heading-sm m-0 text-ink">The archive is empty.</h2>
|
|
||||||
<p className="m-0 max-w-xl text-sm leading-6 text-ink-soft">
|
|
||||||
Publish an MDX post to populate this list and enable archive search.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
</main>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import type { Metadata } from 'next'
|
|
||||||
import { notFound } from 'next/navigation'
|
|
||||||
import { PostCard } from '@/components/posts/PostCard'
|
|
||||||
import { getAllTags, getPostsByTag } from '@/lib/posts'
|
|
||||||
|
|
||||||
type TagPageProps = {
|
|
||||||
params: Promise<{ tag: string }>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const dynamicParams = false
|
|
||||||
export const dynamic = 'force-static'
|
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
|
||||||
const tags = await getAllTags()
|
|
||||||
if (tags.length === 0) {
|
|
||||||
return [{ tag: '__no-tags__' }]
|
|
||||||
}
|
|
||||||
return tags.map((tag) => ({ tag: tag.slug }))
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function generateMetadata({ params }: TagPageProps): Promise<Metadata> {
|
|
||||||
const slug = (await params).tag
|
|
||||||
const tags = await getAllTags()
|
|
||||||
const tag = tags.find((item) => item.slug === slug)
|
|
||||||
|
|
||||||
if (!tag) return { title: 'Not Found' }
|
|
||||||
|
|
||||||
return { title: `${tag.label} posts` }
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function TagPage({ params }: TagPageProps) {
|
|
||||||
const slug = (await params).tag
|
|
||||||
|
|
||||||
if (slug === '__no-tags__') notFound()
|
|
||||||
|
|
||||||
const tags = await getAllTags()
|
|
||||||
const tag = tags.find((item) => item.slug === slug)
|
|
||||||
|
|
||||||
if (!tag) notFound()
|
|
||||||
|
|
||||||
const posts = await getPostsByTag(slug)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="max-w-4xl mx-auto px-6 py-12">
|
|
||||||
<header className="mb-12">
|
|
||||||
<p className="font-mono text-sm text-ink-soft mb-3">Tag</p>
|
|
||||||
<h1 className="heading-xl text-ink mb-3">{tag.label}</h1>
|
|
||||||
<p className="text-ink-soft">
|
|
||||||
{tag.count} {tag.count === 1 ? 'post' : 'posts'}
|
|
||||||
</p>
|
|
||||||
</header>
|
|
||||||
<ul className="space-y-8">
|
|
||||||
{posts.map((post, index) => (
|
|
||||||
<li key={post.slug}>
|
|
||||||
<PostCard {...post} index={index} />
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</main>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { m } from "motion/react";
|
|
||||||
|
|
||||||
export default function Template({ children }: { children: React.ReactNode }) {
|
|
||||||
return (
|
|
||||||
<m.div
|
|
||||||
initial={{ opacity: 0, y: 8 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ duration: 0.4, ease: "easeOut" }}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</m.div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user