feat: squash all the commits for hiding some sensitive information.

This commit is contained in:
Yufan Sheng 2024-11-15 16:01:33 +08:00
commit 5b35d2a5dc
Signed by: syhily
GPG Key ID: 9D18A22A7DCD5A9B
1296 changed files with 41621 additions and 0 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
node_modules
dist

25
.editorconfig Normal file
View File

@ -0,0 +1,25 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{md,mdx}]
indent_size = tab
trim_trailing_whitespace = false
[*.{js,jsx,ts,tsx,css,less,sass,scss}]
indent_style = space
indent_size = 2
[*.{yml,yaml,json}]
indent_style = space
indent_size = 2
[*.go]
indent_style = tab
[Makefile]
indent_size = tab

17
.env.example Normal file
View File

@ -0,0 +1,17 @@
# Since the ".env" file is gitignored, you can use the ".env.example" file to
# build a new ".env" file when you clone the repo. Keep this file up-to-date
# when you add new variables to `.env`.
# This file will be committed to version control, so make sure not to have any
# secrets in it. If you are cloning this repo, create a copy of this file named
# ".env" and populate it with your secrets.
# PostgreSQL configuration.
POSTGRES_HOST=
POSTGRES_PORT=
POSTGRES_USERNAME=
POSTGRES_PASSWORD=
POSTGRES_DATABASE=
# Artalk configuration
ARTALK_HOST=

162
.gitattributes vendored Normal file
View File

@ -0,0 +1,162 @@
# This file is inspired by https://github.com/alexkaratarakis/gitattributes
#
# Auto detect text files and perform LF normalization
# http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
* text=auto
# The above will handle all files NOT found below
# These files are text and should be normalized (Convert crlf => lf)
*.bat text eol=crlf
*.cmd text eol=crlf
*.coffee text
*.css text
*.cql text
*.df text
*.ejs text
*.html text
*.java text
*.js text
*.json text
*.less text
*.properties text
*.sass text
*.scss text
*.sh text
*.sql text
*.txt text
*.ts text
*.xml text
*.yaml text
*.yml text
# Documents
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.markdown text
*.md text
*.adoc text
*.textile text
*.mustache text
*.csv text
*.tab text
*.tsv text
*.txt text
*.config text
AUTHORS text
CHANGELOG text
CHANGES text
CONTRIBUTING text
COPYING text
copyright text
*COPYRIGHT* text
INSTALL text
license text
LICENSE text
NEWS text
readme text
*README* text
TODO text
Jenkinsfile text
# Graphics
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.tif binary
*.tiff binary
*.ico binary
# SVG treated as an asset (binary) by default. If you want to treat it as text,
# comment-out the following line and uncomment the line after.
*.svg binary
#*.svg text
*.eps binary
*.sketch binary
# These files are binary and should be left untouched
# (binary is a macro for -text -diff)
*.class binary
*.jar binary
*.war binary
## LINTERS
.csslintrc text
.eslintrc text
.jscsrc text
.jshintrc text
.jshintignore text
.stylelintrc text
.prettierignore text
.prettierrc text
.scalafmt.conf text
## CONFIGS
*.bowerrc text
*.conf text
*.config text
.editorconfig text
.gitattributes text
.gitconfig text
.gitignore text
.htaccess text
*.npmignore text
## HEROKU
Procfile text
.slugignore text
## AUDIO
*.kar binary
*.m4a binary
*.mid binary
*.midi binary
*.mp3 binary
*.ogg binary
*.ra binary
## VIDEO
*.3gpp binary
*.3gp binary
*.as binary
*.asf binary
*.asx binary
*.fla binary
*.flv binary
*.m4v binary
*.mng binary
*.mov binary
*.mp4 binary
*.mpeg binary
*.mpg binary
*.swc binary
*.swf binary
*.webm binary
## ARCHIVES
*.7z binary
*.gz binary
*.rar binary
*.tar binary
*.zip binary
## FONTS
*.ttf binary
*.eot binary
*.otf binary
*.woff binary
*.woff2 binary
## Bun
*.lockb binary diff=lockb
## License File
LICENSE.* text

29
.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
# build output
dist/
.zeabur/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
# jetbrains setting folder
!.idea/icon.svg
.idea/
# Hide the SQL session
*.session.sql

1
.husky/pre-commit Normal file
View File

@ -0,0 +1 @@
npx lint-staged

