This commit is contained in:
parent
a3366a6513
commit
165aa5636f
30 changed files with 718 additions and 499 deletions
|
|
@ -1,17 +1,27 @@
|
|||
// @ts-check
|
||||
import { defineConfig } from "astro/config";
|
||||
import mdx from "@astrojs/mdx";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import remarkMath from "remark-math";
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
|
||||
import react from "@astrojs/react";
|
||||
// @ts-ignore - TailwindCSS v4 Vite plugin has type compatibility issues with Astro
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import remarkCallouts from "../../src/remark-callouts.mjs";
|
||||
import { ACTIVE_BLOG_SITE } from "./src/blog-sites.js";
|
||||
|
||||
const remarkPlugins = [remarkMath, remarkCallouts];
|
||||
const rehypePlugins = [rehypeKatex];
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: ACTIVE_BLOG_SITE.url,
|
||||
integrations: [mdx(), sitemap(), react()],
|
||||
integrations: [mdx({ remarkPlugins, rehypePlugins }), sitemap(), react()],
|
||||
markdown: {
|
||||
remarkPlugins,
|
||||
rehypePlugins,
|
||||
},
|
||||
output: "static",
|
||||
outDir: "dist",
|
||||
|
||||
|
|
|
|||
|
|
@ -1,87 +1,26 @@
|
|||
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { format } from "date-fns";
|
||||
import { Calendar, Clock, ArrowLeft, User } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
const BlogPost = ({
|
||||
post,
|
||||
children,
|
||||
}: {
|
||||
post: any;
|
||||
children: React.ReactNode;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
const { title, authorName, image, pubDate, description, authorImage } =
|
||||
post.data;
|
||||
const { title, image } = post.data;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Hero section with gradient background and post info */}
|
||||
<section className="pb-8 pt-4">
|
||||
<div className="container max-w-4xl">
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="blog-post-title">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="text-muted-foreground mx-auto max-w-2xl text-lg">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<section className="blog-post-shell">
|
||||
<article className="blog-article">
|
||||
{image ? (
|
||||
<figure className="blog-post-hero-image">
|
||||
<img src={image} alt={title} />
|
||||
</figure>
|
||||
) : null}
|
||||
|
||||
<div className="mx-auto mt-6 flex max-w-md flex-wrap items-center justify-center gap-6">
|
||||
{/* Author info */}
|
||||
{authorImage ? (
|
||||
<div className="flex items-center gap-3">
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="bg-primary/10 text-primary flex h-10 w-10 items-center justify-center rounded-full shadow-sm">
|
||||
<User className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="flex flex-col text-sm">
|
||||
<span className="font-medium">
|
||||
{authorName || "Anonymous"}
|
||||
</span>
|
||||
<span className="text-muted-foreground">Author</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Date info */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="text-muted-foreground flex items-center gap-1 text-sm">
|
||||
<Calendar className="h-4 w-4" />
|
||||
<span>{format(pubDate, "MMMM d, yyyy")}</span>
|
||||
</div>
|
||||
|
||||
<div className="text-muted-foreground flex items-center gap-1 text-sm">
|
||||
<Clock className="h-4 w-4" />
|
||||
<span>5 min read</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Featured image */}
|
||||
{image && (
|
||||
<section className="container max-w-5xl py-4">
|
||||
<div className="aspect-video w-full overflow-hidden rounded-xl border">
|
||||
<img
|
||||
src={image}
|
||||
alt={title}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Article content */}
|
||||
<section className="container my-10 max-w-3xl">
|
||||
<article className="prose prose-lg dark:prose-invert prose-headings:font-semibold prose-a:text-primary mx-auto">
|
||||
{children}
|
||||
</article>
|
||||
</section>
|
||||
</>
|
||||
<div className="article-content">{children}</div>
|
||||
</article>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { getCollection, render } from 'astro:content';
|
|||
import CoralComments from '@/components/CoralComments.astro';
|
||||
import DefaultLayout from '@/layouts/DefaultLayout.astro';
|
||||
import { BlogPost } from '@/components/sections/blog-post';
|
||||
import NewsletterSignup from '@/components/sections/newsletter-signup';
|
||||
import { ACTIVE_BLOG_SITE } from '@/blog-sites.js';
|
||||
import { SITE_TITLE } from '@/consts';
|
||||
|
||||
|
|
@ -23,12 +22,9 @@ const coralStoryURL = new URL(`/${postSlug}/`, ACTIVE_BLOG_SITE.url).toString();
|
|||
---
|
||||
|
||||
<DefaultLayout title={`${post.data.title} | ${SITE_TITLE}`} description={post.data.description}>
|
||||
<div class="py-16 md:py-28 lg:py-32">
|
||||
<BlogPost post={post} client:only="react">
|
||||
<Content />
|
||||
</BlogPost>
|
||||
</div>
|
||||
<BlogPost post={post}>
|
||||
<Content />
|
||||
</BlogPost>
|
||||
|
||||
<CoralComments storyID={coralStoryID} storyURL={coralStoryURL} />
|
||||
<NewsletterSignup client:load />
|
||||
</DefaultLayout>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
@import "tailwindcss";
|
||||
@import "katex/dist/katex.min.css";
|
||||
@import "../../../../src/styles/article.css";
|
||||
|
||||
@plugin 'tailwindcss-animate';
|
||||
@plugin '@tailwindcss/typography';
|
||||
|
|
|
|||
|
|
@ -1,17 +1,27 @@
|
|||
// @ts-check
|
||||
import { defineConfig } from "astro/config";
|
||||
import mdx from "@astrojs/mdx";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import remarkMath from "remark-math";
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
|
||||
import react from "@astrojs/react";
|
||||
// @ts-ignore - TailwindCSS v4 Vite plugin has type compatibility issues with Astro
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import remarkCallouts from "../../src/remark-callouts.mjs";
|
||||
import { ACTIVE_BLOG_SITE } from "./src/blog-sites.js";
|
||||
|
||||
const remarkPlugins = [remarkMath, remarkCallouts];
|
||||
const rehypePlugins = [rehypeKatex];
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: ACTIVE_BLOG_SITE.url,
|
||||
integrations: [mdx(), sitemap(), react()],
|
||||
integrations: [mdx({ remarkPlugins, rehypePlugins }), sitemap(), react()],
|
||||
markdown: {
|
||||
remarkPlugins,
|
||||
rehypePlugins,
|
||||
},
|
||||
output: "static",
|
||||
outDir: "dist",
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
@import "tailwindcss";
|
||||
@import "katex/dist/katex.min.css";
|
||||
@import "../../../../src/styles/article.css";
|
||||
|
||||
@plugin 'tailwindcss-animate';
|
||||
@plugin '@tailwindcss/typography';
|
||||
|
|
|
|||
|
|
@ -1,17 +1,27 @@
|
|||
// @ts-check
|
||||
import { defineConfig } from "astro/config";
|
||||
import mdx from "@astrojs/mdx";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import remarkMath from "remark-math";
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
|
||||
import react from "@astrojs/react";
|
||||
// @ts-ignore - TailwindCSS v4 Vite plugin has type compatibility issues with Astro
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import remarkCallouts from "../../src/remark-callouts.mjs";
|
||||
import { ACTIVE_BLOG_SITE } from "./src/blog-sites.js";
|
||||
|
||||
const remarkPlugins = [remarkMath, remarkCallouts];
|
||||
const rehypePlugins = [rehypeKatex];
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: ACTIVE_BLOG_SITE.url,
|
||||
integrations: [mdx(), sitemap(), react()],
|
||||
integrations: [mdx({ remarkPlugins, rehypePlugins }), sitemap(), react()],
|
||||
markdown: {
|
||||
remarkPlugins,
|
||||
rehypePlugins,
|
||||
},
|
||||
output: "static",
|
||||
outDir: "dist",
|
||||
|
||||
|
|
|
|||
|
|
@ -1,87 +1,26 @@
|
|||
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { format } from "date-fns";
|
||||
import { Calendar, Clock, ArrowLeft, User } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
const BlogPost = ({
|
||||
post,
|
||||
children,
|
||||
}: {
|
||||
post: any;
|
||||
children: React.ReactNode;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
const { title, authorName, image, pubDate, description, authorImage } =
|
||||
post.data;
|
||||
const { title, image } = post.data;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Hero section with gradient background and post info */}
|
||||
<section className="pb-8 pt-4">
|
||||
<div className="container max-w-4xl">
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="blog-post-title">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="text-muted-foreground mx-auto max-w-2xl text-lg">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<section className="blog-post-shell">
|
||||
<article className="blog-article">
|
||||
{image ? (
|
||||
<figure className="blog-post-hero-image">
|
||||
<img src={image} alt={title} />
|
||||
</figure>
|
||||
) : null}
|
||||
|
||||
<div className="mx-auto mt-6 flex max-w-md flex-wrap items-center justify-center gap-6">
|
||||
{/* Author info */}
|
||||
{authorImage ? (
|
||||
<div className="flex items-center gap-3">
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="bg-primary/10 text-primary flex h-10 w-10 items-center justify-center rounded-full shadow-sm">
|
||||
<User className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="flex flex-col text-sm">
|
||||
<span className="font-medium">
|
||||
{authorName || "Anonymous"}
|
||||
</span>
|
||||
<span className="text-muted-foreground">Author</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Date info */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="text-muted-foreground flex items-center gap-1 text-sm">
|
||||
<Calendar className="h-4 w-4" />
|
||||
<span>{format(pubDate, "MMMM d, yyyy")}</span>
|
||||
</div>
|
||||
|
||||
<div className="text-muted-foreground flex items-center gap-1 text-sm">
|
||||
<Clock className="h-4 w-4" />
|
||||
<span>5 min read</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Featured image */}
|
||||
{image && (
|
||||
<section className="container max-w-5xl py-4">
|
||||
<div className="aspect-video w-full overflow-hidden rounded-xl border">
|
||||
<img
|
||||
src={image}
|
||||
alt={title}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Article content */}
|
||||
<section className="container my-10 max-w-3xl">
|
||||
<article className="prose prose-lg dark:prose-invert prose-headings:font-semibold prose-a:text-primary mx-auto">
|
||||
{children}
|
||||
</article>
|
||||
</section>
|
||||
</>
|
||||
<div className="article-content">{children}</div>
|
||||
</article>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { getCollection, render } from 'astro:content';
|
|||
import CoralComments from '@/components/CoralComments.astro';
|
||||
import DefaultLayout from '@/layouts/DefaultLayout.astro';
|
||||
import { BlogPost } from '@/components/sections/blog-post';
|
||||
import NewsletterSignup from '@/components/sections/newsletter-signup';
|
||||
import { ACTIVE_BLOG_SITE } from '@/blog-sites.js';
|
||||
import { SITE_TITLE } from '@/consts';
|
||||
|
||||
|
|
@ -23,12 +22,9 @@ const coralStoryURL = new URL(`/${postSlug}/`, ACTIVE_BLOG_SITE.url).toString();
|
|||
---
|
||||
|
||||
<DefaultLayout title={`${post.data.title} | ${SITE_TITLE}`} description={post.data.description}>
|
||||
<div class="py-16 md:py-28 lg:py-32">
|
||||
<BlogPost post={post} client:only="react">
|
||||
<Content />
|
||||
</BlogPost>
|
||||
</div>
|
||||
<BlogPost post={post}>
|
||||
<Content />
|
||||
</BlogPost>
|
||||
|
||||
<CoralComments storyID={coralStoryID} storyURL={coralStoryURL} />
|
||||
<NewsletterSignup client:load />
|
||||
</DefaultLayout>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
@import "tailwindcss";
|
||||
@import "katex/dist/katex.min.css";
|
||||
@import "../../../../src/styles/article.css";
|
||||
|
||||
@plugin 'tailwindcss-animate';
|
||||
@plugin '@tailwindcss/typography';
|
||||
|
|
|
|||
|
|
@ -1,17 +1,27 @@
|
|||
// @ts-check
|
||||
import { defineConfig } from "astro/config";
|
||||
import mdx from "@astrojs/mdx";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import remarkMath from "remark-math";
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
|
||||
import react from "@astrojs/react";
|
||||
// @ts-ignore - TailwindCSS v4 Vite plugin has type compatibility issues with Astro
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import remarkCallouts from "../../src/remark-callouts.mjs";
|
||||
import { ACTIVE_BLOG_SITE } from "./src/blog-sites.js";
|
||||
|
||||
const remarkPlugins = [remarkMath, remarkCallouts];
|
||||
const rehypePlugins = [rehypeKatex];
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: ACTIVE_BLOG_SITE.url,
|
||||
integrations: [mdx(), sitemap(), react()],
|
||||
integrations: [mdx({ remarkPlugins, rehypePlugins }), sitemap(), react()],
|
||||
markdown: {
|
||||
remarkPlugins,
|
||||
rehypePlugins,
|
||||
},
|
||||
output: "static",
|
||||
outDir: "dist",
|
||||
|
||||
|
|
|
|||
|
|
@ -1,87 +1,26 @@
|
|||
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { format } from "date-fns";
|
||||
import { Calendar, Clock, ArrowLeft, User } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
const BlogPost = ({
|
||||
post,
|
||||
children,
|
||||
}: {
|
||||
post: any;
|
||||
children: React.ReactNode;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
const { title, authorName, image, pubDate, description, authorImage } =
|
||||
post.data;
|
||||
const { title, image } = post.data;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Hero section with gradient background and post info */}
|
||||
<section className="pb-8 pt-4">
|
||||
<div className="container max-w-4xl">
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="blog-post-title">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="text-muted-foreground mx-auto max-w-2xl text-lg">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<section className="blog-post-shell">
|
||||
<article className="blog-article">
|
||||
{image ? (
|
||||
<figure className="blog-post-hero-image">
|
||||
<img src={image} alt={title} />
|
||||
</figure>
|
||||
) : null}
|
||||
|
||||
<div className="mx-auto mt-6 flex max-w-md flex-wrap items-center justify-center gap-6">
|
||||
{/* Author info */}
|
||||
{authorImage ? (
|
||||
<div className="flex items-center gap-3">
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="bg-primary/10 text-primary flex h-10 w-10 items-center justify-center rounded-full shadow-sm">
|
||||
<User className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="flex flex-col text-sm">
|
||||
<span className="font-medium">
|
||||
{authorName || "Anonymous"}
|
||||
</span>
|
||||
<span className="text-muted-foreground">Author</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Date info */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="text-muted-foreground flex items-center gap-1 text-sm">
|
||||
<Calendar className="h-4 w-4" />
|
||||
<span>{format(pubDate, "MMMM d, yyyy")}</span>
|
||||
</div>
|
||||
|
||||
<div className="text-muted-foreground flex items-center gap-1 text-sm">
|
||||
<Clock className="h-4 w-4" />
|
||||
<span>5 min read</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Featured image */}
|
||||
{image && (
|
||||
<section className="container max-w-5xl py-4">
|
||||
<div className="aspect-video w-full overflow-hidden rounded-xl border">
|
||||
<img
|
||||
src={image}
|
||||
alt={title}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Article content */}
|
||||
<section className="container my-10 max-w-3xl">
|
||||
<article className="prose prose-lg dark:prose-invert prose-headings:font-semibold prose-a:text-primary mx-auto">
|
||||
{children}
|
||||
</article>
|
||||
</section>
|
||||
</>
|
||||
<div className="article-content">{children}</div>
|
||||
</article>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { getCollection, render } from 'astro:content';
|
|||
import CoralComments from '@/components/CoralComments.astro';
|
||||
import DefaultLayout from '@/layouts/DefaultLayout.astro';
|
||||
import { BlogPost } from '@/components/sections/blog-post';
|
||||
import NewsletterSignup from '@/components/sections/newsletter-signup';
|
||||
import { ACTIVE_BLOG_SITE } from '@/blog-sites.js';
|
||||
import { SITE_TITLE } from '@/consts';
|
||||
|
||||
|
|
@ -23,12 +22,9 @@ const coralStoryURL = new URL(`/${postSlug}/`, ACTIVE_BLOG_SITE.url).toString();
|
|||
---
|
||||
|
||||
<DefaultLayout title={`${post.data.title} | ${SITE_TITLE}`} description={post.data.description}>
|
||||
<div class="py-16 md:py-28 lg:py-32">
|
||||
<BlogPost post={post} client:only="react">
|
||||
<Content />
|
||||
</BlogPost>
|
||||
</div>
|
||||
<BlogPost post={post}>
|
||||
<Content />
|
||||
</BlogPost>
|
||||
|
||||
<CoralComments storyID={coralStoryID} storyURL={coralStoryURL} />
|
||||
<NewsletterSignup client:load />
|
||||
</DefaultLayout>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
@import "tailwindcss";
|
||||
@import "katex/dist/katex.min.css";
|
||||
@import "../../../../src/styles/article.css";
|
||||
|
||||
@plugin 'tailwindcss-animate';
|
||||
@plugin '@tailwindcss/typography';
|
||||
|
|
|
|||
|
|
@ -1,17 +1,27 @@
|
|||
// @ts-check
|
||||
import { defineConfig } from "astro/config";
|
||||
import mdx from "@astrojs/mdx";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import remarkMath from "remark-math";
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
|
||||
import react from "@astrojs/react";
|
||||
// @ts-ignore - TailwindCSS v4 Vite plugin has type compatibility issues with Astro
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import remarkCallouts from "../../src/remark-callouts.mjs";
|
||||
import { ACTIVE_BLOG_SITE } from "./src/blog-sites.js";
|
||||
|
||||
const remarkPlugins = [remarkMath, remarkCallouts];
|
||||
const rehypePlugins = [rehypeKatex];
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: ACTIVE_BLOG_SITE.url,
|
||||
integrations: [mdx(), sitemap(), react()],
|
||||
integrations: [mdx({ remarkPlugins, rehypePlugins }), sitemap(), react()],
|
||||
markdown: {
|
||||
remarkPlugins,
|
||||
rehypePlugins,
|
||||
},
|
||||
output: "static",
|
||||
outDir: "dist",
|
||||
|
||||
|
|
|
|||
|
|
@ -1,87 +1,26 @@
|
|||
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { format } from "date-fns";
|
||||
import { Calendar, Clock, ArrowLeft, User } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
const BlogPost = ({
|
||||
post,
|
||||
children,
|
||||
}: {
|
||||
post: any;
|
||||
children: React.ReactNode;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
const { title, authorName, image, pubDate, description, authorImage } =
|
||||
post.data;
|
||||
const { title, image } = post.data;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Hero section with gradient background and post info */}
|
||||
<section className="pb-8 pt-4">
|
||||
<div className="container max-w-4xl">
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="blog-post-title">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="text-muted-foreground mx-auto max-w-2xl text-lg">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<section className="blog-post-shell">
|
||||
<article className="blog-article">
|
||||
{image ? (
|
||||
<figure className="blog-post-hero-image">
|
||||
<img src={image} alt={title} />
|
||||
</figure>
|
||||
) : null}
|
||||
|
||||
<div className="mx-auto mt-6 flex max-w-md flex-wrap items-center justify-center gap-6">
|
||||
{/* Author info */}
|
||||
{authorImage ? (
|
||||
<div className="flex items-center gap-3">
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="bg-primary/10 text-primary flex h-10 w-10 items-center justify-center rounded-full shadow-sm">
|
||||
<User className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="flex flex-col text-sm">
|
||||
<span className="font-medium">
|
||||
{authorName || "Anonymous"}
|
||||
</span>
|
||||
<span className="text-muted-foreground">Author</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Date info */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="text-muted-foreground flex items-center gap-1 text-sm">
|
||||
<Calendar className="h-4 w-4" />
|
||||
<span>{format(pubDate, "MMMM d, yyyy")}</span>
|
||||
</div>
|
||||
|
||||
<div className="text-muted-foreground flex items-center gap-1 text-sm">
|
||||
<Clock className="h-4 w-4" />
|
||||
<span>5 min read</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Featured image */}
|
||||
{image && (
|
||||
<section className="container max-w-5xl py-4">
|
||||
<div className="aspect-video w-full overflow-hidden rounded-xl border">
|
||||
<img
|
||||
src={image}
|
||||
alt={title}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Article content */}
|
||||
<section className="container my-10 max-w-3xl">
|
||||
<article className="prose prose-lg dark:prose-invert prose-headings:font-semibold prose-a:text-primary mx-auto">
|
||||
{children}
|
||||
</article>
|
||||
</section>
|
||||
</>
|
||||
<div className="article-content">{children}</div>
|
||||
</article>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { getCollection, render } from 'astro:content';
|
|||
import CoralComments from '@/components/CoralComments.astro';
|
||||
import DefaultLayout from '@/layouts/DefaultLayout.astro';
|
||||
import { BlogPost } from '@/components/sections/blog-post';
|
||||
import NewsletterSignup from '@/components/sections/newsletter-signup';
|
||||
import { ACTIVE_BLOG_SITE } from '@/blog-sites.js';
|
||||
import { SITE_TITLE } from '@/consts';
|
||||
|
||||
|
|
@ -23,12 +22,9 @@ const coralStoryURL = new URL(`/${postSlug}/`, ACTIVE_BLOG_SITE.url).toString();
|
|||
---
|
||||
|
||||
<DefaultLayout title={`${post.data.title} | ${SITE_TITLE}`} description={post.data.description}>
|
||||
<div class="py-16 md:py-28 lg:py-32">
|
||||
<BlogPost post={post} client:only="react">
|
||||
<Content />
|
||||
</BlogPost>
|
||||
</div>
|
||||
<BlogPost post={post}>
|
||||
<Content />
|
||||
</BlogPost>
|
||||
|
||||
<CoralComments storyID={coralStoryID} storyURL={coralStoryURL} />
|
||||
<NewsletterSignup client:load />
|
||||
</DefaultLayout>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
@import "tailwindcss";
|
||||
@import "katex/dist/katex.min.css";
|
||||
@import "../../../../src/styles/article.css";
|
||||
|
||||
@plugin 'tailwindcss-animate';
|
||||
@plugin '@tailwindcss/typography';
|
||||
|
|
|
|||
|
|
@ -1,17 +1,27 @@
|
|||
// @ts-check
|
||||
import { defineConfig } from "astro/config";
|
||||
import mdx from "@astrojs/mdx";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import remarkMath from "remark-math";
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
|
||||
import react from "@astrojs/react";
|
||||
// @ts-ignore - TailwindCSS v4 Vite plugin has type compatibility issues with Astro
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import remarkCallouts from "../../src/remark-callouts.mjs";
|
||||
import { ACTIVE_BLOG_SITE } from "./src/blog-sites.js";
|
||||
|
||||
const remarkPlugins = [remarkMath, remarkCallouts];
|
||||
const rehypePlugins = [rehypeKatex];
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: ACTIVE_BLOG_SITE.url,
|
||||
integrations: [mdx(), sitemap(), react()],
|
||||
integrations: [mdx({ remarkPlugins, rehypePlugins }), sitemap(), react()],
|
||||
markdown: {
|
||||
remarkPlugins,
|
||||
rehypePlugins,
|
||||
},
|
||||
output: "static",
|
||||
outDir: "dist",
|
||||
|
||||
|
|
|
|||
|
|
@ -1,87 +1,26 @@
|
|||
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { format } from "date-fns";
|
||||
import { Calendar, Clock, ArrowLeft, User } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
const BlogPost = ({
|
||||
post,
|
||||
children,
|
||||
}: {
|
||||
post: any;
|
||||
children: React.ReactNode;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
const { title, authorName, image, pubDate, description, authorImage } =
|
||||
post.data;
|
||||
const { title, image } = post.data;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Hero section with gradient background and post info */}
|
||||
<section className="pb-8 pt-4">
|
||||
<div className="container max-w-4xl">
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="blog-post-title">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="text-muted-foreground mx-auto max-w-2xl text-lg">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<section className="blog-post-shell">
|
||||
<article className="blog-article">
|
||||
{image ? (
|
||||
<figure className="blog-post-hero-image">
|
||||
<img src={image} alt={title} />
|
||||
</figure>
|
||||
) : null}
|
||||
|
||||
<div className="mx-auto mt-6 flex max-w-md flex-wrap items-center justify-center gap-6">
|
||||
{/* Author info */}
|
||||
{authorImage ? (
|
||||
<div className="flex items-center gap-3">
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="bg-primary/10 text-primary flex h-10 w-10 items-center justify-center rounded-full shadow-sm">
|
||||
<User className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="flex flex-col text-sm">
|
||||
<span className="font-medium">
|
||||
{authorName || "Anonymous"}
|
||||
</span>
|
||||
<span className="text-muted-foreground">Author</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Date info */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="text-muted-foreground flex items-center gap-1 text-sm">
|
||||
<Calendar className="h-4 w-4" />
|
||||
<span>{format(pubDate, "MMMM d, yyyy")}</span>
|
||||
</div>
|
||||
|
||||
<div className="text-muted-foreground flex items-center gap-1 text-sm">
|
||||
<Clock className="h-4 w-4" />
|
||||
<span>5 min read</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Featured image */}
|
||||
{image && (
|
||||
<section className="container max-w-5xl py-4">
|
||||
<div className="aspect-video w-full overflow-hidden rounded-xl border">
|
||||
<img
|
||||
src={image}
|
||||
alt={title}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Article content */}
|
||||
<section className="container my-10 max-w-3xl">
|
||||
<article className="prose prose-lg dark:prose-invert prose-headings:font-semibold prose-a:text-primary mx-auto">
|
||||
{children}
|
||||
</article>
|
||||
</section>
|
||||
</>
|
||||
<div className="article-content">{children}</div>
|
||||
</article>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { getCollection, render } from 'astro:content';
|
|||
import CoralComments from '@/components/CoralComments.astro';
|
||||
import DefaultLayout from '@/layouts/DefaultLayout.astro';
|
||||
import { BlogPost } from '@/components/sections/blog-post';
|
||||
import NewsletterSignup from '@/components/sections/newsletter-signup';
|
||||
import { ACTIVE_BLOG_SITE } from '@/blog-sites.js';
|
||||
import { SITE_TITLE } from '@/consts';
|
||||
|
||||
|
|
@ -23,12 +22,9 @@ const coralStoryURL = new URL(`/${postSlug}/`, ACTIVE_BLOG_SITE.url).toString();
|
|||
---
|
||||
|
||||
<DefaultLayout title={`${post.data.title} | ${SITE_TITLE}`} description={post.data.description}>
|
||||
<div class="py-16 md:py-28 lg:py-32">
|
||||
<BlogPost post={post} client:only="react">
|
||||
<Content />
|
||||
</BlogPost>
|
||||
</div>
|
||||
<BlogPost post={post}>
|
||||
<Content />
|
||||
</BlogPost>
|
||||
|
||||
<CoralComments storyID={coralStoryID} storyURL={coralStoryURL} />
|
||||
<NewsletterSignup client:load />
|
||||
</DefaultLayout>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
@import "tailwindcss";
|
||||
@import "katex/dist/katex.min.css";
|
||||
@import "../../../../src/styles/article.css";
|
||||
|
||||
@plugin 'tailwindcss-animate';
|
||||
@plugin '@tailwindcss/typography';
|
||||
|
|
|
|||
|
|
@ -1,17 +1,27 @@
|
|||
// @ts-check
|
||||
import { defineConfig } from "astro/config";
|
||||
import mdx from "@astrojs/mdx";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import remarkMath from "remark-math";
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
|
||||
import react from "@astrojs/react";
|
||||
// @ts-ignore - TailwindCSS v4 Vite plugin has type compatibility issues with Astro
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import remarkCallouts from "../../src/remark-callouts.mjs";
|
||||
import { ACTIVE_BLOG_SITE } from "./src/blog-sites.js";
|
||||
|
||||
const remarkPlugins = [remarkMath, remarkCallouts];
|
||||
const rehypePlugins = [rehypeKatex];
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: ACTIVE_BLOG_SITE.url,
|
||||
integrations: [mdx(), sitemap(), react()],
|
||||
integrations: [mdx({ remarkPlugins, rehypePlugins }), sitemap(), react()],
|
||||
markdown: {
|
||||
remarkPlugins,
|
||||
rehypePlugins,
|
||||
},
|
||||
output: "static",
|
||||
outDir: "dist",
|
||||
|
||||
|
|
|
|||
|
|
@ -1,87 +1,26 @@
|
|||
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { format } from "date-fns";
|
||||
import { Calendar, Clock, ArrowLeft, User } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
const BlogPost = ({
|
||||
post,
|
||||
children,
|
||||
}: {
|
||||
post: any;
|
||||
children: React.ReactNode;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
const { title, authorName, image, pubDate, description, authorImage } =
|
||||
post.data;
|
||||
const { title, image } = post.data;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Hero section with gradient background and post info */}
|
||||
<section className="pb-8 pt-4">
|
||||
<div className="container max-w-4xl">
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="blog-post-title">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="text-muted-foreground mx-auto max-w-2xl text-lg">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<section className="blog-post-shell">
|
||||
<article className="blog-article">
|
||||
{image ? (
|
||||
<figure className="blog-post-hero-image">
|
||||
<img src={image} alt={title} />
|
||||
</figure>
|
||||
) : null}
|
||||
|
||||
<div className="mx-auto mt-6 flex max-w-md flex-wrap items-center justify-center gap-6">
|
||||
{/* Author info */}
|
||||
{authorImage ? (
|
||||
<div className="flex items-center gap-3">
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="bg-primary/10 text-primary flex h-10 w-10 items-center justify-center rounded-full shadow-sm">
|
||||
<User className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="flex flex-col text-sm">
|
||||
<span className="font-medium">
|
||||
{authorName || "Anonymous"}
|
||||
</span>
|
||||
<span className="text-muted-foreground">Author</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Date info */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="text-muted-foreground flex items-center gap-1 text-sm">
|
||||
<Calendar className="h-4 w-4" />
|
||||
<span>{format(pubDate, "MMMM d, yyyy")}</span>
|
||||
</div>
|
||||
|
||||
<div className="text-muted-foreground flex items-center gap-1 text-sm">
|
||||
<Clock className="h-4 w-4" />
|
||||
<span>5 min read</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Featured image */}
|
||||
{image && (
|
||||
<section className="container max-w-5xl py-4">
|
||||
<div className="aspect-video w-full overflow-hidden rounded-xl border">
|
||||
<img
|
||||
src={image}
|
||||
alt={title}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Article content */}
|
||||
<section className="container my-10 max-w-3xl">
|
||||
<article className="prose prose-lg dark:prose-invert prose-headings:font-semibold prose-a:text-primary mx-auto">
|
||||
{children}
|
||||
</article>
|
||||
</section>
|
||||
</>
|
||||
<div className="article-content">{children}</div>
|
||||
</article>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { getCollection, render } from 'astro:content';
|
|||
import CoralComments from '@/components/CoralComments.astro';
|
||||
import DefaultLayout from '@/layouts/DefaultLayout.astro';
|
||||
import { BlogPost } from '@/components/sections/blog-post';
|
||||
import NewsletterSignup from '@/components/sections/newsletter-signup';
|
||||
import { ACTIVE_BLOG_SITE } from '@/blog-sites.js';
|
||||
import { SITE_TITLE } from '@/consts';
|
||||
|
||||
|
|
@ -23,12 +22,9 @@ const coralStoryURL = new URL(`/${postSlug}/`, ACTIVE_BLOG_SITE.url).toString();
|
|||
---
|
||||
|
||||
<DefaultLayout title={`${post.data.title} | ${SITE_TITLE}`} description={post.data.description}>
|
||||
<div class="py-16 md:py-28 lg:py-32">
|
||||
<BlogPost post={post} client:only="react">
|
||||
<Content />
|
||||
</BlogPost>
|
||||
</div>
|
||||
<BlogPost post={post}>
|
||||
<Content />
|
||||
</BlogPost>
|
||||
|
||||
<CoralComments storyID={coralStoryID} storyURL={coralStoryURL} />
|
||||
<NewsletterSignup client:load />
|
||||
</DefaultLayout>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
@import "tailwindcss";
|
||||
@import "katex/dist/katex.min.css";
|
||||
@import "../../../../src/styles/article.css";
|
||||
|
||||
@plugin 'tailwindcss-animate';
|
||||
@plugin '@tailwindcss/typography';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue