From 9f6adc0d117c5feaa577e46af071c8696be43767 Mon Sep 17 00:00:00 2001 From: adibarra <93070681+adibarra@users.noreply.github.com> Date: Thu, 19 Mar 2026 11:38:26 -0500 Subject: [PATCH 1/2] feat: add blog infrastructure (pages, components, sitemap, nav link) --- packages/app/src/app/blog/[slug]/page.tsx | 40 ++++++++ packages/app/src/app/blog/page.tsx | 21 ++++ packages/app/src/app/globals.css | 1 + packages/app/src/app/sitemap.ts | 13 +++ .../app/src/components/blog/blog-content.tsx | 97 +++++++++++++++++++ packages/app/src/components/blog/blog-data.ts | 11 +++ .../src/components/blog/blog-post-content.tsx | 54 +++++++++++ .../src/components/blog/posts/_template.ts | 22 +++++ packages/app/src/components/header/header.tsx | 15 +++ 9 files changed, 274 insertions(+) create mode 100644 packages/app/src/app/blog/[slug]/page.tsx create mode 100644 packages/app/src/app/blog/page.tsx create mode 100644 packages/app/src/components/blog/blog-content.tsx create mode 100644 packages/app/src/components/blog/blog-data.ts create mode 100644 packages/app/src/components/blog/blog-post-content.tsx create mode 100644 packages/app/src/components/blog/posts/_template.ts diff --git a/packages/app/src/app/blog/[slug]/page.tsx b/packages/app/src/app/blog/[slug]/page.tsx new file mode 100644 index 0000000..141d136 --- /dev/null +++ b/packages/app/src/app/blog/[slug]/page.tsx @@ -0,0 +1,40 @@ +import type { Metadata } from 'next'; +import { notFound } from 'next/navigation'; + +import { BlogPostContent } from '@/components/blog/blog-post-content'; +import { BLOG_POSTS } from '@/components/blog/blog-data'; +import { SITE_URL } from '@semianalysisai/inferencex-constants'; + +type Params = { slug: string }; + +export function generateStaticParams(): Params[] { + return BLOG_POSTS.map((post) => ({ slug: post.slug })); +} + +export async function generateMetadata({ params }: { params: Promise }): Promise { + const { slug } = await params; + const post = BLOG_POSTS.find((p) => p.slug === slug); + if (!post) return {}; + + return { + title: post.title, + description: post.excerpt, + alternates: { canonical: `${SITE_URL}/blog/${post.slug}` }, + openGraph: { + title: `${post.title} | InferenceX Blog`, + description: post.excerpt, + url: `${SITE_URL}/blog/${post.slug}`, + type: 'article', + publishedTime: post.date, + authors: [post.author], + }, + }; +} + +export default async function BlogPostPage({ params }: { params: Promise }) { + const { slug } = await params; + const post = BLOG_POSTS.find((p) => p.slug === slug); + if (!post) notFound(); + + return ; +} diff --git a/packages/app/src/app/blog/page.tsx b/packages/app/src/app/blog/page.tsx new file mode 100644 index 0000000..42aeed2 --- /dev/null +++ b/packages/app/src/app/blog/page.tsx @@ -0,0 +1,21 @@ +import type { Metadata } from 'next'; + +import { BlogContent } from '@/components/blog/blog-content'; +import { SITE_URL } from '@semianalysisai/inferencex-constants'; + +export const metadata: Metadata = { + title: 'Blog', + description: + 'Deep dives into inference benchmarking, GPU performance, and the economics of AI compute from the InferenceX team at SemiAnalysis.', + alternates: { canonical: `${SITE_URL}/blog` }, + openGraph: { + title: 'Blog | InferenceX by SemiAnalysis', + description: + 'Deep dives into inference benchmarking, GPU performance, and the economics of AI compute.', + url: `${SITE_URL}/blog`, + }, +}; + +export default function BlogPage() { + return ; +} diff --git a/packages/app/src/app/globals.css b/packages/app/src/app/globals.css index 28a40f9..d88cbc1 100644 --- a/packages/app/src/app/globals.css +++ b/packages/app/src/app/globals.css @@ -1,5 +1,6 @@ @import 'tailwindcss'; @import 'tw-animate-css'; +@plugin '@tailwindcss/typography'; @custom-variant dark (&:is(.dark *)); diff --git a/packages/app/src/app/sitemap.ts b/packages/app/src/app/sitemap.ts index e6920a3..c7a9410 100644 --- a/packages/app/src/app/sitemap.ts +++ b/packages/app/src/app/sitemap.ts @@ -1,5 +1,6 @@ import type { MetadataRoute } from 'next'; +import { BLOG_POSTS } from '@/components/blog/blog-data'; import { SITE_URL as BASE_URL } from '@semianalysisai/inferencex-constants'; const TABS = [ @@ -39,5 +40,17 @@ export default function sitemap(): MetadataRoute.Sitemap { changeFrequency: 'monthly', priority: 0.6, }, + { + url: `${BASE_URL}/blog`, + lastModified: now, + changeFrequency: 'weekly', + priority: 0.8, + }, + ...BLOG_POSTS.map((post) => ({ + url: `${BASE_URL}/blog/${post.slug}`, + lastModified: now, + changeFrequency: 'monthly' as const, + priority: 0.7, + })), ]; } diff --git a/packages/app/src/components/blog/blog-content.tsx b/packages/app/src/components/blog/blog-content.tsx new file mode 100644 index 0000000..a30b8e0 --- /dev/null +++ b/packages/app/src/components/blog/blog-content.tsx @@ -0,0 +1,97 @@ +'use client'; + +import Link from 'next/link'; +import { track } from '@/lib/analytics'; + +import { Card } from '@/components/ui/card'; + +import { BLOG_POSTS } from './blog-data'; + +function BlogPostCard({ + slug, + title, + author, + date, + excerpt, + tags, +}: { + slug: string; + title: string; + author: string; + date: string; + excerpt: string; + tags?: string[]; +}) { + const formattedDate = new Date(date + 'T00:00:00').toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + }); + + return ( + track('blog_post_clicked', { slug })} + > + +
+
+ {formattedDate} + · + {author} + {tags?.map((tag) => ( + + {tag} + + ))} +
+