12
.idea/icon.svg Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="300px" height="300px" viewBox="0 0 300 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>favicon</title>
<g id="favicon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#263148" x="0" y="0" width="300" height="300" />
<g id="logo-white" transform="translate(9, 3.888)">
<path d="M159,154.195523 C167.013592,152.617424 174.05936,152.751451 180.129673,154.674602 C190.120795,157.839913 194.348511,161.02564 199.517296,168.40144 C202.925628,173.265093 205.718707,181.861769 208,194.149433 C202.817237,195.503225 198.494331,195.463815 195.074572,193.809733 C188.963104,190.853715 183.30068,186.732689 175.022826,177.505654 C169.768056,171.648346 164.430736,163.875789 159,154.195523 Z" id="font1-dot" fill="#EEB4B4" />
<path d="M151.486676,25.701431 C155.616373,28.5082875 160.109234,31.601356 164.965286,34.9806001 C165.393463,35.2785613 165.822641,35.5757097 166.255249,35.8738863 L167.105716,36.458366 C168.619019,37.4954138 170.108644,38.5008303 171.499238,39.4303971 L172.608374,40.1697969 C174.26724,41.2727003 175.735093,42.2354804 176.860606,42.9690389 L176.448028,45.1207071 L166.710146,84.6326462 C180.591569,66.4261557 193.885083,57.2272012 206.997372,58.6211808 C208.743695,58.806834 210.475625,59.0033727 212.18144,59.2464237 C212.733943,59.3251464 213.291751,59.4029337 213.853878,59.4813177 C215.158468,59.6632319 216.474026,59.8465926 217.787079,60.0508718 L218.661915,60.1902579 C225.09927,61.241972 231.826526,62.8614549 236.986443,68.0144332 C240.889249,71.9119921 243.813448,77.8014483 245.23642,86.708071 C247.095663,98.3453903 243.120654,117.723729 238.770002,139.330906 L238.580551,140.271866 C237.890376,143.700241 237.191343,147.180322 236.50179,150.693982 L236.044022,153.039529 C235.556986,155.549827 235.077075,158.073824 234.610494,160.607168 L234.263405,162.507565 L233.922672,164.406776 C228.900089,192.67064 226.130123,221.241925 234.44056,240.371255 C240.652266,254.669635 245.935235,262.932333 250.296038,267.670416 C253.412465,271.056463 256.180453,272.79959 258.553083,273.698031 C262.383549,275.148509 265.383302,274.528886 267.668585,273.982958 C268.14919,273.865733 268.422366,273.767797 268.665926,273.767797 C271.112421,273.767797 275.809409,272.387068 282.731388,269.370175 C283.375366,277.48654 282.878184,284.333295 281.156359,289.899262 C280.659157,291.506519 280.558961,292.472131 279.535062,293.024782 C275.748117,295.068793 270.498503,293.963559 265.488631,292.694915 C256.722323,290.485677 244.816282,287.548472 234.696635,275.178958 C233.697221,273.957347 232.737161,272.794212 231.813783,271.675534 C222.758366,260.704951 217.370091,254.220034 212.983093,237.93167 C209.908658,226.516685 208.870828,218.179476 208.826484,204.259903 L208.825284,202.840608 C208.836884,194.672214 209.157933,184.633261 209.605114,171.185381 C210.376069,148.00078 213.79261,132.71409 216.334239,121.332289 C218.054209,113.629996 219.354132,107.626433 219.354132,102.212372 C219.354132,100.105514 219.634597,97.1568247 219.926173,94.0567601 L220.027967,92.9734861 C220.416501,88.8241101 220.77388,84.5552796 220.633396,81.5249303 C220.563268,80.0122213 220.36004,78.7458615 220.056532,77.8111784 C219.39849,75.7846738 218.218924,74.7926623 217.050886,74.319003 C213.016784,72.6831053 207.083752,73.8938983 200.767445,77.2643023 C193.5468,81.1172643 185.881323,87.4227388 181.226749,92.1251087 C170.078149,103.388189 168.197052,106.181552 163.449564,130.566909 C160.514746,145.641534 157.152557,190.083423 153.408527,263.882996 C148.310625,260.529506 144.573436,256.539474 142.267189,251.849384 C137.44815,242.049164 135.781889,238.498147 133.16629,221.130117 C131.151349,207.750561 132.216961,189.426224 132.839043,178.568221 C133.052135,174.848508 133.207609,172.07036 133.207609,170.48586 C133.207609,164.09523 134.233106,147.312148 137.188741,122.72916 C137.780573,117.806695 138.265493,113.776123 138.700893,110.283341 L138.823994,109.299731 C139.331717,105.259782 139.777004,101.934213 140.256685,98.7255887 L140.372518,97.9574584 L140.582929,96.5951823 L140.761224,95.4724241 L140.885706,94.7044198 C141.871689,88.670391 143.122232,82.1576033 145.272592,71.2270532 C148.154429,56.5783156 150.225956,41.4023262 151.486676,25.701431 Z" id="font1-right" fill="#FFFFFF" />
<path d="M78.8891291,0 L95.8005079,20.9095227 C95.225924,21.6575488 94.6661502,22.3687531 94.1213578,23.0610595 C90.498037,27.665481 87.5143717,31.5165865 85.0892155,38.8498924 C84.5933419,40.3493393 84.11666,41.7284904 83.6520528,43.0728622 C83.5490334,43.3709557 83.4494774,43.6590286 83.3486814,43.9513373 L82.9235478,45.1899741 L82.6412306,46.0224097 L82.2485912,47.2004158 C80.4111972,52.7806913 78.6427308,59.1891547 76.4859025,71.9475546 C76.3776982,72.5876213 76.2738377,73.2121994 76.1751238,73.8203757 L75.8128598,76.0522841 L78.072301,76.1393226 C78.6520627,76.1616563 79.233139,76.1970078 79.8150176,76.2458239 C87.4717734,76.8881795 94.6057156,79.8018442 100.240592,83.8134948 C106.031451,87.9361953 110.302115,93.214716 111.609957,98.4281539 C115.240663,112.901207 110.993666,140.365178 108.836316,154.571762 C108.454224,157.088603 108.185012,158.862829 107.988397,160.271632 C107.819607,161.481054 107.625793,162.940163 107.407149,164.586499 C106.781752,169.295591 105.954511,175.526376 104.92697,181.804864 L104.678362,183.301099 C104.604458,183.739061 104.529572,184.176727 104.455334,184.604273 L104.223432,185.919703 L103.986615,187.248058 C102.465647,195.80355 101.02459,204.149885 98.485251,205.777632 C97.1963754,206.603817 95.9047926,207.065865 94.5842529,207.065865 C93.8588313,207.065865 93.1344185,206.927653 92.4069507,206.668298 L95.4781875,150.606035 L95.4781875,125.416711 C95.4781875,119.629476 93.0062228,110.908957 89.011414,104.252932 C85.2823118,98.0396202 80.2711525,93.9905379 75.163077,93.8351964 C74.0775524,93.8021846 72.9925685,93.9378881 71.9187092,94.2290372 L71.4263054,96.8428337 C70.7159074,100.613041 70.0525722,104.126731 69.4322911,107.412363 C64.7908163,131.998276 62.5529774,143.865744 61.0463793,154.851795 L60.8656498,156.191994 L60.6074832,158.19028 L60.4564922,159.410154 C59.6660213,165.902037 59.0513782,172.654885 58.2694423,182.117243 L58.1071301,184.090317 C57.9422433,186.102896 57.7692971,188.236388 57.5853124,190.511952 C54.4936668,228.750117 54.5110376,253.296254 57.6025561,277.724448 C58.251829,282.854795 58.3254515,286.119184 57.7997112,287.772531 C57.3533443,287.365152 56.8839604,286.712284 56.3626176,285.917213 C52.9105561,280.652664 48.8648383,269.062761 47.655685,256.055841 C44.8625825,226.010303 44.1900887,198.199537 45.6450201,172.623459 C47.1003171,147.040956 50.0546274,122.362378 54.5097037,98.5879441 C54.6592145,97.7900827 54.8106671,96.9918965 54.9640617,96.1933855 C55.1162766,95.4010154 55.2701489,94.6096509 55.4255602,93.8194108 C54.5845527,93.7480795 53.7194585,93.7489156 52.8332622,93.823043 C51.80541,93.9090195 50.8219304,94.0875047 49.8871243,94.3559954 C40.9569857,96.920869 32.323789,109.590977 26.0163924,128.220937 C25.5897822,129.481002 25.1964216,130.603972 24.8064948,131.717231 C23.9716729,134.100687 23.1541733,136.435766 22.0577662,139.963149 C20.3672515,146.863794 19.2883814,155.11982 17.6274898,174.892165 L17.5002077,176.41617 C17.0934812,181.313351 16.6513406,186.891553 16.1571801,193.292133 C9.8071489,188.98619 5.71118886,185.065024 4.00517668,181.431253 C1.70360048,176.528943 0.414450227,167.507602 0,154.408561 C0.00301447963,88.3903589 8.23407782,59.6337727 27.7303891,30.3791899 L35.9962997,39.4225457 C36.9078334,45.6050425 36.6691333,52.0098284 36.334543,58.397272 L36.1828539,61.2074098 C35.9059422,66.2844755 35.6468393,71.3361913 35.8481016,76.2587175 L59.2444284,76.2587175 L59.8164199,73.1824272 C62.3280788,59.6740418 63.7105033,52.2383301 64.7073785,47.2643843 C65.3214048,44.2279628 65.7161407,42.4192098 66.1158122,40.7377245 L66.5998483,38.7653101 C67.0726403,36.911647 67.4709203,35.4207871 67.9619662,33.5825073 C70.1167026,25.5160341 74.7481339,10.4984271 78.8891291,0 Z" id="font1-left" fill="#FFFFFF" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.0 KiB

3
.ncurc.json Normal file
View File

@ -0,0 +1,3 @@
{
"upgrade": true
}

8
.prettierignore Normal file
View File

@ -0,0 +1,8 @@
dist
.zeabur
node_modules
public/images
# The MDX files
*.mdx
*.md

20
.prettierrc Normal file
View File

@ -0,0 +1,20 @@
{
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 120,
"bracketSpacing": true,
"astroAllowShorthand": true,
"astroOrganizeImportsMode": "All",
"endOfLine": "lf",
"plugins": ["prettier-plugin-astro", "prettier-plugin-organize-imports", "prettier-plugin-astro-organize-imports"],
"overrides": [
{
"files": "**/*.astro",
"options": {
"parser": "astro"
}
}
]
}

4
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode", "esbenp.prettier-vscode", "biomejs.Biome"],
"unwantedRecommendations": []
}

11
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

