├── .github └── workflows │ └── pages.yaml ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── astro.config.mjs ├── biome.json ├── bun.lockb ├── giscus.example.json ├── package.json ├── postcss.config.mjs ├── public ├── apple-touch-icon.png └── favicon.ico ├── screenshot-dark.png ├── screenshot-light.png ├── src ├── assets │ ├── LXGWWenKaiGBScreen.ttf │ ├── logo.svg │ ├── mountains-8314422.svg │ ├── og-logo.png │ └── undraw_dog_c7i6.svg ├── components │ ├── BackToTop.astro │ ├── Comments.astro │ ├── Footer.astro │ ├── Header.astro │ ├── Nav.astro │ ├── Paginator.astro │ ├── PostCard.astro │ └── ThemeToggler.astro ├── config.ts ├── content │ ├── attachments │ │ └── 100.jpg │ ├── config.ts │ └── posts │ │ ├── chinese-preview.md │ │ ├── english-preview.md │ │ ├── getting-started.md │ │ ├── hidden.md │ │ ├── japanese-preview.md │ │ └── theme-preview.md ├── env.d.ts ├── layouts │ ├── BaseLayout.astro │ ├── MarkdownLayout.astro │ └── ScaffoldLayout.astro ├── pages │ ├── about.astro │ ├── archives │ │ └── [...page].astro │ ├── blog │ │ └── [...page].astro │ ├── index.astro │ ├── og.png.ts │ ├── posts │ │ ├── [...slug].astro │ │ └── [...slug].png.ts │ ├── robots.txt.ts │ ├── rss.xml.ts │ ├── search.astro │ └── tags │ │ ├── [tag] │ │ └── [page].astro │ │ └── index.astro ├── store.ts └── utils │ ├── collection.ts │ ├── openGraphImage.tsx │ ├── post.ts │ ├── remark.js │ ├── shiki.tsx │ └── shikiCopyButton.tsx ├── tailwind.config.mjs ├── tsconfig.json └── vercel.json /.github/workflows/pages.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy to Github Pages 2 | 3 | on: 4 | # Runs on pushes targeting the default branch 5 | push: 6 | branches: 7 | - main 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: 'pages' 22 | cancel-in-progress: false 23 | 24 | env: 25 | BUILD_PATH: '.' # default value when not using subfolders 26 | # BUILD_PATH: subfolder 27 | 28 | jobs: 29 | build: 30 | name: Build 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: Checkout 34 | uses: actions/checkout@v4 35 | 36 | - uses: oven-sh/setup-bun@v1 37 | with: 38 | bun-version: latest 39 | 40 | - name: Setup Pages 41 | id: pages 42 | uses: actions/configure-pages@v4 43 | 44 | - name: Build with Astro 45 | run: | 46 | bun install && bun run build \ 47 | --site "${{ steps.pages.outputs.origin }}" \ 48 | --base "${{ steps.pages.outputs.base_path }}" 49 | working-directory: ${{ env.BUILD_PATH }} 50 | 51 | - name: Upload artifact 52 | uses: actions/upload-pages-artifact@v3 53 | with: 54 | path: ${{ env.BUILD_PATH }}/dist 55 | 56 | deploy: 57 | environment: 58 | name: github-pages 59 | url: ${{ steps.deployment.outputs.page_url }} 60 | needs: build 61 | runs-on: ubuntu-latest 62 | name: Deploy 63 | steps: 64 | - name: Deploy to GitHub Pages 65 | id: deployment 66 | uses: actions/deploy-pages@v4 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | .output/ 4 | .astro/ 5 | .vscode/ 6 | .wrangler/ 7 | 8 | # dependencies 9 | node_modules/ 10 | 11 | # logs 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | pnpm-debug.log* 16 | 17 | 18 | # environment variables 19 | .env 20 | .env.production 21 | 22 | # macOS-specific files 23 | .DS_Store 24 | 25 | # Vitest 26 | __coverage__/ 27 | 28 | # Vercel output 29 | .vercel 30 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # Expose Astro dependencies for `pnpm` users 2 | shamefully-hoist=true 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ladit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Astro](https://astro.build) Blog Zozo 2 | 3 | An opinionated and neat blog template built with [Astro](https://astro.build). The theme originates from [hugo-theme-zozo](https://github.com/varkai/hugo-theme-zozo). 4 | 5 | Check out the ✨ [Live Demo](https://astro-blog-zozo.pages.dev/) ✨. 6 | 7 | 8 | 9 | 10 | ## Features 11 | 12 | - Fast and light weight. Lighthouse 4 \* 100/100 on desktop. 1 point decreased on mobile. 13 | - [Astro 4](https://astro.build) 14 | - No other UI frameworks integrated for better performance 15 | - mobile-friendly responsive design with [Tailwind CSS](https://tailwindcss.com/) 16 | - Light / dark theme derived from [hugo-theme-zozo](https://github.com/varkai/hugo-theme-zozo) 17 | - Markdown post support with emoji shortcode, katex 18 | - static search with [Pagefind](https://pagefind.app) 19 | - comment system powered by [giscus](https://github.com/giscus/giscus) 20 | - SEO-friendly setup with canonical URLs and OpenGraph data 21 | - RSS generation 22 | - Sitemap.xml generation 23 | - dynamic open graph image generation 24 | - Hide unwanted elements through [config](./src/config.ts) 25 | 26 | ## Getting Started 27 | 28 | See [this post](https://astro-blog-zozo.pages.dev/posts/getting-started) for using this template. 29 | 30 | ## Todo 31 | 32 | - `@shikijs/transformers` require custom style 33 | - i18n 34 | 35 | ## Contribute 36 | 37 | Issues and PRs are welcome! 38 | 39 | ## License 40 | 41 | [MIT](./LICENSE) 42 | 43 | ## Acknowledgements 44 | 45 | - [varkai/hugo-theme-zozo](https://github.com/varkai/hugo-theme-zozo) 46 | - [Charca/astro-blog-template](https://github.com/Charca/astro-blog-template) 47 | - [satnaing/astro-paper](https://github.com/satnaing/astro-paper) 48 | - [ricora/alg.tus-ricora.com](https://github.com/ricora/alg.tus-ricora.com) 49 | - [one-aalam/astro-ink](https://github.com/one-aalam/astro-ink) 50 | -------------------------------------------------------------------------------- /astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { rehypeHeadingIds } from '@astrojs/markdown-remark'; 2 | import sitemap from '@astrojs/sitemap'; 3 | import tailwind from '@astrojs/tailwind'; 4 | import toc from '@jsdevtools/rehype-toc'; 5 | import compress from '@playform/compress'; 6 | import { 7 | transformerMetaHighlight, 8 | transformerMetaWordHighlight, 9 | transformerNotationDiff, 10 | transformerNotationErrorLevel, 11 | transformerNotationFocus, 12 | transformerNotationHighlight, 13 | transformerNotationWordHighlight, 14 | } from '@shikijs/transformers'; 15 | import icon from 'astro-icon'; 16 | import pagefind from 'astro-pagefind'; 17 | import { defineConfig } from 'astro/config'; 18 | import { rehypeAccessibleEmojis } from 'rehype-accessible-emojis'; 19 | import rehypeExternalLinks from 'rehype-external-links'; 20 | import rehypeKatax from 'rehype-katex'; 21 | import emoji from 'remark-emoji'; 22 | import remarkMath from 'remark-math'; 23 | import { Site } from './src/config'; 24 | import { getMarkdownEntries } from './src/utils/post'; 25 | import { remarkPostTime, remarkReadingTime } from './src/utils/remark'; 26 | import { transformerEnhanser } from './src/utils/shiki'; 27 | 28 | const excludeSitemapFiles = (await getMarkdownEntries()).filter( 29 | (entry) => entry.file.data.hidden, 30 | ); 31 | 32 | // https://astro.build/config 33 | export default defineConfig({ 34 | site: Site, 35 | build: { 36 | format: 'file', 37 | }, 38 | integrations: [ 39 | tailwind({ 40 | applyBaseStyles: false, 41 | }), 42 | icon({ 43 | iconDir: 'src/assets', 44 | }), 45 | pagefind(), 46 | sitemap({ 47 | filter: (page) => 48 | !excludeSitemapFiles.some((entry) => entry.url.href === page), 49 | }), 50 | compress({ 51 | CSS: { 52 | csso: false, 53 | lightningcss: { 54 | minify: true, 55 | }, 56 | }, 57 | HTML: true, 58 | Image: true, 59 | JavaScript: true, 60 | SVG: true, 61 | Parser: { 62 | CSS: 'lightningcss', 63 | }, 64 | }), 65 | ], 66 | prefetch: { 67 | defaultStrategy: 'tap', 68 | }, 69 | markdown: { 70 | shikiConfig: { 71 | themes: { 72 | light: 'vitesse-light', 73 | dark: 'vitesse-dark', 74 | }, 75 | // disable word wrap for horizontal scrolling 76 | wrap: false, 77 | // Add custom transformers: https://shiki.style/guide/transformers 78 | // Find common transformers: https://shiki.style/packages/transformers 79 | transformers: [ 80 | transformerMetaHighlight(), 81 | transformerMetaWordHighlight(), 82 | transformerNotationDiff(), 83 | transformerNotationErrorLevel(), 84 | transformerNotationFocus(), 85 | transformerNotationHighlight(), 86 | transformerNotationWordHighlight(), 87 | transformerEnhanser(), 88 | ], 89 | }, 90 | remarkPlugins: [emoji, remarkMath, remarkPostTime, remarkReadingTime], 91 | rehypePlugins: [ 92 | rehypeAccessibleEmojis, 93 | [rehypeExternalLinks, { target: '_blank' }], 94 | rehypeHeadingIds, 95 | toc, 96 | rehypeKatax, 97 | ], 98 | }, 99 | }); 100 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", 3 | "files": { 4 | "ignore": [".wrangler", ".astro", "dist"] 5 | }, 6 | "organizeImports": { 7 | "enabled": true 8 | }, 9 | "linter": { 10 | "enabled": true, 11 | "rules": { 12 | "recommended": true 13 | } 14 | }, 15 | "javascript": { 16 | "formatter": { 17 | "quoteStyle": "single" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladit/astro-blog-zozo/9b8e8cadc6c7ea2061291afff6494956fe736f22/bun.lockb -------------------------------------------------------------------------------- /giscus.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "origins": ["https://astro-blog-zozo.pages.dev"] 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astro-blog-zozo", 3 | "version": "0.0.3", 4 | "author": { 5 | "name": "Ladit", 6 | "email": "i@ladit.me", 7 | "url": "https://github.com/ladit" 8 | }, 9 | "homepage": "https://github.com/ladit/astro-blog-zozo", 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/ladit/astro-blog-zozo.git" 13 | }, 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/ladit/astro-blog-zozo/issues" 17 | }, 18 | "type": "module", 19 | "engines": { 20 | "bun": ">=1.1.34" 21 | }, 22 | "scripts": { 23 | "dev": "astro dev", 24 | "check": "astro check", 25 | "build": "astro check && bunx @biomejs/biome check --write . && astro build", 26 | "preview": "astro preview", 27 | "preview:wrangler": "wrangler pages dev ./dist", 28 | "astro": "astro", 29 | "lint": "bunx @biomejs/biome check --write ." 30 | }, 31 | "dependencies": { 32 | "@astrojs/markdown-remark": "^5.3.0", 33 | "@astrojs/rss": "^4.0.9", 34 | "@astrojs/sitemap": "^3.2.1", 35 | "@astrojs/tailwind": "^5.1.2", 36 | "@iconify-json/ri": "^1.2.3", 37 | "@jsdevtools/rehype-toc": "^3.0.2", 38 | "@nanostores/persistent": "^0.10.2", 39 | "@playform/compress": "^0.1.6", 40 | "@shikijs/transformers": "^1.22.2", 41 | "@tailwindcss/typography": "^0.5.15", 42 | "astro": "^4.16.12", 43 | "astro-icon": "^1.1.2", 44 | "astro-pagefind": "^1.6.0", 45 | "katex": "^0.16.11", 46 | "lxgw-wenkai-screen-web": "^1.501.0", 47 | "mdast-util-to-string": "^4.0.0", 48 | "node-gyp": "^10.2.0", 49 | "react": "^18.3.1", 50 | "reading-time": "^1.5.0", 51 | "rehype-accessible-emojis": "^0.3.2", 52 | "rehype-external-links": "^3.0.0", 53 | "rehype-katex": "^7.0.1", 54 | "remark-emoji": "^5.0.1", 55 | "remark-math": "^6.0.0", 56 | "satori": "^0.11.3", 57 | "sharp": "^0.33.5", 58 | "tailwind-scrollbar": "^3.1.0", 59 | "tailwindcss": "^3.4.14" 60 | }, 61 | "devDependencies": { 62 | "@astrojs/check": "^0.9.4", 63 | "@biomejs/biome": "1.9.4", 64 | "@types/bun": "^1.1.13", 65 | "@types/react": "^18.3.12", 66 | "globals": "^15.12.0", 67 | "typescript": "^5.6.3" 68 | }, 69 | "trustedDependencies": ["@biomejs/biome", "esbuild", "sharp"] 70 | } 71 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** @type {import("postcss-load-config").Config} */ 4 | export default { 5 | plugins: { 6 | 'postcss-import': {}, 7 | 'tailwindcss/nesting': {}, 8 | tailwindcss: {}, 9 | autoprefixer: {}, 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladit/astro-blog-zozo/9b8e8cadc6c7ea2061291afff6494956fe736f22/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladit/astro-blog-zozo/9b8e8cadc6c7ea2061291afff6494956fe736f22/public/favicon.ico -------------------------------------------------------------------------------- /screenshot-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladit/astro-blog-zozo/9b8e8cadc6c7ea2061291afff6494956fe736f22/screenshot-dark.png -------------------------------------------------------------------------------- /screenshot-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladit/astro-blog-zozo/9b8e8cadc6c7ea2061291afff6494956fe736f22/screenshot-light.png -------------------------------------------------------------------------------- /src/assets/LXGWWenKaiGBScreen.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladit/astro-blog-zozo/9b8e8cadc6c7ea2061291afff6494956fe736f22/src/assets/LXGWWenKaiGBScreen.ttf -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 5 | 6 | 13 | 24 | 25 | 26 | 37 | 42 | 43 | -------------------------------------------------------------------------------- /src/assets/og-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladit/astro-blog-zozo/9b8e8cadc6c7ea2061291afff6494956fe736f22/src/assets/og-logo.png -------------------------------------------------------------------------------- /src/assets/undraw_dog_c7i6.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/BackToTop.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Icon } from 'astro-icon/components'; 3 | --- 4 | 5 | 25 | 26 | 49 | -------------------------------------------------------------------------------- /src/components/Comments.astro: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 68 | -------------------------------------------------------------------------------- /src/components/Footer.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { FooterDescription, Hide } from '~/config'; 3 | --- 4 | 5 | 23 | -------------------------------------------------------------------------------- /src/components/Header.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import fs from 'node:fs/promises'; 3 | import { Icon } from 'astro-icon/components'; 4 | import { Hide, SiteDescription, SiteTitle, Socials } from '~/config'; 5 | import Nav from './Nav.astro'; 6 | import ThemeToggler from './ThemeToggler.astro'; 7 | 8 | interface Props { 9 | currentNav?: string; 10 | } 11 | const { currentNav } = Astro.props; 12 | 13 | const customLogo = (await fs.stat('./src/assets/logo.svg')).isFile(); 14 | 15 | const socialConfig: Record> = { 16 | bilibili: { icon: 'ri:bilibili-fill' }, 17 | dingding: { icon: 'ri:dingding-fill' }, 18 | discord: { icon: 'ri:discord-fill' }, 19 | douban: { icon: 'ri:douban-fill' }, 20 | dribbble: { icon: 'ri:dribbble-fill' }, 21 | facebook: { icon: 'ri:facebook-box-fill' }, 22 | flickr: { icon: 'ri:flickr-fill' }, 23 | github: { icon: 'ri:github-fill' }, 24 | gitlab: { icon: 'ri:gitlab-fill' }, 25 | google: { icon: 'ri:google-fill' }, 26 | instagram: { icon: 'ri:instagram-fill' }, 27 | kakaoTalk: { icon: 'ri:kakao-talk-fill' }, 28 | line: { icon: 'ri:line-fill' }, 29 | linkedin: { icon: 'ri:linkedin-box-fill' }, 30 | mail: { icon: 'ri:mail-fill' }, 31 | mastodon: { icon: 'ri:mastodon-fill' }, 32 | medium: { icon: 'ri:medium-fill' }, 33 | messenger: { icon: 'ri:messenger-fill' }, 34 | miniProgram: { icon: 'ri:mini-program-fill' }, 35 | neteaseCloudMusic: { icon: 'ri:netease-cloud-music-fill' }, 36 | notion: { icon: 'ri:notion-fill' }, 37 | patreon: { icon: 'ri:patreon-fill' }, 38 | paypal: { icon: 'ri:paypal-fill' }, 39 | pinterest: { icon: 'ri:pinterest-fill' }, 40 | playstation: { icon: 'ri:playstation-fill' }, 41 | productHunt: { icon: 'ri:product-hunt-fill' }, 42 | qq: { icon: 'ri:qq-fill' }, 43 | reddit: { icon: 'ri:reddit-fill' }, 44 | rss: { icon: 'ri:rss-fill', label: 'the RSS link' }, 45 | skype: { icon: 'ri:skype-fill' }, 46 | slack: { icon: 'ri:slack-fill' }, 47 | snapchat: { icon: 'ri:snapchat-fill' }, 48 | soundcloud: { icon: 'ri:soundcloud-fill' }, 49 | spotify: { icon: 'ri:spotify-fill' }, 50 | stackOverflow: { icon: 'ri:stack-overflow-fill' }, 51 | steam: { icon: 'ri:steam-fill' }, 52 | switch: { icon: 'ri:switch-fill' }, 53 | telegram: { icon: 'ri:telegram-fill' }, 54 | threads: { icon: 'ri:threads-fill' }, 55 | tiktok: { icon: 'ri:tiktok-fill' }, 56 | trello: { icon: 'ri:trello-fill' }, 57 | tumblr: { icon: 'ri:tumblr-fill' }, 58 | twitch: { icon: 'ri:twitch-fill' }, 59 | x: { icon: 'ri:twitter-x-fill' }, 60 | wechat: { icon: 'ri:wechat-fill' }, 61 | weibo: { icon: 'ri:weibo-fill' }, 62 | whatsapp: { icon: 'ri:whatsapp-fill' }, 63 | xbox: { icon: 'ri:xbox-fill' }, 64 | youtube: { icon: 'ri:youtube-fill' }, 65 | zhihu: { icon: 'ri:zhihu-fill' }, 66 | }; 67 | 68 | const socials: Record[] = []; 69 | for (const platform in Socials) { 70 | if (Socials[platform].url && socialConfig[platform]) { 71 | socials.push({ platform, ...Socials[platform], ...socialConfig[platform] }); 72 | } 73 | } 74 | --- 75 | 76 |
77 | { 78 | !Hide.includes('logo') ? ( 79 | 91 | ) : ( 92 |
93 | ) 94 | } 95 | 106 | 137 |
138 | 139 | 155 | -------------------------------------------------------------------------------- /src/components/Nav.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | currentNav?: string; 4 | } 5 | const { currentNav } = Astro.props; 6 | 7 | const navs = [ 8 | { name: 'Home', href: '/' }, 9 | { name: 'Blog', href: '/blog' }, 10 | { name: 'Tags', href: '/tags' }, 11 | { name: 'Archives', href: '/archives' }, 12 | { name: 'About', href: '/about' }, 13 | ]; 14 | --- 15 | 16 | 35 | -------------------------------------------------------------------------------- /src/components/Paginator.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import type { Page } from 'astro'; 3 | import { Icon } from 'astro-icon/components'; 4 | 5 | type Props = Page; 6 | const page = Astro.props; 7 | --- 8 | 9 |
10 | { 11 | page.url.prev ? ( 12 | 13 | 14 | 15 | ) : null 16 | } 17 | { 18 | page.url.next ? ( 19 | 20 | 21 | 22 | ) : null 23 | } 24 |
25 | -------------------------------------------------------------------------------- /src/components/PostCard.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Picture } from 'astro:assets'; 3 | import type { CollectionEntry } from 'astro:content'; 4 | 5 | type Props = CollectionEntry<'posts'>; 6 | const post = Astro.props; 7 | let date = post.data.lastmod; 8 | if (post.data.hide?.includes('lastmod') && post.data.date) { 9 | date = post.data.date; 10 | if (post.data.hide?.includes('date')) { 11 | date = undefined; 12 | } 13 | } 14 | --- 15 | 16 |
17 |
18 |

