A modern static blog built with Astro. Markdown content, LaTeX math, full-text search, dark mode, RSS, multi-language support, and a settings panel for personalized reading.
๐ Live Demo: blog.qwara.top
- ๐ Markdown โ Write posts in Markdown with Frontmatter
- ๐ LaTeX Math โ KaTeX integration for inline and display math
- ๐ Multi-language โ UI and content i18n (en / zh-CN / zh-TW / ja), toggleable
- ๐ Full-text Search โ Build-time search index, client-side Fuse.js instant search
- ๐ Dark Mode โ Light / Dark / System three modes
- โ๏ธ Settings Panel โ Font size, content width, font family, TOC visibility, language
- ๐ Table of Contents โ Sidebar TOC with scroll-spy highlighting
- โฑ Reading Time โ Auto-estimate for mixed CJK + English content
- ๐ท Tag System โ Tag categorization and tag cloud
- ๐ก RSS Feed โ Per-locale auto-generated RSS
- ๐บ Sitemap โ Auto-generated sitemap
- ๐ฑ Responsive โ Desktop and mobile friendly
- ๐ View Transitions โ Built-in page transition animations
# 1. Clone
git clone https://github.com/Qwara-chan/TerseBlog.git
cd TerseBlog
# 2. Install
npm install
# 3. Dev server
npm run dev
# 4. Build
npm run build
# 5. Preview build
npm run preview
# 6. Type check
npm run checki18n is disabled by default. You can toggle it at any time:
npm run i18n # View current status
npm run i18n:on # Enable i18n
npm run i18n:off # Disable i18n (single-locale mode)When enabled, 4 locales are served under URL prefixes: /en/ /zh-CN/ /zh-TW/ /ja/. The root path / redirects to /zh-CN/.
When disabled, the build emits a single-locale site: no locale-prefixed routes, no per-locale RSS/search-index, no i18n middleware, and the dictionary JSON files are not loaded by any client bundle.
The toggle script physically moves the inactive page tree out of src/pages/ into src/pages/_i18n_disabled/ (along with thin wrappers that import the canonical page implementation in src/components/page-pages/), so Astro only builds the active tree.
Post translations live in src/posts/<locale>/. If a translation is missing for a given locale, it falls back to the default locale (zh-CN).
UI text (search, settings, pagination, etc.) is translated via dictionary files in src/i18n/dictionaries/. The runtime is loaded only when a locale change happens (lazy import('@/i18n/runtime')).
See docs/configuration.md for details.
TerseBlog/
โโโ astro.config.ts
โโโ tsconfig.json
โโโ package.json
โโโ src/
โ โโโ config.ts # Site configuration (including i18n)
โ โโโ i18n/
โ โ โโโ config.ts # Locale resolver, path helpers
โ โ โโโ load.ts # Server-side dictionary loader
โ โ โโโ runtime.ts # Client-side i18n runtime (lazy)
โ โ โโโ lookup.ts # Shared getNested/format helpers
โ โ โโโ dictionaries/ # UI text translations
โ โ โโโ en.json
โ โ โโโ zh-CN.json
โ โ โโโ zh-TW.json
โ โ โโโ ja.json
โ โโโ layouts/
โ โ โโโ BaseLayout.astro # Base layout
โ โโโ components/
โ โ โโโ page-pages/ # Canonical page implementations
โ โ โ โโโ Index.astro
โ โ โ โโโ About.astro
โ โ โ โโโ Archive.astro
โ โ โ โโโ NotFound.astro
โ โ โ โโโ Post.astro
โ โ โ โโโ Tag.astro
โ โ โ โโโ Tags.astro
โ โ โโโ Header.astro
โ โ โโโ Footer.astro
โ โ โโโ PostCard.astro
โ โ โโโ Pagination.astro
โ โ โโโ TOC.astro
โ โ โโโ Search.astro
โ โ โโโ SettingsPanel.astro
โ โ โโโ LangSwitcher.astro
โ โ โโโ BackToTop.astro
โ โ โโโ MobileMenu.astro
โ โ โโโ SortToggle.astro
โ โ โโโ DiffViewer.astro
โ โโโ pages/ # Active Astro routes
โ โ โโโ index.astro # Renders <IndexPage /> (flat or i18n redirect)
โ โ โโโ about.astro
โ โ โโโ archive.astro
โ โ โโโ 404.astro
โ โ โโโ posts/[...slug].astro
โ โ โโโ tags/{index,[tag]}.astro
โ โ โโโ rss.xml.ts
โ โ โโโ search-index.json.ts
โ โ _i18n_disabled/ # Inactive tree (managed by i18n-toggle.mjs)
โ โ โโโ root-index.astro # Used as the i18n-on root redirect
โ โ โโโ flat/ # Flat thin wrappers (stashed when i18n=on)
โ โ โโโ en/, zh-CN/, zh-TW/, ja/ # i18n thin wrappers (stashed when i18n=off)
โ โโโ lib/
โ โ โโโ content.ts # Content collection helpers + reading time
โ โ โโโ utils.ts # formatDate, isExternalUrl, groupPostsByYear
โ โ โโโ search.ts # Client-side Fuse.js search engine
โ โ โโโ build-feed.ts # Shared RSS / search-index builders
โ โ โโโ translate.ts # Shared withFallback() helper
โ โ โโโ rehype-external-links.ts
โ โ โโโ types.ts
โ โโโ styles/
โ โ โโโ global.css
โ โ โโโ interactivity.css
โ โโโ posts/ # Blog posts (locale subdirectories)
โ โโโ en/
โ โโโ zh-CN/
โ โโโ zh-TW/
โ โโโ ja/
โโโ public/ # Static assets
To avoid duplicating the home/post/tag pages 5ร (once for flat, once for each locale), the canonical implementation lives in src/components/page-pages/<Page>.astro and accepts an optional lang prop. Both the active flat route (e.g. src/pages/index.astro) and the i18n wrapper route (e.g. src/pages/_i18n_disabled/en/index.astro) are 5-line Astro files that render the canonical. getStaticPaths lives in the route file (Astro requires it on the route, not on a component).
- Go to Pages in Cloudflare Dashboard
- Create a project โ Connect to Git
- Configure build settings:
- Build command:
npm run build - Build output directory:
dist
- Build command:
- Save and Deploy
- Go to Settings โ Pages in your repo
- Select GitHub Actions as the Source
- Push to
mainโ GitHub Actions builds and deploys automatically
A pre-configured workflow is available at .github/workflows/deploy.yml.
If you created your repo from this template, you can pull in upstream updates at any time:
- Go to Actions โ Sync Upstream โ Run workflow
- A pull request will be created with the latest upstream changes
- Review conflicts (if any) and merge
| Technology | Purpose |
|---|---|
| Astro 5 | Static site generator |
| TypeScript | Type safety |
| KaTeX | LaTeX math rendering |
| Fuse.js | Fuzzy search |
| rehype-katex | Markdown math |
| @astrojs/sitemap | Sitemap |
| @astrojs/rss | RSS feed |
ๅบไบ Astro ๆๅปบ็็ฐไปฃๅ้ๆๅๅฎข๏ผๆฏๆ MarkdownใLaTeX ๆฐๅญฆๅ ฌๅผใๅ จๆๆ็ดขใๆ่ฒๆจกๅผใRSS ่ฎข้ ใๅค่ฏญ่จๅๆข๏ผๅ ็ฝฎ่ฎพ็ฝฎ้ขๆฟๅฏไธชๆงๅ่ฐๆดๅญไฝๅคงๅฐใๅ ๅฎนๅฎฝๅบฆ็ญใ
๐ ็คบไพ็ซ็น: blog.qwara.top
- ๐ Markdown โ Markdown ็ผๅๆ็ซ ๏ผๆฏๆ Frontmatter
- ๐ LaTeX ๆฐๅญฆๅ ฌๅผ โ KaTeX ้ๆ๏ผ่กๅ ๅๅฑ็คบๅ ฌๅผ
- ๐ ๅค่ฏญ่จ โ ็้ขไธๆ็ซ i18n๏ผen / zh-CN / zh-TW / ja๏ผ๏ผๅฏ้ๆถๅผๅ ณ
- ๐ ๅ จๆๆ็ดข โ ๆๅปบๆถ็ๆ็ดขๅผ๏ผFuse.js ๅฎขๆท็ซฏๅณๆถๆ็ดข
- ๐ ๆ่ฒๆจกๅผ โ ไบฎ่ฒ/ๆ่ฒ/่ท้็ณป็ปไธ็งๆจกๅผ
- โ๏ธ ่ฎพ็ฝฎ้ขๆฟ โ ๅญไฝๅคงๅฐใๅ ๅฎนๅฎฝๅบฆใๅญไฝๅๆขใTOC ๆพ้ใ่ฏญ่จ
- ๐ ็ฎๅฝๅฏผ่ช โ ไพง่พน็ฎๅฝ๏ผๆปๅจ้ซไบฎ
- โฑ ้ ่ฏปๆถ้ฟ โ ไธญ่ฑๆๆททๅๅ ๅฎน่ชๅจไผฐ็ฎ
- ๐ท ๆ ็ญพ็ณป็ป โ ๆ็ซ ๆ ็ญพๅ็ฑปไธๆ ็ญพไบ
- ๐ก RSS ่ฎข้ โ ๆฏ็ง่ฏญ่จ็ฌ็ซ RSS Feed
- ๐ฑ ๅๅบๅผ่ฎพ่ฎก โ ้้ ๆก้ขๅ็งปๅจ็ซฏ
- ๐ ้กต้ข่ฟๆธกๅจ็ป โ ๅ ็ฝฎ View Transitions API
# ไธญๅฝๅคง้็จๆทๅฏๅ
้
็ฝฎ npm ้ๅ
npm config set registry https://registry.npmmirror.com
# 1. ๅ
้
git clone https://github.com/Qwara-chan/TerseBlog.git
cd TerseBlog
# 2. ๅฎ่ฃ
ไพ่ต
npm install
# 3. ๅฏๅจๅผๅๆๅกๅจ
npm run dev
# 4. ๆๅปบ
npm run build
# 5. ้ข่งๆๅปบ็ปๆ
npm run preview
# 6. ็ฑปๅๆฃๆฅ
npm run checknpm run i18n # ๆฅ็ๅฝๅ็ถๆ
npm run i18n:on # ๅผๅฏๅค่ฏญ่จ
npm run i18n:off # ๅ
ณ้ญๅค่ฏญ่จ๏ผๅ่ฏญ่จๆจกๅผ๏ผๅผๅฏๅ 4 ็ง่ฏญ่จๅๅซ็จ URL ๅ็ผ่ฎฟ้ฎ๏ผ/en/ /zh-CN/ /zh-TW/ /ja/ใๆ น่ทฏๅพ / ่ชๅจๆขๆตๆต่งๅจ่ฏญ่จ่ทณ่ฝฌใ
ๆ็ซ ็ฟป่ฏๆพๅจ src/posts/<่ฏญ็ง>/ ็ฎๅฝไธใ็ผบ่ฏๆถ่ชๅจๅ้ๅฐ้ป่ฎค่ฏญ็ง๏ผzh-CN๏ผใ
่ฏฆ่งไธๆน Deployment ็ซ ่ใ
ๅฆๆ็จๆญคๆจกๆฟๅๅปบไบไปๅบ๏ผไฝ ๅฏไปฅ้ๆถๆๅไธๆธธๆดๆฐ๏ผ
- ่ฟๅ ฅ Actions โ Sync Upstream โ Run workflow
- ไผ่ชๅจๅๅปบไธไธชๅ ๅซไธๆธธๆๆฐๅๆด็ Pull Request
- ๅฎกๆฅๅนถๅค็ๅๅๅนถๅณๅฏ
| ๆๆฏ | ็จ้ |
|---|---|
| Astro 5 | ้ๆ็ซ็น็ๆๅจ |
| TypeScript | ็ฑปๅๅฎๅ จ |
| KaTeX | LaTeX ๆฐๅญฆๅ ฌๅผๆธฒๆ |
| Fuse.js | ๆจก็ณๆ็ดข |
| @astrojs/sitemap | ็ซ็นๅฐๅพ |
| @astrojs/rss | RSS ่ฎข้ |