+ {title} +

+

{excerpt}

+
+
+ + ); +} + +export function BlogContent() { + const sorted = [...BLOG_POSTS].sort((a, b) => b.date.localeCompare(a.date)); + + return ( +
+
+
+ +

+ InferenceX™ Blog +

+

+ Deep dives into inference benchmarking, GPU performance, and the economics of AI + compute. +

+
+ {sorted.length > 0 ? ( + +
+ {sorted.map((post) => ( + + ))} +
+
+ ) : ( + +

+ Blog posts coming soon. Stay tuned! +

+
+ )} +
+
+
+ ); +} diff --git a/packages/app/src/components/blog/blog-data.ts b/packages/app/src/components/blog/blog-data.ts new file mode 100644 index 0000000..06262f1 --- /dev/null +++ b/packages/app/src/components/blog/blog-data.ts @@ -0,0 +1,11 @@ +export interface BlogPost { + slug: string; + title: string; + author: string; + date: string; // ISO format: 'YYYY-MM-DD' + excerpt: string; + content: string; // Markdown + tags?: string[]; +} + +export const BLOG_POSTS: BlogPost[] = []; diff --git a/packages/app/src/components/blog/blog-post-content.tsx b/packages/app/src/components/blog/blog-post-content.tsx new file mode 100644 index 0000000..d570d8f --- /dev/null +++ b/packages/app/src/components/blog/blog-post-content.tsx @@ -0,0 +1,54 @@ +'use client'; + +import Link from 'next/link'; +import Markdown from 'react-markdown'; +import { track } from '@/lib/analytics'; + +import { Card } from '@/components/ui/card'; + +import type { BlogPost } from './blog-data'; + +export function BlogPostContent({ post }: { post: BlogPost }) { + const formattedDate = new Date(post.date + 'T00:00:00').toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + }); + + return ( +
+
+
+ + track('blog_back_clicked')} + > + ← Back to Blog + +

{post.title}

