diff --git a/public/assets/hero-demo.mp4 b/public/assets/hero-demo.mp4 index 0aaa609..e77299e 100644 Binary files a/public/assets/hero-demo.mp4 and b/public/assets/hero-demo.mp4 differ diff --git a/public/assets/hero-poster.png b/public/assets/hero-poster.png index e22fb65..d92d4ae 100644 Binary files a/public/assets/hero-poster.png and b/public/assets/hero-poster.png differ diff --git a/src/app/(home)/roadmap/page.tsx b/src/app/(home)/roadmap/page.tsx index 66d1cd9..641ebda 100644 --- a/src/app/(home)/roadmap/page.tsx +++ b/src/app/(home)/roadmap/page.tsx @@ -54,7 +54,15 @@ function parseBlocks(markdown: string, docsSlugs: Set) { const flushParagraph = () => { if (paragraph.length > 0) { - blocks.push({ kind: 'paragraph', text: paragraph.join(' ') }); + // The markdown's checkbox notation (`[x]`/`[~]`/`[ ]`) means nothing to a + // visitor with no context for it and the page already renders status as + // dots, so strip it from prose rather than show it literally. + const text = paragraph + .join(' ') + .replace(/\s*marked\s*`\[[x~ ]\]`/g, '') + // The doc says "Open an issue" as plain prose; give it a real link here. + .replace(/Open an issue/, `[Open an issue](${repoBase}/issues)`); + blocks.push({ kind: 'paragraph', text }); paragraph = []; } }; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 29bf61a..5007620 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -7,9 +7,9 @@ import { cn } from '@/lib/cn'; const geistSans = Geist({ subsets: ['latin'], variable: '--font-sans' }); const geistMono = Geist_Mono({ subsets: ['latin'], variable: '--font-mono' }); -const title = 'fossui — A Flutter UI Kit With Its Own Identity'; +const title = 'fossui — The Fresh, Minimal Flutter UI Kit'; const description = - 'A minimal, lightweight Flutter component set for developers tired of every app looking like Material. Drops into any Flutter app, no heavy dependencies.'; + 'A fresh, minimal Flutter UI kit: a lightweight component library and design system you drop into any app, no heavy dependencies.'; const jsonLd = { '@context': 'https://schema.org', diff --git a/src/components/github-stars.tsx b/src/components/github-stars.tsx index 04a6fa1..882b13c 100644 --- a/src/components/github-stars.tsx +++ b/src/components/github-stars.tsx @@ -1,3 +1,6 @@ +'use client'; + +import { useEffect, useState } from 'react'; import { Star } from 'lucide-react'; import { gitConfig } from '@/lib/shared'; @@ -9,22 +12,18 @@ function formatCount(count: number) { return `${(count / 1000).toFixed(1).replace(/\.0$/, '')}k`; } -async function getStarCount(): Promise { - try { - const res = await fetch(apiUrl, { - next: { revalidate: 86400 }, - headers: { Accept: 'application/vnd.github+json' }, - }); - if (!res.ok) return null; - const data = (await res.json()) as { stargazers_count?: number }; - return typeof data.stargazers_count === 'number' ? data.stargazers_count : null; - } catch { - return null; - } -} +export function GitHubStars() { + const [stars, setStars] = useState(null); + + useEffect(() => { + fetch(apiUrl, { headers: { Accept: 'application/vnd.github+json' } }) + .then((res) => (res.ok ? res.json() : null)) + .then((data: { stargazers_count?: number } | null) => { + if (typeof data?.stargazers_count === 'number') setStars(data.stargazers_count); + }) + .catch(() => {}); + }, []); -export async function GitHubStars() { - const stars = await getStarCount(); return (