163
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,163 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"quickfix.biome": "explicit"
},
"astro.content-intellisense": true,
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[astro]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"cSpell.words": [
"alexinea",
"alignfull",
"alignleft",
"alignright",
"alignwide",
"ameho",
"amehochan",
"aplayer",
"artalk",
"astro",
"astrojs",
"batang",
"bigserial",
"biomejs",
"blogroll",
"blogster",
"blurhash",
"bxaw",
"captainofphb",
"cfilter",
"cimage",
"ckenh",
"cnip",
"commentform",
"csvg",
"ctan",
"cynosura",
"datetime",
"dichen",
"dinesh",
"dotum",
"duoshuo",
"emde",
"eparams",
"firis",
"flexdinesh",
"fmoran",
"fong",
"forencrypt",
"giscus",
"gogogo",
"gotop",
"gungseo",
"hefei",
"heiti",
"honorbkk",
"huaweiyal",
"ianvs",
"iconfont",
"iroha",
"isodate",
"jamo",
"jian",
"jiasm",
"jing",
"jmoiron",
"jungshik",
"khalil",
"khtml",
"koanughi",
"koaunghi",
"lafoyer",
"lanczos",
"lantinghei",
"lastmod",
"lazyload",
"linuxapi",
"longluo",
"loveness",
"luoli",
"luxon",
"mboker",
"microflash",
"minagi",
"miui",
"mmwebid",
"mmwebsdk",
"mochi",
"napi",
"netease",
"nextval",
"nian",
"nocolor",
"nofollow",
"nopd",
"noto",
"opendal",
"oppo",
"opposans",
"pandiyan",
"penheulim",
"pilgi",
"plaiceholder",
"playform",
"psql",
"pwsz",
"qrcode",
"quan",
"recma",
"redmi",
"regclass",
"rehype",
"rivo",
"sauvignon",
"sheng",
"shiki",
"shinmun",
"shmily",
"skrs",
"sqlx",
"syhily",
"tabindex",
"tagcloud",
"taza",
"teruteru",
"timestamptz",
"tlyric",
"toolsmp",
"tsconfigs",
"tview",
"ultrahtml",
"unpic",
"unsharp",
"upyun",
"urlset",
"varchar",
"velite",
"vercel",
"weapi",
"wechat",
"weibo",
"xiao",
"xinsenz",
"xweb",
"yefengs",
"yetgul",
"ying",
"yuaanlin",
"yual",
"yufan",
"yyjn",
"zeabur",
"zhihu"
],
"[mdx]": {
"editor.defaultFormatter": "unifiedjs.vscode-mdx"
},
"typescript.tsdk": "node_modules/typescript/lib"
}

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Yufan Sheng
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

246
README.md Normal file
View File

