Skip to content

Qwara-chan/TerseBlog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

23 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

TerseBlog

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


ไธญๆ–‡็‰ˆๆœฌ

Features

  • ๐Ÿ“ 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

Quick Start

# 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 check

Multi-language (i18n)

i18n 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.

Project Structure

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

Page architecture

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).

Deployment

Cloudflare Pages

  1. Go to Pages in Cloudflare Dashboard
  2. Create a project โ†’ Connect to Git
  3. Configure build settings:
    • Build command: npm run build
    • Build output directory: dist
  4. Save and Deploy

GitHub Pages

  1. Go to Settings โ†’ Pages in your repo
  2. Select GitHub Actions as the Source
  3. Push to main โ€” GitHub Actions builds and deploys automatically

A pre-configured workflow is available at .github/workflows/deploy.yml.

Keeping in Sync

If you created your repo from this template, you can pull in upstream updates at any time:

  1. Go to Actions โ†’ Sync Upstream โ†’ Run workflow
  2. A pull request will be created with the latest upstream changes
  3. Review conflicts (if any) and merge

Tech Stack

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

License

CC BY-NC-SA 4.0


ไธญๆ–‡

TerseBlog

ๅŸบไบŽ 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 check

ๅคš่ฏญ่จ€ๅผ€ๅ…ณ

npm run i18n          # ๆŸฅ็œ‹ๅฝ“ๅ‰็Šถๆ€
npm run i18n:on       # ๅผ€ๅฏๅคš่ฏญ่จ€
npm run i18n:off      # ๅ…ณ้—ญๅคš่ฏญ่จ€๏ผˆๅ•่ฏญ่จ€ๆจกๅผ๏ผ‰

ๅผ€ๅฏๅŽ 4 ็ง่ฏญ่จ€ๅˆ†ๅˆซ็”จ URL ๅ‰็ผ€่ฎฟ้—ฎ๏ผš/en/ /zh-CN/ /zh-TW/ /ja/ใ€‚ๆ น่ทฏๅพ„ / ่‡ชๅŠจๆŽขๆต‹ๆต่งˆๅ™จ่ฏญ่จ€่ทณ่ฝฌใ€‚

ๆ–‡็ซ ็ฟป่ฏ‘ๆ”พๅœจ src/posts/<่ฏญ็ง>/ ็›ฎๅฝ•ไธ‹ใ€‚็ผบ่ฏ‘ๆ—ถ่‡ชๅŠจๅ›ž้€€ๅˆฐ้ป˜่ฎค่ฏญ็ง๏ผˆzh-CN๏ผ‰ใ€‚

้ƒจ็ฝฒ

่ฏฆ่งไธŠๆ–น Deployment ็ซ ่Š‚ใ€‚

ๅŒๆญฅไธŠๆธธๆ›ดๆ–ฐ

ๅฆ‚ๆžœ็”จๆญคๆจกๆฟๅˆ›ๅปบไบ†ไป“ๅบ“๏ผŒไฝ ๅฏไปฅ้šๆ—ถๆ‹‰ๅ–ไธŠๆธธๆ›ดๆ–ฐ๏ผš

  1. ่ฟ›ๅ…ฅ Actions โ†’ Sync Upstream โ†’ Run workflow
  2. ไผš่‡ชๅŠจๅˆ›ๅปบไธ€ไธชๅŒ…ๅซไธŠๆธธๆœ€ๆ–ฐๅ˜ๆ›ด็š„ Pull Request
  3. ๅฎกๆŸฅๅนถๅค„็†ๅŽๅˆๅนถๅณๅฏ

ๆŠ€ๆœฏๆ ˆ

ๆŠ€ๆœฏ ็”จ้€”
Astro 5 ้™ๆ€็ซ™็‚น็”Ÿๆˆๅ™จ
TypeScript ็ฑปๅž‹ๅฎ‰ๅ…จ
KaTeX LaTeX ๆ•ฐๅญฆๅ…ฌๅผๆธฒๆŸ“
Fuse.js ๆจก็ณŠๆœ็ดข
@astrojs/sitemap ็ซ™็‚นๅœฐๅ›พ
@astrojs/rss RSS ่ฎข้˜…

่ฎธๅฏ่ฏ

CC BY-NC-SA 4.0

About

A minimalist black & white Astro blog

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors