- Schema phase 1 (tasks 01-02): EntityId, EntityKind, Timestamp, Score, SignalTypeDef, DecayModel, Window, WindowSet — all with property tests and benchmarks scaffolding - Stub modules for storage, signals, query, ranking - Full documentation suite: VISION, USE_CASES, SEQUENCE, API, CODING_GUIDELINES, ai-lookup, research docs, specs, roadmap, planning docs - Marketing site (Next.js) with blog infrastructure - .claude/ agents and skills for the tidalDB development workflow - Foundation standards enforced: thiserror + tracing declared as dependencies, clippy::unwrap_used = deny added to lint config - .gitignore hardened: .next/, node_modules/, .env, secrets, logs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
136 lines
3.9 KiB
TypeScript
136 lines
3.9 KiB
TypeScript
import { getAllPosts, getPostBySlug } from "@/lib/blog";
|
|
import Link from "next/link";
|
|
import { MDXRemote } from "next-mdx-remote/rsc";
|
|
import { notFound } from "next/navigation";
|
|
|
|
export function generateStaticParams() {
|
|
return getAllPosts().map((post) => ({ slug: post.slug }));
|
|
}
|
|
|
|
export async function generateMetadata({
|
|
params,
|
|
}: {
|
|
params: Promise<{ slug: string }>;
|
|
}) {
|
|
const { slug } = await params;
|
|
const post = getPostBySlug(slug);
|
|
if (!post) return {};
|
|
return {
|
|
title: `${post.title} — tidalDB`,
|
|
description: post.description,
|
|
};
|
|
}
|
|
|
|
const components = {
|
|
h1: (props: React.ComponentProps<"h1">) => (
|
|
<h1
|
|
className="mt-12 mb-4 font-serif text-3xl font-bold leading-tight md:text-4xl"
|
|
{...props}
|
|
/>
|
|
),
|
|
h2: (props: React.ComponentProps<"h2">) => (
|
|
<h2
|
|
className="mt-10 mb-4 font-serif text-2xl font-bold leading-tight"
|
|
{...props}
|
|
/>
|
|
),
|
|
h3: (props: React.ComponentProps<"h3">) => (
|
|
<h3
|
|
className="mt-8 mb-3 font-mono text-sm font-medium uppercase tracking-wide text-foreground"
|
|
{...props}
|
|
/>
|
|
),
|
|
p: (props: React.ComponentProps<"p">) => (
|
|
<p className="mb-6 text-lg leading-relaxed text-muted" {...props} />
|
|
),
|
|
ul: (props: React.ComponentProps<"ul">) => (
|
|
<ul className="mb-6 list-disc pl-6 text-muted" {...props} />
|
|
),
|
|
ol: (props: React.ComponentProps<"ol">) => (
|
|
<ol className="mb-6 list-decimal pl-6 text-muted" {...props} />
|
|
),
|
|
li: (props: React.ComponentProps<"li">) => (
|
|
<li className="mb-2 text-lg leading-relaxed" {...props} />
|
|
),
|
|
pre: (props: React.ComponentProps<"pre">) => (
|
|
<pre
|
|
className="mb-6 overflow-x-auto rounded-lg border border-border bg-code-bg p-6 font-mono text-sm leading-relaxed text-code-text"
|
|
{...props}
|
|
/>
|
|
),
|
|
code: (props: React.ComponentProps<"code">) => {
|
|
const isBlock =
|
|
typeof props.className === "string" &&
|
|
props.className.includes("language-");
|
|
if (isBlock) return <code {...props} />;
|
|
return (
|
|
<code
|
|
className="rounded bg-code-bg px-1.5 py-0.5 font-mono text-sm text-accent"
|
|
{...props}
|
|
/>
|
|
);
|
|
},
|
|
blockquote: (props: React.ComponentProps<"blockquote">) => (
|
|
<blockquote
|
|
className="mb-6 border-l-2 border-accent pl-6 italic text-muted"
|
|
{...props}
|
|
/>
|
|
),
|
|
a: (props: React.ComponentProps<"a">) => (
|
|
<a
|
|
className="text-foreground underline decoration-accent/40 underline-offset-4 transition-colors hover:decoration-accent"
|
|
{...props}
|
|
/>
|
|
),
|
|
hr: () => <hr className="my-12 border-border" />,
|
|
};
|
|
|
|
export default async function BlogPost({
|
|
params,
|
|
}: {
|
|
params: Promise<{ slug: string }>;
|
|
}) {
|
|
const { slug } = await params;
|
|
const post = getPostBySlug(slug);
|
|
if (!post) notFound();
|
|
|
|
return (
|
|
<div className="pt-20">
|
|
<article className="mx-auto max-w-3xl px-6 py-24">
|
|
<header className="mb-12">
|
|
<time className="font-mono text-xs text-subtle">{post.date}</time>
|
|
<h1 className="mt-3 font-serif text-4xl font-bold leading-tight md:text-5xl">
|
|
{post.title}
|
|
</h1>
|
|
<p className="mt-4 text-lg text-muted">{post.description}</p>
|
|
{post.tags.length > 0 && (
|
|
<div className="mt-4 flex gap-2">
|
|
{post.tags.map((tag) => (
|
|
<span
|
|
key={tag}
|
|
className="rounded-full border border-border px-2.5 py-0.5 font-mono text-xs text-subtle"
|
|
>
|
|
{tag}
|
|
</span>
|
|
))}
|
|
</div>
|
|
)}
|
|
</header>
|
|
|
|
<div className="prose-dark">
|
|
<MDXRemote source={post.content} components={components} />
|
|
</div>
|
|
|
|
<footer className="mt-16 border-t border-border pt-8">
|
|
<Link
|
|
href="/blog"
|
|
className="font-mono text-sm text-subtle transition-colors hover:text-muted"
|
|
>
|
|
← All posts
|
|
</Link>
|
|
</footer>
|
|
</article>
|
|
</div>
|
|
);
|
|
}
|