+
+ {formattedDate} + · + {post.author} + {post.tags?.map((tag) => ( + + {tag} + + ))} +
+
+ +
+ {post.content} +
+
+
+
+
+ ); +} diff --git a/packages/app/src/components/blog/posts/_template.ts b/packages/app/src/components/blog/posts/_template.ts new file mode 100644 index 0000000..6cc5a68 --- /dev/null +++ b/packages/app/src/components/blog/posts/_template.ts @@ -0,0 +1,22 @@ +import type { BlogPost } from '../blog-data'; + +export const post: BlogPost = { + slug: 'your-post-slug', + title: 'Your Post Title', + author: 'SemiAnalysis', + date: '2026-03-19', + excerpt: 'A short summary for the listing page and SEO description.', + tags: ['inference', 'benchmarks'], + content: ` +Your markdown content goes here. + +## Subheading + +Paragraphs, **bold**, *italic*, [links](https://example.com), images, tables — all standard markdown. + +\`\`\`python +# Code blocks work too +print("hello world") +\`\`\` +`, +}; diff --git a/packages/app/src/components/header/header.tsx b/packages/app/src/components/header/header.tsx index d088064..70f85b3 100644 --- a/packages/app/src/components/header/header.tsx +++ b/packages/app/src/components/header/header.tsx @@ -80,6 +80,14 @@ export const Header = () => { > Supporters + track('header_blog_clicked')} + > + Blog + @@ -106,6 +114,13 @@ export const Header = () => { > Supporters + track('header_blog_clicked')} + > + Blog + From a158758cb7993f5a3a5cb46f4fe7a306dd0cf63c Mon Sep 17 00:00:00 2001 From: adibarra <93070681+adibarra@users.noreply.github.com> Date: Thu, 19 Mar 2026 11:53:41 -0500 Subject: [PATCH 2/2] feat: add blog SEO (JSON-LD, RSS feed, dynamic OG images, enriched metadata) --- packages/app/package.json | 2 + .../src/app/blog/[slug]/opengraph-image.tsx | 137 ++++ packages/app/src/app/blog/[slug]/page.tsx | 111 ++- packages/app/src/app/blog/page.tsx | 86 ++- packages/app/src/app/feed.xml/route.ts | 52 ++ packages/app/src/app/layout.tsx | 8 +- packages/app/src/app/sitemap.ts | 2 +- packages/app/src/components/blog/blog-data.ts | 8 + .../src/components/blog/blog-post-content.tsx | 8 +- pnpm-lock.yaml | 697 ++++++++++++++++++ 10 files changed, 1090 insertions(+), 21 deletions(-) create mode 100644 packages/app/src/app/blog/[slug]/opengraph-image.tsx create mode 100644 packages/app/src/app/feed.xml/route.ts diff --git a/packages/app/package.json b/packages/app/package.json index ae487c7..f188db1 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -36,6 +36,7 @@ "@radix-ui/react-tooltip": "^1.2.8", "@semianalysisai/inferencex-constants": "workspace:*", "@semianalysisai/inferencex-db": "workspace:*", + "@tailwindcss/typography": "^0.5.19", "@tanstack/react-query": "^5.91.0", "@tanstack/react-query-persist-client": "^5.90.25", "@vercel/analytics": "^2.0.1", @@ -52,6 +53,7 @@ "posthog-js": "^1.362.0", "react": "19.2.4", "react-dom": "19.2.4", + "react-markdown": "^10.1.0", "tailwind-merge": "^3.5.0" }, "devDependencies": { diff --git a/packages/app/src/app/blog/[slug]/opengraph-image.tsx b/packages/app/src/app/blog/[slug]/opengraph-image.tsx new file mode 100644 index 0000000..e2e2624 --- /dev/null +++ b/packages/app/src/app/blog/[slug]/opengraph-image.tsx @@ -0,0 +1,137 @@ +import { ImageResponse } from 'next/og'; + +import { BLOG_POSTS, getReadingTime } from '@/components/blog/blog-data'; + +export const alt = 'InferenceX Blog'; +export const size = { width: 1200, height: 630 }; +export const contentType = 'image/png'; + +export function generateStaticParams() { + return BLOG_POSTS.map((post) => ({ slug: post.slug })); +} + +export default async function OgImage({ params }: { params: Promise<{ slug: string }> }) { + const { slug } = await params; + const post = BLOG_POSTS.find((p) => p.slug === slug); + + if (!post) { + return new ImageResponse( +
+ InferenceX Blog +
, + size, + ); + } + + const readingTime = getReadingTime(post.content); + const tags = post.tags?.slice(0, 3) ?? []; + const truncatedExcerpt = + post.excerpt.length > 160 ? post.excerpt.slice(0, 157) + '...' : post.excerpt; + const formattedDate = new Date(post.date + 'T00:00:00').toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + }); + + return new ImageResponse( +
+
+ InferenceX Blog — SemiAnalysis +
+ +
+
60 ? 44 : 52, + fontWeight: 700, + color: '#fafafa', + lineHeight: 1.15, + letterSpacing: '-0.03em', + maxWidth: '1050px', + }} + > + {post.title} +
+
+ {truncatedExcerpt} +
+
+ +
+
+
+ {post.author} +
+
{formattedDate}
+
+ {`${readingTime} min read`} +
+
+
+ {tags.map((tag) => ( +
+ {tag} +
+ ))} +
+
+
, + size, + ); +} diff --git a/packages/app/src/app/blog/[slug]/page.tsx b/packages/app/src/app/blog/[slug]/page.tsx index 141d136..bb51b34 100644 --- a/packages/app/src/app/blog/[slug]/page.tsx +++ b/packages/app/src/app/blog/[slug]/page.tsx @@ -2,8 +2,15 @@ import type { Metadata } from 'next'; import { notFound } from 'next/navigation'; import { BlogPostContent } from '@/components/blog/blog-post-content'; -import { BLOG_POSTS } from '@/components/blog/blog-data'; -import { SITE_URL } from '@semianalysisai/inferencex-constants'; +import { BLOG_POSTS, getReadingTime } from '@/components/blog/blog-data'; +import { + AUTHOR_HANDLE, + AUTHOR_NAME, + AUTHOR_URL, + OG_IMAGE, + SITE_NAME, + SITE_URL, +} from '@semianalysisai/inferencex-constants'; type Params = { slug: string }; @@ -16,17 +23,36 @@ export async function generateMetadata({ params }: { params: Promise }): const post = BLOG_POSTS.find((p) => p.slug === slug); if (!post) return {}; + const ogImage = post.coverImage ?? `${SITE_URL}/blog/${post.slug}/opengraph-image`; + return { title: post.title, description: post.excerpt, - alternates: { canonical: `${SITE_URL}/blog/${post.slug}` }, + keywords: post.tags, + authors: [{ name: post.author }], + alternates: { + canonical: `${SITE_URL}/blog/${post.slug}`, + }, openGraph: { - title: `${post.title} | InferenceX Blog`, + title: `${post.title} | ${SITE_NAME} Blog`, description: post.excerpt, url: `${SITE_URL}/blog/${post.slug}`, + siteName: SITE_NAME, type: 'article', - publishedTime: post.date, - authors: [post.author], + publishedTime: `${post.date}T00:00:00Z`, + ...(post.modifiedDate && { modifiedTime: `${post.modifiedDate}T00:00:00Z` }), + authors: post.author.split(', '), + tags: post.tags, + images: [{ url: ogImage, width: 1200, height: 630, alt: post.title }], + locale: 'en_US', + }, + twitter: { + card: 'summary_large_image', + title: post.title, + description: post.excerpt, + images: [ogImage], + creator: AUTHOR_HANDLE, + site: AUTHOR_HANDLE, }, }; } @@ -36,5 +62,76 @@ export default async function BlogPostPage({ params }: { params: Promise const post = BLOG_POSTS.find((p) => p.slug === slug); if (!post) notFound(); - return ; + const readingTime = getReadingTime(post.content); + + const jsonLd = { + '@context': 'https://schema.org', + '@graph': [ + { + '@type': 'BreadcrumbList', + itemListElement: [ + { + '@type': 'ListItem', + position: 1, + name: SITE_NAME, + item: SITE_URL, + }, + { + '@type': 'ListItem', + position: 2, + name: 'Blog', + item: `${SITE_URL}/blog`, + }, + { + '@type': 'ListItem', + position: 3, + name: post.title, + item: `${SITE_URL}/blog/${post.slug}`, + }, + ], + }, + { + '@type': 'BlogPosting', + headline: post.title, + description: post.excerpt, + url: `${SITE_URL}/blog/${post.slug}`, + datePublished: `${post.date}T00:00:00Z`, + ...(post.modifiedDate && { dateModified: `${post.modifiedDate}T00:00:00Z` }), + wordCount: post.content.trim().split(/\s+/).length, + timeRequired: `PT${readingTime}M`, + author: post.author.split(', ').map((name) => ({ + '@type': 'Person', + name, + })), + publisher: { + '@type': 'Organization', + name: AUTHOR_NAME, + url: AUTHOR_URL, + logo: { '@type': 'ImageObject', url: OG_IMAGE }, + }, + image: post.coverImage ?? `${SITE_URL}/blog/${post.slug}/opengraph-image`, + mainEntityOfPage: { + '@type': 'WebPage', + '@id': `${SITE_URL}/blog/${post.slug}`, + }, + ...(post.tags && { + keywords: post.tags.join(', '), + articleSection: post.tags[0], + }), + isPartOf: { + '@type': 'Blog', + '@id': `${SITE_URL}/blog`, + name: `${SITE_NAME} Blog`, + }, + inLanguage: 'en-US', + }, + ], + }; + + return ( + <> + + + + ); } diff --git a/packages/app/src/app/blog/page.tsx b/packages/app/src/app/blog/page.tsx index 42aeed2..b09c111 100644 --- a/packages/app/src/app/blog/page.tsx +++ b/packages/app/src/app/blog/page.tsx @@ -1,21 +1,91 @@ import type { Metadata } from 'next'; import { BlogContent } from '@/components/blog/blog-content'; -import { SITE_URL } from '@semianalysisai/inferencex-constants'; +import { BLOG_POSTS } from '@/components/blog/blog-data'; +import { + AUTHOR_NAME, + AUTHOR_URL, + OG_IMAGE, + SITE_NAME, + SITE_URL, +} from '@semianalysisai/inferencex-constants'; + +const BLOG_DESCRIPTION = + 'Deep dives into inference benchmarking, GPU performance, and the economics of AI compute from the InferenceX team at SemiAnalysis.'; export const metadata: Metadata = { title: 'Blog', - description: - 'Deep dives into inference benchmarking, GPU performance, and the economics of AI compute from the InferenceX team at SemiAnalysis.', - alternates: { canonical: `${SITE_URL}/blog` }, + description: BLOG_DESCRIPTION, + alternates: { + canonical: `${SITE_URL}/blog`, + types: { 'application/rss+xml': `${SITE_URL}/feed.xml` }, + }, openGraph: { - title: 'Blog | InferenceX by SemiAnalysis', - description: - 'Deep dives into inference benchmarking, GPU performance, and the economics of AI compute.', + title: `Blog | ${SITE_NAME} by ${AUTHOR_NAME}`, + description: BLOG_DESCRIPTION, url: `${SITE_URL}/blog`, + siteName: SITE_NAME, + type: 'website', + locale: 'en_US', }, }; export default function BlogPage() { - return ; + const sorted = [...BLOG_POSTS].sort((a, b) => b.date.localeCompare(a.date)); + + const jsonLd = { + '@context': 'https://schema.org', + '@graph': [ + { + '@type': 'BreadcrumbList', + itemListElement: [ + { + '@type': 'ListItem', + position: 1, + name: SITE_NAME, + item: SITE_URL, + }, + { + '@type': 'ListItem', + position: 2, + name: 'Blog', + item: `${SITE_URL}/blog`, + }, + ], + }, + { + '@type': 'Blog', + '@id': `${SITE_URL}/blog`, + name: `${SITE_NAME} Blog`, + description: BLOG_DESCRIPTION, + url: `${SITE_URL}/blog`, + publisher: { + '@type': 'Organization', + name: AUTHOR_NAME, + url: AUTHOR_URL, + logo: { '@type': 'ImageObject', url: OG_IMAGE }, + }, + inLanguage: 'en-US', + blogPost: sorted.map((post) => ({ + '@type': 'BlogPosting', + headline: post.title, + description: post.excerpt, + url: `${SITE_URL}/blog/${post.slug}`, + datePublished: `${post.date}T00:00:00Z`, + ...(post.modifiedDate && { dateModified: `${post.modifiedDate}T00:00:00Z` }), + author: post.author.split(', ').map((name) => ({ + '@type': 'Person', + name, + })), + })), + }, + ], + }; + + return ( + <> + + + + ); } diff --git a/packages/app/src/app/feed.xml/route.ts b/packages/app/src/app/feed.xml/route.ts new file mode 100644 index 0000000..eb61ee2 --- /dev/null +++ b/packages/app/src/app/feed.xml/route.ts @@ -0,0 +1,52 @@ +import { BLOG_POSTS } from '@/components/blog/blog-data'; +import { AUTHOR_NAME, SITE_NAME, SITE_URL } from '@semianalysisai/inferencex-constants'; + +function escapeXml(str: string): string { + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +export function GET() { + const sorted = [...BLOG_POSTS].sort((a, b) => b.date.localeCompare(a.date)); + const lastBuildDate = sorted[0] + ? new Date(sorted[0].date + 'T00:00:00Z').toUTCString() + : new Date().toUTCString(); + + const items = sorted + .map( + (post) => ` + ${escapeXml(post.title)} + ${SITE_URL}/blog/${post.slug} + ${SITE_URL}/blog/${post.slug} + ${escapeXml(post.excerpt)} + ${new Date(post.date + 'T00:00:00Z').toUTCString()} + ${escapeXml(post.author)} + ${post.tags?.map((t) => `${escapeXml(t)}`).join('\n ') ?? ''} + `, + ) + .join('\n'); + + const xml = ` + + + ${SITE_NAME} Blog + ${SITE_URL}/blog + Deep dives into inference benchmarking, GPU performance, and the economics of AI compute from ${AUTHOR_NAME}. + en-US + ${lastBuildDate} + +${items} + +`; + + return new Response(xml, { + headers: { + 'Content-Type': 'application/rss+xml; charset=utf-8', + 'Cache-Control': 'public, max-age=3600, s-maxage=3600', + }, + }); +} diff --git a/packages/app/src/app/layout.tsx b/packages/app/src/app/layout.tsx index 4dbb344..b915ee4 100644 --- a/packages/app/src/app/layout.tsx +++ b/packages/app/src/app/layout.tsx @@ -76,6 +76,9 @@ export const metadata: Metadata = { }, alternates: { canonical: SITE_URL, + types: { + 'application/rss+xml': `${SITE_URL}/feed.xml`, + }, }, icons: { icon: [ @@ -161,10 +164,7 @@ export default function RootLayout({ - ({ url: `${BASE_URL}/blog/${post.slug}`, - lastModified: now, + lastModified: new Date((post.modifiedDate ?? post.date) + 'T00:00:00Z').toISOString(), changeFrequency: 'monthly' as const, priority: 0.7, })), diff --git a/packages/app/src/components/blog/blog-data.ts b/packages/app/src/components/blog/blog-data.ts index 06262f1..c33af9d 100644 --- a/packages/app/src/components/blog/blog-data.ts +++ b/packages/app/src/components/blog/blog-data.ts @@ -3,9 +3,17 @@ export interface BlogPost { title: string; author: string; date: string; // ISO format: 'YYYY-MM-DD' + modifiedDate?: string; // ISO format: 'YYYY-MM-DD' excerpt: string; content: string; // Markdown tags?: string[]; + coverImage?: string; // absolute URL for OG/structured data +} + +/** Estimated reading time in minutes (assumes 238 wpm average). */ +export function getReadingTime(content: string): number { + const words = content.trim().split(/\s+/).length; + return Math.max(1, Math.round(words / 238)); } export const BLOG_POSTS: BlogPost[] = []; diff --git a/packages/app/src/components/blog/blog-post-content.tsx b/packages/app/src/components/blog/blog-post-content.tsx index d570d8f..244a0b6 100644 --- a/packages/app/src/components/blog/blog-post-content.tsx +++ b/packages/app/src/components/blog/blog-post-content.tsx @@ -8,7 +8,7 @@ import { Card } from '@/components/ui/card'; import type { BlogPost } from './blog-data'; -export function BlogPostContent({ post }: { post: BlogPost }) { +export function BlogPostContent({ post, readingTime }: { post: BlogPost; readingTime?: number }) { const formattedDate = new Date(post.date + 'T00:00:00').toLocaleDateString('en-US', { year: 'numeric', month: 'long', @@ -32,6 +32,12 @@ export function BlogPostContent({ post }: { post: BlogPost }) { {formattedDate} · {post.author} + {readingTime && ( + <> + · + {readingTime} min read + + )} {post.tags?.map((tag) => ( =3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + '@tanstack/query-core@5.91.0': resolution: {integrity: sha512-FYXN8Kk9Q5VKuV6AIVaNwMThSi0nvAtR4X7HQoigf6ePOtFcavJYVIzgFhOVdtbBQtCJE3KimDIMMJM2DR1hjw==} @@ -1851,21 +1862,36 @@ packages: '@types/d3@7.4.3': resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/geojson@7946.0.16': resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} '@types/lodash@4.17.24': resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node@22.19.15': resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==} @@ -1895,9 +1921,18 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@vercel/analytics@2.0.1': resolution: {integrity: sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==} peerDependencies: @@ -2067,6 +2102,9 @@ packages: aws4@1.13.2: resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -2122,6 +2160,9 @@ packages: caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -2130,6 +2171,18 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + ci-info@4.4.0: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} @@ -2182,6 +2235,9 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -2219,6 +2275,11 @@ packages: resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -2385,6 +2446,9 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + delaunator@5.0.1: resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} @@ -2392,6 +2456,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -2399,6 +2467,9 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dompurify@3.3.3: resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} @@ -2482,6 +2553,9 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -2621,6 +2695,12 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + html-encoding-sniffer@6.0.0: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -2628,6 +2708,9 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + http-signature@1.4.0: resolution: {integrity: sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==} engines: {node: '>=0.10'} @@ -2654,10 +2737,19 @@ packages: resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} engines: {node: '>=10'} + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + internmap@2.0.3: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-buffer@2.0.5: resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} engines: {node: '>=4'} @@ -2666,10 +2758,16 @@ packages: resolution: {integrity: sha512-Ab9bQDQ11lWootZUI5qxgN2ZXwxNI5hTwnsvOc1wyxQ7zQ8OkEDw79mI0+9jI3x432NfwbVRru+3noJfXF6lSQ==} hasBin: true + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-installed-globally@0.4.0: resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} engines: {node: '>=10'} @@ -2681,6 +2779,10 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -2919,6 +3021,9 @@ packages: long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + lru-cache@11.2.7: resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} engines: {node: 20 || >=22} @@ -2945,12 +3050,99 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -3052,6 +3244,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse5@8.0.0: resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} @@ -3097,6 +3292,10 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + postcss@8.4.31: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} @@ -3139,6 +3338,9 @@ packages: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + protobufjs@7.5.4: resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} engines: {node: '>=12.0.0'} @@ -3165,6 +3367,12 @@ packages: peerDependencies: react: ^19.2.4 + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -3207,6 +3415,12 @@ packages: resolution: {integrity: sha512-7KA6+N9IGat52d83dvxnApAWN+MtVb1MiVuMR/cf1O4kYsJG+g/Aav0AHcHKsb6StinayfPLne0+fMX2sOzAKg==} engines: {node: '>=6'} + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + request-progress@3.0.0: resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} @@ -3322,6 +3536,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + split@1.0.1: resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} @@ -3346,6 +3563,9 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -3358,6 +3578,12 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -3469,6 +3695,12 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} @@ -3520,6 +3752,24 @@ packages: resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==} engines: {node: '>=20.18.1'} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -3559,6 +3809,12 @@ packages: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} engines: {'0': node >=0.6.0} + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite@7.3.1: resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3700,6 +3956,9 @@ packages: yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@alloc/quick-lru@5.2.0': {} @@ -4827,6 +5086,11 @@ snapshots: postcss: 8.5.8 tailwindcss: 4.2.2 + '@tailwindcss/typography@0.5.19(tailwindcss@4.2.2)': + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 4.2.2 + '@tanstack/query-core@5.91.0': {} '@tanstack/query-persist-client-core@5.92.2': @@ -4970,18 +5234,36 @@ snapshots: '@types/d3-transition': 3.0.9 '@types/d3-zoom': 3.0.8 + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + '@types/deep-eql@4.0.2': {} + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + '@types/estree@1.0.8': {} '@types/geojson@7946.0.16': {} + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/lodash-es@4.17.12': dependencies: '@types/lodash': 4.17.24 '@types/lodash@4.17.24': {} + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + '@types/node@22.19.15': dependencies: undici-types: 6.21.0 @@ -5013,11 +5295,17 @@ snapshots: '@types/trusted-types@2.0.7': optional: true + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + '@types/yauzl@2.10.3': dependencies: '@types/node': 25.5.0 optional: true + '@ungap/structured-clone@1.3.0': {} + '@vercel/analytics@2.0.1(next@16.2.0(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': optionalDependencies: next: 16.2.0(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -5159,6 +5447,8 @@ snapshots: aws4@1.13.2: {} + bail@2.0.2: {} + balanced-match@4.0.4: {} base64-js@1.5.1: {} @@ -5207,6 +5497,8 @@ snapshots: caseless@0.12.0: {} + ccount@2.0.1: {} + chai@6.2.2: {} chalk@4.1.2: @@ -5214,6 +5506,14 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + ci-info@4.4.0: {} class-variance-authority@0.7.1: @@ -5262,6 +5562,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + comma-separated-tokens@2.0.3: {} + commander@2.20.3: optional: true @@ -5293,6 +5595,8 @@ snapshots: mdn-data: 2.27.1 source-map-js: 1.2.1 + cssesc@3.0.0: {} + csstype@3.2.3: {} cypress@15.12.0: @@ -5520,16 +5824,26 @@ snapshots: decimal.js@10.6.0: {} + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + delaunator@5.0.1: dependencies: robust-predicates: 3.0.2 delayed-stream@1.0.0: {} + dequal@2.0.3: {} + detect-libc@2.1.2: {} detect-node-es@1.1.0: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + dompurify@3.3.3: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -5632,6 +5946,8 @@ snapshots: escape-string-regexp@4.0.0: {} + estree-util-is-identifier-name@3.0.0: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 @@ -5785,6 +6101,30 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + html-encoding-sniffer@6.0.0: dependencies: '@exodus/bytes': 1.15.0 @@ -5793,6 +6133,8 @@ snapshots: html-escaper@2.0.2: {} + html-url-attributes@3.0.1: {} + http-signature@1.4.0: dependencies: assert-plus: 1.0.0 @@ -5813,16 +6155,29 @@ snapshots: ini@2.0.0: {} + inline-style-parser@0.2.7: {} + internmap@2.0.3: {} + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + is-buffer@2.0.5: {} is-ci@4.1.0: dependencies: ci-info: 4.4.0 + is-decimal@2.0.1: {} + is-fullwidth-code-point@3.0.0: {} + is-hexadecimal@2.0.1: {} + is-installed-globally@0.4.0: dependencies: global-dirs: 3.0.1 @@ -5832,6 +6187,8 @@ snapshots: is-path-inside@3.0.3: {} + is-plain-obj@4.1.0: {} + is-potential-custom-element-name@1.0.1: {} is-stream@2.0.1: {} @@ -6042,6 +6399,8 @@ snapshots: long@5.3.2: {} + longest-streak@3.1.0: {} + lru-cache@11.2.7: {} lucide-react@0.577.0(react@19.2.4): @@ -6066,10 +6425,232 @@ snapshots: math-intrinsics@1.1.0: {} + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdn-data@2.27.1: {} merge-stream@2.0.0: {} + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3(supports-color@8.1.1) + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + mime-db@1.52.0: {} mime-types@2.1.35: @@ -6190,6 +6771,16 @@ snapshots: package-json-from-dist@1.0.1: {} + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + parse5@8.0.0: dependencies: entities: 6.0.1 @@ -6229,6 +6820,11 @@ snapshots: pify@2.3.0: {} + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss@8.4.31: dependencies: nanoid: 3.3.11 @@ -6275,6 +6871,8 @@ snapshots: process@0.11.10: {} + property-information@7.1.0: {} + protobufjs@7.5.4: dependencies: '@protobufjs/aspromise': 1.1.2 @@ -6310,6 +6908,24 @@ snapshots: react: 19.2.4 scheduler: 0.27.0 + react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.4): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 19.2.14 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 19.2.4 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.4): dependencies: react: 19.2.4 @@ -6347,6 +6963,23 @@ snapshots: readline-transform@1.0.0: {} + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + request-progress@3.0.0: dependencies: throttleit: 1.0.1 @@ -6515,6 +7148,8 @@ snapshots: source-map@0.6.1: optional: true + space-separated-tokens@2.0.2: {} + split@1.0.1: dependencies: through: 2.3.8 @@ -6550,6 +7185,11 @@ snapshots: dependencies: safe-buffer: 5.2.1 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -6558,6 +7198,14 @@ snapshots: strip-final-newline@2.0.0: {} + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + styled-jsx@5.1.6(react@19.2.4): dependencies: client-only: 0.0.1 @@ -6640,6 +7288,10 @@ snapshots: tree-kill@1.2.2: {} + trim-lines@3.0.1: {} + + trough@2.2.0: {} + tsconfig-paths@4.2.0: dependencies: json5: 2.2.3 @@ -6679,6 +7331,39 @@ snapshots: undici@7.24.4: {} + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + universalify@2.0.1: {} untildify@4.0.0: {} @@ -6708,6 +7393,16 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0): dependencies: esbuild: 0.27.4 @@ -6818,3 +7513,5 @@ snapshots: dependencies: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 + + zwitch@2.0.4: {}