19 | 20 | {post.data.title} 21 | 22 |

23 |

{post.data.description}

24 |
25 | { 26 | date && ( 27 | 30 | ) 31 | } 32 | { 33 | (!post.data.hide || !post.data.hide.includes('tags')) && 34 | post.data.tags?.map((tag) => ( 35 | 40 | {tag} 41 | 42 | )) 43 | } 44 |
45 |
46 | { 47 | post.data.image && ( 48 | 54 | ) 55 | } 56 |
57 |
58 | -------------------------------------------------------------------------------- /src/components/ThemeToggler.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Icon } from 'astro-icon/components'; 3 | 4 | const themes = [ 5 | { name: 'os-default', icon: 'ri:contrast-fill' }, 6 | { name: 'light', icon: 'ri:sun-fill' }, 7 | { name: 'dark', icon: 'ri:moon-line' }, 8 | ]; 9 | --- 10 | 11 |
12 | { 13 | themes.map((theme) => ( 14 | 23 | )) 24 | } 25 |
26 | 57 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import type { PostHideElements } from '~/content/config'; 2 | 3 | export const Site = 'https://astro-blog-zozo.pages.dev'; 4 | export const SiteLanguage = 'zh'; 5 | export const SiteTitle = '无尽光芒'; 6 | export const SiteDescription = '踏歌长行,梦想永在'; 7 | export const FooterDescription = '肆意闪耀'; 8 | export const AdminName = 'Ladit'; 9 | export const PageSize = 15; 10 | 11 | // socialPlatform => userName 12 | // check components/Header.astro socialConfig for more info 13 | export const Socials: Record> = { 14 | mail: { url: 'mailto:i@ladit.me' }, 15 | github: { url: 'https://github.com/ladit/astro-blog-zozo' }, 16 | x: { url: 'https://x.com/userName' }, 17 | mastodon: { url: 'https://mastodon.social/@userName' }, 18 | facebook: { url: 'https://facebook.com/userName' }, 19 | instagram: { url: 'https://instagram.com/userName' }, 20 | telegram: { url: 'https://t.me/@userName' }, 21 | youtube: { url: 'https://youtube.com/@userName' }, 22 | skype: { url: 'https://skype.com/userName' }, 23 | slack: { url: 'https://slack.com/userName' }, 24 | messenger: { url: 'https://messenger.com/userName' }, 25 | whatsapp: { url: 'https://whatsapp.com/userName' }, 26 | snapchat: { url: 'https://snapchat.com/userName' }, 27 | line: { url: 'https://line.com/userName' }, 28 | twitch: { url: 'https://twitch.com/userName' }, 29 | weibo: { url: 'https://weibo.com/userName' }, 30 | bilibili: { url: 'https://space.bilibili.com/userName' }, 31 | dingding: { url: 'https://dingtalk.com/userName' }, 32 | zhihu: { url: 'https://zhihu.com/userName' }, 33 | douban: { url: 'https://douban.com/userName' }, 34 | rss: { url: '/rss.xml' }, 35 | }; 36 | 37 | // doc: https://giscus.app 38 | // data-theme is auto changed between noborder_light / noborder_gray 39 | export const GiscusConfig: Record = { 40 | 'data-repo': 'ladit/astro-blog-zozo', 41 | 'data-repo-id': 'R_kgDOLgobXQ', 42 | 'data-category': 'Announcements', 43 | 'data-category-id': 'DIC_kwDOLgobXc4Cd_N6', 44 | 'data-mapping': 'pathname', 45 | 'data-strict': '0', 46 | 'data-reactions-enabled': '1', 47 | 'data-emit-metadata': '0', 48 | 'data-input-position': 'top', 49 | 'data-lang': 'zh-CN', 50 | 'data-loading': 'lazy', 51 | crossorigin: 'anonymous', 52 | async: '', 53 | }; 54 | 55 | export type HideElements = 56 | | PostHideElements 57 | | 'logo' 58 | | 'search' 59 | | 'themeToggler' 60 | | 'siteDescription' 61 | | 'footerDescription'; 62 | // Always hide elements from site 63 | export const Hide: HideElements[] = []; 64 | -------------------------------------------------------------------------------- /src/content/attachments/100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladit/astro-blog-zozo/9b8e8cadc6c7ea2061291afff6494956fe736f22/src/content/attachments/100.jpg -------------------------------------------------------------------------------- /src/content/config.ts: -------------------------------------------------------------------------------- 1 | import { defineCollection, z } from 'astro:content'; 2 | 3 | const hideElements = z.enum([ 4 | 'title', 5 | 'date', 6 | 'lastmod', 7 | 'tags', 8 | 'readingTime', 9 | 'toc', 10 | 'comments', 11 | ]); 12 | export type PostHideElements = z.infer; 13 | 14 | const posts = defineCollection({ 15 | type: 'content', 16 | schema: ({ image }) => 17 | z.object({ 18 | title: z.string(), 19 | description: z.string().optional(), 20 | image: image().optional(), 21 | date: z.date().optional(), 22 | lastmod: z.date().optional(), 23 | hidden: z.boolean().optional(), 24 | tags: z.array(z.string()).optional(), 25 | readingTime: z 26 | .object({ 27 | text: z.string(), 28 | time: z.number(), 29 | words: z.number(), 30 | minutes: z.number(), 31 | }) 32 | .optional(), 33 | hide: z.array(hideElements).optional(), 34 | }), 35 | }); 36 | 37 | export const collections = { 38 | posts, 39 | }; 40 | -------------------------------------------------------------------------------- /src/content/posts/chinese-preview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 药师灌顶真言 3 | date: 2019-04-11T14:26:00+08:00 4 | tags: 5 | - sample post 6 | - 中文 7 | - 真言 8 | - theme 9 | --- 10 | 11 | > 修学药师灌顶真言是药师法门的一部分,读诵修持有着极大的功德利益 12 | 13 | # 内容 14 | 15 | 南无薄伽伐帝鞞杀社 16 | 17 | 窭噜薜琉璃 18 | 19 | 跋喇婆 20 | 21 | 喝啰阇也 22 | 23 | 24 | 25 | 怛他揭多也 26 | 27 | 阿啰喝帝 28 | 29 | 三藐三勃陀耶 30 | 31 | 怛侄他 32 | 33 | 唵 34 | 35 | 鞞刹逝 36 | 37 | 鞞刹逝 38 | 39 | 鞞刹社 40 | 41 | 三没揭帝莎诃 42 | 43 | # 读法 44 | 45 | 那摩波切罚地 46 | 47 | 皮杀社 48 | 49 | 句度比琉璃 50 | 51 | 波拉婆 52 | 53 | 和拉舌也 54 | 55 | 大拖接多也 56 | 57 | 艾拉和地 58 | 59 | 三秒三波拖也 60 | 61 | 大至拖 62 | 63 | 安 64 | 65 | 皮杀逝 66 | 67 | 皮杀逝 68 | 69 | 皮杀社 70 | 71 | 三摩接地缩和 72 | 73 | # 简单释义 74 | 75 | 南无 薄伽伐帝 76 | 77 | 归命 世尊 78 | 79 | 鞞杀社 窭噜 薜琉璃 跋喇婆 喝啰阇也 80 | 81 | 药 师 琉璃 光 王 82 | 83 | 怛他揭多也 阿啰喝帝 三藐三勃陀耶 84 | 85 | 如来 应供 正等正觉 86 | 87 | 怛侄他 88 | 89 | 即说咒曰 90 | 91 | 唵 鞞刹逝 鞞刹逝 鞞刹社 三没揭帝 92 | 93 | 唵 药 药 药师 自度度他 94 | 95 | 莎诃 96 | 97 | 成就 98 | -------------------------------------------------------------------------------- /src/content/posts/english-preview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: English Preview 3 | description: A sample page with the most common elements of an article, including headings, paragraphs, lists, and images. Use it as a starting point for applying your own styles. 4 | date: 2019-04-01T14:26:00+08:00 5 | tags: 6 | - sample post 7 | - English 8 | - theme 9 | toc: false 10 | --- 11 | 12 | This page is an adapted version of [markdown-test-page](https://github.com/fullpipe/markdown-test-page). It should give you an idea of how different elements are styled on this template. 13 | 14 | ## Table of Contents 15 | 16 | - [Headings](#Headings) 17 | - [Paragraphs](#Paragraphs) 18 | - [Blockquotes](#Blockquotes) 19 | - [Lists](#Lists) 20 | - [Horizontal rule](#Horizontal) 21 | - [Table](#Table) 22 | - [Code](#Code) 23 | - [Inline elements](#Inline) 24 | 25 | --- 26 | 27 | # Headings 28 | 29 | # Heading one 30 | 31 | Sint sit cillum pariatur eiusmod nulla pariatur ipsum. Sit laborum anim qui mollit tempor pariatur nisi minim dolor. Aliquip et adipisicing sit sit fugiat commodo id sunt. Nostrud enim ad commodo incididunt cupidatat in ullamco ullamco Lorem cupidatat velit enim et Lorem. Ut laborum cillum laboris fugiat culpa sint irure do reprehenderit culpa occaecat. Exercitation esse mollit tempor magna aliqua in occaecat aliquip veniam reprehenderit nisi dolor in laboris dolore velit. 32 | 33 | ## Heading two 34 | 35 | Aute officia nulla deserunt do deserunt cillum velit magna. Officia veniam culpa anim minim dolore labore pariatur voluptate id ad est duis quis velit dolor pariatur enim. Incididunt enim excepteur do veniam consequat culpa do voluptate dolor fugiat ad adipisicing sit. Labore officia est adipisicing dolore proident eiusmod exercitation deserunt ullamco anim do occaecat velit. Elit dolor consectetur proident sunt aliquip est do tempor quis aliqua culpa aute. Duis in tempor exercitation pariatur et adipisicing mollit irure tempor ut enim esse commodo laboris proident. Do excepteur laborum anim esse aliquip eu sit id Lorem incididunt elit irure ea nulla dolor et. Nulla amet fugiat qui minim deserunt enim eu cupidatat aute officia do velit ea reprehenderit. 36 | 37 | ### Heading three 38 | 39 | Voluptate cupidatat cillum elit quis ipsum eu voluptate fugiat consectetur enim. Quis ut voluptate culpa ex anim aute consectetur dolore proident voluptate exercitation eiusmod. Esse in do anim magna minim culpa sint. Adipisicing ipsum consectetur proident ullamco magna sit amet aliqua aute fugiat laborum exercitation duis et. 40 | 41 | #### Heading four 42 | 43 | Commodo fugiat aliqua minim quis pariatur mollit id tempor. Non occaecat minim esse enim aliqua adipisicing nostrud duis consequat eu adipisicing qui. Minim aliquip sit excepteur ipsum consequat laborum pariatur excepteur. Veniam fugiat et amet ad elit anim laborum duis mollit occaecat et et ipsum et reprehenderit. Occaecat aliquip dolore adipisicing sint labore occaecat officia fugiat. Quis adipisicing exercitation exercitation eu amet est laboris sunt nostrud ipsum reprehenderit ullamco. Enim sint ut consectetur id anim aute voluptate exercitation mollit dolore magna magna est Lorem. Ut adipisicing adipisicing aliqua ullamco voluptate labore nisi tempor esse magna incididunt. 44 | 45 | ##### Heading five 46 | 47 | Veniam enim esse amet veniam deserunt laboris amet enim consequat. Minim nostrud deserunt cillum consectetur commodo eu enim nostrud ullamco occaecat excepteur. Aliquip et ut est commodo enim dolor amet sint excepteur. Amet ad laboris laborum deserunt sint sunt aliqua commodo ex duis deserunt enim est ex labore ut. Duis incididunt velit adipisicing non incididunt adipisicing adipisicing. Ad irure duis nisi tempor eu dolor fugiat magna et consequat tempor eu ex dolore. Mollit esse nisi qui culpa ut nisi ex proident culpa cupidatat cillum culpa occaecat anim. Ut officia sit ea nisi ea excepteur nostrud ipsum et nulla. 48 | 49 | ###### Heading six 50 | 51 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 52 | 53 | [[Top]](#top) 54 | 55 | # Paragraphs 56 | 57 | Incididunt ex adipisicing ea ullamco consectetur in voluptate proident fugiat tempor deserunt reprehenderit ullamco id dolore laborum. Do laboris laboris minim incididunt qui consectetur exercitation adipisicing dolore et magna consequat magna anim sunt. Officia fugiat Lorem sunt pariatur incididunt Lorem reprehenderit proident irure. Dolore ipsum aliqua mollit ad officia fugiat sit eu aliquip cupidatat ipsum duis laborum laborum fugiat esse. Voluptate anim ex dolore deserunt ea ex eiusmod irure. Occaecat excepteur aliqua exercitation aliquip dolor esse eu eu. 58 | 59 | Officia dolore laborum aute incididunt commodo nisi velit est est elit et dolore elit exercitation. Enim aliquip magna id ipsum aliquip consectetur ad nulla quis. Incididunt pariatur dolor consectetur cillum enim velit cupidatat laborum quis ex. 60 | 61 | Officia irure in non voluptate adipisicing sit amet tempor duis dolore deserunt enim ut. Reprehenderit incididunt in ad anim et deserunt deserunt Lorem laborum quis. Enim aute anim labore proident laboris voluptate elit excepteur in. Ex labore nulla velit officia ullamco Lorem Lorem id do. Dolore ullamco ipsum magna dolor pariatur voluptate ipsum id occaecat ipsum. Dolore tempor quis duis commodo quis quis enim. 62 | 63 | [[Top]](#top) 64 | 65 | # Blockquotes 66 | 67 | Ad nisi laborum aute cupidatat magna deserunt eu id laboris id. Aliquip nulla cupidatat sint ex Lorem mollit laborum dolor amet est ut esse aute. Nostrud ex consequat id incididunt proident ipsum minim duis aliqua ut ex et ad quis. Laborum sint esse cillum anim nulla cillum consectetur aliqua sit. Nisi excepteur cillum labore amet excepteur commodo enim occaecat consequat ipsum proident exercitation duis id in. 68 | 69 | > Ipsum et cupidatat mollit exercitation enim duis sunt irure aliqua reprehenderit mollit. Pariatur Lorem pariatur laboris do culpa do elit irure. Eiusmod amet nulla voluptate velit culpa et aliqua ad reprehenderit sit ut. 70 | 71 | Labore ea magna Lorem consequat aliquip consectetur cillum duis dolore. Et veniam dolor qui incididunt minim amet laboris sit. Dolore ad esse commodo et dolore amet est velit ut nisi ea. Excepteur ea nulla commodo dolore anim dolore adipisicing eiusmod labore id enim esse quis mollit deserunt est. Minim ea culpa voluptate nostrud commodo proident in duis aliquip minim. 72 | 73 | > Qui est sit et reprehenderit aute est esse enim aliqua id aliquip ea anim. Pariatur sint reprehenderit mollit velit voluptate enim consectetur sint enim. Quis exercitation proident elit non id qui culpa dolore esse aliquip consequat. 74 | 75 | Ipsum excepteur cupidatat sunt minim ad eiusmod tempor sit. 76 | 77 | > Deserunt excepteur adipisicing culpa pariatur cillum laboris ullamco nisi fugiat cillum officia. In cupidatat nulla aliquip tempor ad Lorem Lorem quis voluptate officia consectetur pariatur ex in est duis. Mollit id esse est elit exercitation voluptate nostrud nisi laborum magna dolore dolore tempor in est consectetur. 78 | 79 | Adipisicing voluptate ipsum culpa voluptate id aute laboris labore esse fugiat veniam ullamco occaecat do ut. Tempor et esse reprehenderit veniam proident ipsum irure sit ullamco et labore ea excepteur nulla labore ut. Ex aute minim quis tempor in eu id id irure ea nostrud dolor esse. 80 | 81 | [[Top]](#top) 82 | 83 | # Lists 84 | 85 | ### Ordered List 86 | 87 | 1. Longan 88 | 2. Lychee 89 | 3. Excepteur ad cupidatat do elit laborum amet cillum reprehenderit consequat quis. 90 | Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip aliquip velit pariatur dolore. 91 | 4. Marionberry 92 | 5. Melon 93 | - Cantaloupe 94 | - Honeydew 95 | - Watermelon 96 | 6. Miracle fruit 97 | 7. Mulberry 98 | 99 | ### Unordered List 100 | 101 | - Olive 102 | - Orange 103 | - Blood orange 104 | - Clementine 105 | - Papaya 106 | - Ut aute ipsum occaecat nisi culpa Lorem id occaecat cupidatat id id magna laboris ad duis. Fugiat cillum dolore veniam nostrud proident sint consectetur eiusmod irure adipisicing. 107 | - Passionfruit 108 | 109 | [[Top]](#top) 110 | 111 | # Horizontal rule 112 | 113 | In dolore velit aliquip labore mollit minim tempor veniam eu veniam ad in sint aliquip mollit mollit. Ex occaecat non deserunt elit laborum sunt tempor sint consequat culpa culpa qui sit. Irure ad commodo eu voluptate mollit cillum cupidatat veniam proident amet minim reprehenderit. 114 | 115 | --- 116 | 117 | In laboris eiusmod reprehenderit aliquip sit proident occaecat. Non sit labore anim elit veniam Lorem minim commodo eiusmod irure do minim nisi. Dolor amet cillum excepteur consequat sint non sint. 118 | 119 | [[Top]](#top) 120 | 121 | # Table 122 | 123 | Duis sunt ut pariatur reprehenderit mollit mollit magna dolore in pariatur nulla commodo sit dolor ad fugiat. Laboris amet ea occaecat duis eu enim exercitation deserunt ea laborum occaecat reprehenderit. Et incididunt dolor commodo consequat mollit nisi proident non pariatur in et incididunt id. Eu ut et Lorem ea ex magna minim ipsum ipsum do. 124 | 125 | | Table Heading 1 | Table Heading 2 | Center align | Right align | Table Heading 5 | 126 | | :-------------- | :-------------- | :----------: | ----------: | :-------------- | 127 | | Item 1 | Item 2 | Item 3 | Item 4 | Item 5 | 128 | | Item 1 | Item 2 | Item 3 | Item 4 | Item 5 | 129 | | Item 1 | Item 2 | Item 3 | Item 4 | Item 5 | 130 | | Item 1 | Item 2 | Item 3 | Item 4 | Item 5 | 131 | | Item 1 | Item 2 | Item 3 | Item 4 | Item 5 | 132 | 133 | Minim id consequat adipisicing cupidatat laborum culpa veniam non consectetur et duis pariatur reprehenderit eu ex consectetur. Sunt nisi qui eiusmod ut cillum laborum Lorem officia aliquip laboris ullamco nostrud laboris non irure laboris. Cillum dolore labore Lorem deserunt mollit voluptate esse incididunt ex dolor. 134 | 135 | [[Top]](#top) 136 | 137 | # Code 138 | 139 | ## Inline code 140 | 141 | Ad amet irure est magna id mollit Lorem in do duis enim. Excepteur velit nisi magna ea pariatur pariatur ullamco fugiat deserunt sint non sint. Duis duis est `code in text` velit velit aute culpa ex quis pariatur pariatur laborum aute pariatur duis tempor sunt ad. Irure magna voluptate dolore consectetur consectetur irure esse. Anim magna `in culpa qui officia` dolor eiusmod esse amet aute cupidatat aliqua do id voluptate cupidatat reprehenderit amet labore deserunt. 142 | 143 | ## Highlighted 144 | 145 | Et fugiat ad nisi amet magna labore do cillum fugiat occaecat cillum Lorem proident. In sint dolor ullamco ad do adipisicing amet id excepteur Lorem aliquip sit irure veniam laborum duis cillum. Aliqua occaecat minim cillum deserunt magna sunt laboris do do irure ea nostrud consequat ut voluptate ex. 146 | 147 | ```html 148 |
149 |
150 | Woman paying for a purchase 155 |
156 |
157 |
158 | Marketing 159 |
160 | Finding customers for your new business 165 |