@ -0,0 +1,246 @@
<picture>
<source media="(prefers-color-scheme: dark)" srcset="public/blog-poster-dark.png">
<img alt="Yufan Blog Logo" src="public/blog-poster.png">
</picture>
# Yufan Personal Weblog
This is a personal weblog for [Yufan Sheng](https://github.com/syhily)
which is built on [Astro](https://astro.build) and hosted on [zeabur](https://zeabur.com).
[![Deployed on Zeabur](https://zeabur.com/deployed-on-zeabur-dark.svg)](https://zeabur.com?referralCode=syhily&utm_source=syhily)
## History
The blog's source code has evolved through four stages. Initially, it was built on WordPress in 2011.
In 2017, I switched to Hexo and converted all my blog posts to Markdown;
the code is available in the [Hexo branch](https://github.com/syhily/yufan.me/tree/hexo).
By 2024, the blog had been rewritten using Next.js with App Router;
you can find this version in the [Next branch](https://github.com/syhily/yufan.me/tree/next).
Currently, the blog has transitioned to Astro, which is located in
the [Astro branch](https://github.com/syhily/yufan.me/tree/astro).
## Core Frameworks
- [Node.js](https://nodejs.org): The latest Node.js LTS
- [Astro](https://astro.build): Core engine
- [Artalk](https://artalk.js.org): The self-hosted comment system
- [Fuse.js](https://www.fusejs.io): Search engine
- [Postgres](https://zeabur.com/docs/marketplace/postgresql): The view counter and like button for all my posts
## Local Development
You can fork and clone this project for your own use. But do so at your own risk.
The project uses npm for development. Run it locally with these commands:
```shell
# Install the dependencies by using bun.
npm install
# Init git hooks.
npx husky
# Check the newer dependencies.
npm update
# Start local development with a live preview. The weblog is hosted on http://localhost:4321
npm run dev
```
### Postgres Database
This blog uses Postgres to store post views and favorites. For security reasons,
the configuration isn't defined in the `.env` file.
Modify the `.env.example` file and rename it to `.env` for local development.
You can create a Postgres database by installing [Postgres.app](https://postgresapp.com).
The default username is `your system username`, with no password.
Create and initialize the database and user with these commands:
```postgresql
-- Create a database.
CREATE DATABASE <db>;
-- Create a user.
CREATE USER <db_user> PASSWORD '<strong_password>';
-- Grant the connection.
GRANT CONNECT ON DATABASE <db> TO <db_user>;
-- Grant the database privilege.
GRANT ALL PRIVILEGES ON DATABASE <db> TO <db_user>;
-- If you are using Postgres 15 or above.
-- Switch to the created database and execute SQL.
GRANT ALL ON SCHEMA public TO <db_user>;
```
Most tables are created by the Artalk. [Execute the Artalk](https://artalk.js.org/guide/deploy.html) to create the
tables.
The like table should be created manually. Execute the SQL below.
```postgresql
-- Sequence and defined type
CREATE SEQUENCE IF NOT EXISTS atk_likes_id_seq;
-- Table Definition
CREATE TABLE "public"."atk_likes"
(
"id" int8 NOT NULL DEFAULT nextval('atk_likes_id_seq'::regclass),
"created_at" timestamptz,
"updated_at" timestamptz,
"deleted_at" timestamptz,
"token" varchar(255),
"page_key" varchar(255),
PRIMARY KEY ("id")
);
-- Create table index
CREATE INDEX IF NOT EXISTS "idx_atk_likes_token" ON "public"."atk_likes" ("token");
```
### Comments Integration
This weblog uses artalk as its backend comment service. But since artalk didn't provide the latest comments API.
We decide to query it directly from the Postgres database. So the comments and fav clicks are living in the same
database.
### S3 Compatible Storage Integration
This blog will upload all the built resources at build stage. You can remove this feature by removing the
`uploader` integration in `astro.config.ts`.
## Writing
All the posts should be placed in `src/content/posts` directory with MDX format.
All the pages should be placed in `src/content/pages` directory with MDX format.
You can add any scripts or other customizable features by leveraging the MDX.
### Front Matter
Front-matter is a block of YAML at the beginning of the file that is used to configure settings for your writings.
Front-matter is terminated by three dashes when written in YAML.
```yaml
---
title: Hello World
date: 2013/7/13 20:46:25
---
```
### Post Front Matter Settings
| Setting | Description | Required | Default |
|-------------|--------------------------------------|----------|----------------------|
| `slug` | ID (unique), used as the permalink | true | Filename |
| `title` | Title | true | Filename |
| `date` | Published date | true | |
| `updated` | Updated date | false | Published date |
| `comments` | Enables comment feature for the post | false | `true` |
| `tags` | Tags | false | `null` |
| `category` | Category | true | `null` |
| `summary` | Post summary in plain text | false | First 140 characters |
| `cover` | The cover image | false | `null` |
| `published` | Whether the post should be published | false | `true` |
| `toc` | Display the Table of Contents | false | `false` |
| `alias` | The alternatives slugs for post | false | `[]` |
### Pages Front Matter Settings
| Setting | Description | Required | Default |
|-------------|--------------------------------------|----------|----------------|
| `slug` | ID (unique), used as the permalink | true | Filename |
| `title` | Title | true | Filename |
| `date` | Published date | true | |
| `updated` | Updated date | false | Published date |
| `comments` | Enables comment feature for the post | false | `true` |
| `cover` | The cover image | false | `null` |
| `published` | Whether the post should be published | false | `true` |
| `toc` | Display the Table of Contents | false | `false` |
## Weblog Design
Almost all the design resources are placed in the file [yufan.me.sketch](docs/yufan.me.sketch).
I mainly use the [Sketch](https://www.sketch.com) as my design tools.
The favicon is almost the same as the weblog logo. The main different is that we simplify the elements used in logo.
Pick up the main park from the logo and change the dot color for readability in small icon.
The background color is included in the exported favicon.
That is because we want to make sure it could be viewed clearly in any browser.
The size of the favicon is following this
[tutorial](https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs)
to get it worked everywhere.
## Deploy the Weblog
This weblog is deployed on the [zeabur](https://zeabur.com) platform.
You can check their documents and get your own weblog to be published without any budget at first.
Or you can host on your own machine. Use [Dockerfile](./Dockerfile) to build an image and run it locally.
The comment system is leverage the [Artalk](https://artalk.js.org), a self-hosted comment system.
You should host it on your own machine.
## Short-Term TODO Checklist
- [ ] Add last modified time component for post.
- [ ] Add light box for images and album component.
- [ ] Check article grammar errors by using ChatGPT. Remain **42** posts.
- [ ] Add music to the articles. Remain **42** posts.
## Long-Term TODO Checklist
- [ ] Use self-developed comment solution.
- [ ] Support modification after commenting in 60 minutes even if you have refreshed the page.
- [ ] Support login into the blog for managing the comments.
- [ ] Slide share components integration.
- [ ] Add han.js support for better typography.
- [ ] Drop bootstrap, in favor of tailwind css.
## License
The source code of this blog is licensed under the [MIT](LICENSE) license,
feel to free to use it without any legal risks.
The [content](src/content) of this blog's posts is licensed under the
[CC BY-NC-SA 4.0](src/content/LICENSE) license.
### Logo Fonts License
The fonts used in weblog logo is [M+A1](https://booth.pm/ja/items/2347968) with [license](licenses/LICENSE.m-plus.txt),
[UnGungseo](https://kldp.net/unfonts) with [license](licenses/LICENSE.un-fonts.txt),
and [Iroha Mochi](https://modi.jpn.org/font_iroha-mochi.php) with [license](licenses/LICENSE.iroha-mochi.txt).
They are the fonts that can be used in business without any charge.
### Open Graph Font & Web Font License
The [OPPOSans 4.0](https://open.oppomobile.com/new/developmentDoc/info?id=13223)
is used for rendering the open graph image in my weblog.
We also use it as the main web font for articles.
It can be used in business scenarios without any modification.
The license file is [here](licenses/LICENSE.opposans.txt)
### Third Party Codes License
Some codes in this project are copied from other projects. I have added the comments in the files' header.
The source codes used from third party projects are:
- [seo.ts](src/helpers/seo.ts)
from [flexdinesh/blogster](https://github.com/flexdinesh/blogster/blob/main/packages/shared/src/seo.ts)
with [license](licenses/LICENSE.flexdinesh.txt)
- [og.ts](src/helpers/og.ts)
from [yuaanlin/yual.in](https://github.com/yuaanlin/yual.in/blob/main/pages/og_image/%5Bslug%5D.tsx)
with [permission](licenses/LICENSE.yuaanlin.jpg)
- [images.ts](src/helpers/images.ts)
and [config.ts](src/content/config.ts)
from [zce/velite](https://github.com/zce/velite/blob/main/src/assets.ts)
with [license](licenses/LICENSE.zce.txt)
- [images.ts](src/helpers/images.ts)
from [vercel/next.js](https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/image-blur-svg.ts)
with [license](licenses/LICENSE.vercel.txt)

80
astro.config.ts Normal file
View File

@ -0,0 +1,80 @@
import mdx from '@astrojs/mdx';
import zeabur from '@zeabur/astro-adapter/serverless';
import { uploader } from 'astro-uploader';
import { defineConfig, envField } from 'astro/config';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import rehypeExternalLinks from 'rehype-external-links';
import rehypeSlug from 'rehype-slug';
import options from './options';
import { astroImage } from './plugins/images';
// https://astro.build/config
export default defineConfig({
// This will override the import.meta.env.SITE. No need to introduce method.
site: options.isProd() ? options.website : options.local.website,
output: 'server',
security: {
checkOrigin: true,
},
image: {
domains: ['localhost', '127.0.0.1'],
service: !options.isProd() ? { entrypoint: './plugins/resize', config: {} } : undefined,
},
experimental: {
contentLayer: true,
contentIntellisense: true,
env: {
schema: {
// Postgres Database
POSTGRES_HOST: envField.string({ context: 'server', access: 'secret' }),
POSTGRES_PORT: envField.number({ context: 'server', access: 'secret' }),
POSTGRES_USERNAME: envField.string({ context: 'server', access: 'secret' }),
POSTGRES_PASSWORD: envField.string({ context: 'server', access: 'secret' }),
POSTGRES_DATABASE: envField.string({ context: 'server', access: 'secret' }),
// Artalk Comment
ARTALK_HOST: envField.string({ context: 'server', access: 'secret' }),
},
validateSecrets: true,
},
},
integrations: [
mdx({
remarkPlugins: [astroImage],
rehypePlugins: [
[rehypeExternalLinks, { rel: 'nofollow', target: '_blank' }],
rehypeSlug,
[rehypeAutolinkHeadings, { behavior: 'append', properties: {} }],
],
}),
uploader({
paths: ['images', 'og', 'cats'],
endpoint: process.env.S3_ENDPOINT,
bucket: process.env.S3_BUCKET as string,
accessKey: process.env.S3_ACCESS_KEY as string,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY as string,
}),
],
adapter: zeabur(),
markdown: {
gfm: true,
shikiConfig: {
theme: 'solarized-light',
wrap: false,
},
},
server: {
port: options.local.port,
},
devToolbar: {
// I don't need such toolbar.
enabled: false,
},
vite: {
// Add this for avoiding the needless import optimize in Vite.
optimizeDeps: { exclude: ['@napi-rs/canvas', 'opendal'] },
},
build: {
assets: 'cats',
assetsPrefix: options.assetsPrefix(),
},
});

23
biome.json Normal file
View File

@ -0,0 +1,23 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"formatter": {
"enabled": false
},
"organizeImports": {
"enabled": false
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"a11y": {
"useGenericFontNames": "off"
}
}
},
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
}
}

18
docs/Dockerfile Normal file
View File

@ -0,0 +1,18 @@
FROM node:lts AS base
WORKDIR /app
COPY package.json package-lock.json ./
FROM base AS build
COPY . .
ENV ASTRO_TELEMETRY_DISABLED=1
RUN NODE_ENV=development npm install
RUN NODE_ENV=production npm run build
FROM base AS runtime
RUN npm install --omit=dev
COPY --from=build /app/dist ./dist
ENV HOST=0.0.0.0
ENV PORT=4321
EXPOSE 4321
CMD NODE_ENV=production node ./dist/server/entry.mjs

BIN
docs/yufan.me.sketch Normal file

Binary file not shown.

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Dinesh Pandiyan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,64 @@
いろはモチ ver.1.00
OpenType形式
公開日 2018.01.12
MODI工場倒神神倒
紹介・詳細ページ
http://modi.jpn.org/font_iroha-mochi.php
----------------------------------------------
懐かしい絵本みたいなひらがなフォントです。
ウェイト(太さ)は1種類のみです。
----------------------------------------------
## 仕様・収録文字について
−ひらがな・カタカナ
illustratorやInDesignの合成フォント機能における「かな」に含まれる全文字です。
−おまけでいくつか基本的な約物(記号)
ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゝゞ
ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶーヽヾ
濁点付きの『う』
小書きの『か』
小書きの『け』
濁点付きの『ワ』
濁点付きの『ヰ』
濁点付きの『ヱ』
濁点付きの『ヲ』
"',{}、。,.・:;?!゛゜´`¨^ ̄_〆ー—‐/〜|…‥‘’“”()〔〕[]{}〈〉《》「」『』【】′″
----------------------------------------------
## ライセンスについて
「いろはモチ」は、MODI工場独自ライセンスです。
−連絡表記不要。商用、非商用問わずご自由にどうぞ。素材集書籍などへの収録も同様です。
通常利用、商用利用、改変、再配布、埋め込みもOK。とにかくご自由にどうぞ。
−オリジナル・改造フォントともに、フォント単体で有料販売してはいけません。
----------------------------------------------
## 免責事項
このフォントの使用に関してはすべて無保証とさせていただきます。
----------------------------------------------
## 更新履歴
■2018.01.12 v1.00
公開
----------------------------------------------
## MODI工場ホームページ
http://modi.jpn.org/
## 連絡先
modi@modi.jpn.org
誤字などのミスがあったら教えていただければ幸いです。
お返事はできないですがご了承ください。

View File

@ -0,0 +1,10 @@
M+ FONTS Copyright (C) 2002-2019 M+ FONTS PROJECT
-
These fonts are free software.
Unlimited permission is granted to use, copy, and distribute them, with
or without modification, either commercially or noncommercially.
THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
http://mplus-fonts.osdn.jp

View File

@ -0,0 +1,34 @@
License Notice
Copyright 2024 Guangdong OPPO Mobile Telecommunications Corp., Ltd.
OPPO Sans Fonts Software is licensed under OPPO Sans Fonts License Agreement.
--------------------------------------------------------------------------------
OPPO Sans Fonts License Agreement
THIS OPPO SANS FONTS LICENSE AGREEMENT ("AGREEMENT") IS A LEGAL AGREEMENT BETWEEN YOU (EITHER A SINGLE INDIVIDUAL, OR SINGLE LEGAL ENTITY) AND GUANGDONG OPPO MOBILE TELECOMMUNICATION CORP.,LTD. ( "LICENSOR") FOR THE USE OF THE OPPO SANS FONTS ACCOMPANYING THIS AGREEMENT. BY DOWNLOADING, COPYING OR OTHERWISE USING OPPO SANS FONTS YOU INDICATE THAT YOU AGREE TO BE BOUND BY ALL OF THE TERMS OF THIS AGREEMENT. IF YOU DO NOT AGREE TO THE TERMS OF THIS AGREEMENT, YOU MAY NOT DOWNLOAD, COPY OR OTHERWISE USE THE OPPO SANS FONTS, AND YOU SHALL PROMPTLY DESTROY AND DELETE ALL THE OPPO SANS FONTS.
1.Definition
"OPPO Sans Fonts" shall mean the collection of fonts software components provided by Licensor under this Agreement and clearly marked as "OPPO Sans".
"YOU" shall mean an individual or legal entity exercising permissions granted by this Agreement.
2.GRANT OF LICENSE
Subject to the terms and conditions of this Agreement, Licensor hereby grant YOU a non-transferable, non-exclusive, royalty-free, revocable, worldwide copyright license to use, copy, merge, embed, bundle, redistribute and/or sell unmodified copies of OPPO Sans Fonts with any software except for fonts softwaresubject to the following conditions:
1)YOU shall make a prominent notice in the software to state that OPPO Sans Fonts are used.
2)YOU may not make any modifications to OPPO Sans Fonts or any of their individual components.
3)Neither OPPO Sans Fonts nor any of their individual components may be redistributed or sold in a stand-alone base. This limitation does not apply to any work created by using OPPO Sans Fonts. You can freely distribute or sell your work, such as materials, logos, application software etc. created by using OPPO Sans Fonts.
4)YOU shall retain the copyright notice and this Agreement in any copies of OPPO Sans Fonts.
3.Termination
This Agreement will be automatically terminated if YOU breach any of this Agreement.
4.NO WARRANTY
YOU AGREE THAT THE OPPO SANS FONTS ARE PROVIDED BY LICENSOR ON AN "AS IS" BASIS. LICENSOR MAKES NO WARRANTY, EXPRESSED OR IMPLIED OR STATUTORY, WITH RESPECT TO ANY OF THE OPPO SANS FONTS, INCLUDING WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.
5.NO LIABILITY
IN NO EVENT SHALL LICENSOR BE LIABLE FOR ANY DIRECT OR INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE OF OR INABILITY TO USE OPPO SANS FONTS, WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHER LEGAL THEORY, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
YOU EXPRESSLY ASSUME ALL LIABILITIES AND RISKS FOR USE OF OPPO SANS FONTS. SHOULD THE OPPO SANS FONTS PROVE DEFECTIVE, YOU ASSUME THE ENTIRE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

View File

@ -0,0 +1,37 @@
Copyright (c) 1998-2004 Koanughi Un.
Please see the enclosed file COPYING for license information (GPL). If you
did not receive this file, see http://www.gnu.org/licenses/gpl.txt.
Un-fonts is comes from the HLaTeX as type1 fonts in 1998 by Koaunghi Un,
he made type1 fonts to use with Korean TeX(HLaTeX [1]) in the late 1990's and
release it under the GNU GPL license.
It converted to TrueType with the FontForge(PfaEdit) by Won-kyu Park in 2003.
Core families (9 fonts)
* UnBatang, UnBatangBold: serif
* UnDotum, UnDotumBold: sans-serif
* UnGraphic, UnGraphicBold: sans-serif style
* UnPilgi, UnPilgiBold: script
* UnGungseo: cursive, brush-stroke
Extra families (10 fonts)
* UnPen, UnPenheulim: script
* UnTaza: typewriter style
* UnBom: decorative
* UnShinmun
* UnYetgul: old Korean printing style
* UnJamoSora, UnJamoNovel, UnJamoDotum, UnJamoBatang
Please send problems and feedback at http://kldp.net/projects/unfonts/
Project maintainers:
* Won-kyu Park <wkpark@kldp.org>
* Jungshik Shin <jshin@mailaps.org>
----
[1] http://ftp.dante.de/tex-archive/fonts/korean/HLaTeX and other CTAN archives)

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2024 Vercel, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

21
licenses/LICENSE.zce.txt Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2024 zce <w@zce.me> (https://zce.me)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

221
options.ts Normal file
View File

@ -0,0 +1,221 @@
import { z } from 'astro/zod';
const isProd = (): boolean => import.meta.env.MODE === 'production' || process.env.NODE_ENV === 'production';
// The type of the options, use zod for better validation.
const Options = z
.object({
local: z
.object({
port: z.number(),
})
.transform((local) => ({ ...local, website: `http://localhost:${local.port}` })),
title: z.string().max(40),
website: z
.string()
.url()
.refine((u) => !u.endsWith('/'))
.readonly(),
description: z.string().max(100),
keywords: z.array(z.string()),
author: z.object({ name: z.string(), email: z.string().email(), url: z.string().url() }),
navigation: z.array(z.object({ text: z.string().trim(), link: z.string(), target: z.string().optional() })),
socials: z.array(
z.object({
name: z.string(),
icon: z.string(),
type: z.enum(['link', 'qrcode']),
title: z.string().optional(),
link: z.string().url(),
}),
),
settings: z.object({
footer: z.object({
initialYear: z.number().max(2024),
icpNo: z.string().optional(),
moeIcpNo: z.string().optional(),
astroBadge: z.boolean().default(true),
}),
locale: z.string().optional().default('zh-CN'),
timeZone: z.string().optional().default('Asia/Shanghai'),
timeFormat: z.string().optional().default('yyyy-MM-dd HH:mm:ss'),
twitter: z.string(),
assetPrefix: z
.string()
.url()
.refine((u) => !u.endsWith('/'))
.readonly(),
post: z.object({
sort: z.enum(['asc', 'desc']),
feature: z.array(z.string()).min(3).optional(),
category: z.array(z.string()).optional(),
}),
pagination: z.object({
posts: z.number().optional().default(5),
category: z.number().optional().default(7),
tags: z.number().optional().default(7),
search: z.number().optional().default(7),
}),
feed: z.object({
full: z.boolean().optional().default(true),
size: z.number().optional().default(20),
}),
sidebar: z.object({
search: z.boolean().default(false),
post: z.number().default(6),
comment: z.number().default(0),
tag: z.number().default(20),
}),
comments: z.object({
server: z.string().url().readonly(),
size: z.number().default(10).readonly(),
avatar: z.object({
mirror: z.string().url().readonly(),
size: z.number(),
}),
}),
toc: z.object({
minHeadingLevel: z.number().optional().default(2),
maxHeadingLevel: z.number().optional().default(3),
}),
}),
thumbnail: z
.function()
.args(z.object({ src: z.string().min(1), width: z.number().or(z.string()), height: z.number().or(z.string()) }))
.returns(z.string()),
})
.transform((opts) => {
const assetsPrefix = (): string => (isProd() ? opts.settings.assetPrefix : opts.local.website);
return {
...opts,
// Monkey patch for the issue https://github.com/withastro/astro/issues/11282
// No need to fallback to the import.meta.env.PROD I think.
isProd,
// Given that the import.meta.env.ASSETS_PREFIX has two types.
// I have to use this uniform method instead.
assetsPrefix,
defaultOpenGraph: (): string => `${assetsPrefix()}/images/open-graph.png`,
};
})
.refine(
(options) => options.settings.toc.minHeadingLevel <= options.settings.toc.maxHeadingLevel,
'Invalid toc setting, the minHeadingLevel should bellow the maxHeadingLevel',
);
const options: z.input<typeof Options> = {
local: {
port: 4321,
},
title: '且听书吟',
website: 'https://yufan.me',
description: '诗与梦想的远方',
keywords: ['雨帆', '且听书吟', 'syhily', 'amehochan', 'yufan'],
author: {
name: '雨帆',
email: 'syhily@gmail.com',
url: 'https://yufan.me',
},
navigation: [
{
text: '首页',
link: '/',
},
{
text: '关于',
link: '/about',
},
{
text: '留言',
link: '/guestbook',
},
{
text: '友链',
link: '/links',
},
{
text: '笔记',
link: 'https://note.yufan.me',
target: '_blank',
},
],
socials: [
{
name: 'GitHub',
icon: 'icon-github-fill',
type: 'link',
link: 'https://github.com/syhily',
},
{
name: '知乎',
icon: 'icon-zhihu-square-fill',
type: 'link',
link: 'https://www.zhihu.com/people/syhily',
},
{
name: '微信',
icon: 'icon-wechat',
type: 'qrcode',
title: '扫码加我微信好友',
link: 'https://u.wechat.com/EBpmuKmrVz4YVFnoCJdnruA',
},
],
settings: {
footer: {
initialYear: 2011,
icpNo: '皖ICP备2021002315号-2',
moeIcpNo: '萌ICP备20200318号',
astroBadge: true,
},
locale: 'zh-CN',
timeZone: 'Asia/Shanghai',
timeFormat: 'yyyy-MM-dd',
twitter: 'amehochan',
assetPrefix: 'https://cat.yufan.me',
post: {
sort: 'desc',
category: ['article', 'think', 'gossip', 'coding'],
},
pagination: {
posts: 5,
category: 7,
tags: 7,
search: 7,
},
feed: {
full: true,
size: 10,
},
sidebar: {
search: true,
post: 6,
comment: 6,
tag: 20,
},
comments: {
server: 'https://comment.yufan.me',
size: 10,
avatar: {
mirror: 'https://weavatar.com/avatar',
size: 120,
},
},
toc: {
minHeadingLevel: 2,
maxHeadingLevel: 3,
},
},
thumbnail: ({ src, width, height }) => {
if (src.endsWith('.svg')) {
return src;
}
if (isProd()) {
// Add upyun thumbnail support.
return `${src}!upyun520/both/${width}x${height}/format/webp/quality/100/unsharp/true/progressive/true`;
}
// See https://docs.astro.build/en/reference/image-service-reference/#local-services
// Remember to add the localhost to you image service settings.
return `http://localhost:4321/_image?href=${src}&w=${width}&h=${height}&f=webp&q=100`;
},
};
export default Options.parse(options);

9644
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

88
package.json Normal file
View File

@ -0,0 +1,88 @@
{
"name": "yufan.me",
"version": "3.0.0",
"private": true,
"description": "The personal blog for Yufan Sheng",
"keywords": [
"blog",
"astro",
"yufan",
"ameho",
"syhily"
],
"homepage": "https://yufan.me",
"bugs": {
"url": "https://github.com/syhily/yufan.me/issues",
"email": "syhily@gmail.com"
},
"repository": {
"type": "git",
"url": "https://github.com/syhily/yufan.me.git"
},
"license": "MIT",
"author": {
"name": "Yufan Sheng",
"email": "syhily@gmail.com",
"url": "https://yufan.me"
},
"type": "module",
"scripts": {
"astro": "astro",
"build": "rimraf dist && rimraf .zeabur && astro check && astro build",
"dev": "astro dev",
"lint": "biome check --write . && prettier . --write",
"prepare": "npx husky",
"preview": "astro preview",
"start": "astro dev"
},
"lint-staged": {
"**/*": [
"biome check --no-errors-on-unmatched --files-ignore-unknown=true",
"prettier --write --ignore-unknown"
]
},
"dependencies": {
"@astrojs/mdx": "^3.1.9",
"@astrojs/rss": "^4.0.9",
"@zeabur/astro-adapter": "^1.0.6",
"astro": "^4.16.12",
"drizzle-orm": "^0.36.2",
"fuse.js": "^7.0.0",
"lodash": "^4.17.21",
"luxon": "^3.5.0",
"marked": "^15.0.0",
"pg": "^8.13.1",
"qrcode-svg": "^1.1.0",
"ultrahtml": "^1.5.3"
},
"devDependencies": {
"@astrojs/check": "^0.9.4",
"@biomejs/biome": "^1.9.4",
"@napi-rs/canvas": "^0.1.61",
"@types/lodash": "^4.17.13",
"@types/luxon": "^3.4.2",
"@types/node": "^22.9.0",
"@types/pg": "^8.11.10",
"@types/qrcode-svg": "^1.1.5",
"@types/unist": "^3.0.3",
"aplayer": "^1.10.1",
"astro-uploader": "^1.1.3",
"bootstrap": "^5.3.3",
"prettier": "^3.3.3",
"prettier-plugin-astro": "^0.14.1",
"prettier-plugin-astro-organize-imports": "^0.4.11",
"prettier-plugin-organize-imports": "^4.1.0",
"rehype-autolink-headings": "^7.1.0",
"rehype-external-links": "^3.0.0",
"rehype-slug": "^6.0.0",
"resize-sensor": "^0.0.6",
"rimraf": "^6.0.1",
"sharp": "^0.33.5",
"typescript": "^5.6.3",
"unist-util-select": "^5.1.0"
},
"packageManager": "npm@10.9.0",
"engines": {
"node": "22.11.0"
}
}

59
plugins/images.ts Normal file
View File

@ -0,0 +1,59 @@
import type { Literal, Node, Parent } from 'unist';
import { selectAll } from 'unist-util-select';
import options from '../options';
import { imageMetadata } from '../src/helpers/images';
import { urlJoin } from '../src/helpers/tools';
type ImageNode = Parent & {
url: string;
alt: string;
name: string;
width?: number;
height?: number;
attributes: (Literal & { name: string })[];
};
export const astroImage = () => {
return async (tree: Node) => {
// Find all the image node.
const imageNodes = selectAll('image', tree)
.map((node) => node as ImageNode)
.filter((imageNode) => !imageNode.url.startsWith('http'))
.map(transformAstroImage);
// Process image with blur metadata.
await Promise.all(imageNodes);
return tree;
};
};
const transformAstroImage = async (imageNode: ImageNode) => {
imageNode.type = 'mdxJsxFlowElement';
imageNode.name = 'Image';
try {
const metadata = await imageMetadata(imageNode.url);
if (metadata == null) {
throw new Error(`Failed to get image metadata: ${imageNode.url}`);
}
imageNode.attributes = [
{ type: 'mdxJsxAttribute', name: 'alt', value: imageNode.alt },
{ type: 'mdxJsxAttribute', name: 'src', value: metadata.src },
{ type: 'mdxJsxAttribute', name: 'width', value: imageNode.width ?? metadata.width },
{ type: 'mdxJsxAttribute', name: 'height', value: imageNode.height ?? metadata.height },
{ type: 'mdxJsxAttribute', name: 'blurDataURL', value: metadata.blurDataURL },
{ type: 'mdxJsxAttribute', name: 'blurWidth', value: metadata.blurWidth },
{ type: 'mdxJsxAttribute', name: 'blurHeight', value: metadata.blurHeight },
];
} catch (error) {
imageNode.attributes = [
{ type: 'mdxJsxAttribute', name: 'alt', value: imageNode.alt },
{
type: 'mdxJsxAttribute',
name: 'src',
value: imageNode.url.startsWith('/') ? urlJoin(options.assetsPrefix(), imageNode.url) : imageNode.url,
},
];
}
};

91
plugins/resize.ts Normal file
View File

@ -0,0 +1,91 @@
import type { ImageOutputFormat, ImageQualityPreset, LocalImageService } from 'astro';
import { baseService } from 'astro/assets';
const qualityTable: Record<ImageQualityPreset, number> = {
low: 25,
mid: 50,
high: 80,
max: 100,
};
const parseQuality = (quality: string): string | number => {
const result = Number.parseInt(quality);
if (Number.isNaN(result)) {
return quality;
}
return result;
};
type BaseServiceTransform = {
src: string;
width?: number;
height?: number;
format: string;
quality?: string | null;
};
interface SharpImageServiceConfig {
/**
* The `limitInputPixels` option passed to Sharp. See https://sharp.pixelplumbing.com/api-constructor for more information
*/
limitInputPixels?: import('sharp').SharpOptions['limitInputPixels'];
}
const imageService: LocalImageService<SharpImageServiceConfig> = {
getURL: baseService.getURL,
getSrcSet: baseService.getSrcSet,
getHTMLAttributes: baseService.getHTMLAttributes,
validateOptions: baseService.validateOptions,
parseURL: baseService.parseURL,
async transform(inputBuffer, transformOptions, config) {
const { default: sharp } = await import('sharp');
const transform: BaseServiceTransform = transformOptions as BaseServiceTransform;
// Sharp has some support for SVGs, we could probably support this once Sharp is the default and only service.
if (transform.format === 'svg') return { data: inputBuffer, format: 'svg' };
const result = sharp(inputBuffer, {
failOnError: false,
pages: -1,
limitInputPixels: config.service.config.limitInputPixels,
});
result.rotate();
// Never resize using both width and height at the same time, prioritizing width.
if (transform.height) {
if (!transform.width) {
result.resize({ height: Math.round(transform.height) });
} else {
// Allow the width and height to be set.
result.resize({ width: Math.round(transform.width), height: Math.round(transform.height) });
}
} else if (transform.width) {
result.resize({ width: Math.round(transform.width) });
}
if (transform.format) {
let quality: number | string | undefined = undefined;
if (transform.quality) {
const parsedQuality = parseQuality(transform.quality);
if (typeof parsedQuality === 'number') {
quality = parsedQuality;
} else {
quality = transform.quality in qualityTable ? qualityTable[transform.quality] : undefined;
}
}
result.toFormat(transform.format as keyof import('sharp').FormatEnum, { quality: quality });
}
const { data, info } = await result.toBuffer({ resolveWithObject: true });
return {
data: data,
format: info.format as ImageOutputFormat,
};
},
};
export default imageService;

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

BIN
public/blog-poster-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
public/blog-poster.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

12
public/favicon.svg Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="300px" height="300px" viewBox="0 0 300 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>favicon</title>
<g id="favicon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#263148" x="0" y="0" width="300" height="300" />
<g id="logo-white" transform="translate(9, 3.888)">
<path d="M159,154.195523 C167.013592,152.617424 174.05936,152.751451 180.129673,154.674602 C190.120795,157.839913 194.348511,161.02564 199.517296,168.40144 C202.925628,173.265093 205.718707,181.861769 208,194.149433 C202.817237,195.503225 198.494331,195.463815 195.074572,193.809733 C188.963104,190.853715 183.30068,186.732689 175.022826,177.505654 C169.768056,171.648346 164.430736,163.875789 159,154.195523 Z" id="font1-dot" fill="#EEB4B4" />
<path d="M151.486676,25.701431 C155.616373,28.5082875 160.109234,31.601356 164.965286,34.9806001 C165.393463,35.2785613 165.822641,35.5757097 166.255249,35.8738863 L167.105716,36.458366 C168.619019,37.4954138 170.108644,38.5008303 171.499238,39.4303971 L172.608374,40.1697969 C174.26724,41.2727003 175.735093,42.2354804 176.860606,42.9690389 L176.448028,45.1207071 L166.710146,84.6326462 C180.591569,66.4261557 193.885083,57.2272012 206.997372,58.6211808 C208.743695,58.806834 210.475625,59.0033727 212.18144,59.2464237 C212.733943,59.3251464 213.291751,59.4029337 213.853878,59.4813177 C215.158468,59.6632319 216.474026,59.8465926 217.787079,60.0508718 L218.661915,60.1902579 C225.09927,61.241972 231.826526,62.8614549 236.986443,68.0144332 C240.889249,71.9119921 243.813448,77.8014483 245.23642,86.708071 C247.095663,98.3453903 243.120654,117.723729 238.770002,139.330906 L238.580551,140.271866 C237.890376,143.700241 237.191343,147.180322 236.50179,150.693982 L236.044022,153.039529 C235.556986,155.549827 235.077075,158.073824 234.610494,160.607168 L234.263405,162.507565 L233.922672,164.406776 C228.900089,192.67064 226.130123,221.241925 234.44056,240.371255 C240.652266,254.669635 245.935235,262.932333 250.296038,267.670416 C253.412465,271.056463 256.180453,272.79959 258.553083,273.698031 C262.383549,275.148509 265.383302,274.528886 267.668585,273.982958 C268.14919,273.865733 268.422366,273.767797 268.665926,273.767797 C271.112421,273.767797 275.809409,272.387068 282.731388,269.370175 C283.375366,277.48654 282.878184,284.333295 281.156359,289.899262 C280.659157,291.506519 280.558961,292.472131 279.535062,293.024782 C275.748117,295.068793 270.498503,293.963559 265.488631,292.694915 C256.722323,290.485677 244.816282,287.548472 234.696635,275.178958 C233.697221,273.957347 232.737161,272.794212 231.813783,271.675534 C222.758366,260.704951 217.370091,254.220034 212.983093,237.93167 C209.908658,226.516685 208.870828,218.179476 208.826484,204.259903 L208.825284,202.840608 C208.836884,194.672214 209.157933,184.633261 209.605114,171.185381 C210.376069,148.00078 213.79261,132.71409 216.334239,121.332289 C218.054209,113.629996 219.354132,107.626433 219.354132,102.212372 C219.354132,100.105514 219.634597,97.1568247 219.926173,94.0567601 L220.027967,92.9734861 C220.416501,88.8241101 220.77388,84.5552796 220.633396,81.5249303 C220.563268,80.0122213 220.36004,78.7458615 220.056532,77.8111784 C219.39849,75.7846738 218.218924,74.7926623 217.050886,74.319003 C213.016784,72.6831053 207.083752,73.8938983 200.767445,77.2643023 C193.5468,81.1172643 185.881323,87.4227388 181.226749,92.1251087 C170.078149,103.388189 168.197052,106.181552 163.449564,130.566909 C160.514746,145.641534 157.152557,190.083423 153.408527,263.882996 C148.310625,260.529506 144.573436,256.539474 142.267189,251.849384 C137.44815,242.049164 135.781889,238.498147 133.16629,221.130117 C131.151349,207.750561 132.216961,189.426224 132.839043,178.568221 C133.052135,174.848508 133.207609,172.07036 133.207609,170.48586 C133.207609,164.09523 134.233106,147.312148 137.188741,122.72916 C137.780573,117.806695 138.265493,113.776123 138.700893,110.283341 L138.823994,109.299731 C139.331717,105.259782 139.777004,101.934213 140.256685,98.7255887 L140.372518,97.9574584 L140.582929,96.5951823 L140.761224,95.4724241 L140.885706,94.7044198 C141.871689,88.670391 143.122232,82.1576033 145.272592,71.2270532 C148.154429,56.5783156 150.225956,41.4023262 151.486676,25.701431 Z" id="font1-right" fill="#FFFFFF" />
<path d="M78.8891291,0 L95.8005079,20.9095227 C95.225924,21.6575488 94.6661502,22.3687531 94.1213578,23.0610595 C90.498037,27.665481 87.5143717,31.5165865 85.0892155,38.8498924 C84.5933419,40.3493393 84.11666,41.7284904 83.6520528,43.0728622 C83.5490334,43.3709557 83.4494774,43.6590286 83.3486814,43.9513373 L82.9235478,45.1899741 L82.6412306,46.0224097 L82.2485912,47.2004158 C80.4111972,52.7806913 78.6427308,59.1891547 76.4859025,71.9475546 C76.3776982,72.5876213 76.2738377,73.2121994 76.1751238,73.8203757 L75.8128598,76.0522841 L78.072301,76.1393226 C78.6520627,76.1616563 79.233139,76.1970078 79.8150176,76.2458239 C87.4717734,76.8881795 94.6057156,79.8018442 100.240592,83.8134948 C106.031451,87.9361953 110.302115,93.214716 111.609957,98.4281539 C115.240663,112.901207 110.993666,140.365178 108.836316,154.571762 C108.454224,157.088603 108.185012,158.862829 107.988397,160.271632 C107.819607,161.481054 107.625793,162.940163 107.407149,164.586499 C106.781752,169.295591 105.954511,175.526376 104.92697,181.804864 L104.678362,183.301099 C104.604458,183.739061 104.529572,184.176727 104.455334,184.604273 L104.223432,185.919703 L103.986615,187.248058 C102.465647,195.80355 101.02459,204.149885 98.485251,205.777632 C97.1963754,206.603817 95.9047926,207.065865 94.5842529,207.065865 C93.8588313,207.065865 93.1344185,206.927653 92.4069507,206.668298 L95.4781875,150.606035 L95.4781875,125.416711 C95.4781875,119.629476 93.0062228,110.908957 89.011414,104.252932 C85.2823118,98.0396202 80.2711525,93.9905379 75.163077,93.8351964 C74.0775524,93.8021846 72.9925685,93.9378881 71.9187092,94.2290372 L71.4263054,96.8428337 C70.7159074,100.613041 70.0525722,104.126731 69.4322911,107.412363 C64.7908163,131.998276 62.5529774,143.865744 61.0463793,154.851795 L60.8656498,156.191994 L60.6074832,158.19028 L60.4564922,159.410154 C59.6660213,165.902037 59.0513782,172.654885 58.2694423,182.117243 L58.1071301,184.090317 C57.9422433,186.102896 57.7692971,188.236388 57.5853124,190.511952 C54.4936668,228.750117 54.5110376,253.296254 57.6025561,277.724448 C58.251829,282.854795 58.3254515,286.119184 57.7997112,287.772531 C57.3533443,287.365152 56.8839604,286.712284 56.3626176,285.917213 C52.9105561,280.652664 48.8648383,269.062761 47.655685,256.055841 C44.8625825,226.010303 44.1900887,198.199537 45.6450201,172.623459 C47.1003171,147.040956 50.0546274,122.362378 54.5097037,98.5879441 C54.6592145,97.7900827 54.8106671,96.9918965 54.9640617,96.1933855 C55.1162766,95.4010154 55.2701489,94.6096509 55.4255602,93.8194108 C54.5845527,93.7480795 53.7194585,93.7489156 52.8332622,93.823043 C51.80541,93.9090195 50.8219304,94.0875047 49.8871243,94.3559954 C40.9569857,96.920869 32.323789,109.590977 26.0163924,128.220937 C25.5897822,129.481002 25.1964216,130.603972 24.8064948,131.717231 C23.9716729,134.100687 23.1541733,136.435766 22.0577662,139.963149 C20.3672515,146.863794 19.2883814,155.11982 17.6274898,174.892165 L17.5002077,176.41617 C17.0934812,181.313351 16.6513406,186.891553 16.1571801,193.292133 C9.8071489,188.98619 5.71118886,185.065024 4.00517668,181.431253 C1.70360048,176.528943 0.414450227,167.507602 0,154.408561 C0.00301447963,88.3903589 8.23407782,59.6337727 27.7303891,30.3791899 L35.9962997,39.4225457 C36.9078334,45.6050425 36.6691333,52.0098284 36.334543,58.397272 L36.1828539,61.2074098 C35.9059422,66.2844755 35.6468393,71.3361913 35.8481016,76.2587175 L59.2444284,76.2587175 L59.8164199,73.1824272 C62.3280788,59.6740418 63.7105033,52.2383301 64.7073785,47.2643843 C65.3214048,44.2279628 65.7161407,42.4192098 66.1158122,40.7377245 L66.5998483,38.7653101 C67.0726403,36.911647 67.4709203,35.4207871 67.9619662,33.5825073 C70.1167026,25.5160341 74.7481339,10.4984271 78.8891291,0 Z" id="font1-left" fill="#FFFFFF" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.0 KiB

68
public/feed.xsl Normal file

File diff suppressed because one or more lines are too long

BIN
public/icon-192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
public/icon-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

Some files were not shown because too many files have changed in this diff Show More