fix: image relative path in rss feed, modify the path of the local images

-
This commit is contained in:
radishzzz 2025-05-10 21:59:45 +01:00
parent 4d3ce1f73f
commit 385b5508aa
27 changed files with 220 additions and 164 deletions

View file

@ -1,5 +1,5 @@
<img alt="Cover Image" src="assets/retypeset-en-desktop.webp"/>
<img alt="Cover Image" src="assets/retypeset-en-mobile.webp"/>
<img alt="Cover Image" src="images/retypeset-en-desktop.webp"/>
<img alt="Cover Image" src="images/retypeset-en-mobile.webp"/>
<div align="center">
<picture>
@ -44,8 +44,8 @@ Retypeset is a static blog theme based on the [Astro](https://astro.build/) fram
<br>
<p align="center">
<a href="https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fretypeset.radishzz.cc%2F">
<img width="710" alt="Retypeset Lighthouse Score" src="assets/retypeset-lighthouse-score.svg">
<a href="https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fretypeset.radishzz.cc%2Fen%2F">
<img width="710" alt="Retypeset Lighthouse Score" src="images/retypeset-lighthouse-score.svg">
<a>
</p>
@ -78,8 +78,8 @@ Retypeset is a static blog theme based on the [Astro](https://astro.build/) fram
5. Refer to the [Astro Deployment Guides](https://docs.astro.build/en/guides/deploy/) to deploy your blog to Netlify, Vercel, or other platforms.
&emsp;[![Deploy to Netlify](assets/deploy-netlify.svg)](https://app.netlify.com/start)
[![Deploy to Vercel](assets/deploy-vercel.svg)](https://vercel.com/new)
&emsp;[![Deploy to Netlify](images/deploy-netlify.svg)](https://app.netlify.com/start)
[![Deploy to Vercel](images/deploy-vercel.svg)](https://vercel.com/new)
## Updates

View file

@ -1,5 +1,5 @@
<img alt="Cover Image" src="assets/retypeset-zh-desktop.webp"/>
<img alt="Cover Image" src="assets/retypeset-zh-mobile.webp"/>
<img alt="Cover Image" src="images/retypeset-zh-desktop.webp"/>
<img alt="Cover Image" src="images/retypeset-zh-mobile.webp"/>
<div align="center">
<a title="en" href="README.md">
@ -45,7 +45,7 @@ Retypeset 是一款基于 [Astro](https://astro.build/) 框架的静态博客主
<p align="center">
<a href="https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fretypeset.radishzz.cc%2F">
<img width="710" alt="Retypeset Lighthouse Score" src="assets/retypeset-lighthouse-score.svg">
<img width="710" alt="Retypeset Lighthouse Score" src="images/retypeset-lighthouse-score.svg">
<a>
</p>
@ -78,8 +78,8 @@ Retypeset 是一款基于 [Astro](https://astro.build/) 框架的静态博客主
5. 参考 [Astro 部署指南](https://docs.astro.build/zh-cn/guides/deploy/),将博客部署至 Netlify、Vercel 等平台。
&emsp;[![Deploy to Netlify](assets/deploy-netlify.svg)](https://app.netlify.com/start)
[![Deploy to Vercel](assets/deploy-vercel.svg)](https://vercel.com/new)
&emsp;[![Deploy to Netlify](images/deploy-netlify.svg)](https://app.netlify.com/start)
[![Deploy to Vercel](images/deploy-vercel.svg)](https://vercel.com/new)
## 更新

View file

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 240 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 227 KiB

After

Width:  |  Height:  |  Size: 227 KiB

Before After
Before After

View file

@ -25,6 +25,7 @@
"katex": "^0.16.22",
"markdown-it": "^14.1.0",
"mdast-util-to-string": "^4.0.0",
"node-html-parser": "^7.0.1",
"overlayscrollbars": "^2.11.2",
"photoswipe": "^5.4.4",
"reading-time": "^1.5.0",
@ -48,7 +49,7 @@
"astro-eslint-parser": "^1.2.2",
"eslint": "^9.26.0",
"eslint-plugin-astro": "^1.3.1",
"lint-staged": "^15.5.2",
"lint-staged": "^16.0.0",
"sharp": "^0.34.1",
"typescript": "~5.8.3",
"unocss": "66.1.1",

135
pnpm-lock.yaml generated
View file

@ -44,6 +44,9 @@ importers:
mdast-util-to-string:
specifier: ^4.0.0
version: 4.0.0
node-html-parser:
specifier: ^7.0.1
version: 7.0.1
overlayscrollbars:
specifier: ^2.11.2
version: 2.11.2
@ -109,8 +112,8 @@ importers:
specifier: ^1.3.1
version: 1.3.1(eslint@9.26.0(jiti@2.4.2))
lint-staged:
specifier: ^15.5.2
version: 15.5.2
specifier: ^16.0.0
version: 16.0.0
sharp:
specifier: ^0.34.1
version: 0.34.1
@ -1741,10 +1744,17 @@ packages:
crossws@0.3.5:
resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==}
css-select@5.1.0:
resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
css-tree@3.1.0:
resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
css-what@6.1.0:
resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
engines: {node: '>= 6'}
cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
@ -1997,8 +2007,8 @@ packages:
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
eslint-plugin-jsdoc@50.6.11:
resolution: {integrity: sha512-k4+MnBCGR8cuIB5MZ++FGd4gbXxjob2rX1Nq0q3nWFF4xSGZENTgTLZSjb+u9B8SAnP6lpGV2FJrBjllV3pVSg==}
eslint-plugin-jsdoc@50.6.14:
resolution: {integrity: sha512-JUudvooQbUx3iB8n/MzXMOV/VtaXq7xL4CeXhYryinr8osck7nV6fE2/xUXTiH3epPXcvq6TE3HQfGQuRHErTQ==}
engines: {node: '>=18'}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
@ -2161,10 +2171,6 @@ packages:
resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==}
engines: {node: '>=18.0.0'}
execa@8.0.1:
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
engines: {node: '>=16.17'}
expect-type@1.2.1:
resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==}
engines: {node: '>=12.0.0'}
@ -2296,10 +2302,6 @@ packages:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
get-stream@8.0.1:
resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
engines: {node: '>=16'}
get-tsconfig@4.10.0:
resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==}
@ -2403,6 +2405,10 @@ packages:
hastscript@9.0.1:
resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==}
he@1.2.0:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
html-escaper@3.0.3:
resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==}
@ -2419,10 +2425,6 @@ packages:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
human-signals@5.0.0:
resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
engines: {node: '>=16.17.0'}
iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
@ -2535,10 +2537,6 @@ packages:
is-promise@4.0.0:
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
is-stream@3.0.0:
resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
is-wsl@3.1.0:
resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==}
engines: {node: '>=16'}
@ -2683,9 +2681,9 @@ packages:
linkify-it@5.0.0:
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
lint-staged@15.5.2:
resolution: {integrity: sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==}
engines: {node: '>=18.12.0'}
lint-staged@16.0.0:
resolution: {integrity: sha512-sUCprePs6/rbx4vKC60Hez6X10HPkpDJaGcy3D1NdwR7g1RcNkWL8q9mJMreOqmHBTs+1sNFp+wOiX9fr+hoOQ==}
engines: {node: '>=20.18'}
hasBin: true
listr2@8.3.3:
@ -2824,9 +2822,6 @@ packages:
resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
engines: {node: '>=18'}
merge-stream@2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@ -2957,10 +2952,6 @@ packages:
resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==}
engines: {node: '>= 0.6'}
mimic-fn@4.0.0:
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
engines: {node: '>=12'}
mimic-function@5.0.1:
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
engines: {node: '>=18'}
@ -2993,6 +2984,10 @@ packages:
muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
nano-spawn@1.0.1:
resolution: {integrity: sha512-BfcvzBlUTxSDWfT+oH7vd6CbUV+rThLLHCIym/QO6GGLBsyVXleZs00fto2i2jzC/wPiBYk5jyOmpXWg4YopiA==}
engines: {node: '>=20.18'}
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@ -3033,6 +3028,9 @@ packages:
encoding:
optional: true
node-html-parser@7.0.1:
resolution: {integrity: sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA==}
node-mock-http@1.0.0:
resolution: {integrity: sha512-0uGYQ1WQL1M5kKvGRXWQ3uZCHtLTO8hln3oBjIusM75WoesZ909uQJs/Hb946i2SS+Gsrhkaa6iAO17jRIv6DQ==}
@ -3043,10 +3041,6 @@ packages:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
npm-run-path@5.3.0:
resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
@ -3071,10 +3065,6 @@ packages:
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
onetime@6.0.0:
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
engines: {node: '>=12'}
onetime@7.0.0:
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
engines: {node: '>=18'}
@ -3159,10 +3149,6 @@ packages:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
path-key@4.0.0:
resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
engines: {node: '>=12'}
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
@ -3616,10 +3602,6 @@ packages:
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
engines: {node: '>=12'}
strip-final-newline@3.0.0:
resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
engines: {node: '>=12'}
strip-indent@4.0.0:
resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==}
engines: {node: '>=12'}
@ -4281,7 +4263,7 @@ snapshots:
eslint-plugin-antfu: 3.1.1(eslint@9.26.0(jiti@2.4.2))
eslint-plugin-command: 3.2.0(eslint@9.26.0(jiti@2.4.2))
eslint-plugin-import-x: 4.11.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
eslint-plugin-jsdoc: 50.6.11(eslint@9.26.0(jiti@2.4.2))
eslint-plugin-jsdoc: 50.6.14(eslint@9.26.0(jiti@2.4.2))
eslint-plugin-jsonc: 2.20.0(eslint@9.26.0(jiti@2.4.2))
eslint-plugin-n: 17.18.0(eslint@9.26.0(jiti@2.4.2))
eslint-plugin-no-only-tests: 3.3.0
@ -6086,11 +6068,21 @@ snapshots:
dependencies:
uncrypto: 0.1.3
css-select@5.1.0:
dependencies:
boolbase: 1.0.0
css-what: 6.1.0
domhandler: 5.0.3
domutils: 3.2.2
nth-check: 2.1.1
css-tree@3.1.0:
dependencies:
mdn-data: 2.12.2
source-map-js: 1.2.1
css-what@6.1.0: {}
cssesc@3.0.0: {}
csstype@3.1.3: {}
@ -6343,7 +6335,7 @@ snapshots:
- supports-color
- typescript
eslint-plugin-jsdoc@50.6.11(eslint@9.26.0(jiti@2.4.2)):
eslint-plugin-jsdoc@50.6.14(eslint@9.26.0(jiti@2.4.2)):
dependencies:
'@es-joy/jsdoccomment': 0.49.0
are-docs-informative: 0.0.2
@ -6604,18 +6596,6 @@ snapshots:
dependencies:
eventsource-parser: 3.0.1
execa@8.0.1:
dependencies:
cross-spawn: 7.0.6
get-stream: 8.0.1
human-signals: 5.0.0
is-stream: 3.0.0
merge-stream: 2.0.0
npm-run-path: 5.3.0
onetime: 6.0.0
signal-exit: 4.1.0
strip-final-newline: 3.0.0
expect-type@1.2.1:
optional: true
@ -6778,8 +6758,6 @@ snapshots:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
get-stream@8.0.1: {}
get-tsconfig@4.10.0:
dependencies:
resolve-pkg-maps: 1.0.0
@ -6979,6 +6957,8 @@ snapshots:
property-information: 7.1.0
space-separated-tokens: 2.0.2
he@1.2.0: {}
html-escaper@3.0.3: {}
html-void-elements@3.0.0: {}
@ -7000,8 +6980,6 @@ snapshots:
statuses: 2.0.1
toidentifier: 1.0.1
human-signals@5.0.0: {}
iconv-lite@0.6.3:
dependencies:
safer-buffer: 2.1.2
@ -7082,8 +7060,6 @@ snapshots:
is-promise@4.0.0: {}
is-stream@3.0.0: {}
is-wsl@3.1.0:
dependencies:
is-inside-container: 1.0.0
@ -7192,15 +7168,15 @@ snapshots:
dependencies:
uc.micro: 2.1.0
lint-staged@15.5.2:
lint-staged@16.0.0:
dependencies:
chalk: 5.4.1
commander: 13.1.0
debug: 4.4.0
execa: 8.0.1
lilconfig: 3.1.3
listr2: 8.3.3
micromatch: 4.0.8
nano-spawn: 1.0.1
pidtree: 0.6.0
string-argv: 0.3.2
yaml: 2.7.1
@ -7490,8 +7466,6 @@ snapshots:
merge-descriptors@2.0.0: {}
merge-stream@2.0.0: {}
merge2@1.4.1: {}
micromark-core-commonmark@2.0.3:
@ -7796,8 +7770,6 @@ snapshots:
dependencies:
mime-db: 1.54.0
mimic-fn@4.0.0: {}
mimic-function@5.0.1: {}
min-indent@1.0.1: {}
@ -7827,6 +7799,8 @@ snapshots:
muggle-string@0.4.1: {}
nano-spawn@1.0.1: {}
nanoid@3.3.11: {}
napi-postinstall@0.2.3: {}
@ -7849,16 +7823,17 @@ snapshots:
dependencies:
whatwg-url: 5.0.0
node-html-parser@7.0.1:
dependencies:
css-select: 5.1.0
he: 1.2.0
node-mock-http@1.0.0: {}
node-releases@2.0.19: {}
normalize-path@3.0.0: {}
npm-run-path@5.3.0:
dependencies:
path-key: 4.0.0
nth-check@2.1.1:
dependencies:
boolbase: 1.0.0
@ -7883,10 +7858,6 @@ snapshots:
dependencies:
wrappy: 1.0.2
onetime@6.0.0:
dependencies:
mimic-fn: 4.0.0
onetime@7.0.0:
dependencies:
mimic-function: 5.0.1
@ -7978,8 +7949,6 @@ snapshots:
path-key@3.1.1: {}
path-key@4.0.0: {}
path-parse@1.0.7: {}
path-to-regexp@8.2.0: {}
@ -8614,8 +8583,6 @@ snapshots:
dependencies:
ansi-regex: 6.1.0
strip-final-newline@3.0.0: {}
strip-indent@4.0.0:
dependencies:
min-indent: 1.0.1

View file

Before

Width:  |  Height:  |  Size: 1,005 KiB

After

Width:  |  Height:  |  Size: 1,005 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 999 KiB

After

Width:  |  Height:  |  Size: 999 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1 MiB

After

Width:  |  Height:  |  Size: 1 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1 MiB

After

Width:  |  Height:  |  Size: 1 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1 MiB

After

Width:  |  Height:  |  Size: 1 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1,016 KiB

After

Width:  |  Height:  |  Size: 1,016 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Before After
Before After

View file

@ -14,8 +14,8 @@ To meet personalization needs, I've created several color schemes for the theme.
## Scallion White
![Light mode](../../../assets/images/1-light.jpeg)
![Dark mode](../../../assets/images/1-dark.jpeg)
![Light mode](../_images/1-light.jpeg)
![Dark mode](../_images/1-dark.jpeg)
```
light: {
@ -32,8 +32,8 @@ dark: {
## Raven Teal
![Light mode](../../../assets/images/2-light.jpeg)
![Dark mode](../../../assets/images/2-dark.jpeg)
![Light mode](../_images/2-light.jpeg)
![Dark mode](../_images/2-dark.jpeg)
```
light: {
@ -50,8 +50,8 @@ dark: {
## Ink Blue
![Light mode](../../../assets/images/4-light.jpeg)
![Dark mode](../../../assets/images/4-dark.jpeg)
![Light mode](../_images/4-light.jpeg)
![Dark mode](../_images/4-dark.jpeg)
```
light: {
@ -68,8 +68,8 @@ dark: {
## Ecru
![Light mode](../../../assets/images/3-light.jpeg)
![Dark mode](../../../assets/images/3-dark.jpeg)
![Light mode](../_images/3-light.jpeg)
![Dark mode](../_images/3-dark.jpeg)
```
light: {

View file

@ -14,8 +14,8 @@ Para satisfacer las necesidades de personalización, he creado varios esquemas d
## Blanco Cebollino
![Light mode](../../../assets/images/1-light.jpeg)
![Dark mode](../../../assets/images/1-dark.jpeg)
![Light mode](../_images/1-light.jpeg)
![Dark mode](../_images/1-dark.jpeg)
```
light: {
@ -32,8 +32,8 @@ dark: {
## Azul Cuervo
![Light mode](../../../assets/images/2-light.jpeg)
![Dark mode](../../../assets/images/2-dark.jpeg)
![Light mode](../_images/2-light.jpeg)
![Dark mode](../_images/2-dark.jpeg)
```
light: {
@ -50,8 +50,8 @@ dark: {
## Azul Tinta
![Light mode](../../../assets/images/4-light.jpeg)
![Dark mode](../../../assets/images/4-dark.jpeg)
![Light mode](../_images/4-light.jpeg)
![Dark mode](../_images/4-dark.jpeg)
```
light: {
@ -68,8 +68,8 @@ dark: {
## Beige
![Light mode](../../../assets/images/3-light.jpeg)
![Dark mode](../../../assets/images/3-dark.jpeg)
![Light mode](../_images/3-light.jpeg)
![Dark mode](../_images/3-dark.jpeg)
```
light: {

View file

@ -14,8 +14,8 @@ Retypesetは、[OKLCH](https://oklch.com/)カラースペースに基づいて
## 葱白色
![Light mode](../../../assets/images/1-light.jpeg)
![Dark mode](../../../assets/images/1-dark.jpeg)
![Light mode](../_images/1-light.jpeg)
![Dark mode](../_images/1-dark.jpeg)
```
light: {
@ -32,8 +32,8 @@ dark: {
## 烏青
![Light mode](../../../assets/images/2-light.jpeg)
![Dark mode](../../../assets/images/2-dark.jpeg)
![Light mode](../_images/2-light.jpeg)
![Dark mode](../_images/2-dark.jpeg)
```
light: {
@ -50,8 +50,8 @@ dark: {
## 墨藍
![Light mode](../../../assets/images/4-light.jpeg)
![Dark mode](../../../assets/images/4-dark.jpeg)
![Light mode](../_images/4-light.jpeg)
![Dark mode](../_images/4-dark.jpeg)
```
light: {
@ -68,8 +68,8 @@ dark: {
## 米色
![Light mode](../../../assets/images/3-light.jpeg)
![Dark mode](../../../assets/images/3-dark.jpeg)
![Light mode](../_images/3-light.jpeg)
![Dark mode](../_images/3-dark.jpeg)
```
light: {

View file

@ -14,8 +14,8 @@ Retypeset определяет цветовые схемы темы на осн
## Бледно-зелёный
![Light mode](../../../assets/images/1-light.jpeg)
![Dark mode](../../../assets/images/1-dark.jpeg)
![Light mode](../_images/1-light.jpeg)
![Dark mode](../_images/1-dark.jpeg)
```
light: {
@ -32,8 +32,8 @@ dark: {
## Воронёный
![Light mode](../../../assets/images/2-light.jpeg)
![Dark mode](../../../assets/images/2-dark.jpeg)
![Light mode](../_images/2-light.jpeg)
![Dark mode](../_images/2-dark.jpeg)
```
light: {
@ -50,8 +50,8 @@ dark: {
## Чернильно-синий
![Light mode](../../../assets/images/4-light.jpeg)
![Dark mode](../../../assets/images/4-dark.jpeg)
![Light mode](../_images/4-light.jpeg)
![Dark mode](../_images/4-dark.jpeg)
```
light: {
@ -68,8 +68,8 @@ dark: {
## Кремовый
![Light mode](../../../assets/images/3-light.jpeg)
![Dark mode](../../../assets/images/3-dark.jpeg)
![Light mode](../_images/3-light.jpeg)
![Dark mode](../_images/3-dark.jpeg)
```
light: {

View file

@ -14,8 +14,8 @@ Retypeset 基於 [OKLCH](https://oklch.com/) 顏色空間來定義主題配色
## 蔥白
![Light mode](../../../assets/images/1-light.jpeg)
![Dark mode](../../../assets/images/1-dark.jpeg)
![Light mode](../_images/1-light.jpeg)
![Dark mode](../_images/1-dark.jpeg)
```
light: {
@ -32,8 +32,8 @@ dark: {
## 鴉青
![Light mode](../../../assets/images/2-light.jpeg)
![Dark mode](../../../assets/images/2-dark.jpeg)
![Light mode](../_images/2-light.jpeg)
![Dark mode](../_images/2-dark.jpeg)
```
light: {
@ -50,8 +50,8 @@ dark: {
## 墨藍
![Light mode](../../../assets/images/4-light.jpeg)
![Dark mode](../../../assets/images/4-dark.jpeg)
![Light mode](../_images/4-light.jpeg)
![Dark mode](../_images/4-dark.jpeg)
```
light: {
@ -68,8 +68,8 @@ dark: {
## 米黃
![Light mode](../../../assets/images/3-light.jpeg)
![Dark mode](../../../assets/images/3-dark.jpeg)
![Light mode](../_images/3-light.jpeg)
![Dark mode](../_images/3-dark.jpeg)
```
light: {

View file

@ -14,8 +14,8 @@ Retypeset 基于 [OKLCH](https://oklch.com/) 颜色空间来定义主题配色
## 葱白
![Light mode](../../../assets/images/1-light.jpeg)
![Dark mode](../../../assets/images/1-dark.jpeg)
![Light mode](../_images/1-light.jpeg)
![Dark mode](../_images/1-dark.jpeg)
```
light: {
@ -32,8 +32,8 @@ dark: {
## 鸦青
![Light mode](../../../assets/images/2-light.jpeg)
![Dark mode](../../../assets/images/2-dark.jpeg)
![Light mode](../_images/2-light.jpeg)
![Dark mode](../_images/2-dark.jpeg)
```
light: {
@ -50,8 +50,8 @@ dark: {
## 墨蓝
![Light mode](../../../assets/images/4-light.jpeg)
![Dark mode](../../../assets/images/4-dark.jpeg)
![Light mode](../_images/4-light.jpeg)
![Dark mode](../_images/4-dark.jpeg)
```
light: {
@ -68,8 +68,8 @@ dark: {
## 米黄
![Light mode](../../../assets/images/3-light.jpeg)
![Dark mode](../../../assets/images/3-dark.jpeg)
![Light mode](../_images/3-light.jpeg)
![Dark mode](../_images/3-dark.jpeg)
```
light: {
@ -82,4 +82,4 @@ dark: {
secondary: 'oklch(0.80 0.017 59.39)',
background: 'oklch(0.23 0 0)',
},
```
```

View file

@ -1,24 +1,100 @@
import type { APIContext } from 'astro'
import type { APIContext, ImageMetadata } from 'astro'
import type { CollectionEntry } from 'astro:content'
import type { Author } from 'feed'
import { defaultLocale, themeConfig } from '@/config'
import { ui } from '@/i18n/ui'
import { memoize } from '@/utils/cache'
import { generateDescription } from '@/utils/description'
import { getImage } from 'astro:assets'
import { getCollection } from 'astro:content'
import { Feed } from 'feed'
import MarkdownIt from 'markdown-it'
import { parse as htmlParser } from 'node-html-parser'
import sanitizeHtml from 'sanitize-html'
const markdownParser = new MarkdownIt()
const { title, description, url, author: siteAuthor } = themeConfig.site
const followConfig = themeConfig.seo?.follow
interface GenerateFeedOptions {
lang?: string
}
const markdownParser = new MarkdownIt()
const { title, description, url, author: siteAuthor } = themeConfig.site
const followConfig = themeConfig.seo?.follow
// Dynamically import all images from /src/content/posts/_images
const imagesGlob = import.meta.glob<{ default: ImageMetadata }>(
'/src/content/posts/_images/**/*.{jpeg,jpg,png,gif,webp}',
)
/**
* Optimize image URLs
*
* @param srcPath Source relative path of the image
* @param baseUrl Base URL of the site
* @returns Optimized full image URL or null
*/
const getOptimizedImageUrl = memoize(async (srcPath: string, baseUrl: string) => {
const prefixRemoved = srcPath.replace(/^\.\.\/|^\.\//g, '')
const rawImagePath = `/src/content/posts/${prefixRemoved}`
const rawImageMetadata = await imagesGlob[rawImagePath]?.()?.then(res => res.default)
if (rawImageMetadata) {
const processedImageData = await getImage({ src: rawImageMetadata })
return new URL(processedImageData.src, baseUrl).toString()
}
return null
})
/**
* Fix relative image paths in HTML content
*
* @param htmlContent HTML content string
* @param baseUrl Base URL of the site
* @returns Processed HTML string with all image paths converted to absolute URLs
*/
async function fixRelativeImagePaths(htmlContent: string, baseUrl: string): Promise<string> {
const htmlDoc = htmlParser(htmlContent)
const images = htmlDoc.querySelectorAll('img')
const imagePromises = []
for (const img of images) {
const src = img.getAttribute('src')
if (!src)
continue
imagePromises.push((async () => {
try {
// Process images from src/content/posts/_images directory
if (src.startsWith('./') || src.startsWith('../')) {
const optimizedImageUrl = await getOptimizedImageUrl(src, baseUrl)
if (optimizedImageUrl) {
img.setAttribute('src', optimizedImageUrl)
}
}
// Process images from public/images directory
else if (src.startsWith('/images')) {
const publicImageUrl = new URL(src, baseUrl).toString()
img.setAttribute('src', publicImageUrl)
}
}
catch (error) {
console.warn(`Failed to process image in RSS feed: ${src}`, error)
}
})())
}
await Promise.all(imagePromises)
return htmlDoc.toString()
}
/**
* Generate post URL with language prefix and abbrlink/slug
*
* @param post The post collection entry
* @param baseUrl Base URL of the site
* @returns The fully formed URL for the post
*/
function generatePostUrl(post: CollectionEntry<'posts'>, baseUrl: string): string {
const needsLangPrefix = post.data.lang !== defaultLocale && post.data.lang !== ''
@ -30,6 +106,10 @@ function generatePostUrl(post: CollectionEntry<'posts'>, baseUrl: string): strin
/**
* Generate a feed object supporting both RSS and Atom formats
*
* @param options Feed generation options
* @param options.lang Optional language code
* @returns A Feed instance ready for RSS or Atom output
*/
export async function generateFeed({ lang }: GenerateFeedOptions = {}) {
const currentUI = ui[lang as keyof typeof ui] || ui[defaultLocale as keyof typeof ui]
@ -66,24 +146,28 @@ export async function generateFeed({ lang }: GenerateFeedOptions = {}) {
(!data.draft && (data.lang === lang || data.lang === '' || (lang === undefined && data.lang === defaultLocale))),
)
// Sort posts by published date in descending order
const sortedPosts = [...posts].sort((a, b) =>
new Date(b.data.published).getTime() - new Date(a.data.published).getTime(),
)
// Limit to the latest 25 posts
const limitedPosts = sortedPosts.slice(0, 25)
// Sort posts by published date in descending order and limit to the latest 25
const limitedPosts = [...posts]
.sort((a, b) => new Date(b.data.published).getTime() - new Date(a.data.published).getTime())
.slice(0, 25)
// Add posts to feed
for (const post of limitedPosts) {
const postLink = generatePostUrl(post, url)
// Optimize content processing
const postContent = post.body
? sanitizeHtml(markdownParser.render(post.body), {
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']),
})
? sanitizeHtml(
await fixRelativeImagePaths(markdownParser.render(post.body), url),
{ allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']) },
)
: ''
// publishDate -> Atom:<published>, RSS:<pubDate>
const publishDate = new Date(post.data.published)
// updateDate -> Atom:<updated>, RSS has no update tag
const updateDate = post.data.updated ? new Date(post.data.updated) : publishDate
feed.addItem({
title: post.data.title,
id: postLink,
@ -91,10 +175,8 @@ export async function generateFeed({ lang }: GenerateFeedOptions = {}) {
description: generateDescription(post, 'feed'),
content: postContent,
author: [author],
// published -> Atom:<published>, RSS:<pubDate>
published: new Date(post.data.published),
// date -> Atom:<updated>, RSS has no update tag
date: post.data.updated ? new Date(post.data.updated) : new Date(post.data.published),
published: publishDate,
date: updateDate,
})
}
@ -114,6 +196,9 @@ export async function generateFeed({ lang }: GenerateFeedOptions = {}) {
/**
* Generate RSS 2.0 format feed
*
* @param context Astro API context containing request params
* @returns Response object with RSS XML content
*/
export async function generateRSS(context: APIContext) {
const feed = await generateFeed({
@ -135,6 +220,9 @@ export async function generateRSS(context: APIContext) {
/**
* Generate Atom 1.0 format feed
*
* @param context Astro API context containing request params
* @returns Response object with Atom XML content
*/
export async function generateAtom(context: APIContext) {
const feed = await generateFeed({