166 | Getting a new business off the ground is a lot of hard work. Here are five 167 | ideas you can use to find your first customers. 168 |

169 |
170 |
171 | ``` 172 | 173 | Ex amet id ex aliquip id do laborum excepteur exercitation elit sint commodo occaecat nostrud est. Nostrud pariatur esse veniam laborum non sint magna sit laboris minim in id. Aliqua pariatur pariatur excepteur adipisicing irure culpa consequat commodo et ex id ad. 174 | 175 | ```html 176 | 183 | 184 | 187 | ``` 188 | 189 | [[Top]](#top) 190 | 191 | # Inline elements 192 | 193 | Sint ea anim ipsum ad commodo cupidatat do **exercitation** incididunt et minim ad labore sunt. Minim deserunt labore laboris velit nulla incididunt ipsum nulla. Ullamco ad laborum ea qui et anim in laboris exercitation tempor sit officia laborum reprehenderit culpa velit quis. **Consequat commodo** reprehenderit duis [irure](#) esse esse exercitation minim enim Lorem dolore duis irure. Nisi Lorem reprehenderit ea amet excepteur dolor excepteur magna labore proident voluptate ipsum. Reprehenderit ex esse deserunt aliqua ea officia mollit Lorem nulla magna enim. Et ad ipsum labore enim ipsum **cupidatat consequat**. Commodo non ea cupidatat magna deserunt dolore ipsum velit nulla elit veniam nulla eiusmod proident officia. 194 | 195 | _Proident sit veniam in est proident officia adipisicing_ ea tempor cillum non cillum velit deserunt. Voluptate laborum incididunt sit consectetur Lorem irure incididunt voluptate nostrud. Commodo ut eiusmod tempor cupidatat esse enim minim ex anim consequat. Mollit sint culpa qui laboris quis consectetur ad sint esse. Amet anim anim minim ullamco et duis non irure. Sit tempor adipisicing ea laboris `culpa ex duis sint` anim aute reprehenderit id eu ea. Aute [excepteur proident](#) Lorem minim adipisicing nostrud mollit ad ut voluptate do nulla esse occaecat aliqua sint anim. 196 | 197 | Incididunt in culpa cupidatat mollit cillum qui proident sit. In cillum aliquip incididunt voluptate magna amet cupidatat cillum pariatur sint aliqua est _enim **anim** voluptate_. Magna aliquip proident incididunt id duis pariatur eiusmod incididunt commodo culpa dolore sit. Culpa do nostrud elit ad exercitation anim pariatur non minim nisi **adipisicing sunt _officia_**. Do deserunt magna mollit Lorem commodo ipsum do cupidatat mollit enim ut elit veniam ea voluptate. Sint ea anim ipsum ad commodo cupidatat do **exercitation** incididunt et minim ad labore sunt. Minim deserunt labore laboris velit nulla incididunt ipsum nulla. 198 | 199 | 200 | 201 | Reprehenderit non eu quis in ad elit esse qui aute id [incididunt](#) dolore cillum. Esse laboris consequat dolor anim exercitation tempor aliqua deserunt velit magna laboris. Culpa culpa minim duis amet mollit do quis amet commodo nulla irure. 202 | -------------------------------------------------------------------------------- /src/content/posts/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started🚀 3 | description: How to use this blog template✨ 4 | date: 2040-03-09T00:00:00+08:00 5 | tags: 6 | - sample post 7 | - theme 8 | --- 9 | 10 | ## Getting Started 11 | 12 | Download the [codebase](https://github.com/ladit/astro-blog-zozo) first. [Bun](https://bun.sh) is recommended for this project. 13 | 14 | ```shell 15 | # let bun to create a local repo using this template 16 | bun create ladit/astro-blog-zozo 17 | bun install 18 | ``` 19 | 20 | ### populate your content 21 | 22 | To craft your own site further, check the folders below and modify anything as you wish. Markdown post with frontmatters is supported. 23 | 24 | ``` 25 | public # favicons here 26 | ├── apple-touch-icon.png 27 | ├── favicon.ico 28 | src 29 | ├── assets 30 | │   ├── logo.svg # site logo read in components/Header.astro 31 | │   ├── og-logo.png # logo image for OG generation. Check src/utils/openGraphImage.tsx for detail 32 | ├── config.ts # main config 33 | ├── content # markdown posts and attachments go here 34 | │   ├── attachments 35 | │   └── posts 36 | ├── pages 37 | │   ├── index.astro # index page written in Astro component 38 | │   └── about.astro # about page written in Astro component 39 | giscus.example.json # rename to giscus.json for advanced giscus configurations 40 | ``` 41 | 42 | All frontmatter fields except title are optional. Date field is under ISO 8601 format without quotation marks. 43 | The posts frontmatter describes as: 44 | 45 | ```yaml 46 | --- 47 | slug: my-custom-slug 48 | title: My awesome title 49 | description: some desc 50 | image: ../attachments/100.jpg # hero & social image 51 | date: 2024-02-26T23:30:47+08:00 52 | lastmod: 2024-02-26T23:30:47+08:00 53 | hidden: false # set this to true to make this post hidden from posts list. But the uri is still accessable. 54 | tags: 55 | - demo 56 | - theme 57 | hide: # set a element list to hide in the post page 58 | - title 59 | - date 60 | - lastmod 61 | - tags 62 | - readingTime 63 | - toc 64 | - comments 65 | --- 66 | ``` 67 | 68 | Note: to use the Astro `Content Collection` feature, the site content must be placed at `src/content`. In fact, I linked the `src/content` folder to my Obsidian vault for better editing experience. 69 | 70 | ### comments 71 | 72 | Post comments is powered by [giscus](https://github.com/giscus/giscus). So your blog visitors should login Github to leave a comment. Follow instructions [here](https://giscus.app/) to initialize your Github repo, install giscus app, enable discussion and get your parameters to fill in `src/config.ts: GiscusConfig`. 73 | 74 | To restrict the domains that can load giscus with your repository's discussions, rename `giscus.example.json` to `giscus.json` and modify it. Document is [here](https://github.com/giscus/giscus/blob/main/ADVANCED-USAGE.md). 75 | 76 | ### preview 77 | 78 | Build it first by `bun run build` and preview it by `bun run preview` or `bun run preview:wrangler`. 79 | 80 | `wrangler` is a cli from Cloudflare. You may install it first. 81 | 82 | ### deploy to Cloudflare Pages 83 | 84 | Refer to [Cloudflare Pages doc](https://developers.cloudflare.com/pages/). You can use `wrangler` cli to upload from local or git integration. 85 | 86 | Build settings: 87 | 88 | Framework: Astro 89 | 90 | Build command: `bun install && bun run build` 91 | 92 | Evironments: 93 | 94 | ```shell 95 | BUN_VERSION=latest 96 | NODE_VERSION=v22.11.0 97 | ``` 98 | 99 | To add a custom domain, check [the doc](https://developers.cloudflare.com/pages/configuration/custom-domains/). 100 | 101 | ### deploy to Github Pages 102 | 103 | Follow [this doc](https://docs.github.com/en/pages/getting-started-with-github-pages/creating-a-github-pages-site). 104 | 105 | You must have a `.github.io` repo in Github for deploying. You can rename this repo. 106 | 107 | In the repository, go to Settings > Pages > Build and deployment. Select `Github Actions` as Source. 108 | 109 | Github workflow(`.github/workflows/pages.yaml`) will deploy for you everytime you push to the default branch. 110 | 111 | To add a custom domain, check [the doc](https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/about-custom-domains-and-github-pages). 112 | 113 | ### deploy to Vercel 114 | 115 | Link your Github account to Vercel and import your blog repo to it. 116 | 117 | Build settings: 118 | 119 | Framework: Astro 120 | 121 | Build command overwrite: `bun run build` 122 | 123 | To add a custom domain, check [the doc](https://vercel.com/docs/projects/domains/add-a-domain). 124 | -------------------------------------------------------------------------------- /src/content/posts/hidden.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: a hidden post 3 | slug: my-hidden-post 4 | description: some desc 5 | date: 2024-02-27T23:30:47+08:00 6 | lastmod: 2024-02-27T23:30:47+08:00 7 | hidden: true 8 | tags: 9 | - sample post 10 | - hidden 11 | --- 12 | 13 | # This post is hidden 14 | 15 | It will not be shown in RSS and sitemap. But you can still access it at `/posts/my-hidden-post`. 16 | 17 | Note that if you deploy on public Github repo, hidden posts may be seen in build output such as workflow artifacts. 18 | -------------------------------------------------------------------------------- /src/content/posts/japanese-preview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 日本語敬語体系 3 | date: 2019-04-10T14:26:00+08:00 4 | tags: 5 | - sample post 6 | - 日本語 7 | - 体系 8 | - theme 9 | --- 10 | 11 | > 日本語の敬語体系は、一般に、大きく尊敬語・謙譲語・丁寧語に分類される。文化審議会国語分科会は、2007年2月に「敬語の指針」を答申し、これに丁重語および美化語を含めた5分類を示している。 12 | 13 | # 尊敬語 14 | 15 | 尊敬語は、動作の主体を高めることで、主体への敬意を表す言い方である。動詞に「お(ご)~になる」を付けた形、また、助動詞「(ら)れる」を付けた形などが用いられる。たとえば、動詞「取る」の尊敬形として、「(先生が)お取りになる」「(先生が)取られる」などが用いられる。 16 | 17 | 18 | 19 | 語によっては、特定の尊敬語が対応するものもある。たとえば、「言う」の尊敬語は「おっしゃる」、「食べる」の尊敬語は「召し上がる」、「行く・来る・いる」の尊敬語は「いらっしゃる」である。 20 | 21 | # 謙譲語 22 | 23 | 謙譲語は、古代から基本的に動作の客体への敬意を表す言い方であり、現代では「動作の主体を低める」と解釈するほうがよい場合がある。動詞に「お~する」「お~いたします」(謙譲語+丁寧語)をつけた形などが用いられる。たとえば、「取る」の謙譲形として、「お取りする」などが用いられる。 24 | 25 | 語によっては、特定の謙譲語が対応するものもある。たとえば、「言う」の謙譲語は「申し上げる」、「食べる」の謙譲語は「いただく」、「(相手の所に)行く」の謙譲語は「伺う」「参上する」「まいる」である。 26 | 27 | なお、「夜も更けてまいりました」の「まいり」など、謙譲表現のようでありながら、誰かを低めているわけではない表現がある。これは、「夜も更けてきた」という話題を丁重に表現することによって、聞き手への敬意を表すものである。宮地裕は、この表現に使われる語を、特に「丁重語」と称している[104][105]。丁重語にはほかに「いたし(マス)」「申し(マス)」「存じ(マス)」「小生」「小社」「弊社」などがある。文化審議会の「敬語の指針」でも、「明日から海外へまいります」の「まいり」のように、相手とは関りのない自分側の動作を表現する言い方を丁重語としている。 28 | 29 | # 丁寧語 30 | 31 | 丁寧語は、文末を丁寧にすることで、聞き手への敬意を表すものである。動詞・形容詞の終止形で終わる常体に対して、名詞・形容動詞語幹などに「です」を付けた形(「学生です」「きれいです」)や、動詞に「ます」をつけた形(「行きます」「分かりました」)等の丁寧語を用いた文体を敬体という。 32 | 33 | 一般に、目上の人には丁寧語を用い、同等・目下の人には丁寧語を用いないといわれる。しかし、実際の言語生活に照らして考えれば、これは事実ではない。母が子を叱るとき、「お母さんはもう知りませんよ」と丁寧語を用いる場合ももある。丁寧語が用いられる多くの場合は、敬意や謝意の表現とされるが、、稀に一歩引いた心理的な距離をとろうとする場合もある。 34 | 35 | 「お弁当」「ご飯」などの「お」「ご」も、広い意味では丁寧語に含まれるが、宮地裕は特に「美化語」と称して区別する[104][105]。相手への丁寧の意を示すというよりは、話し手が自分の言葉遣いに配慮した表現である。したがって、「お弁当食べようよ。」のように、丁寧体でない文でも美化語を用いることがある。文化審議会の「敬語の指針」でも「美化語」を設けている。 36 | -------------------------------------------------------------------------------- /src/content/posts/theme-preview.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: my-theme-preview 3 | title: Theme Preview with a so long long long long title~ 4 | description: What do they got in there? King Kong? You're a very talented young man, with your own clever thoughts and ideas. Do you need a manager? I was part of something special. Did he just throw my cat out of the window? Do you have any idea how long it takes those cups to decompose. 5 | image: ../attachments/100.jpg 6 | date: 2031-03-31T23:30:47+08:00 7 | lastmod: 2024-03-01T23:30:47+08:00 8 | hidden: false 9 | tags: 10 | - sample post 11 | - demo 12 | - theme 13 | - apple 14 | - banana 15 | - cherry 16 | - ice-cream 17 | - chocolate 18 | - cake 19 | - cookie 20 | - donut 21 | - egg 22 | - fish 23 | - grape 24 | - honey 25 | - ice 26 | - jam 27 | - kiwi 28 | - lemon 29 | - mango 30 | - nut 31 | - orange 32 | --- 33 | 34 | # Headings 35 | 36 | ```markdown 37 | # H1 38 | 39 | ## H2 40 | 41 | ### H3 42 | 43 | #### H4 44 | 45 | ##### H5 46 | 47 | ###### H6 48 | ``` 49 | 50 | 51 | 52 | # H1 53 | 54 | ## H2 55 | 56 | ### H3 57 | 58 | #### H4 59 | 60 | ##### H5 61 | 62 | ###### H6 63 | 64 | # Paragraphs 65 | 66 | ```markdown 67 | This is a paragraph. // [!code --] 68 | I am still part of the paragraph. // [!code ++] 69 | 70 | New paragraph. 71 | ``` 72 | 73 | This is a paragraph. 74 | I am still part of the paragraph. 75 | 76 | New paragraph. 77 | 78 | # long paragraph 79 | 80 | ```markdown 81 | Hey, you know how I'm, like, always trying to save the planet? Here's my chance. Must go faster... go, go, go, go, go! Jaguar shark! So tell me - does it really exist? Forget the fat lady! You're obsessed with the fat lady! Drive us out of here! I was part of something special. 82 | 83 | My dad once told me, laugh and the world laughs with you, Cry, and I'll give you something to cry about you little bastard! We gotta burn the rain forest, dump toxic waste, pollute the air, and rip up the OZONE! 'Cause maybe if we screw up this planet enough, they won't want it anymore! 84 | 85 | Must go faster... go, go, go, go, go! This thing comes fully loaded. AM/FM radio, reclining bucket seats, and... power windows. Must go faster... go, go, go, go, go! Yeah, but John, if The Pirates of the Caribbean breaks down, the pirates don’t eat the tourists. 86 | 87 | Yeah, but John, if The Pirates of the Caribbean breaks down, the pirates don’t eat the tourists. Is this my espresso machine? Wh-what is-h-how did you get my espresso machine? This thing comes fully loaded. AM/FM radio, reclining bucket seats, and... power windows. 88 | ``` 89 | 90 | Hey, you know how I'm, like, always trying to save the planet? Here's my chance. Must go faster... go, go, go, go, go! Jaguar shark! So tell me - does it really exist? Forget the fat lady! You're obsessed with the fat lady! Drive us out of here! I was part of something special. 91 | 92 | My dad once told me, laugh and the world laughs with you, Cry, and I'll give you something to cry about you little bastard! We gotta burn the rain forest, dump toxic waste, pollute the air, and rip up the OZONE! 'Cause maybe if we screw up this planet enough, they won't want it anymore! 93 | 94 | Must go faster... go, go, go, go, go! This thing comes fully loaded. AM/FM radio, reclining bucket seats, and... power windows. Must go faster... go, go, go, go, go! Yeah, but John, if The Pirates of the Caribbean breaks down, the pirates don’t eat the tourists. 95 | 96 | Yeah, but John, if The Pirates of the Caribbean breaks down, the pirates don’t eat the tourists. Is this my espresso machine? Wh-what is-h-how did you get my espresso machine? This thing comes fully loaded. AM/FM radio, reclining bucket seats, and... power windows. 97 | 98 | # Inline code 99 | 100 | This is `Inline code`. 101 | 102 | # Image 103 | 104 | ```markdown 105 | Web Image 106 | 107 | ![Web Image](https://i.loli.net/2019/04/13/5cb1d33cf0ee6.jpg) 108 | 109 | Local Image 110 | 111 | ![Local Image](../attachments/100.jpg) 112 | ``` 113 | 114 | Web Image 115 | 116 | ![Web Image](https://i.loli.net/2019/04/13/5cb1d33cf0ee6.jpg) 117 | 118 | Local Image 119 | 120 | ![Local Image](../attachments/100.jpg) 121 | 122 | # Block Quotes 123 | 124 | ```markdown 125 | > This is a block quote 126 | ``` 127 | 128 | > This is a block quote 129 | 130 | # Code Blocks 131 | 132 | ````markdown 133 | ```javascript 134 | // Fenced **with** highlighting 135 | function doIt() { 136 | for (var i = 1; i <= slen ; i^^) { 137 | setTimeout("document.z.textdisplay.value = newMake()", i*300); 138 | setTimeout("window.status = newMake()", i*300); 139 | } 140 | } 141 | ``` 142 | ```` 143 | 144 | ```javascript 145 | function doIt() { 146 | for (var i = 1; i <= slen ; i^^) { 147 | setTimeout("document.z.textdisplay.value = newMake()", i*300); 148 | setTimeout("window.status = newMake()", i*300); 149 | } 150 | } 151 | ``` 152 | 153 | ````markdown 154 | ```go 155 | // Fenced **with** highlighting 156 | package main 157 | 158 | import "fmt" 159 | 160 | func main() { 161 | fmt.Println("Hello, World!") 162 | } 163 | ``` 164 | ```` 165 | 166 | ```go 167 | // Fenced **with** highlighting 168 | package main 169 | 170 | import "fmt" 171 | 172 | func main() { 173 | fmt.Println("Hello, World!") 174 | } 175 | ``` 176 | 177 | # Tables 178 | 179 | ```markdown 180 | | Colors | Fruits | Vegetable | 181 | | ---------- | :-------------: | ----------------: | 182 | | Red | _Apple_ | [Pepper](#Tables) | 183 | | ~~Orange~~ | Oranges | **Carrot** | 184 | | Green | ~~**_Pears_**~~ | Spinach | 185 | ``` 186 | 187 | | Colors | Fruits | Vegetable | 188 | | ---------- | :-------------: | ----------------: | 189 | | Red | _Apple_ | [Pepper](#Tables) | 190 | | ~~Orange~~ | Oranges | **Carrot** | 191 | | Green | ~~**_Pears_**~~ | Spinach | 192 | 193 | # List Types 194 | 195 | #### Ordered List 196 | 197 | ```markdown 198 | 1. First item 199 | 2. Second item 200 | 3. Third item 201 | ``` 202 | 203 | 1. First item 204 | 2. Second item 205 | 3. Third item 206 | 207 | #### Unordered List 208 | 209 | ```markdown 210 | - First item 211 | - Second item 212 | - Third item 213 | ``` 214 | 215 | - First item 216 | - Second item 217 | - Third item 218 | 219 | # Math 220 | 221 | ```tex 222 | $$ 223 | evidence\_{i}=\sum\_{j}W\_{ij}x\_{j}+b\_{i} 224 | $$ 225 | 226 | $$ 227 | AveP = \int_0^1 p(r) dr 228 | $$ 229 | 230 | When $a \ne 0$, there are two solutions to \(ax^2 + bx + c = 0\) and they are 231 | $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$ 232 | ``` 233 | 234 | $$ 235 | evidence\_{i}=\sum\_{j}W\_{ij}x\_{j}+b\_{i} 236 | $$ 237 | 238 | $$ 239 | AveP = \int_0^1 p(r) dr 240 | $$ 241 | 242 | When $a \ne 0$, there are two solutions to \(ax^2 + bx + c = 0\) and they are 243 | $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$ 244 | 245 | #### Emoji 246 | 247 | This is a test for emoji. 248 | :smile: 249 | :see_no_evil: 250 | :smile_cat: 251 | :watermelon: 252 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /src/layouts/BaseLayout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { ViewTransitions } from 'astro:transitions'; 3 | import { SiteLanguage, SiteTitle } from '~/config'; 4 | import 'lxgw-wenkai-screen-web/lxgwwenkaigbscreen/result.css'; 5 | 6 | export interface Props { 7 | title?: string; 8 | description?: string; 9 | socialImage?: string; 10 | rootClass?: 11 | | Iterable< 12 | string | Record | string[] | false | null | undefined 13 | > 14 | | string; 15 | bodyClass?: 16 | | Iterable< 17 | string | Record | string[] | false | null | undefined 18 | > 19 | | string; 20 | } 21 | const { title, description, socialImage, rootClass, bodyClass } = Astro.props; 22 | const socialImg = new URL(socialImage ?? '/og.png', Astro.url).href; 23 | --- 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | {title} 37 | 38 | 39 | 45 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /src/layouts/MarkdownLayout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import type { CollectionEntry } from 'astro:content'; 3 | import { Icon } from 'astro-icon/components'; 4 | import Comments from '~/components/Comments.astro'; 5 | import { Hide, type HideElements } from '~/config'; 6 | import type { PostHideElements } from '~/content/config'; 7 | import ScaffoldLayout from './ScaffoldLayout.astro'; 8 | import 'katex/dist/katex.css'; 9 | 10 | type Props = CollectionEntry<'posts'>['data'] & { 11 | currentNav?: string; 12 | socialImage?: string; 13 | }; 14 | const { title, date, lastmod, tags, readingTime, hide } = Astro.props; 15 | const show = (key: string) => 16 | !( 17 | Hide.includes(key as HideElements) || 18 | hide?.includes(key as PostHideElements) 19 | ); 20 | const showDate = date && show('date'); 21 | const showLastmod = lastmod && show('lastmod'); 22 | const showReadingTime = readingTime && show('readingTime'); 23 | const showTimes = showDate || showLastmod || showReadingTime; 24 | --- 25 | 26 | 27 | {title && show('title') &&

{title}

} 28 |
29 | { 30 | showTimes && ( 31 |
32 | {showDate && ( 33 | 37 | )} 38 | {showLastmod && ( 39 | 48 | )} 49 | {showReadingTime && ( 50 | 51 | 57 | {readingTime.text} 58 | 59 | )} 60 |
61 | ) 62 | } 63 | { 64 | tags && show('tags') && ( 65 |
66 | {tags.map((tag: string) => ( 67 | 72 | {tag} 73 | 74 | ))} 75 |
76 | ) 77 | } 78 |
79 |
102 | 103 |
104 | { 105 | show('comments') && ( 106 | <> 107 |
108 | 109 | 110 | ) 111 | } 112 |
113 | 131 | -------------------------------------------------------------------------------- /src/layouts/ScaffoldLayout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BackToTop from '~/components/BackToTop.astro'; 3 | import Footer from '~/components/Footer.astro'; 4 | import Header from '~/components/Header.astro'; 5 | import BaseLayout from './BaseLayout.astro'; 6 | 7 | interface Props { 8 | title?: string; 9 | description?: string; 10 | socialImage?: string; 11 | currentNav?: string; 12 | } 13 | --- 14 | 15 | 33 |
50 |
51 |
52 | 53 |
54 |
55 |