From f91008a665f528eb913b2a4bce8af875a2de0f3d Mon Sep 17 00:00:00 2001 From: Beast Date: Wed, 16 Sep 2026 10:45:44 +0800 Subject: [PATCH 1/6] feat: make blog content centered --- website/src/components/features/blog/BlogPost.astro | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/website/src/components/features/blog/BlogPost.astro b/website/src/components/features/blog/BlogPost.astro index cc32e89b..849e5f5b 100644 --- a/website/src/components/features/blog/BlogPost.astro +++ b/website/src/components/features/blog/BlogPost.astro @@ -82,7 +82,7 @@ const jsonLd = JsonLdGraph({
+
) }
From 18e489acd08617d0682275a7bb06d1dfe80ddce3 Mon Sep 17 00:00:00 2001 From: Beast Date: Wed, 16 Sep 2026 11:23:35 +0800 Subject: [PATCH 2/6] feat: update layout and categories feature --- website/scripts/check-seo-meta.mjs | 19 ++- .../src/components/features/blog/BlogList.tsx | 127 ++++++++++++++---- .../components/features/blog/BlogPost.astro | 44 +++--- .../src/components/features/blog/Card.astro | 30 +++-- .../features/blog/YouTubeEmbed.astro | 2 +- .../features/community/BlogSection.astro | 9 +- .../features/home/BlogSection.astro | 9 +- .../features/home/SolutionSection.astro | 8 +- .../features/launch/FactsSection.astro | 2 +- .../whitepaper/WhitepaperPrintCover.astro | 4 +- website/src/constants/blog-categories.ts | 2 + website/src/content.config.ts | 4 +- .../blogs/de-DE/weekly-update-01-13-2026.md | 1 + .../blogs/de-DE/weekly-update-01-21-2026.md | 1 + .../blogs/de-DE/weekly-update-01-28-2026.md | 1 + .../blogs/de-DE/weekly-update-02-04-2026.md | 1 + .../blogs/de-DE/weekly-update-02-13-2026.md | 1 + .../blogs/de-DE/weekly-update-02-20-2026.md | 1 + .../blogs/de-DE/weekly-update-02-27-2026.md | 1 + .../blogs/de-DE/weekly-update-03-07-2026.md | 1 + .../blogs/de-DE/weekly-update-03-16-2026.md | 1 + .../blogs/de-DE/weekly-update-03-24-2026.md | 1 + .../blogs/de-DE/weekly-update-03-31-2026.md | 1 + .../blogs/de-DE/weekly-update-04-07-2026.md | 1 + .../blogs/de-DE/weekly-update-04-15-2026.md | 1 + .../blogs/de-DE/weekly-update-04-22-2026.md | 1 + .../blogs/de-DE/weekly-update-04-29-2026.md | 1 + .../blogs/de-DE/weekly-update-05-06-2026.md | 1 + .../blogs/de-DE/weekly-update-05-13-2026.md | 1 + .../blogs/de-DE/weekly-update-05-21-2026.md | 1 + .../blogs/de-DE/weekly-update-05-29-2026.md | 1 + .../blogs/de-DE/weekly-update-06-09-2026.mdx | 1 + .../blogs/de-DE/weekly-update-06-18-2026.mdx | 1 + .../blogs/de-DE/weekly-update-06-26-2026.mdx | 1 + .../blogs/de-DE/weekly-update-07-09-2026.mdx | 1 + .../blogs/de-DE/weekly-update-07-19-2026.mdx | 1 + .../blogs/de-DE/weekly-update-07-31-2026.mdx | 1 + .../blogs/de-DE/weekly-update-08-10-2026.mdx | 1 + .../blogs/de-DE/weekly-update-08-19-2026.mdx | 1 + .../blogs/de-DE/weekly-update-09-08-2026.mdx | 1 + .../blogs/de-DE/weekly-update-10-14-2025.md | 1 + .../blogs/de-DE/weekly-update-10-21-2025.md | 1 + .../blogs/de-DE/weekly-update-11-10-2025.md | 1 + .../blogs/de-DE/weekly-update-11-17-2025.md | 1 + .../blogs/de-DE/weekly-update-11-25-2025.md | 1 + .../blogs/de-DE/weekly-update-12-01-2025.md | 1 + .../blogs/de-DE/weekly-update-12-09-2025.md | 1 + .../blogs/de-DE/weekly-update-12-15-2025.md | 1 + .../blogs/de-DE/weekly-update-12-22-2025.md | 1 + .../contents/blogs/de-DE/what-is-q-day.mdx | 25 ++-- .../blogs/en-US/weekly-update-01-13-2026.md | 1 + .../blogs/en-US/weekly-update-01-21-2026.md | 1 + .../blogs/en-US/weekly-update-01-28-2026.md | 1 + .../blogs/en-US/weekly-update-02-04-2026.md | 1 + .../blogs/en-US/weekly-update-02-13-2026.md | 1 + .../blogs/en-US/weekly-update-02-20-2026.md | 1 + .../blogs/en-US/weekly-update-02-27-2026.md | 1 + .../blogs/en-US/weekly-update-03-07-2026.md | 1 + .../blogs/en-US/weekly-update-03-16-2026.md | 1 + .../blogs/en-US/weekly-update-03-24-2026.md | 1 + .../blogs/en-US/weekly-update-03-31-2026.md | 1 + .../blogs/en-US/weekly-update-04-07-2026.md | 1 + .../blogs/en-US/weekly-update-04-15-2026.md | 1 + .../blogs/en-US/weekly-update-04-22-2026.md | 1 + .../blogs/en-US/weekly-update-04-29-2026.md | 1 + .../blogs/en-US/weekly-update-05-06-2026.md | 1 + .../blogs/en-US/weekly-update-05-13-2026.md | 1 + .../blogs/en-US/weekly-update-05-21-2026.md | 1 + .../blogs/en-US/weekly-update-05-29-2026.md | 1 + .../blogs/en-US/weekly-update-06-09-2026.mdx | 1 + .../blogs/en-US/weekly-update-06-18-2026.mdx | 1 + .../blogs/en-US/weekly-update-06-26-2026.mdx | 1 + .../blogs/en-US/weekly-update-07-09-2026.mdx | 1 + .../blogs/en-US/weekly-update-07-19-2026.mdx | 1 + .../blogs/en-US/weekly-update-07-31-2026.mdx | 1 + .../blogs/en-US/weekly-update-08-10-2026.mdx | 1 + .../blogs/en-US/weekly-update-08-19-2026.mdx | 1 + .../blogs/en-US/weekly-update-09-08-2026.mdx | 1 + .../blogs/en-US/weekly-update-10-14-2025.md | 1 + .../blogs/en-US/weekly-update-10-21-2025.md | 1 + .../blogs/en-US/weekly-update-11-10-2025.md | 1 + .../blogs/en-US/weekly-update-11-17-2025.md | 1 + .../blogs/en-US/weekly-update-11-25-2025.md | 1 + .../blogs/en-US/weekly-update-12-01-2025.md | 1 + .../blogs/en-US/weekly-update-12-09-2025.md | 1 + .../blogs/en-US/weekly-update-12-15-2025.md | 1 + .../blogs/en-US/weekly-update-12-22-2025.md | 1 + .../contents/blogs/en-US/what-is-q-day.mdx | 25 ++-- .../blogs/es-ES/weekly-update-01-13-2026.md | 1 + .../blogs/es-ES/weekly-update-01-21-2026.md | 1 + .../blogs/es-ES/weekly-update-01-28-2026.md | 1 + .../blogs/es-ES/weekly-update-02-04-2026.md | 1 + .../blogs/es-ES/weekly-update-02-13-2026.md | 1 + .../blogs/es-ES/weekly-update-02-20-2026.md | 1 + .../blogs/es-ES/weekly-update-02-27-2026.md | 1 + .../blogs/es-ES/weekly-update-03-07-2026.md | 1 + .../blogs/es-ES/weekly-update-03-16-2026.md | 1 + .../blogs/es-ES/weekly-update-03-24-2026.md | 1 + .../blogs/es-ES/weekly-update-03-31-2026.md | 1 + .../blogs/es-ES/weekly-update-04-07-2026.md | 1 + .../blogs/es-ES/weekly-update-04-15-2026.md | 1 + .../blogs/es-ES/weekly-update-04-22-2026.md | 1 + .../blogs/es-ES/weekly-update-04-29-2026.md | 1 + .../blogs/es-ES/weekly-update-05-06-2026.md | 1 + .../blogs/es-ES/weekly-update-05-13-2026.md | 1 + .../blogs/es-ES/weekly-update-05-21-2026.md | 1 + .../blogs/es-ES/weekly-update-05-29-2026.md | 1 + .../blogs/es-ES/weekly-update-06-09-2026.mdx | 1 + .../blogs/es-ES/weekly-update-06-18-2026.mdx | 1 + .../blogs/es-ES/weekly-update-06-26-2026.mdx | 1 + .../blogs/es-ES/weekly-update-07-09-2026.mdx | 1 + .../blogs/es-ES/weekly-update-07-19-2026.mdx | 1 + .../blogs/es-ES/weekly-update-07-31-2026.mdx | 1 + .../blogs/es-ES/weekly-update-08-10-2026.mdx | 1 + .../blogs/es-ES/weekly-update-08-19-2026.mdx | 1 + .../blogs/es-ES/weekly-update-09-08-2026.mdx | 1 + .../blogs/es-ES/weekly-update-10-14-2025.md | 1 + .../blogs/es-ES/weekly-update-10-21-2025.md | 1 + .../blogs/es-ES/weekly-update-11-10-2025.md | 1 + .../blogs/es-ES/weekly-update-11-17-2025.md | 1 + .../blogs/es-ES/weekly-update-11-25-2025.md | 1 + .../blogs/es-ES/weekly-update-12-01-2025.md | 1 + .../blogs/es-ES/weekly-update-12-09-2025.md | 1 + .../blogs/es-ES/weekly-update-12-15-2025.md | 1 + .../blogs/es-ES/weekly-update-12-22-2025.md | 1 + .../contents/blogs/es-ES/what-is-q-day.mdx | 25 ++-- .../blogs/hi-IN/weekly-update-01-13-2026.md | 1 + .../blogs/hi-IN/weekly-update-01-21-2026.md | 1 + .../blogs/hi-IN/weekly-update-01-28-2026.md | 1 + .../blogs/hi-IN/weekly-update-02-04-2026.md | 1 + .../blogs/hi-IN/weekly-update-02-13-2026.md | 1 + .../blogs/hi-IN/weekly-update-02-20-2026.md | 1 + .../blogs/hi-IN/weekly-update-02-27-2026.md | 1 + .../blogs/hi-IN/weekly-update-03-07-2026.md | 1 + .../blogs/hi-IN/weekly-update-03-16-2026.md | 1 + .../blogs/hi-IN/weekly-update-03-24-2026.md | 1 + .../blogs/hi-IN/weekly-update-03-31-2026.md | 1 + .../blogs/hi-IN/weekly-update-04-07-2026.md | 1 + .../blogs/hi-IN/weekly-update-04-15-2026.md | 1 + .../blogs/hi-IN/weekly-update-04-22-2026.md | 1 + .../blogs/hi-IN/weekly-update-04-29-2026.md | 1 + .../blogs/hi-IN/weekly-update-05-06-2026.md | 1 + .../blogs/hi-IN/weekly-update-05-13-2026.md | 1 + .../blogs/hi-IN/weekly-update-05-21-2026.md | 1 + .../blogs/hi-IN/weekly-update-05-29-2026.md | 1 + .../blogs/hi-IN/weekly-update-06-09-2026.mdx | 1 + .../blogs/hi-IN/weekly-update-06-18-2026.mdx | 1 + .../blogs/hi-IN/weekly-update-06-26-2026.mdx | 1 + .../blogs/hi-IN/weekly-update-07-09-2026.mdx | 1 + .../blogs/hi-IN/weekly-update-07-19-2026.mdx | 1 + .../blogs/hi-IN/weekly-update-07-31-2026.mdx | 1 + .../blogs/hi-IN/weekly-update-08-10-2026.mdx | 1 + .../blogs/hi-IN/weekly-update-08-19-2026.mdx | 1 + .../blogs/hi-IN/weekly-update-09-08-2026.mdx | 1 + .../blogs/hi-IN/weekly-update-10-14-2025.md | 1 + .../blogs/hi-IN/weekly-update-10-21-2025.md | 1 + .../blogs/hi-IN/weekly-update-11-10-2025.md | 1 + .../blogs/hi-IN/weekly-update-11-17-2025.md | 1 + .../blogs/hi-IN/weekly-update-11-25-2025.md | 1 + .../blogs/hi-IN/weekly-update-12-01-2025.md | 1 + .../blogs/hi-IN/weekly-update-12-09-2025.md | 1 + .../blogs/hi-IN/weekly-update-12-15-2025.md | 1 + .../blogs/hi-IN/weekly-update-12-22-2025.md | 1 + .../contents/blogs/hi-IN/what-is-q-day.mdx | 25 ++-- .../blogs/id-ID/weekly-update-01-13-2026.md | 1 + .../blogs/id-ID/weekly-update-01-21-2026.md | 1 + .../blogs/id-ID/weekly-update-01-28-2026.md | 1 + .../blogs/id-ID/weekly-update-02-04-2026.md | 1 + .../blogs/id-ID/weekly-update-02-13-2026.md | 1 + .../blogs/id-ID/weekly-update-02-20-2026.md | 1 + .../blogs/id-ID/weekly-update-02-27-2026.md | 1 + .../blogs/id-ID/weekly-update-03-07-2026.md | 1 + .../blogs/id-ID/weekly-update-03-16-2026.md | 1 + .../blogs/id-ID/weekly-update-03-24-2026.md | 1 + .../blogs/id-ID/weekly-update-03-31-2026.md | 1 + .../blogs/id-ID/weekly-update-04-07-2026.md | 1 + .../blogs/id-ID/weekly-update-04-15-2026.md | 1 + .../blogs/id-ID/weekly-update-04-22-2026.md | 1 + .../blogs/id-ID/weekly-update-04-29-2026.md | 1 + .../blogs/id-ID/weekly-update-05-06-2026.md | 1 + .../blogs/id-ID/weekly-update-05-13-2026.md | 1 + .../blogs/id-ID/weekly-update-05-21-2026.md | 1 + .../blogs/id-ID/weekly-update-05-29-2026.md | 1 + .../blogs/id-ID/weekly-update-06-09-2026.mdx | 1 + .../blogs/id-ID/weekly-update-06-18-2026.mdx | 1 + .../blogs/id-ID/weekly-update-06-26-2026.mdx | 1 + .../blogs/id-ID/weekly-update-07-09-2026.mdx | 1 + .../blogs/id-ID/weekly-update-07-19-2026.mdx | 1 + .../blogs/id-ID/weekly-update-07-31-2026.mdx | 1 + .../blogs/id-ID/weekly-update-08-10-2026.mdx | 1 + .../blogs/id-ID/weekly-update-08-19-2026.mdx | 1 + .../blogs/id-ID/weekly-update-09-08-2026.mdx | 1 + .../blogs/id-ID/weekly-update-10-14-2025.md | 1 + .../blogs/id-ID/weekly-update-10-21-2025.md | 1 + .../blogs/id-ID/weekly-update-11-10-2025.md | 1 + .../blogs/id-ID/weekly-update-11-17-2025.md | 1 + .../blogs/id-ID/weekly-update-11-25-2025.md | 1 + .../blogs/id-ID/weekly-update-12-01-2025.md | 1 + .../blogs/id-ID/weekly-update-12-09-2025.md | 1 + .../blogs/id-ID/weekly-update-12-15-2025.md | 1 + .../blogs/id-ID/weekly-update-12-22-2025.md | 1 + .../contents/blogs/id-ID/what-is-q-day.mdx | 25 ++-- .../blogs/ja-JP/weekly-update-01-13-2026.md | 1 + .../blogs/ja-JP/weekly-update-01-21-2026.md | 1 + .../blogs/ja-JP/weekly-update-01-28-2026.md | 1 + .../blogs/ja-JP/weekly-update-02-04-2026.md | 1 + .../blogs/ja-JP/weekly-update-02-13-2026.md | 1 + .../blogs/ja-JP/weekly-update-02-20-2026.md | 1 + .../blogs/ja-JP/weekly-update-02-27-2026.md | 1 + .../blogs/ja-JP/weekly-update-03-07-2026.md | 1 + .../blogs/ja-JP/weekly-update-03-16-2026.md | 1 + .../blogs/ja-JP/weekly-update-03-24-2026.md | 1 + .../blogs/ja-JP/weekly-update-03-31-2026.md | 1 + .../blogs/ja-JP/weekly-update-04-07-2026.md | 1 + .../blogs/ja-JP/weekly-update-04-15-2026.md | 1 + .../blogs/ja-JP/weekly-update-04-22-2026.md | 1 + .../blogs/ja-JP/weekly-update-04-29-2026.md | 1 + .../blogs/ja-JP/weekly-update-05-06-2026.md | 1 + .../blogs/ja-JP/weekly-update-05-13-2026.md | 1 + .../blogs/ja-JP/weekly-update-05-21-2026.md | 1 + .../blogs/ja-JP/weekly-update-05-29-2026.md | 1 + .../blogs/ja-JP/weekly-update-06-09-2026.mdx | 1 + .../blogs/ja-JP/weekly-update-06-18-2026.mdx | 1 + .../blogs/ja-JP/weekly-update-06-26-2026.mdx | 1 + .../blogs/ja-JP/weekly-update-07-09-2026.mdx | 1 + .../blogs/ja-JP/weekly-update-07-19-2026.mdx | 1 + .../blogs/ja-JP/weekly-update-07-31-2026.mdx | 1 + .../blogs/ja-JP/weekly-update-08-10-2026.mdx | 1 + .../blogs/ja-JP/weekly-update-08-19-2026.mdx | 1 + .../blogs/ja-JP/weekly-update-09-08-2026.mdx | 1 + .../blogs/ja-JP/weekly-update-10-14-2025.md | 1 + .../blogs/ja-JP/weekly-update-10-21-2025.md | 1 + .../blogs/ja-JP/weekly-update-11-10-2025.md | 1 + .../blogs/ja-JP/weekly-update-11-17-2025.md | 1 + .../blogs/ja-JP/weekly-update-11-25-2025.md | 1 + .../blogs/ja-JP/weekly-update-12-01-2025.md | 1 + .../blogs/ja-JP/weekly-update-12-09-2025.md | 1 + .../blogs/ja-JP/weekly-update-12-15-2025.md | 1 + .../blogs/ja-JP/weekly-update-12-22-2025.md | 1 + .../contents/blogs/ja-JP/what-is-q-day.mdx | 25 ++-- .../blogs/ko-KR/weekly-update-01-13-2026.md | 1 + .../blogs/ko-KR/weekly-update-01-21-2026.md | 1 + .../blogs/ko-KR/weekly-update-01-28-2026.md | 1 + .../blogs/ko-KR/weekly-update-02-04-2026.md | 1 + .../blogs/ko-KR/weekly-update-02-13-2026.md | 1 + .../blogs/ko-KR/weekly-update-02-20-2026.md | 1 + .../blogs/ko-KR/weekly-update-02-27-2026.md | 1 + .../blogs/ko-KR/weekly-update-03-07-2026.md | 1 + .../blogs/ko-KR/weekly-update-03-16-2026.md | 1 + .../blogs/ko-KR/weekly-update-03-24-2026.md | 1 + .../blogs/ko-KR/weekly-update-03-31-2026.md | 1 + .../blogs/ko-KR/weekly-update-04-07-2026.md | 1 + .../blogs/ko-KR/weekly-update-04-15-2026.md | 1 + .../blogs/ko-KR/weekly-update-04-22-2026.md | 1 + .../blogs/ko-KR/weekly-update-04-29-2026.md | 1 + .../blogs/ko-KR/weekly-update-05-06-2026.md | 1 + .../blogs/ko-KR/weekly-update-05-13-2026.md | 1 + .../blogs/ko-KR/weekly-update-05-21-2026.md | 1 + .../blogs/ko-KR/weekly-update-05-29-2026.md | 1 + .../blogs/ko-KR/weekly-update-06-09-2026.mdx | 1 + .../blogs/ko-KR/weekly-update-06-18-2026.mdx | 1 + .../blogs/ko-KR/weekly-update-06-26-2026.mdx | 1 + .../blogs/ko-KR/weekly-update-07-09-2026.mdx | 1 + .../blogs/ko-KR/weekly-update-07-19-2026.mdx | 1 + .../blogs/ko-KR/weekly-update-07-31-2026.mdx | 1 + .../blogs/ko-KR/weekly-update-08-10-2026.mdx | 1 + .../blogs/ko-KR/weekly-update-08-19-2026.mdx | 1 + .../blogs/ko-KR/weekly-update-09-08-2026.mdx | 1 + .../blogs/ko-KR/weekly-update-10-14-2025.md | 1 + .../blogs/ko-KR/weekly-update-10-21-2025.md | 1 + .../blogs/ko-KR/weekly-update-11-10-2025.md | 1 + .../blogs/ko-KR/weekly-update-11-17-2025.md | 1 + .../blogs/ko-KR/weekly-update-11-25-2025.md | 1 + .../blogs/ko-KR/weekly-update-12-01-2025.md | 1 + .../blogs/ko-KR/weekly-update-12-09-2025.md | 1 + .../blogs/ko-KR/weekly-update-12-15-2025.md | 1 + .../blogs/ko-KR/weekly-update-12-22-2025.md | 1 + .../contents/blogs/ko-KR/what-is-q-day.mdx | 25 ++-- .../blogs/ru-RU/weekly-update-01-13-2026.md | 1 + .../blogs/ru-RU/weekly-update-01-21-2026.md | 1 + .../blogs/ru-RU/weekly-update-01-28-2026.md | 1 + .../blogs/ru-RU/weekly-update-02-04-2026.md | 1 + .../blogs/ru-RU/weekly-update-02-13-2026.md | 1 + .../blogs/ru-RU/weekly-update-02-20-2026.md | 1 + .../blogs/ru-RU/weekly-update-02-27-2026.md | 1 + .../blogs/ru-RU/weekly-update-03-07-2026.md | 1 + .../blogs/ru-RU/weekly-update-03-16-2026.md | 1 + .../blogs/ru-RU/weekly-update-03-24-2026.md | 1 + .../blogs/ru-RU/weekly-update-03-31-2026.md | 1 + .../blogs/ru-RU/weekly-update-04-07-2026.md | 1 + .../blogs/ru-RU/weekly-update-04-15-2026.md | 1 + .../blogs/ru-RU/weekly-update-04-22-2026.md | 1 + .../blogs/ru-RU/weekly-update-04-29-2026.md | 1 + .../blogs/ru-RU/weekly-update-05-06-2026.md | 1 + .../blogs/ru-RU/weekly-update-05-13-2026.md | 1 + .../blogs/ru-RU/weekly-update-05-21-2026.md | 1 + .../blogs/ru-RU/weekly-update-05-29-2026.md | 1 + .../blogs/ru-RU/weekly-update-06-09-2026.mdx | 1 + .../blogs/ru-RU/weekly-update-06-18-2026.mdx | 1 + .../blogs/ru-RU/weekly-update-06-26-2026.mdx | 1 + .../blogs/ru-RU/weekly-update-07-09-2026.mdx | 1 + .../blogs/ru-RU/weekly-update-07-19-2026.mdx | 1 + .../blogs/ru-RU/weekly-update-07-31-2026.mdx | 1 + .../blogs/ru-RU/weekly-update-08-10-2026.mdx | 1 + .../blogs/ru-RU/weekly-update-08-19-2026.mdx | 1 + .../blogs/ru-RU/weekly-update-09-08-2026.mdx | 1 + .../blogs/ru-RU/weekly-update-10-14-2025.md | 1 + .../blogs/ru-RU/weekly-update-10-21-2025.md | 1 + .../blogs/ru-RU/weekly-update-11-10-2025.md | 1 + .../blogs/ru-RU/weekly-update-11-17-2025.md | 1 + .../blogs/ru-RU/weekly-update-11-25-2025.md | 1 + .../blogs/ru-RU/weekly-update-12-01-2025.md | 1 + .../blogs/ru-RU/weekly-update-12-09-2025.md | 1 + .../blogs/ru-RU/weekly-update-12-15-2025.md | 1 + .../blogs/ru-RU/weekly-update-12-22-2025.md | 1 + .../contents/blogs/ru-RU/what-is-q-day.mdx | 25 ++-- .../blogs/zh-CN/weekly-update-01-13-2026.md | 1 + .../blogs/zh-CN/weekly-update-01-21-2026.md | 1 + .../blogs/zh-CN/weekly-update-01-28-2026.md | 1 + .../blogs/zh-CN/weekly-update-02-04-2026.md | 1 + .../blogs/zh-CN/weekly-update-02-13-2026.md | 1 + .../blogs/zh-CN/weekly-update-02-20-2026.md | 1 + .../blogs/zh-CN/weekly-update-02-27-2026.md | 1 + .../blogs/zh-CN/weekly-update-03-07-2026.md | 1 + .../blogs/zh-CN/weekly-update-03-16-2026.md | 1 + .../blogs/zh-CN/weekly-update-03-24-2026.md | 1 + .../blogs/zh-CN/weekly-update-03-31-2026.md | 1 + .../blogs/zh-CN/weekly-update-04-07-2026.md | 1 + .../blogs/zh-CN/weekly-update-04-15-2026.md | 1 + .../blogs/zh-CN/weekly-update-04-22-2026.md | 1 + .../blogs/zh-CN/weekly-update-04-29-2026.md | 1 + .../blogs/zh-CN/weekly-update-05-06-2026.md | 1 + .../blogs/zh-CN/weekly-update-05-13-2026.md | 1 + .../blogs/zh-CN/weekly-update-05-21-2026.md | 1 + .../blogs/zh-CN/weekly-update-05-29-2026.md | 1 + .../blogs/zh-CN/weekly-update-06-09-2026.mdx | 1 + .../blogs/zh-CN/weekly-update-06-18-2026.mdx | 1 + .../blogs/zh-CN/weekly-update-06-26-2026.mdx | 1 + .../blogs/zh-CN/weekly-update-07-09-2026.mdx | 1 + .../blogs/zh-CN/weekly-update-07-19-2026.mdx | 1 + .../blogs/zh-CN/weekly-update-07-31-2026.mdx | 1 + .../blogs/zh-CN/weekly-update-08-10-2026.mdx | 1 + .../blogs/zh-CN/weekly-update-08-19-2026.mdx | 1 + .../blogs/zh-CN/weekly-update-09-08-2026.mdx | 1 + .../blogs/zh-CN/weekly-update-10-14-2025.md | 1 + .../blogs/zh-CN/weekly-update-10-21-2025.md | 1 + .../blogs/zh-CN/weekly-update-11-10-2025.md | 1 + .../blogs/zh-CN/weekly-update-11-17-2025.md | 1 + .../blogs/zh-CN/weekly-update-11-25-2025.md | 1 + .../blogs/zh-CN/weekly-update-12-01-2025.md | 1 + .../blogs/zh-CN/weekly-update-12-09-2025.md | 1 + .../blogs/zh-CN/weekly-update-12-15-2025.md | 1 + .../blogs/zh-CN/weekly-update-12-22-2025.md | 1 + .../contents/blogs/zh-CN/what-is-q-day.mdx | 25 ++-- .../src/contents/whitepapers/de-DE/v0.3.3.mdx | 5 +- .../src/contents/whitepapers/en-US/v0.3.3.mdx | 4 +- .../src/contents/whitepapers/es-ES/v0.3.3.mdx | 4 +- .../src/contents/whitepapers/hi-IN/v0.3.3.mdx | 3 +- .../src/contents/whitepapers/id-ID/v0.3.3.mdx | 4 +- .../src/contents/whitepapers/ru-RU/v0.3.3.mdx | 4 +- website/src/i18n/de-DE.json | 6 + website/src/i18n/en-US.json | 6 + website/src/i18n/es-ES.json | 6 + website/src/i18n/hi-IN.json | 6 + website/src/i18n/id-ID.json | 6 + website/src/i18n/ja-JP.json | 6 + website/src/i18n/ko-KR.json | 6 + website/src/i18n/ru-RU.json | 6 + website/src/i18n/zh-CN.json | 6 + website/src/pages/[lang]/blog/[...slug].astro | 19 +-- website/src/pages/[lang]/blog/index.astro | 4 + .../src/pages/[lang]/blog/tags/[tag].astro | 1 + website/src/pages/blog/[...slug].astro | 12 +- website/src/pages/blog/index.astro | 4 + website/src/pages/blog/tags/[tag].astro | 1 + .../src/scripts/home/ticker-mainnet.test.ts | 8 +- website/src/utils/blog-categories.test.ts | 107 +++++++++++++++ website/src/utils/blog-categories.ts | 54 ++++++++ website/src/utils/blog-posts.ts | 4 +- website/src/utils/blog-titles.test.ts | 3 +- 380 files changed, 885 insertions(+), 228 deletions(-) create mode 100644 website/src/constants/blog-categories.ts create mode 100644 website/src/utils/blog-categories.test.ts create mode 100644 website/src/utils/blog-categories.ts diff --git a/website/scripts/check-seo-meta.mjs b/website/scripts/check-seo-meta.mjs index b4ceba8a..7825b0a3 100644 --- a/website/scripts/check-seo-meta.mjs +++ b/website/scripts/check-seo-meta.mjs @@ -64,7 +64,9 @@ function checkLength(kind, locale, key, value, errors) { } function checkI18n(errors) { - for (const file of fs.readdirSync(i18nDir).filter((f) => f.endsWith(".json"))) { + for (const file of fs + .readdirSync(i18nDir) + .filter((f) => f.endsWith(".json"))) { const locale = path.basename(file, ".json"); const data = JSON.parse(fs.readFileSync(path.join(i18nDir, file), "utf8")); for (const [key, value] of walkJson(data)) { @@ -121,7 +123,9 @@ function checkBlogs(errors) { const raw = fs.readFileSync(file, "utf8"); const meta = parseFrontmatter(raw); if (!meta) { - errors.push(`[parse] ${path.relative(websiteRoot, file)}: missing title/description`); + errors.push( + `[parse] ${path.relative(websiteRoot, file)}: missing title/description`, + ); continue; } const rel = path.relative(websiteRoot, file); @@ -135,9 +139,16 @@ function checkDefaultMetadata(errors) { const raw = fs.readFileSync(file, "utf8"); const title = raw.match(/default:\s*"([^"]+)"/)?.[1]; const desc = raw.match(/const description =\s*\n?\s*"([^"]+)"/)?.[1]; - if (title) checkLength("title", "en-US", "default-metadata.title", title, errors); + if (title) + checkLength("title", "en-US", "default-metadata.title", title, errors); if (desc) - checkLength("description", "en-US", "default-metadata.description", desc, errors); + checkLength( + "description", + "en-US", + "default-metadata.description", + desc, + errors, + ); } const errors = []; diff --git a/website/src/components/features/blog/BlogList.tsx b/website/src/components/features/blog/BlogList.tsx index 0ca0e4b3..3f317bab 100644 --- a/website/src/components/features/blog/BlogList.tsx +++ b/website/src/components/features/blog/BlogList.tsx @@ -3,9 +3,21 @@ import Fuse from "fuse.js"; import { Search as SearchIcon } from "lucide-react"; import { useDebounceValue } from "usehooks-ts"; import { INPUT_DEBOUNCE_INTERVAL } from "@/constants/debounce-interval"; +import { + ALL_BLOG_CATEGORY, + filterPostsByCategory, + parseBlogCategoryFilter, + type BlogCategory, + type BlogCategoryFilter, +} from "@/utils/blog-categories"; +import { BLOG_CATEGORIES } from "@/constants/blog-categories"; const INITIAL_VISIBLE_COUNT = 6; const LOAD_MORE_COUNT = 6; +const CATEGORY_FILTERS: BlogCategoryFilter[] = [ + ALL_BLOG_CATEGORY, + ...BLOG_CATEGORIES, +]; interface Post { id: string; @@ -14,6 +26,7 @@ interface Post { title: string; description: string; pubDate: string; + category: BlogCategory; tags: string[]; heroImage?: string; heroAlt?: string; @@ -31,6 +44,8 @@ interface Props { featuredLabel: string; readLabel: string; tagsMap: Record; + categoriesMap: Record; + categoriesLabel: string; } function useCardEntrance(deps: unknown[]) { @@ -90,27 +105,59 @@ export const BlogList: React.FC = ({ featuredLabel, readLabel, tagsMap, + categoriesMap, + categoriesLabel, }) => { const [query, setQuery] = useState(""); const [debouncedQuery] = useDebounceValue(query, INPUT_DEBOUNCE_INTERVAL); + const [selectedCategory, setSelectedCategory] = + useState(ALL_BLOG_CATEGORY); const [visibleCount, setVisibleCount] = useState(INITIAL_VISIBLE_COUNT); const observerTarget = useRef(null); + useEffect(() => { + const applyCategoryFromUrl = () => { + const raw = new URLSearchParams(window.location.search).get("category"); + setSelectedCategory(parseBlogCategoryFilter(raw)); + }; + + applyCategoryFromUrl(); + window.addEventListener("popstate", applyCategoryFromUrl); + return () => window.removeEventListener("popstate", applyCategoryFromUrl); + }, []); + + const selectCategory = (category: BlogCategoryFilter) => { + setSelectedCategory(category); + const url = new URL(window.location.href); + if (category === ALL_BLOG_CATEGORY) { + url.searchParams.delete("category"); + } else { + url.searchParams.set("category", category); + } + window.history.replaceState(null, "", url); + }; + + const categoryFilteredPosts = useMemo( + () => filterPostsByCategory(posts, selectedCategory), + [posts, selectedCategory], + ); + const fuse = useMemo(() => { - return new Fuse(posts, { - keys: ["data.title", "data.description", "data.tags"], + return new Fuse(categoryFilteredPosts, { + keys: ["data.title", "data.description", "data.tags", "data.category"], }); - }, [posts]); + }, [categoryFilteredPosts]); const results = useMemo(() => { - if (!debouncedQuery) return posts; + if (!debouncedQuery) return categoryFilteredPosts; return fuse.search(debouncedQuery).map((result) => result.item); - }, [fuse, debouncedQuery, posts]); + }, [fuse, debouncedQuery, categoryFilteredPosts]); const featuredPost = useMemo(() => { if (debouncedQuery) return null; + if (selectedCategory !== ALL_BLOG_CATEGORY) return null; return posts.find((post) => post.data.featured) ?? posts[0] ?? null; - }, [posts, debouncedQuery]); + }, [posts, debouncedQuery, selectedCategory]); const displayedPosts = useMemo(() => { let filteredResults = results; @@ -122,7 +169,7 @@ export const BlogList: React.FC = ({ useEffect(() => { setVisibleCount(INITIAL_VISIBLE_COUNT); - }, [debouncedQuery]); + }, [debouncedQuery, selectedCategory]); useEffect(() => { const observer = new IntersectionObserver( @@ -155,16 +202,41 @@ export const BlogList: React.FC = ({ return (
- {/* Search */} -
- - setQuery(e.target.value)} - className="border-border text-content placeholder:text-content-35 focus:border-content-25 h-11 w-full border bg-transparent pr-4 pl-10 font-mono text-sm focus:outline-none" - /> +
+
+ {CATEGORY_FILTERS.map((category) => { + const isActive = selectedCategory === category; + return ( + + ); + })} +
+
+ + setQuery(e.target.value)} + className="border-border text-content placeholder:text-content-35 focus:border-content-25 h-11 w-full border bg-transparent pr-4 pl-10 font-mono text-sm focus:outline-none" + /> +
{/* Featured post */} @@ -200,6 +272,10 @@ export const BlogList: React.FC = ({ > {formatDate(featuredPost.data.pubDate)} + + {categoriesMap[featuredPost.data.category] || + featuredPost.data.category} + {featuredLabel} @@ -259,12 +335,17 @@ export const BlogList: React.FC = ({
)}
- +
+ + + {categoriesMap[post.data.category] || post.data.category} + +
{post.data.title} diff --git a/website/src/components/features/blog/BlogPost.astro b/website/src/components/features/blog/BlogPost.astro index 849e5f5b..0c0a518e 100644 --- a/website/src/components/features/blog/BlogPost.astro +++ b/website/src/components/features/blog/BlogPost.astro @@ -15,6 +15,7 @@ import { getPersonJsonLd } from "@/constants/person-jsonld"; import { organizationJsonLd } from "@/constants/default-jsonld"; import { resolveTwitterCreator } from "@/utils/resolve-blog-author"; import { blogMetaTitle } from "@/utils/blog-titles"; +import { blogCategoryListHref } from "@/utils/blog-categories"; import env from "@/config"; type Props = CollectionEntry<"blog">["data"] & { @@ -30,6 +31,7 @@ const { updatedDate, heroImage, heroAlt, + category, tags = [], author: authorId, relatedPosts = [], @@ -86,27 +88,17 @@ const jsonLd = JsonLdGraph({ >
{t("blog.post.back_to_blog")} - { - tags.length > 0 && ( -
- {tags.map((tag: string) => ( - - ))} -
- ) - } + + {t(`blog.categories.${category}`)} + { isUnpublishedPreview && ( @@ -160,6 +152,23 @@ const jsonLd = JsonLdGraph({

) } + + { + tags.length > 0 && ( +
+ {tags.map((tag: string) => ( + + ))} +
+ ) + }
@@ -207,6 +216,7 @@ const jsonLd = JsonLdGraph({ `/blog/${relatedPost.id.split("/").slice(1).join("/")}`, )} tags={relatedPost.data.tags} + category={relatedPost.data.category} heroImage={relatedPost.data.heroImage} heroAlt={relatedPost.data.heroAlt} featured={relatedPost.data.featured} diff --git a/website/src/components/features/blog/Card.astro b/website/src/components/features/blog/Card.astro index 7b4bc789..64c05d32 100644 --- a/website/src/components/features/blog/Card.astro +++ b/website/src/components/features/blog/Card.astro @@ -1,9 +1,6 @@ --- -import { - getLocaleFromUrl, - getLocalizedPath, - createTranslator, -} from "@/utils/i18n"; +import { getLocaleFromUrl, createTranslator } from "@/utils/i18n"; +import type { BlogCategory } from "@/utils/blog-categories"; interface Props { title: string; @@ -11,6 +8,7 @@ interface Props { pubDate: Date; href: string; tags?: string[]; + category?: BlogCategory; heroImage?: string; heroAlt?: string; featured?: boolean; @@ -23,6 +21,7 @@ const { pubDate, href, tags = [], + category, heroImage, heroAlt, featured, @@ -80,12 +79,21 @@ const formattedDate = pubDate horizontal ? "px-5 py-5" : "px-5 pt-6 pb-7", ]} > - +
+ + { + category && ( + + {t(`blog.categories.${category}`)} + + ) + } +
+