第一版
This commit is contained in:
parent
e3b7a06b39
commit
2500865a46
@ -7,15 +7,82 @@ import react from '@astrojs/react';
|
||||
import node from '@astrojs/node';
|
||||
import remarkEmoji from 'remark-emoji';
|
||||
import rehypeExternalLinks from 'rehype-external-links';
|
||||
import sitemap from '@astrojs/sitemap';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { SITE_URL } from './src/consts';
|
||||
|
||||
function getArticleDate(articleId) {
|
||||
try {
|
||||
const mdPath = path.join(process.cwd(), 'src/content', articleId + '.md');
|
||||
if (fs.existsSync(mdPath)) {
|
||||
const content = fs.readFileSync(mdPath, 'utf-8');
|
||||
const match = content.match(/date:\s*(\d{4}-\d{2}-\d{2})/);
|
||||
if (match) {
|
||||
return new Date(match[1]).toISOString();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error reading article date:', error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: SITE_URL, // 替换为您的实际网站 URL
|
||||
output: 'server',
|
||||
trailingSlash: 'ignore',
|
||||
build: {
|
||||
format: 'directory'
|
||||
},
|
||||
vite: {
|
||||
plugins: [tailwindcss()]
|
||||
},
|
||||
|
||||
integrations: [react()],
|
||||
integrations: [
|
||||
react(),
|
||||
sitemap({
|
||||
filter: (page) => !page.includes('/api/'),
|
||||
serialize(item) {
|
||||
if (!item) return undefined;
|
||||
|
||||
// 文章页面
|
||||
if (item.url.includes('/articles/')) {
|
||||
// 从 URL 中提取文章 ID
|
||||
const articleId = item.url.replace(SITE_URL + '/articles/', '').replace(/\/$/, '');
|
||||
const publishDate = getArticleDate(articleId);
|
||||
if (publishDate) {
|
||||
return {
|
||||
...item,
|
||||
priority: 0.8,
|
||||
lastmod: publishDate
|
||||
};
|
||||
}
|
||||
}
|
||||
// 其他页面
|
||||
else {
|
||||
let priority = 0.7; // 默认优先级
|
||||
|
||||
// 首页最高优先级
|
||||
if (item.url === SITE_URL + '/') {
|
||||
priority = 1.0;
|
||||
}
|
||||
// 文章列表页次高优先级
|
||||
else if (item.url === SITE_URL + '/articles/') {
|
||||
priority = 0.9;
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
priority
|
||||
};
|
||||
}
|
||||
},
|
||||
// 设置较小的条目限制,这样会自动分割成多个文件
|
||||
entryLimit: 5
|
||||
})
|
||||
],
|
||||
|
||||
// 添加 Node.js 适配器配置
|
||||
adapter: node({
|
||||
|
558
package-lock.json
generated
558
package-lock.json
generated
@ -9,11 +9,12 @@
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^9.1.2",
|
||||
"@astrojs/react": "^4.2.0",
|
||||
"@astrojs/react": "^4.2.1",
|
||||
"@astrojs/sitemap": "^3.2.1",
|
||||
"@tailwindcss/vite": "^4.0.9",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.4.2",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"echarts": "^5.6.0",
|
||||
"node-fetch": "^3.3.0",
|
||||
@ -49,17 +50,18 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@astrojs/internal-helpers": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@astrojs/internal-helpers/-/internal-helpers-0.5.1.tgz",
|
||||
"integrity": "sha512-M7rAge1n2+aOSxNvKUFa0u/KFn0W+sZy7EW91KOSERotm2Ti8qs+1K0xx3zbOxtAVrmJb5/J98eohVvvEqtNkw==",
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/@astrojs/internal-helpers/-/internal-helpers-0.6.0.tgz",
|
||||
"integrity": "sha512-XgHIJDQaGlFnTr0sDp1PiJrtqsWzbHP2qkTU+JpQ8SnBewKP2IKOe/wqCkl0CyfyRXRu3TSWu4t/cpYMVfuBNA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@astrojs/markdown-remark": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/@astrojs/markdown-remark/-/markdown-remark-6.1.0.tgz",
|
||||
"integrity": "sha512-emZNNSTPGgPc3V399Cazpp5+snogjaF04ocOSQn9vy3Kw/eIC4vTQjXOrWDEoSEy+AwPDZX9bQ4wd3bxhpmGgQ==",
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@astrojs/markdown-remark/-/markdown-remark-6.2.0.tgz",
|
||||
"integrity": "sha512-LUDjgd9p1yG0qTFSocaj3GOLmZs8Hsw/pNtvqzvNY58Acebxvb/46vDO/e/wxYgsKgIfWS+p+ZI5SfOjoVrbCg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@astrojs/internal-helpers": "0.6.0",
|
||||
"@astrojs/prism": "3.2.0",
|
||||
"github-slugger": "^2.0.0",
|
||||
"hast-util-from-html": "^2.0.3",
|
||||
@ -73,7 +75,7 @@
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-rehype": "^11.1.1",
|
||||
"remark-smartypants": "^3.0.2",
|
||||
"shiki": "^1.29.1",
|
||||
"shiki": "^1.29.2",
|
||||
"smol-toml": "^1.3.1",
|
||||
"unified": "^11.0.5",
|
||||
"unist-util-remove-position": "^5.0.0",
|
||||
@ -96,12 +98,6 @@
|
||||
"astro": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@astrojs/node/node_modules/@astrojs/internal-helpers": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/@astrojs/internal-helpers/-/internal-helpers-0.6.0.tgz",
|
||||
"integrity": "sha512-XgHIJDQaGlFnTr0sDp1PiJrtqsWzbHP2qkTU+JpQ8SnBewKP2IKOe/wqCkl0CyfyRXRu3TSWu4t/cpYMVfuBNA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@astrojs/prism": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@astrojs/prism/-/prism-3.2.0.tgz",
|
||||
@ -115,14 +111,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@astrojs/react": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@astrojs/react/-/react-4.2.0.tgz",
|
||||
"integrity": "sha512-2OccnYFK+mLuy9GpJqPM3BQGvvemnXNeww+nBVYFuiH04L7YIdfg4Gq0LT7v/BraiuADV5uTl9VhTDL/ZQPAhw==",
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/@astrojs/react/-/react-4.2.1.tgz",
|
||||
"integrity": "sha512-g0P6zxG7RPHNcbmMB15dJJ83+ApBVFBcgnf6BnMz/PVXM150Pa1vYKeuTcWhERqLNgmpI2uXuch5MecIhrUlqQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"ultrahtml": "^1.5.3",
|
||||
"vite": "^6.0.9"
|
||||
"vite": "^6.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.1 || ^20.3.0 || >=22.0.0"
|
||||
@ -134,6 +130,17 @@
|
||||
"react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@astrojs/sitemap": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/@astrojs/sitemap/-/sitemap-3.2.1.tgz",
|
||||
"integrity": "sha512-uxMfO8f7pALq0ADL6Lk68UV6dNYjJ2xGUzyjjVj60JLBs5a6smtlkBYv3tQ0DzoqwS7c9n4FUx5lgv0yPo/fgA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sitemap": "^8.0.0",
|
||||
"stream-replace-string": "^2.0.0",
|
||||
"zod": "^3.23.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@astrojs/telemetry": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@astrojs/telemetry/-/telemetry-3.2.0.tgz",
|
||||
@ -444,9 +451,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
|
||||
"integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
|
||||
"integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@ -460,9 +467,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
|
||||
"integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.0.tgz",
|
||||
"integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -476,9 +483,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -492,9 +499,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -508,9 +515,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -524,9 +531,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -540,9 +547,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -556,9 +563,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -572,9 +579,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
|
||||
"integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz",
|
||||
"integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -588,9 +595,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -604,9 +611,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
|
||||
"integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz",
|
||||
"integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@ -620,9 +627,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
|
||||
"integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz",
|
||||
"integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@ -636,9 +643,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
|
||||
"integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz",
|
||||
"integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
@ -652,9 +659,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
|
||||
"integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz",
|
||||
"integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@ -668,9 +675,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
|
||||
"integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz",
|
||||
"integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@ -684,9 +691,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
|
||||
"integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz",
|
||||
"integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@ -700,9 +707,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -716,9 +723,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -732,9 +739,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -748,9 +755,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -764,9 +771,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -780,9 +787,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -796,9 +803,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -812,9 +819,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
|
||||
"integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz",
|
||||
"integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@ -828,9 +835,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -1252,41 +1259,6 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "2.0.5",
|
||||
"run-parallel": "^1.1.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.stat": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.walk": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.scandir": "2.1.5",
|
||||
"fastq": "^1.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/app": {
|
||||
"version": "14.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/@octokit/app/-/app-14.1.0.tgz",
|
||||
@ -2452,6 +2424,15 @@
|
||||
"@types/react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/sax": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmmirror.com/@types/sax/-/sax-1.2.7.tgz",
|
||||
"integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@types/unist/-/unist-3.0.3.tgz",
|
||||
@ -2607,6 +2588,12 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz",
|
||||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
|
||||
@ -2633,14 +2620,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/astro/-/astro-5.3.0.tgz",
|
||||
"integrity": "sha512-e88l/Yk/6enR/ZDddLbqtM+oblBFk5mneNSmNesyVYGL/6Dj4UA67GPAZOk79VxT5dbLlclZSyyw/wlxN1aj3A==",
|
||||
"version": "5.4.2",
|
||||
"resolved": "https://registry.npmmirror.com/astro/-/astro-5.4.2.tgz",
|
||||
"integrity": "sha512-9Z3fAniIRJaK/o43OroZA1wHUIU+qHiOR9ovlVT/2XQaN25QRXScIsKWlFp0G/zrx5OuuoJ+QnaoHHW061u26A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "^2.10.3",
|
||||
"@astrojs/internal-helpers": "0.5.1",
|
||||
"@astrojs/markdown-remark": "6.1.0",
|
||||
"@astrojs/compiler": "^2.10.4",
|
||||
"@astrojs/internal-helpers": "0.6.0",
|
||||
"@astrojs/markdown-remark": "6.2.0",
|
||||
"@astrojs/telemetry": "3.2.0",
|
||||
"@oslojs/encoding": "^1.1.0",
|
||||
"@rollup/pluginutils": "^5.1.4",
|
||||
@ -2661,9 +2648,8 @@
|
||||
"dlv": "^1.1.3",
|
||||
"dset": "^3.1.4",
|
||||
"es-module-lexer": "^1.6.0",
|
||||
"esbuild": "^0.24.2",
|
||||
"esbuild": "^0.25.0",
|
||||
"estree-walker": "^3.0.3",
|
||||
"fast-glob": "^3.3.3",
|
||||
"flattie": "^1.1.1",
|
||||
"github-slugger": "^2.0.0",
|
||||
"html-escaper": "3.0.3",
|
||||
@ -2672,30 +2658,31 @@
|
||||
"kleur": "^4.1.5",
|
||||
"magic-string": "^0.30.17",
|
||||
"magicast": "^0.3.5",
|
||||
"micromatch": "^4.0.8",
|
||||
"mrmime": "^2.0.0",
|
||||
"mrmime": "^2.0.1",
|
||||
"neotraverse": "^0.6.18",
|
||||
"p-limit": "^6.2.0",
|
||||
"p-queue": "^8.1.0",
|
||||
"picomatch": "^4.0.2",
|
||||
"preferred-pm": "^4.1.1",
|
||||
"prompts": "^2.4.2",
|
||||
"rehype": "^13.0.2",
|
||||
"semver": "^7.7.1",
|
||||
"shiki": "^1.29.2",
|
||||
"tinyexec": "^0.3.2",
|
||||
"tsconfck": "^3.1.4",
|
||||
"tinyglobby": "^0.2.12",
|
||||
"tsconfck": "^3.1.5",
|
||||
"ultrahtml": "^1.5.3",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"unstorage": "^1.14.4",
|
||||
"unstorage": "^1.15.0",
|
||||
"vfile": "^6.0.3",
|
||||
"vite": "^6.0.11",
|
||||
"vitefu": "^1.0.5",
|
||||
"vite": "^6.2.0",
|
||||
"vitefu": "^1.0.6",
|
||||
"which-pm": "^3.0.1",
|
||||
"xxhash-wasm": "^1.1.0",
|
||||
"yargs-parser": "^21.1.1",
|
||||
"yocto-spinner": "^0.2.0",
|
||||
"zod": "^3.24.1",
|
||||
"zod-to-json-schema": "^3.24.1",
|
||||
"yocto-spinner": "^0.2.1",
|
||||
"zod": "^3.24.2",
|
||||
"zod-to-json-schema": "^3.24.3",
|
||||
"zod-to-ts": "^1.2.0"
|
||||
},
|
||||
"bin": {
|
||||
@ -3195,9 +3182,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/decode-named-character-reference": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
|
||||
"integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz",
|
||||
"integrity": "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"character-entities": "^2.0.0"
|
||||
@ -3494,9 +3481,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.24.2.tgz",
|
||||
"integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.0.tgz",
|
||||
"integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@ -3506,31 +3493,31 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.24.2",
|
||||
"@esbuild/android-arm": "0.24.2",
|
||||
"@esbuild/android-arm64": "0.24.2",
|
||||
"@esbuild/android-x64": "0.24.2",
|
||||
"@esbuild/darwin-arm64": "0.24.2",
|
||||
"@esbuild/darwin-x64": "0.24.2",
|
||||
"@esbuild/freebsd-arm64": "0.24.2",
|
||||
"@esbuild/freebsd-x64": "0.24.2",
|
||||
"@esbuild/linux-arm": "0.24.2",
|
||||
"@esbuild/linux-arm64": "0.24.2",
|
||||
"@esbuild/linux-ia32": "0.24.2",
|
||||
"@esbuild/linux-loong64": "0.24.2",
|
||||
"@esbuild/linux-mips64el": "0.24.2",
|
||||
"@esbuild/linux-ppc64": "0.24.2",
|
||||
"@esbuild/linux-riscv64": "0.24.2",
|
||||
"@esbuild/linux-s390x": "0.24.2",
|
||||
"@esbuild/linux-x64": "0.24.2",
|
||||
"@esbuild/netbsd-arm64": "0.24.2",
|
||||
"@esbuild/netbsd-x64": "0.24.2",
|
||||
"@esbuild/openbsd-arm64": "0.24.2",
|
||||
"@esbuild/openbsd-x64": "0.24.2",
|
||||
"@esbuild/sunos-x64": "0.24.2",
|
||||
"@esbuild/win32-arm64": "0.24.2",
|
||||
"@esbuild/win32-ia32": "0.24.2",
|
||||
"@esbuild/win32-x64": "0.24.2"
|
||||
"@esbuild/aix-ppc64": "0.25.0",
|
||||
"@esbuild/android-arm": "0.25.0",
|
||||
"@esbuild/android-arm64": "0.25.0",
|
||||
"@esbuild/android-x64": "0.25.0",
|
||||
"@esbuild/darwin-arm64": "0.25.0",
|
||||
"@esbuild/darwin-x64": "0.25.0",
|
||||
"@esbuild/freebsd-arm64": "0.25.0",
|
||||
"@esbuild/freebsd-x64": "0.25.0",
|
||||
"@esbuild/linux-arm": "0.25.0",
|
||||
"@esbuild/linux-arm64": "0.25.0",
|
||||
"@esbuild/linux-ia32": "0.25.0",
|
||||
"@esbuild/linux-loong64": "0.25.0",
|
||||
"@esbuild/linux-mips64el": "0.25.0",
|
||||
"@esbuild/linux-ppc64": "0.25.0",
|
||||
"@esbuild/linux-riscv64": "0.25.0",
|
||||
"@esbuild/linux-s390x": "0.25.0",
|
||||
"@esbuild/linux-x64": "0.25.0",
|
||||
"@esbuild/netbsd-arm64": "0.25.0",
|
||||
"@esbuild/netbsd-x64": "0.25.0",
|
||||
"@esbuild/openbsd-arm64": "0.25.0",
|
||||
"@esbuild/openbsd-x64": "0.25.0",
|
||||
"@esbuild/sunos-x64": "0.25.0",
|
||||
"@esbuild/win32-arm64": "0.25.0",
|
||||
"@esbuild/win32-ia32": "0.25.0",
|
||||
"@esbuild/win32-x64": "0.25.0"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
@ -3603,29 +3590,18 @@
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||
"node_modules/fdir": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.3.tgz",
|
||||
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
"@nodelib/fs.walk": "^1.2.3",
|
||||
"glob-parent": "^5.1.2",
|
||||
"merge2": "^1.3.0",
|
||||
"micromatch": "^4.0.8"
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6.0"
|
||||
"peerDependenciesMeta": {
|
||||
"picomatch": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.19.0.tgz",
|
||||
"integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/fetch-blob": {
|
||||
@ -3769,18 +3745,6 @@
|
||||
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz",
|
||||
@ -4138,15 +4102,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
@ -4156,18 +4111,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-inside-container": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/is-inside-container/-/is-inside-container-1.0.0.tgz",
|
||||
@ -4941,19 +4884,10 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
|
||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/micromark/-/micromark-4.0.1.tgz",
|
||||
"integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==",
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/micromark/-/micromark-4.0.2.tgz",
|
||||
"integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
@ -4986,9 +4920,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-core-commonmark": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz",
|
||||
"integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
|
||||
"integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
@ -5460,9 +5394,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-util-subtokenize": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.4.tgz",
|
||||
"integrity": "sha512-N6hXjrin2GTJDe3MVjf5FuXpm12PGm80BrUAeub9XFXca8JZbP+oIwY4LJSVwFUCL1IPm/WwSVUN7goFHmSGGQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
|
||||
"integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
@ -6049,26 +5983,6 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/radix3": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/radix3/-/radix3-1.1.2.tgz",
|
||||
@ -6400,16 +6314,6 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/reusify": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz",
|
||||
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"iojs": ">=1.0.0",
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.34.8",
|
||||
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.34.8.tgz",
|
||||
@ -6448,29 +6352,6 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
@ -6497,6 +6378,12 @@
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz",
|
||||
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.25.0.tgz",
|
||||
@ -6622,6 +6509,31 @@
|
||||
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sitemap": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/sitemap/-/sitemap-8.0.0.tgz",
|
||||
"integrity": "sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "^17.0.5",
|
||||
"@types/sax": "^1.2.1",
|
||||
"arg": "^5.0.0",
|
||||
"sax": "^1.2.4"
|
||||
},
|
||||
"bin": {
|
||||
"sitemap": "dist/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0",
|
||||
"npm": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sitemap/node_modules/@types/node": {
|
||||
"version": "17.0.45",
|
||||
"resolved": "https://registry.npmmirror.com/@types/node/-/node-17.0.45.tgz",
|
||||
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/skin-tone": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/skin-tone/-/skin-tone-2.0.0.tgz",
|
||||
@ -6681,6 +6593,12 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/stream-replace-string": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/stream-replace-string/-/stream-replace-string-2.0.0.tgz",
|
||||
"integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz",
|
||||
@ -6757,6 +6675,22 @@
|
||||
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.12",
|
||||
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.12.tgz",
|
||||
"integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.3",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
@ -7224,13 +7158,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-6.1.1.tgz",
|
||||
"integrity": "sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA==",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-6.2.1.tgz",
|
||||
"integrity": "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.24.2",
|
||||
"postcss": "^8.5.2",
|
||||
"esbuild": "^0.25.0",
|
||||
"postcss": "^8.5.3",
|
||||
"rollup": "^4.30.1"
|
||||
},
|
||||
"bin": {
|
||||
@ -7295,9 +7229,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitefu": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/vitefu/-/vitefu-1.0.5.tgz",
|
||||
"integrity": "sha512-h4Vflt9gxODPFNGPwp4zAMZRpZR7eslzwH2c5hn5kNZ5rhnKyRJ50U+yGCdc2IRaBs8O4haIgLNGrV5CrpMsCA==",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/vitefu/-/vitefu-1.0.6.tgz",
|
||||
"integrity": "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"tests/deps/*",
|
||||
@ -7445,9 +7379,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/yocto-spinner": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/yocto-spinner/-/yocto-spinner-0.2.0.tgz",
|
||||
"integrity": "sha512-Qu6WAqNLGleB687CCGcmgHIo8l+J19MX/32UrSMfbf/4L8gLoxjpOYoiHT1asiWyqvjRZbgvOhLlvne6E5Tbdw==",
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/yocto-spinner/-/yocto-spinner-0.2.1.tgz",
|
||||
"integrity": "sha512-lHHxjh0bXaLgdJy3cNnVb/F9myx3CkhrvSOEVTkaUgNMXnYFa2xYPVhtGnqhh3jErY2gParBOHallCbc7NrlZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"yoctocolors": "^2.1.1"
|
||||
|
@ -10,11 +10,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^9.1.2",
|
||||
"@astrojs/react": "^4.2.0",
|
||||
"@astrojs/react": "^4.2.1",
|
||||
"@astrojs/sitemap": "^3.2.1",
|
||||
"@tailwindcss/vite": "^4.0.9",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.4.2",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"echarts": "^5.6.0",
|
||||
"node-fetch": "^3.3.0",
|
||||
|
BIN
public/images/national.png
Normal file
BIN
public/images/national.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -13,7 +13,7 @@ const {
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
{title && <h1 class="text-3xl font-bold mb-6 text-primary-900 dark:text-primary-100">{title}</h1>}
|
||||
|
||||
<div id="article-timeline" class="space-y-6">
|
||||
<div id="article-timeline" class="relative space-y-8 before:absolute before:inset-0 before:ml-5 before:h-full before:w-0.5 before:-translate-x-px before:bg-gradient-to-b before:from-transparent before:via-primary-300 before:to-transparent md:before:mx-auto md:before:translate-x-0">
|
||||
<!-- 内容将通过JS动态加载 -->
|
||||
</div>
|
||||
|
||||
@ -78,35 +78,52 @@ const {
|
||||
return;
|
||||
}
|
||||
|
||||
const articlesHTML = articles.map(article => `
|
||||
<div class="bg-white dark:bg-dark-card rounded-xl shadow-sm hover:shadow-md overflow-hidden border border-secondary-200 dark:border-dark-border transition-all duration-300">
|
||||
<a href="/articles/${article.id}" class="block p-6 no-underline text-inherit">
|
||||
<div class="flex flex-col md:flex-row md:items-center gap-4">
|
||||
<div class="flex-1">
|
||||
<h3 class="text-xl font-bold text-primary-800 dark:text-primary-200 mb-2 hover:text-primary-600 dark:hover:text-primary-400 transition-colors">${article.title}</h3>
|
||||
<div class="flex items-center text-sm text-secondary-500 dark:text-secondary-400 mb-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<time datetime="${article.date}">${new Date(article.date).toLocaleDateString('zh-CN')}</time>
|
||||
const articlesHTML = articles.map((article, index) => {
|
||||
const isEven = index % 2 === 0;
|
||||
return `
|
||||
<div class="relative group">
|
||||
<!-- 时间线节点 -->
|
||||
<div class="absolute left-5 -translate-x-1/2 md:left-1/2 top-6 flex h-3 w-3 items-center justify-center">
|
||||
<div class="h-2 w-2 rounded-full bg-primary-500 dark:bg-primary-400 ring-2 ring-white dark:ring-gray-900 ring-offset-2 ring-offset-white dark:ring-offset-gray-900"></div>
|
||||
</div>
|
||||
|
||||
<!-- 文章卡片 -->
|
||||
<a href="/articles/${article.id}"
|
||||
class="group/card ml-10 md:ml-0 ${isEven ? 'md:mr-[50%] md:pr-8' : 'md:ml-[50%] md:pl-8'} block">
|
||||
<article class="relative flex flex-col gap-4 rounded-xl bg-white dark:bg-gray-800 p-6 shadow-lg hover:shadow-xl hover:-translate-y-1 transition-all duration-300 border border-gray-200 dark:border-gray-700">
|
||||
<!-- 日期标签 -->
|
||||
<time datetime="${article.date}"
|
||||
class="absolute top-4 right-4 text-xs font-medium text-secondary-500 dark:text-secondary-400">
|
||||
${new Date(article.date).toLocaleDateString('zh-CN', {year: 'numeric', month: 'long', day: 'numeric'})}
|
||||
</time>
|
||||
|
||||
<!-- 文章标题 -->
|
||||
<h3 class="pr-16 text-xl font-bold text-gray-900 dark:text-gray-100 group-hover/card:text-primary-600 dark:group-hover/card:text-primary-400 transition-colors">
|
||||
${article.title}
|
||||
</h3>
|
||||
|
||||
<!-- 文章摘要 -->
|
||||
${article.summary ? `
|
||||
<p class="text-secondary-600 dark:text-secondary-300 line-clamp-2">
|
||||
${article.summary}
|
||||
</p>
|
||||
` : ''}
|
||||
|
||||
<!-- 文章元信息 -->
|
||||
<div class="flex flex-wrap items-center gap-4 text-sm">
|
||||
${article.section ? `
|
||||
<span class="mx-2 text-secondary-300 dark:text-secondary-600">•</span>
|
||||
<span class="flex items-center">
|
||||
<span class="flex items-center text-secondary-500 dark:text-secondary-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
|
||||
</svg>
|
||||
${article.section}
|
||||
</span>
|
||||
` : ''}
|
||||
</div>
|
||||
|
||||
${article.summary ? `<p class="text-secondary-600 dark:text-secondary-300 line-clamp-2 mb-3">${article.summary}</p>` : ''}
|
||||
|
||||
${article.tags && article.tags.length > 0 ? `
|
||||
<div class="flex flex-wrap gap-2">
|
||||
${article.tags.map(tag => `
|
||||
<span class="text-xs bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 py-1 px-2 rounded-full">
|
||||
<span class="text-xs bg-primary-50 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400 py-1 px-2 rounded-full">
|
||||
#${tag}
|
||||
</span>
|
||||
`).join('')}
|
||||
@ -114,15 +131,18 @@ const {
|
||||
` : ''}
|
||||
</div>
|
||||
|
||||
<div class="text-primary-500 dark:text-primary-400 hidden md:block">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
|
||||
<!-- 阅读更多指示器 -->
|
||||
<div class="flex items-center text-sm text-primary-600 dark:text-primary-400 group-hover/card:translate-x-1 transition-transform">
|
||||
<span class="font-medium">阅读全文</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</a>
|
||||
</div>
|
||||
`).join('');
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
if (append) {
|
||||
articleTimeline.innerHTML += articlesHTML;
|
||||
@ -134,7 +154,11 @@ const {
|
||||
function showLoading(show) {
|
||||
const loading = document.getElementById('loading');
|
||||
if (loading) {
|
||||
loading.classList.toggle('hidden', !show);
|
||||
if (show) {
|
||||
loading.classList.remove('hidden');
|
||||
} else {
|
||||
loading.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,11 +170,11 @@ const {
|
||||
}
|
||||
|
||||
function setupInfiniteScroll() {
|
||||
// 直接使用滚动事件
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
|
||||
setTimeout(() => {
|
||||
handleScroll();
|
||||
}, 500);
|
||||
// 初始检查一次,以防内容不足一屏
|
||||
setTimeout(handleScroll, 500);
|
||||
}
|
||||
|
||||
function handleScroll() {
|
||||
@ -162,6 +186,7 @@ const {
|
||||
const windowHeight = window.innerHeight;
|
||||
const documentHeight = document.documentElement.scrollHeight;
|
||||
|
||||
// 当滚动到距离底部300px时加载更多
|
||||
if (scrollY + windowHeight >= documentHeight - 300) {
|
||||
fetchArticles(currentPage + 1, true);
|
||||
}
|
||||
@ -170,8 +195,6 @@ const {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
fetchArticles(1, false).then(() => {
|
||||
setupInfiniteScroll();
|
||||
}).catch(err => {
|
||||
// 错误已在fetchArticles中处理
|
||||
});
|
||||
});
|
||||
</script>
|
51
src/components/Countdown.tsx
Normal file
51
src/components/Countdown.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
interface CountdownProps {
|
||||
targetDate: string; // 目标日期,格式:'YYYY-MM-DD'
|
||||
}
|
||||
|
||||
export const Countdown: React.FC<CountdownProps> = ({ targetDate }) => {
|
||||
const [timeLeft, setTimeLeft] = useState({
|
||||
days: 0,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
seconds: 0
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
const now = new Date().getTime();
|
||||
const target = new Date(targetDate).getTime();
|
||||
const difference = target - now;
|
||||
|
||||
if (difference > 0) {
|
||||
const days = Math.floor(difference / (1000 * 60 * 60 * 24));
|
||||
const hours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60));
|
||||
const seconds = Math.floor((difference % (1000 * 60)) / 1000);
|
||||
|
||||
setTimeLeft({ days, hours, minutes, seconds });
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(timer);
|
||||
}, [targetDate]);
|
||||
|
||||
const TimeBox = ({ value, label }: { value: number; label: string }) => (
|
||||
<div className="text-center px-4">
|
||||
<div className="text-4xl font-light">
|
||||
{value.toString().padStart(2, '0')}
|
||||
</div>
|
||||
<div className="text-sm mt-1 text-gray-500">{label}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center">
|
||||
<TimeBox value={timeLeft.days} label="天" />
|
||||
<TimeBox value={timeLeft.hours} label="时" />
|
||||
<TimeBox value={timeLeft.minutes} label="分" />
|
||||
<TimeBox value={timeLeft.seconds} label="秒" />
|
||||
</div>
|
||||
);
|
||||
};
|
@ -55,11 +55,6 @@ const DoubanCollection: React.FC<DoubanCollectionProps> = ({ type }) => {
|
||||
fetchData();
|
||||
}, [type]);
|
||||
|
||||
const updatePage = (page: number) => {
|
||||
const start = (page - 1) * 15;
|
||||
fetchData(start);
|
||||
};
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
const start = (page - 1) * 15;
|
||||
|
||||
|
49
src/components/Footer.astro
Normal file
49
src/components/Footer.astro
Normal file
@ -0,0 +1,49 @@
|
||||
---
|
||||
interface Props {
|
||||
icp?: string;
|
||||
psbIcp?: string;
|
||||
psbIcpUrl?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
icp = "",
|
||||
psbIcp = "",
|
||||
psbIcpUrl = "http://www.beian.gov.cn/portal/registerSystemInfo",
|
||||
} = Astro.props;
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
---
|
||||
|
||||
<footer class="w-full py-6 px-4 bg-gray-50 dark:bg-dark-bg border-t border-gray-200 dark:border-gray-800 mt-auto">
|
||||
<div class="max-w-5xl mx-auto flex flex-col items-center justify-center space-y-4">
|
||||
<div class="flex flex-wrap items-center justify-center gap-4 text-sm text-gray-600 dark:text-gray-400">
|
||||
|
||||
{icp && (
|
||||
<a
|
||||
href="https://beian.miit.gov.cn/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="hover:text-primary-600 dark:hover:text-primary-400 transition-colors"
|
||||
>
|
||||
{icp}
|
||||
</a>
|
||||
)}
|
||||
|
||||
{psbIcp && (
|
||||
<a
|
||||
href={psbIcpUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex items-center hover:text-primary-600 dark:hover:text-primary-400 transition-colors"
|
||||
>
|
||||
<img src="/images/national.png" alt="公安备案" class="h-4 mr-1" />
|
||||
{psbIcp}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div class="text-sm text-gray-500 dark:text-gray-500 font-light">
|
||||
© {currentYear} New Echoes. All rights reserved.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
@ -187,14 +187,6 @@ const GitProjectCollection: React.FC<GitProjectCollectionProps> = ({
|
||||
700: 1
|
||||
};
|
||||
|
||||
if (loading && projects.length === 0) {
|
||||
return <div className="flex justify-center p-8">加载中...</div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div className="text-red-500 p-4">错误: {error}</div>;
|
||||
}
|
||||
|
||||
const getPlatformName = (platform: GitPlatform) => {
|
||||
return GIT_PLATFORM_CONFIG.platformNames[platform];
|
||||
};
|
||||
@ -203,7 +195,7 @@ const GitProjectCollection: React.FC<GitProjectCollectionProps> = ({
|
||||
const displayTitle = title || `${getPlatformName(platform)} 项目`;
|
||||
|
||||
return (
|
||||
<div className="git-project-collection">
|
||||
<div className="git-project-collection max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-2xl font-bold mb-6 text-primary-700">
|
||||
{displayTitle}
|
||||
{effectiveUsername && <span className="ml-2 text-secondary-500">(@{effectiveUsername})</span>}
|
||||
@ -227,13 +219,13 @@ const GitProjectCollection: React.FC<GitProjectCollectionProps> = ({
|
||||
columnClassName="pl-4 bg-clip-padding"
|
||||
>
|
||||
{projects.map((project, index) => (
|
||||
<div key={`${project.platform}-${project.owner}-${project.name}-${index}`} className="mb-4 overflow-hidden rounded-lg border border-secondary-200 bg-white shadow-sm transition-shadow hover:shadow-md">
|
||||
<a href={project.url} target="_blank" rel="noopener noreferrer" className="block p-4">
|
||||
<div className="flex items-center mb-2">
|
||||
<div className="mr-2 text-secondary-600">
|
||||
<div key={`${project.platform}-${project.owner}-${project.name}-${index}`} className="mb-4 overflow-hidden rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:shadow-xl hover:-translate-y-1 transition-all duration-300 shadow-lg">
|
||||
<a href={project.url} target="_blank" rel="noopener noreferrer" className="block p-5">
|
||||
<div className="flex items-start">
|
||||
<div className="w-10 h-10 flex-shrink-0 flex items-center justify-center rounded-lg bg-primary-100 text-primary-600 group-hover:bg-primary-200 transition-colors">
|
||||
{getPlatformIcon(project.platform as GitPlatform)}
|
||||
</div>
|
||||
<div className="flex-1 truncate">
|
||||
<div className="ml-3 flex-1">
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
src={project.avatarUrl}
|
||||
@ -245,44 +237,48 @@ const GitProjectCollection: React.FC<GitProjectCollectionProps> = ({
|
||||
target.src = 'https://via.placeholder.com/40';
|
||||
}}
|
||||
/>
|
||||
<span className="text-sm text-secondary-600 truncate">{project.owner}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400 truncate">{project.owner}</span>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-semibold mb-1 text-primary-800 line-clamp-1">{project.name}</h3>
|
||||
<h3 className="font-bold text-base text-gray-800 dark:text-gray-100 group-hover:text-primary-700 dark:group-hover:text-primary-300 transition-colors line-clamp-1 mt-2">{project.name}</h3>
|
||||
|
||||
{project.description && (
|
||||
<p className="text-secondary-700 text-sm mb-3 line-clamp-2">{project.description}</p>
|
||||
<div className="h-12 mb-3">
|
||||
{project.description ? (
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">{project.description}</p>
|
||||
) : (
|
||||
<p className="text-sm text-gray-400 dark:text-gray-500 italic">暂无描述</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center text-xs mt-2">
|
||||
<div className="flex flex-wrap items-center text-xs gap-4">
|
||||
{project.language && (
|
||||
<div className="flex items-center mr-4 mb-1">
|
||||
<span className={`w-3 h-3 rounded-full mr-1 ${getLanguageColor(project.language)}`}></span>
|
||||
<span className="text-secondary-600">{project.language}</span>
|
||||
<div className="flex items-center">
|
||||
<span className={`w-3 h-3 rounded-full mr-1.5 ${getLanguageColor(project.language)}`}></span>
|
||||
<span className="text-gray-600 dark:text-gray-400">{project.language}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center mr-4 mb-1">
|
||||
<svg className="w-4 h-4 mr-1 text-secondary-500" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 2a1 1 0 011 1v1.323l3.954 1.582 1.599-.8a1 1 0 01.894 1.79l-1.233.616 1.738 5.42a1 1 0 01-.285 1.05A3.989 3.989 0 0115 15a3.989 3.989 0 01-2.667-1.019 1 1 0 01-.285-1.05l1.715-5.349L11 6.477V16h2a1 1 0 110 2H7a1 1 0 110-2h2V6.477L6.237 7.582l1.715 5.349a1 1 0 01-.285 1.05A3.989 3.989 0 015 15a3.989 3.989 0 01-2.667-1.019 1 1 0 01-.285-1.05l1.738-5.42-1.233-.617a1 1 0 01.894-1.788l1.599.799L9 4.323V3a1 1 0 011-1zm-5 8.274l-.818 2.552c.25.112.526.174.818.174.292 0 .569-.062.818-.174L5 10.274zm10 0l-.818 2.552c.25.112.526.174.818.174.292 0 .569-.062.818-.174L15 10.274z" clipRule="evenodd" />
|
||||
<div className="flex items-center">
|
||||
<svg className="w-4 h-4 mr-1.5 text-gray-500 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" />
|
||||
</svg>
|
||||
<span className="text-secondary-600">{project.stars}</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">{project.stars}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center mr-4 mb-1">
|
||||
<svg className="w-4 h-4 mr-1 text-secondary-500" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M12.316 3.051a1 1 0 01.633 1.265l-4 12a1 1 0 11-1.898-.632l4-12a1 1 0 011.265-.633zM5.707 6.293a1 1 0 010 1.414L3.414 10l2.293 2.293a1 1 0 11-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0zm8.586 0a1 1 0 011.414 0l3 3a1 1 0 010 1.414l-3 3a1 1 0 11-1.414-1.414L16.586 10l-2.293-2.293a1 1 0 010-1.414z" clipRule="evenodd" />
|
||||
<div className="flex items-center">
|
||||
<svg className="w-4 h-4 mr-1.5 text-gray-500 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" />
|
||||
</svg>
|
||||
<span className="text-secondary-600">{project.forks}</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">{project.forks}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center mb-1 ml-auto">
|
||||
<svg className="w-4 h-4 mr-1 text-secondary-500" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clipRule="evenodd" />
|
||||
<div className="flex items-center ml-auto">
|
||||
<svg className="w-4 h-4 mr-1.5 text-gray-500 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span className="text-secondary-500">{new Date(project.updatedAt).toLocaleDateString('zh-CN')}</span>
|
||||
<span className="text-gray-500 dark:text-gray-400">{new Date(project.updatedAt).toLocaleDateString('zh-CN')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
@ -1,16 +1,60 @@
|
||||
---
|
||||
import "@/styles/global.css";
|
||||
import Header from "@/components/header.astro";
|
||||
import Footer from "@/components/Footer.astro";
|
||||
import { ICP, PSB_ICP, PSB_ICP_URL, SITE_NAME } from "@/consts";
|
||||
|
||||
// 定义Props接口
|
||||
interface Props {
|
||||
title?: string;
|
||||
description?: string;
|
||||
date?: Date;
|
||||
author?: string;
|
||||
tags?: string[];
|
||||
image?: string;
|
||||
}
|
||||
|
||||
// 获取完整的 URL
|
||||
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
||||
|
||||
// 从props中获取页面特定信息
|
||||
const { title = SITE_NAME, description, date, author, tags, image } = Astro.props;
|
||||
---
|
||||
<!doctype html>
|
||||
<html lang="en" class="m-0 w-full h-full">
|
||||
<html lang="zh-CN" class="m-0 w-full h-full">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>New Echoes</title>
|
||||
|
||||
<!-- 基本元数据 -->
|
||||
<title>{title}</title>
|
||||
<meta name="description" content={description || `${SITE_NAME} - 个人博客`} />
|
||||
<link rel="canonical" href={canonicalURL} />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:url" content={canonicalURL} />
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description || `${SITE_NAME} - 个人博客`} />
|
||||
{image && <meta property="og:image" content={new URL(image, Astro.site)} />}
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content={canonicalURL} />
|
||||
<meta property="twitter:title" content={title} />
|
||||
<meta property="twitter:description" content={description || `${SITE_NAME} - 个人博客`} />
|
||||
{image && <meta property="twitter:image" content={new URL(image, Astro.site)} />}
|
||||
|
||||
<!-- 文章特定元数据 -->
|
||||
{date && <meta property="article:published_time" content={date.toISOString()} />}
|
||||
{author && <meta name="author" content={author} />}
|
||||
{tags && tags.map(tag => (
|
||||
<meta property="article:tag" content={tag} />
|
||||
))}
|
||||
|
||||
<script is:inline>
|
||||
// 立即执行主题初始化
|
||||
const theme = (() => {
|
||||
@ -25,10 +69,11 @@ import Header from "@/components/header.astro";
|
||||
document.documentElement.dataset.theme = theme;
|
||||
</script>
|
||||
</head>
|
||||
<body class="m-0 w-full h-full bg-white dark:bg-dark-bg transition-colors duration-300">
|
||||
<body class="m-0 w-full h-full bg-gray-50 dark:bg-dark-bg transition-colors duration-300 flex flex-col min-h-screen">
|
||||
<Header />
|
||||
<main class="pt-16">
|
||||
<main class="pt-16 flex-grow">
|
||||
<slot />
|
||||
</main>
|
||||
<Footer icp={ICP} psbIcp={PSB_ICP} psbIcpUrl={PSB_ICP_URL} />
|
||||
</body>
|
||||
</html>
|
@ -7,7 +7,7 @@ interface Props {
|
||||
const { type, title } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<h1 class="text-3xl font-bold mb-6">{title}</h1>
|
||||
|
||||
<div id="media-list" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
|
||||
@ -102,12 +102,9 @@ const { type, title } = Astro.props;
|
||||
const loading = document.getElementById('loading');
|
||||
if (loading) {
|
||||
if (show) {
|
||||
loading.innerHTML = `
|
||||
<div class="inline-block h-8 w-8 animate-spin rounded-full border-4 border-solid border-current border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]"></div>
|
||||
<p class="mt-2 text-gray-600">加载更多...</p>
|
||||
`;
|
||||
loading.classList.remove('hidden');
|
||||
} else {
|
||||
loading.innerHTML = `<div class="h-8"></div>`; // 保持元素可见但内容为空
|
||||
loading.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -124,9 +121,7 @@ const { type, title } = Astro.props;
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
|
||||
// 初始检查一次,以防内容不足一屏
|
||||
setTimeout(() => {
|
||||
handleScroll();
|
||||
}, 500);
|
||||
setTimeout(handleScroll, 500);
|
||||
}
|
||||
|
||||
function handleScroll() {
|
||||
|
@ -1,25 +1,31 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export function ThemeToggle({ height = 20, width = 20, fill = "currentColor" }) {
|
||||
// 从 document.documentElement.dataset.theme 获取初始主题
|
||||
const [theme, setTheme] = useState(() => {
|
||||
if (typeof document !== 'undefined') {
|
||||
return document.documentElement.dataset.theme || 'light';
|
||||
}
|
||||
return 'light';
|
||||
});
|
||||
export function ThemeToggle({ height = 16, width = 16, fill = "currentColor" }) {
|
||||
// 使用null作为初始状态,表示尚未确定主题
|
||||
const [theme, setTheme] = useState(null);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
// 在客户端挂载后再确定主题
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
// 从 localStorage 或 document.documentElement.dataset.theme 获取主题
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
const rootTheme = document.documentElement.dataset.theme;
|
||||
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
|
||||
// 优先使用已保存的主题,其次是文档根元素的主题,最后是系统主题
|
||||
const initialTheme = savedTheme || rootTheme || systemTheme;
|
||||
setTheme(initialTheme);
|
||||
|
||||
// 确保文档根元素的主题与状态一致
|
||||
document.documentElement.dataset.theme = initialTheme;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mounted) return;
|
||||
if (!mounted || theme === null) return;
|
||||
|
||||
// 当主题改变时更新 DOM 和 localStorage
|
||||
const root = document.documentElement;
|
||||
root.dataset.theme = theme;
|
||||
document.documentElement.dataset.theme = theme;
|
||||
|
||||
if (theme === getSystemTheme()) {
|
||||
localStorage.removeItem('theme');
|
||||
@ -36,25 +42,39 @@ export function ThemeToggle({ height = 20, width = 20, fill = "currentColor" })
|
||||
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
|
||||
}
|
||||
|
||||
// 在客户端挂载前,返回一个空的占位符
|
||||
if (!mounted || theme === null) {
|
||||
return (
|
||||
<div
|
||||
className="inline-flex items-center justify-center cursor-pointer"
|
||||
style={{ height: `${height}px`, width: `${width}px` }}
|
||||
className="inline-flex items-center justify-center h-8 w-8 cursor-pointer rounded-md transition-all duration-200 hover:bg-gray-100 dark:hover:bg-gray-700/50 text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 mt-1"
|
||||
>
|
||||
<span className="sr-only">加载主题切换按钮...</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="inline-flex items-center justify-center h-8 w-8 cursor-pointer rounded-md transition-all duration-200 hover:bg-gray-100 dark:hover:bg-gray-700/50 text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 mt-1"
|
||||
onClick={toggleTheme}
|
||||
role="button"
|
||||
aria-label={`Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`}
|
||||
>
|
||||
{theme === 'dark' ? (
|
||||
<svg
|
||||
style={{ height: `${height * 2/3}px`, width: `${width * 2/3}px` }}
|
||||
style={{ height: `${height}px`, width: `${width}px` }}
|
||||
fill={fill}
|
||||
viewBox="0 0 16 16"
|
||||
className="transition-transform duration-200 hover:scale-110"
|
||||
>
|
||||
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
style={{ height: `${height * 2/3}px`, width: `${width * 2/3}px` }}
|
||||
style={{ height: `${height}px`, width: `${width}px` }}
|
||||
fill={fill}
|
||||
viewBox="0 0 16 16"
|
||||
className="transition-transform duration-200 hover:scale-110"
|
||||
>
|
||||
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/>
|
||||
</svg>
|
||||
|
@ -11,13 +11,14 @@ const normalizedPath = currentPath.endsWith('/') ? currentPath.slice(0, -1) : cu
|
||||
// 定义导航链接
|
||||
|
||||
---
|
||||
<header class="fixed w-full top-0 z-50 bg-white/80 dark:bg-dark-surface/80 backdrop-blur-md border-b border-secondary-200 dark:border-dark-border">
|
||||
<nav>
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<header class="fixed w-full top-0 z-50 transition-all duration-300" id="main-header">
|
||||
<div class="absolute inset-0 bg-gray-50/95 dark:bg-dark-bg/95 transition-all duration-300" id="header-bg"></div>
|
||||
<nav class="relative">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between h-16">
|
||||
<!-- Logo 部分 -->
|
||||
<div class="flex items-center">
|
||||
<a href="/" class="text-lg font-semibold text-primary-900 dark:text-primary-100 hover:text-primary-600 dark:hover:text-primary-400">
|
||||
<a href="/" class="text-xl md:text-2xl font-bold tracking-tight transition-all duration-300 ease-in-out bg-gradient-to-r from-primary-600 to-primary-400 bg-clip-text text-transparent hover:from-primary-500 hover:to-primary-300 dark:from-primary-400 dark:to-primary-200 dark:hover:from-primary-300 dark:hover:to-primary-100">
|
||||
{SITE_NAME}
|
||||
</a>
|
||||
</div>
|
||||
@ -45,14 +46,143 @@ const normalizedPath = currentPath.endsWith('/') ? currentPath.slice(0, -1) : cu
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center p-2 rounded-md text-secondary-400 dark:text-secondary-500 hover:text-secondary-500 dark:hover:text-secondary-400 hover:bg-secondary-100 dark:hover:bg-dark-card focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary-500"
|
||||
id="mobile-menu-button"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span class="sr-only">打开菜单</span>
|
||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<svg class="h-6 w-6 block" id="menu-open-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
<svg class="h-6 w-6 hidden" id="menu-close-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 移动端菜单 -->
|
||||
<div class="hidden md:hidden fixed inset-x-0 top-16 z-40" id="mobile-menu">
|
||||
<div id="mobile-menu-bg">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-2">
|
||||
<div class="grid gap-1">
|
||||
{NAV_LINKS.map(link => (
|
||||
<a
|
||||
href={link.href}
|
||||
class:list={[
|
||||
'flex items-center px-3 py-3 rounded-lg text-base font-medium transition-all duration-200 ease-in-out',
|
||||
normalizedPath === (link.href === '/' ? '' : link.href)
|
||||
? 'text-white bg-primary-600 dark:bg-primary-500 shadow-sm'
|
||||
: 'text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800/70'
|
||||
]}
|
||||
>
|
||||
{link.text}
|
||||
</a>
|
||||
))}
|
||||
<div class="mt-2 pt-3 border-t border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<span class="text-sm font-medium text-gray-600 dark:text-gray-300">切换主题</span>
|
||||
<ThemeToggle client:load />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
#header-bg {
|
||||
opacity: 1;
|
||||
backdrop-filter: blur(0);
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
#header-bg.scrolled {
|
||||
backdrop-filter: blur(6px);
|
||||
background: rgba(249, 250, 251, 0.8);
|
||||
box-shadow:
|
||||
0 1px 2px rgba(0, 0, 0, 0.04),
|
||||
0 2px 4px rgba(0, 0, 0, 0.04),
|
||||
0 4px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
:global([data-theme="dark"]) #header-bg.scrolled {
|
||||
background: rgba(15, 23, 42, 0.8);
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* 移动端菜单样式 */
|
||||
#mobile-menu {
|
||||
/* 移除过渡效果,确保菜单内容立即显示 */
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
max-height: calc(100vh - 4rem);
|
||||
overflow-y: auto;
|
||||
/* 确保子元素不受过渡效果影响 */
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
/* 移动端菜单背景 */
|
||||
#mobile-menu-bg {
|
||||
/* 直接应用高斯模糊,不使用过渡效果 */
|
||||
-webkit-backdrop-filter: blur(6px);
|
||||
backdrop-filter: blur(6px);
|
||||
background: rgba(249, 250, 251, 0.8);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.04);
|
||||
/* 确保高斯模糊立即应用 */
|
||||
will-change: backdrop-filter;
|
||||
}
|
||||
|
||||
:global([data-theme="dark"]) #mobile-menu-bg {
|
||||
background: rgba(15, 23, 42, 0.8);
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const header = document.getElementById('header-bg');
|
||||
const mobileMenuBg = document.getElementById('mobile-menu-bg');
|
||||
const scrollThreshold = 50;
|
||||
|
||||
function updateHeaderBackground() {
|
||||
if (window.scrollY > scrollThreshold) {
|
||||
header?.classList.add('scrolled');
|
||||
} else {
|
||||
header?.classList.remove('scrolled');
|
||||
}
|
||||
}
|
||||
|
||||
// 初始检查
|
||||
updateHeaderBackground();
|
||||
|
||||
// 添加滚动事件监听
|
||||
window.addEventListener('scroll', updateHeaderBackground);
|
||||
|
||||
// 移动端菜单逻辑
|
||||
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
||||
const mobileMenu = document.getElementById('mobile-menu');
|
||||
const menuOpenIcon = document.getElementById('menu-open-icon');
|
||||
const menuCloseIcon = document.getElementById('menu-close-icon');
|
||||
|
||||
if (mobileMenuButton && mobileMenu && menuOpenIcon && menuCloseIcon) {
|
||||
mobileMenuButton.addEventListener('click', () => {
|
||||
const expanded = mobileMenuButton.getAttribute('aria-expanded') === 'true';
|
||||
|
||||
// 切换菜单状态
|
||||
mobileMenuButton.setAttribute('aria-expanded', (!expanded).toString());
|
||||
|
||||
if (expanded) {
|
||||
// 直接隐藏菜单,不使用过渡效果
|
||||
mobileMenu.classList.add('hidden');
|
||||
} else {
|
||||
// 直接显示菜单,不使用过渡效果
|
||||
mobileMenu.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// 切换图标
|
||||
menuOpenIcon.classList.toggle('hidden');
|
||||
menuCloseIcon.classList.toggle('hidden');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -1,13 +1,19 @@
|
||||
export const SITE_NAME = "Blog Name";
|
||||
export const SITE_URL = 'https://lsy22.com';
|
||||
export const SITE_NAME = "echoes";
|
||||
|
||||
export const NAV_LINKS = [
|
||||
{ href: '/', text: '首页' },
|
||||
{ href: '/articles', text: '文章' },
|
||||
{ href: '/movies', text: '观影' },
|
||||
{ href: '/books', text: '读书' },
|
||||
{ href: '/about', text: '关于' },
|
||||
{ href: '/projects', text: '项目' }
|
||||
{ href: '/projects', text: '项目' },
|
||||
{ href: '/about', text: '关于' }
|
||||
];
|
||||
|
||||
export const ICP = '渝ICP备2022009272号';
|
||||
export const PSB_ICP = '渝公网安备50011902000520号';
|
||||
export const PSB_ICP_URL = 'http://www.beian.gov.cn/portal/registerSystemInfo';
|
||||
|
||||
export const VISITED_PLACES = [ '黑龙江', '吉林', '辽宁', '北京', '天津', '广东', '西藏', '河北', '山东', '湖南','重庆','四川' ];
|
||||
|
||||
export const DOUBAN_ID = 'lsy22';
|
||||
|
@ -1,19 +0,0 @@
|
||||
---
|
||||
title: "测试文章"
|
||||
date: 2023-03-03
|
||||
tags: ["测试"]
|
||||
category: "测试分类"
|
||||
summary: "这是一篇测试文章,用于测试目录结构"
|
||||
---
|
||||
|
||||
# 测试文章
|
||||
|
||||
这是一篇测试文章,用于测试目录结构功能。
|
||||
|
||||
## 二级标题
|
||||
|
||||
这是二级标题下的内容。
|
||||
|
||||
### 三级标题
|
||||
|
||||
这是三级标题下的内容。
|
@ -1,335 +0,0 @@
|
||||
---
|
||||
title: "多级目录测试文章"
|
||||
date: 2023-03-03
|
||||
tags: ["测试", "多级目录"]
|
||||
category: "测试分类"
|
||||
summary: "这是一篇用于测试多级目录功能的文章"
|
||||
---
|
||||
## 1. 基础语法
|
||||
|
||||
### 1.1 粗体文本
|
||||
|
||||
```markdown
|
||||
**这是粗体文本**
|
||||
```
|
||||
|
||||
**这是粗体文本**
|
||||
|
||||
### 1.2 斜体文本
|
||||
|
||||
```markdown
|
||||
*这是斜体文本*
|
||||
```
|
||||
|
||||
*这是斜体文本*
|
||||
|
||||
### 1.3 粗斜体文本
|
||||
|
||||
```markdown
|
||||
***这是粗斜体文本***
|
||||
```
|
||||
|
||||
***这是粗斜体文本***
|
||||
|
||||
### 1.4 删除线文本
|
||||
|
||||
```markdown
|
||||
~~这是删除线文本~~
|
||||
```
|
||||
|
||||
~~这是删除线文本~~
|
||||
|
||||
### 1.5 无序列表
|
||||
|
||||
```markdown
|
||||
- 第一项
|
||||
- 子项 1
|
||||
- 子项 2
|
||||
- 第二项
|
||||
- 第三项
|
||||
```
|
||||
|
||||
- 第一项
|
||||
- 子项 1
|
||||
- 子项 2
|
||||
- 第二项
|
||||
- 第三项
|
||||
|
||||
### 1.6 有序列表
|
||||
|
||||
```markdown
|
||||
1. 第一步
|
||||
1. 子步骤 1
|
||||
2. 子步骤 2
|
||||
2. 第二步
|
||||
3. 第三步
|
||||
```
|
||||
|
||||
1. 第一步
|
||||
1. 子步骤 1
|
||||
2. 子步骤 2
|
||||
2. 第二步
|
||||
3. 第三步
|
||||
|
||||
### 1.7 任务列表
|
||||
|
||||
```markdown
|
||||
- [x] 已完成任务
|
||||
- [ ] 未完成任务
|
||||
- [x] 又一个已完成任务
|
||||
```
|
||||
|
||||
- [x] 已完成任务
|
||||
- [ ] 未完成任务
|
||||
- [x] 又一个已完成任务
|
||||
|
||||
### 1.8 行内代码
|
||||
|
||||
```markdown
|
||||
这是一段包含`const greeting = "Hello World";`的行内代码
|
||||
```
|
||||
|
||||
这是一段包含`const greeting = "Hello World";`的行内代码
|
||||
|
||||
### 1.9 代码块
|
||||
|
||||
````markdown
|
||||
```typescript
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
function greet(user: User): string {
|
||||
return `Hello, \${user.name}!`;
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
```typescript
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
function greet(user: User): string {
|
||||
return `Hello, \${user.name}!`;
|
||||
}
|
||||
```
|
||||
|
||||
### 1.10 表格
|
||||
|
||||
```markdown
|
||||
| 功能 | 基础版 | 高级版 |
|
||||
|:-----|:------:|-------:|
|
||||
| 文本编辑 | ✓ | ✓ |
|
||||
| 实时预览 | ✗ | ✓ |
|
||||
| 导出格式 | 2种 | 5种 |
|
||||
```
|
||||
|
||||
| 功能 | 基础版 | 高级版 |
|
||||
|:-----|:------:|-------:|
|
||||
| 文本编辑 | ✓ | ✓ |
|
||||
| 实时预览 | ✗ | ✓ |
|
||||
| 导出格式 | 2种 | 5种 |
|
||||
|
||||
### 1.11 引用
|
||||
|
||||
```markdown
|
||||
> 📌 **最佳实践**
|
||||
>
|
||||
> 好的文章需要有清晰的结构和流畅的表达。
|
||||
```
|
||||
|
||||
> 📌 **最佳实践**
|
||||
>
|
||||
> 好的文章需要有清晰的结构和流畅的表达。
|
||||
|
||||
### 1.12 脚注
|
||||
|
||||
```markdown
|
||||
这里有一个脚注[^1]。
|
||||
|
||||
[^1]: 这是脚注的内容。
|
||||
```
|
||||
|
||||
这里有一个脚注[^1]。
|
||||
|
||||
[^1]: 这是脚注的内容。
|
||||
|
||||
### 1.13 表情符号
|
||||
|
||||
```markdown
|
||||
:smile: :heart: :star: :rocket:
|
||||
```
|
||||
|
||||
:smile: :heart: :star: :rocket:
|
||||
|
||||
### 1.14 可折叠内容
|
||||
|
||||
```markdown
|
||||
<details>
|
||||
<summary >
|
||||
🎯 如何选择合适的写作工具?
|
||||
</summary>
|
||||
|
||||
选择写作工具时需要考虑以下几点:
|
||||
|
||||
1. **跨平台支持** - 确保在不同设备上都能访问
|
||||
2. **实时预览** - Markdown 实时渲染很重要
|
||||
3. **版本控制** - 最好能支持文章的版本管理
|
||||
4. **导出功能** - 支持导出为多种格式
|
||||
</details>
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
🎯 如何选择合适的写作工具?
|
||||
</summary>
|
||||
|
||||
选择写作工具时需要考虑以下几点:
|
||||
|
||||
1. **跨平台支持** - 确保在不同设备上都能访问
|
||||
2. **实时预览** - Markdown 实时渲染很重要
|
||||
3. **版本控制** - 最好能支持文章的版本管理
|
||||
4. **导出功能** - 支持导出为多种格式
|
||||
</details>
|
||||
|
||||
### 1.15 引用式
|
||||
|
||||
```markdown
|
||||
> 📌 **最佳实践**
|
||||
>
|
||||
> 好的文章需要有清晰的结构和流畅的表达。以下是一些建议:
|
||||
>
|
||||
> 1. 开门见山,直入主题
|
||||
> 2. 层次分明,逻辑清晰
|
||||
> 3. 语言简洁,表达准确
|
||||
>
|
||||
> *— 写作指南*
|
||||
```
|
||||
|
||||
> 📌 **最佳实践**
|
||||
>
|
||||
> 好的文章需要有清晰的结构和流畅的表达。以下是一些建议:
|
||||
>
|
||||
> 1. 开门见山,直入主题
|
||||
> 2. 层次分明,逻辑清晰
|
||||
> 3. 语言简洁,表达准确
|
||||
>
|
||||
> *— 写作指南*
|
||||
|
||||
|
||||
## 2. HTML排版
|
||||
|
||||
### 2.1 图文混排布局
|
||||
|
||||
```markdown
|
||||
<div class="flex items-center gap-6 my-8">
|
||||
<img src="https://images.unsplash.com/photo-1516116216624-53e697fedbea?w=400&h=400"
|
||||
alt="写作工具"
|
||||
class="w-1/3 rounded-lg shadow-lg" />
|
||||
<div class="flex-1">
|
||||
<h4 class="text-xl font-bold mb-2">高效写作工具</h4>
|
||||
<p>使用合适的写作工具可以极大提升写作效率。推荐使用支持即时预览的编辑器,这样可以实时查看排版效果。</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
<div class="flex items-center gap-6 my-8">
|
||||
<img src="https://images.unsplash.com/photo-1516116216624-53e697fedbea?w=400&h=400"
|
||||
alt="写作工具"
|
||||
class="w-1/3 rounded-lg shadow-lg" />
|
||||
<div class="flex-1">
|
||||
<h4 class="text-xl font-bold mb-2">高效写作工具</h4>
|
||||
<p>使用合适的写作工具可以极大提升写作效率。推荐使用支持即时预览的编辑器,这样可以实时查看排版效果。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
### 2.2 并排卡片
|
||||
|
||||
```markdown
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 my-8">
|
||||
<div class="p-6 bg-gray-100 rounded-lg">
|
||||
<h4 class="text-lg font-bold mb-2">🚀 快速上手</h4>
|
||||
<p>通过简单的标记语法,快速创建格式化的文档,无需复杂的排版工具。</p>
|
||||
</div>
|
||||
<div class="p-6 bg-gray-100 rounded-lg">
|
||||
<h4 class="text-lg font-bold mb-2">⚡ 高效输出</h4>
|
||||
<p>专注于内容创作,让工具自动处理排版,提高写作效率。</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 my-8">
|
||||
<div class="p-6 bg-gray-100 rounded-lg">
|
||||
<h4 class="text-lg font-bold mb-2">🚀 快速上手</h4>
|
||||
<p>通过简单的标记语法,快速创建格式化的文档,无需复杂的排版工具。</p>
|
||||
</div>
|
||||
<div class="p-6 bg-gray-100 rounded-lg">
|
||||
<h4 class="text-lg font-bold mb-2">⚡ 高效输出</h4>
|
||||
<p>专注于内容创作,让工具自动处理排版,提高写作效率。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
### 2.4 高亮提示框
|
||||
|
||||
```markdown
|
||||
<div class="p-6 bg-blue-50 border-l-4 border-blue-500 rounded-lg my-8">
|
||||
<h4 class="text-lg font-bold text-blue-700 mb-2">💡 小贴士</h4>
|
||||
<p class="text-blue-600">在写作时,可以先列出文章大纲,再逐步充实内容。这可以保证文章结构清晰,内容完整。</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
<div class="p-6 bg-blue-50 border-l-4 border-blue-500 rounded-lg my-8">
|
||||
<h4 class="text-lg font-bold text-blue-700 mb-2">小贴士</h4>
|
||||
<p class="text-blue-600">在写作时,可以先列出文章大纲,再逐步充实内容。这样可以保证文章结构清晰,内容完整。</p>
|
||||
</div>
|
||||
|
||||
### 2.5 时间线
|
||||
|
||||
```markdown
|
||||
<div class="relative pl-8 my-8 border-l-2 border-gray-200">
|
||||
<div class="mb-8 relative">
|
||||
<div class="absolute -left-10 w-4 h-4 bg-blue-500 rounded-full"></div>
|
||||
<div class="font-bold mb-2">1. 确定主题</div>
|
||||
<p>根据目标受众和写作目的,确定文章主题。</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-8 relative">
|
||||
<div class="absolute -left-10 w-4 h-4 bg-blue-500 rounded-full"></div>
|
||||
<div class="font-bold mb-2">2. 收集资料</div>
|
||||
<p>广泛搜集相关资料,为写作做充实准备。</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
<div class="relative pl-8 my-8 border-l-2 border-gray-200">
|
||||
<div class="mb-8 relative">
|
||||
<div class="absolute -left-10 w-4 h-4 bg-blue-500 rounded-full"></div>
|
||||
<div class="font-bold mb-2">1. 确定主题</div>
|
||||
<p>根据目标受众和写作目的,确定文章主题。</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-8 relative">
|
||||
<div class="absolute -left-10 w-4 h-4 bg-blue-500 rounded-full"></div>
|
||||
<div class="font-bold mb-2">2. 收集资料</div>
|
||||
<p>广泛搜集相关资料,为写作做充实准备。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
## 3. 总结
|
||||
|
||||
本文展示了 Markdown 从基础到高级的各种用法:
|
||||
|
||||
1. 基础语法:文本格式化、列表、代码、表格等
|
||||
2. 高级排版:图文混排、叠面板、卡片布局等
|
||||
3. 特殊语法:数学公式、脚注、表情符号等
|
||||
|
||||
> 💡 **提示**:部分高级排版功能可能需要特定的 Markdown 编辑器或渲染支持,请确认是否支持这些功能。
|
@ -1,108 +0,0 @@
|
||||
---
|
||||
title: Astro 框架介绍
|
||||
date: 2023-07-20
|
||||
tags: [Astro, 前端, Web开发]
|
||||
category: 技术
|
||||
summary: Astro 是一个现代的静态站点生成器,专注于内容驱动的网站,本文将介绍其基本特性和使用方法。
|
||||
---
|
||||
|
||||
# Astro 框架介绍
|
||||
|
||||
[Astro](https://astro.build/) 是一个现代的静态站点生成器,专注于内容驱动的网站。它允许开发者使用他们喜欢的 UI 组件框架(如 React、Vue、Svelte 等),同时生成快速、优化的静态 HTML。
|
||||
|
||||
## Astro 的主要特点
|
||||
|
||||
### 1. 零 JavaScript 默认
|
||||
|
||||
Astro 默认不会向客户端发送任何 JavaScript。这意味着您的网站将以闪电般的速度加载,因为浏览器不需要下载、解析和执行 JavaScript 代码。
|
||||
|
||||
### 2. 组件岛屿架构
|
||||
|
||||
Astro 引入了"组件岛屿"的概念,允许您在需要交互性的地方选择性地使用 JavaScript。这种方法确保了最佳的性能,同时仍然提供了丰富的用户体验。
|
||||
|
||||
```astro
|
||||
---
|
||||
import ReactCounter from '../components/ReactCounter.jsx';
|
||||
import VueCounter from '../components/VueCounter.vue';
|
||||
import SvelteCounter from '../components/SvelteCounter.svelte';
|
||||
---
|
||||
|
||||
<!-- 这些组件将在客户端激活(水合) -->
|
||||
<ReactCounter client:load />
|
||||
<VueCounter client:visible />
|
||||
<SvelteCounter client:idle />
|
||||
```
|
||||
|
||||
### 3. 多框架支持
|
||||
|
||||
Astro 允许您在同一个项目中使用多种 UI 框架,如 React、Vue、Svelte、Solid 等。这意味着您可以使用最适合特定任务的工具,而不必被单一框架所限制。
|
||||
|
||||
### 4. 内置优化
|
||||
|
||||
Astro 自动优化您的网站,包括:
|
||||
|
||||
- 代码分割
|
||||
- CSS 优化
|
||||
- 图像优化
|
||||
- 预渲染
|
||||
- 延迟加载
|
||||
|
||||
## 基本使用
|
||||
|
||||
### 安装
|
||||
|
||||
使用以下命令创建一个新的 Astro 项目:
|
||||
|
||||
```bash
|
||||
# 使用 npm
|
||||
npm create astro@latest
|
||||
|
||||
# 使用 yarn
|
||||
yarn create astro
|
||||
|
||||
# 使用 pnpm
|
||||
pnpm create astro@latest
|
||||
```
|
||||
|
||||
### 项目结构
|
||||
|
||||
一个基本的 Astro 项目结构如下:
|
||||
|
||||
```
|
||||
/
|
||||
├── public/
|
||||
│ └── favicon.svg
|
||||
├── src/
|
||||
│ ├── components/
|
||||
│ │ └── Card.astro
|
||||
│ ├── layouts/
|
||||
│ │ └── Layout.astro
|
||||
│ └── pages/
|
||||
│ └── index.astro
|
||||
└── package.json
|
||||
```
|
||||
|
||||
### 创建页面
|
||||
|
||||
在 Astro 中,`src/pages/` 目录中的每个 `.astro` 文件都会生成一个对应的 HTML 页面:
|
||||
|
||||
```astro
|
||||
---
|
||||
// src/pages/index.astro
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>我的 Astro 网站</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>欢迎来到我的网站!</h1>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## 结语
|
||||
|
||||
Astro 是一个强大而灵活的框架,特别适合内容驱动的网站,如博客、文档站点、营销网站等。它的零 JavaScript 默认策略和组件岛屿架构使其成为构建高性能网站的绝佳选择。
|
||||
|
||||
如果您正在寻找一个能够提供出色开发体验和最终用户体验的框架,Astro 绝对值得一试!
|
@ -1,19 +0,0 @@
|
||||
---
|
||||
title: "测试文章"
|
||||
date: 2023-03-03
|
||||
tags: ["测试"]
|
||||
category: "测试分类"
|
||||
summary: "这是一篇测试文章,用于测试目录结构"
|
||||
---
|
||||
|
||||
# 测试文章
|
||||
|
||||
这是一篇测试文章,用于测试目录结构功能。
|
||||
|
||||
## 二级标题
|
||||
|
||||
这是二级标题下的内容。
|
||||
|
||||
### 三级标题
|
||||
|
||||
这是三级标题下的内容。
|
@ -1,79 +0,0 @@
|
||||
---
|
||||
title: Hello World
|
||||
date: 2023-06-15
|
||||
tags: [示例, Markdown, 入门]
|
||||
category: 教程
|
||||
summary: 这是一篇示例文章,展示了 Markdown 的基本语法和使用方法。
|
||||
---
|
||||
|
||||
# Hello World
|
||||
|
||||
这是一篇示例文章,用于展示 Markdown 的基本语法和使用方法。
|
||||
|
||||
## Markdown 简介
|
||||
|
||||
Markdown 是一种轻量级标记语言,创建于 2004 年,其设计目标是易读易写,成为一种适用于网络的书写语言。Markdown 的语法简洁明了,易于掌握,目前被广泛用于撰写博客、文档、笔记等。
|
||||
|
||||
## 基本语法
|
||||
|
||||
### 标题
|
||||
|
||||
使用 `#` 号可以表示不同级别的标题,例如:
|
||||
|
||||
```markdown
|
||||
# 一级标题
|
||||
## 二级标题
|
||||
### 三级标题
|
||||
```
|
||||
|
||||
### 强调
|
||||
|
||||
使用星号或下划线可以强调文本:
|
||||
|
||||
*斜体* 或 _斜体_
|
||||
**粗体** 或 __粗体__
|
||||
***粗斜体*** 或 ___粗斜体___
|
||||
|
||||
### 列表
|
||||
|
||||
无序列表使用星号、加号或减号作为列表标记:
|
||||
|
||||
* 项目一
|
||||
* 项目二
|
||||
* 项目三
|
||||
|
||||
有序列表使用数字加点:
|
||||
|
||||
1. 第一项
|
||||
2. 第二项
|
||||
3. 第三项
|
||||
|
||||
### 链接
|
||||
|
||||
[链接文本](https://www.example.com)
|
||||
|
||||
### 图片
|
||||
|
||||

|
||||
|
||||
### 引用
|
||||
|
||||
> 这是一段引用文本。
|
||||
>
|
||||
> 引用可以有多个段落。
|
||||
|
||||
### 代码
|
||||
|
||||
行内代码使用反引号:`console.log('Hello World')`
|
||||
|
||||
代码块使用三个反引号:
|
||||
|
||||
```javascript
|
||||
function sayHello() {
|
||||
console.log('Hello World');
|
||||
}
|
||||
```
|
||||
|
||||
## 结语
|
||||
|
||||
Markdown 的语法非常简单,但功能强大,希望这篇文章能帮助您快速入门 Markdown。更多高级用法,请参考 [Markdown 官方文档](https://daringfireball.net/projects/markdown/)。
|
257
src/content/markdown使用教程.md
Normal file
257
src/content/markdown使用教程.md
Normal file
@ -0,0 +1,257 @@
|
||||
---
|
||||
title: "markdown使用教程"
|
||||
date: 2023-03-03
|
||||
tags: ["测试", "多级目录"]
|
||||
---
|
||||
### 1.1 标题语法
|
||||
|
||||
```markdown
|
||||
# 一级标题
|
||||
## 二级标题
|
||||
### 三级标题
|
||||
#### 四级标题
|
||||
##### 五级标题
|
||||
###### 六级标题
|
||||
```
|
||||
|
||||
### 1.2 文本格式化
|
||||
|
||||
#### 1.2.1 粗体文本
|
||||
|
||||
```markdown
|
||||
**这是粗体文本**
|
||||
```
|
||||
|
||||
**这是粗体文本**
|
||||
|
||||
#### 1.2.2 斜体文本
|
||||
|
||||
```markdown
|
||||
*这是斜体文本*
|
||||
```
|
||||
|
||||
*这是斜体文本*
|
||||
|
||||
#### 1.2.3 粗斜体文本
|
||||
|
||||
```markdown
|
||||
***这是粗斜体文本***
|
||||
```
|
||||
|
||||
***这是粗斜体文本***
|
||||
|
||||
#### 1.2.4 删除线文本
|
||||
|
||||
```markdown
|
||||
~~这是删除线文本~~
|
||||
```
|
||||
|
||||
~~这是删除线文本~~
|
||||
|
||||
#### 1.2.5 下划线文本
|
||||
|
||||
```markdown
|
||||
<u>这是下划线文本</u>
|
||||
```
|
||||
|
||||
<u>这是下划线文本</u>
|
||||
|
||||
### 1.3 列表
|
||||
|
||||
#### 1.3.1 无序列表
|
||||
|
||||
```markdown
|
||||
- 第一项
|
||||
- 子项 1
|
||||
- 子项 2
|
||||
- 第二项
|
||||
- 第三项
|
||||
```
|
||||
|
||||
- 第一项
|
||||
- 子项 1
|
||||
- 子项 2
|
||||
- 第二项
|
||||
- 第三项
|
||||
|
||||
#### 1.3.2 有序列表
|
||||
|
||||
```markdown
|
||||
1. 第一步
|
||||
1. 子步骤 1
|
||||
2. 子步骤 2
|
||||
2. 第二步
|
||||
3. 第三步
|
||||
```
|
||||
|
||||
1. 第一步
|
||||
1. 子步骤 1
|
||||
2. 子步骤 2
|
||||
2. 第二步
|
||||
3. 第三步
|
||||
|
||||
#### 1.3.3 任务列表
|
||||
|
||||
```markdown
|
||||
- [x] 已完成任务
|
||||
- [ ] 未完成任务
|
||||
- [x] 又一个已完成任务
|
||||
```
|
||||
|
||||
- [x] 已完成任务
|
||||
- [ ] 未完成任务
|
||||
- [x] 又一个已完成任务
|
||||
|
||||
### 1.4 代码
|
||||
|
||||
#### 1.4.1 行内代码
|
||||
|
||||
```markdown
|
||||
这是一段包含`const greeting = "Hello World";`的行内代码
|
||||
```
|
||||
|
||||
这是一段包含`const greeting = "Hello World";`的行内代码
|
||||
|
||||
#### 1.4.2 代码块
|
||||
|
||||
````markdown
|
||||
```typescript
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
function greet(user: User): string {
|
||||
return `Hello, ${user.name}!`;
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
```typescript
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
function greet(user: User): string {
|
||||
return `Hello, ${user.name}!`;
|
||||
}
|
||||
```
|
||||
|
||||
### 1.5 表格
|
||||
|
||||
```markdown
|
||||
| 功能 | 基础版 | 高级版 |
|
||||
|:-----|:------:|-------:|
|
||||
| 文本编辑 | ✓ | ✓ |
|
||||
| 实时预览 | ✗ | ✓ |
|
||||
| 导出格式 | 2种 | 5种 |
|
||||
```
|
||||
|
||||
| 功能 | 基础版 | 高级版 |
|
||||
|:-----|:------:|-------:|
|
||||
| 文本编辑 | ✓ | ✓ |
|
||||
| 实时预览 | ✗ | ✓ |
|
||||
| 导出格式 | 2种 | 5种 |
|
||||
|
||||
### 1.6 引用
|
||||
|
||||
```markdown
|
||||
> 📌 **最佳实践**
|
||||
>
|
||||
> 好的文章需要有清晰的结构和流畅的表达。
|
||||
>
|
||||
> 可以包含多个段落
|
||||
```
|
||||
|
||||
> 📌 **最佳实践**
|
||||
>
|
||||
> 好的文章需要有清晰的结构和流畅的表达。
|
||||
>
|
||||
> 可以包含多个段落
|
||||
|
||||
### 1.7 链接和图片
|
||||
|
||||
#### 1.7.1 链接
|
||||
|
||||
```markdown
|
||||
[Markdown 官方文档](https://www.markdownguide.org)
|
||||
[相对路径链接](../path/to/file.md)
|
||||
```
|
||||
|
||||
[Markdown 官方文档](https://www.markdownguide.org)
|
||||
[相对路径链接](../path/to/file.md)
|
||||
|
||||
#### 1.7.2 图片
|
||||
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||
### 1.8 水平分割线
|
||||
|
||||
```markdown
|
||||
---
|
||||
或
|
||||
***
|
||||
或
|
||||
___
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1.9 脚注
|
||||
|
||||
```markdown
|
||||
这里有一个脚注[^1],这里有另一个脚注[^2]。
|
||||
|
||||
[^1]: 这是第一个脚注的内容。
|
||||
[^2]: 这是第二个脚注的内容。
|
||||
```
|
||||
|
||||
这里有一个脚注[^1],这里有另一个脚注[^2]。
|
||||
|
||||
[^1]: 这是第一个脚注的内容。
|
||||
[^2]: 这是第二个脚注的内容。
|
||||
|
||||
### 1.10 转义字符
|
||||
|
||||
```markdown
|
||||
\* 这不是斜体 \*
|
||||
\` 这不是代码 \`
|
||||
\[ 这不是链接 \]
|
||||
```
|
||||
|
||||
\* 这不是斜体 \*
|
||||
\` 这不是代码 \`
|
||||
\[ 这不是链接 \]
|
||||
|
||||
### 1.11 收纳语法
|
||||
|
||||
```markdown
|
||||
<details>
|
||||
<summary>点击展开</summary>
|
||||
|
||||
> 这里是被收纳的内容。
|
||||
> 可以包含任何 Markdown 格式的内容。
|
||||
>
|
||||
> - 列表项1
|
||||
> - 列表项2
|
||||
> - 列表项3
|
||||
|
||||
</details>
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>点击展开</summary>
|
||||
|
||||
> 这里是被收纳的内容。
|
||||
> 可以包含任何 Markdown 格式的内容。
|
||||
>
|
||||
> - 列表项1
|
||||
> - 列表项2
|
||||
> - 列表项3
|
||||
|
||||
</details>
|
23
src/pages/404.astro
Normal file
23
src/pages/404.astro
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
import Layout from '@/components/Layout.astro';
|
||||
import { SITE_NAME } from '@/consts';
|
||||
---
|
||||
|
||||
<Layout title={`404 - 页面未找到 | ${SITE_NAME}`}>
|
||||
<div class="min-h-[calc(100vh-4rem)] flex items-center justify-center">
|
||||
<div class="text-center px-4">
|
||||
<h1 class="text-6xl md:text-8xl font-bold bg-gradient-to-r from-primary-600 to-primary-400 dark:from-primary-400 dark:to-primary-200 text-transparent bg-clip-text mb-6">
|
||||
404
|
||||
</h1>
|
||||
<p class="text-2xl md:text-3xl font-semibold text-secondary-800 dark:text-secondary-200 mb-4">
|
||||
页面未找到
|
||||
</p>
|
||||
<p class="text-lg md:text-xl text-secondary-600 dark:text-secondary-400 max-w-2xl mx-auto leading-relaxed mb-8">
|
||||
您访问的页面不存在或已被移动到其他位置
|
||||
</p>
|
||||
<a href="/" class="inline-block px-6 py-3 bg-primary-500 hover:bg-primary-600 text-white font-medium rounded-lg transition-colors duration-300">
|
||||
返回首页
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
15
src/pages/about.astro
Normal file
15
src/pages/about.astro
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
import Layout from "@/components/Layout.astro";
|
||||
import { Countdown } from "@/components/Countdown";
|
||||
|
||||
const targetDate = "2070-04-06";
|
||||
---
|
||||
|
||||
<Layout title="退休倒计时">
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<h1 class="text-3xl font-bold text-center mb-8">距离退休还有</h1>
|
||||
<div class="max-w-2xl mx-auto bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
|
||||
<Countdown client:load targetDate={targetDate} />
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
@ -1,9 +1,9 @@
|
||||
---
|
||||
import { getCollection, render } from 'astro:content';
|
||||
import { contentStructure, getRelativePath, getBasename, getDirPath } from '../../content.config';
|
||||
import type { SectionStructure } from '../../content.config';
|
||||
import Layout from '../../components/Layout.astro';
|
||||
import Breadcrumb from '../../components/Breadcrumb.astro';
|
||||
import { contentStructure, getRelativePath, getBasename, getDirPath } from '@/content.config';
|
||||
import type { SectionStructure } from '@/content.config';
|
||||
import Layout from '@/components/Layout.astro';
|
||||
import Breadcrumb from '@/components/Breadcrumb.astro';
|
||||
|
||||
// 添加这一行,告诉Astro预渲染这个页面
|
||||
export const prerender = true;
|
||||
@ -91,20 +91,29 @@ const relatedArticles = allArticles
|
||||
))
|
||||
.sort((a, b) => b.data.date.getTime() - a.data.date.getTime())
|
||||
.slice(0, 3);
|
||||
|
||||
// 准备文章描述
|
||||
const description = article.data.summary || `${article.data.title} - 发布于 ${article.data.date.toLocaleDateString('zh-CN')}`;
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<div class="bg-gradient-to-b from-primary-50 dark:from-dark-surface to-white dark:to-dark-bg min-h-screen">
|
||||
<div class="max-w-4xl mx-auto px-4 py-8">
|
||||
<Layout
|
||||
title={article.data.title}
|
||||
description={description}
|
||||
date={article.data.date}
|
||||
author={article.data.author}
|
||||
tags={article.data.tags}
|
||||
image={article.data.image}
|
||||
>
|
||||
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<!-- 阅读进度条 -->
|
||||
<div class="fixed top-0 left-0 w-full h-1 bg-transparent z-50" id="progress-container">
|
||||
<div class="fixed top-0 left-0 w-full h-1 bg-transparent z-50" id="progress-container 9">
|
||||
<div class="h-full w-0 bg-primary-500 transition-width duration-100" id="progress-bar"></div>
|
||||
</div>
|
||||
|
||||
<!-- 文章头部 -->
|
||||
<header class="mb-8">
|
||||
<!-- 导航区域 -->
|
||||
<div class="bg-white dark:bg-dark-card rounded-xl p-4 mb-6">
|
||||
<div class="bg-white dark:bg-dark-card rounded-xl p-4 mb-6 shadow-lg border border-gray-200 dark:border-gray-700">
|
||||
<div class="flex items-center justify-between">
|
||||
<Breadcrumb
|
||||
pageType="article"
|
||||
@ -112,6 +121,7 @@ const relatedArticles = allArticles
|
||||
articleTitle={article.data.title}
|
||||
/>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
{/* 返回按钮 */}
|
||||
<a href="/articles" class="text-secondary-500 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors flex items-center text-sm">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
@ -121,6 +131,7 @@ const relatedArticles = allArticles
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="text-3xl font-bold mb-4 text-gray-900 dark:text-gray-100">{article.data.title}</h1>
|
||||
|
||||
@ -154,26 +165,35 @@ const relatedArticles = allArticles
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{article.data.summary && (
|
||||
<div class="bg-primary-50 dark:bg-primary-900/30 p-4 rounded-lg mb-6 text-secondary-700 dark:text-secondary-300 italic">
|
||||
{article.data.summary}
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
|
||||
<!-- 文章内容区域 -->
|
||||
<div class="relative">
|
||||
<!-- 文章内容 -->
|
||||
<article class="prose prose-lg dark:prose-invert prose-primary prose-table:rounded-lg prose-thead:bg-secondary-50 dark:prose-thead:bg-dark-card prose-ul:list-disc prose-ol:list-decimal prose-li:my-1 max-w-none mb-12 bg-white dark:bg-dark-card p-8 rounded-xl">
|
||||
<article class="prose prose-lg dark:prose-invert prose-primary prose-table:rounded-lg prose-table:border-separate prose-table:border-2 prose-thead:bg-primary-50 dark:prose-thead:bg-dark-surface prose-ul:list-disc prose-ol:list-decimal prose-li:my-1 prose-blockquote:border-l-4 prose-blockquote:border-primary-500 prose-blockquote:bg-gray-100 prose-blockquote:dark:bg-dark-surface prose-a:text-primary-600 prose-a:dark:text-primary-400 prose-a:no-underline prose-a:border-b prose-a:border-primary-300 prose-a:hover:border-primary-600 max-w-none mb-12">
|
||||
<Content />
|
||||
</article>
|
||||
|
||||
<!-- 固定目录面板 - 脱离文档流 -->
|
||||
<div class="hidden 2xl:block fixed right-[calc(50%-48rem)] top-20 w-64 z-30">
|
||||
<div class="bg-white dark:bg-dark-card rounded-lg shadow-lg p-4 max-h-[calc(100vh-8rem)] overflow-y-auto border border-gray-200 dark:border-gray-700">
|
||||
<div class="border-b border-secondary-100 dark:border-dark-border pb-2 mb-3 sticky top-0 bg-white dark:bg-dark-card">
|
||||
<h3 class="font-bold text-primary-700 dark:text-primary-400">文章目录</h3>
|
||||
</div>
|
||||
<div id="toc-content" class="text-sm">
|
||||
<!-- 目录内容将通过JavaScript动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 相关文章 -->
|
||||
{relatedArticles.length > 0 && (
|
||||
<div class="mt-12 pt-8 border-t border-secondary-200 dark:border-dark-border">
|
||||
<h2 class="text-2xl font-bold mb-6 text-primary-900 dark:text-primary-100">相关文章</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{relatedArticles.map(relatedArticle => (
|
||||
<a href={`/articles/${relatedArticle.id}`} class="block p-4 border border-primary-100 dark:border-dark-border rounded-lg bg-white dark:bg-dark-card hover:shadow-md transition-shadow">
|
||||
<a href={`/articles/${relatedArticle.id}`} class="block p-5 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-dark-card hover:shadow-xl hover:-translate-y-1 transition-all duration-300 shadow-lg">
|
||||
<h3 class="font-bold text-lg mb-2 line-clamp-2 text-gray-800 dark:text-gray-200 hover:text-primary-700 dark:hover:text-primary-400">{relatedArticle.data.title}</h3>
|
||||
<p class="text-sm text-secondary-600 dark:text-secondary-400 mb-2">{relatedArticle.data.date.toLocaleDateString('zh-CN')}</p>
|
||||
{relatedArticle.data.summary && (
|
||||
@ -192,7 +212,6 @@ const relatedArticles = allArticles
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<script>
|
||||
@ -237,6 +256,176 @@ const relatedArticles = allArticles
|
||||
// 初始化
|
||||
updateReadingProgress();
|
||||
|
||||
// 目录功能
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const tocContent = document.getElementById('toc-content');
|
||||
const tocPanel = document.querySelector('[class*="2xl:block"][class*="fixed"]');
|
||||
|
||||
// 检查是否有足够空间显示目录
|
||||
function checkTocVisibility() {
|
||||
if (!tocPanel) return;
|
||||
|
||||
// 如果窗口宽度小于1536px (2xl breakpoint),隐藏目录
|
||||
if (window.innerWidth < 1536) {
|
||||
tocPanel.classList.add('hidden');
|
||||
tocPanel.classList.remove('2xl:block');
|
||||
} else {
|
||||
tocPanel.classList.remove('hidden');
|
||||
tocPanel.classList.add('2xl:block');
|
||||
}
|
||||
}
|
||||
|
||||
// 监听窗口大小变化
|
||||
window.addEventListener('resize', checkTocVisibility);
|
||||
|
||||
// 初始检查
|
||||
checkTocVisibility();
|
||||
|
||||
// 生成目录内容
|
||||
function generateTableOfContents() {
|
||||
// 获取文章中的所有标题元素
|
||||
const article = document.querySelector('article');
|
||||
if (!article || !tocContent) {
|
||||
console.error('找不到文章内容或目录容器');
|
||||
if (tocContent) {
|
||||
tocContent.innerHTML = '<p class="text-secondary-500 dark:text-secondary-400 italic">无法生成目录</p>';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const headings = article.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
||||
if (headings.length === 0) {
|
||||
tocContent.innerHTML = '<p class="text-secondary-500 dark:text-secondary-400 italic">此文章没有目录</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建目录列表
|
||||
const tocList = document.createElement('ul');
|
||||
tocList.className = 'space-y-2';
|
||||
|
||||
// 为每个标题创建目录项
|
||||
headings.forEach((heading, index) => {
|
||||
// 为每个标题添加ID,如果没有的话
|
||||
if (!heading.id) {
|
||||
heading.id = `heading-${index}`;
|
||||
}
|
||||
|
||||
// 创建目录项
|
||||
const listItem = document.createElement('li');
|
||||
|
||||
// 根据标题级别设置缩进
|
||||
const headingLevel = parseInt(heading.tagName.substring(1));
|
||||
const indent = (headingLevel - 1) * 0.75; // 每级缩进0.75rem
|
||||
|
||||
// 创建链接
|
||||
const link = document.createElement('a');
|
||||
link.href = `#${heading.id}`;
|
||||
link.className = `block hover:text-primary-600 dark:hover:text-primary-400 transition-colors duration-50 ${headingLevel > 2 ? 'text-secondary-600 dark:text-secondary-400' : 'text-secondary-800 dark:text-secondary-200 font-medium'}`;
|
||||
link.style.paddingLeft = `${indent}rem`;
|
||||
link.textContent = heading.textContent;
|
||||
|
||||
// 点击链接时滚动到目标位置
|
||||
link.addEventListener('click', (e) => {
|
||||
// 平滑滚动到目标位置
|
||||
e.preventDefault();
|
||||
const targetId = link.getAttribute('href')?.substring(1);
|
||||
if (!targetId) return;
|
||||
|
||||
const targetElement = document.getElementById(targetId);
|
||||
|
||||
if (targetElement) {
|
||||
// 滚动到目标位置,并添加一些偏移以避免被固定导航遮挡
|
||||
const offset = 100; // 可以根据需要调整
|
||||
const targetPosition = targetElement.getBoundingClientRect().top + window.scrollY - offset;
|
||||
|
||||
window.scrollTo({
|
||||
top: targetPosition,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
|
||||
// 高亮目标标题
|
||||
targetElement.classList.add('bg-primary-50', 'dark:bg-primary-900/20');
|
||||
setTimeout(() => {
|
||||
targetElement.classList.remove('bg-primary-50', 'dark:bg-primary-900/20');
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
|
||||
listItem.appendChild(link);
|
||||
tocList.appendChild(listItem);
|
||||
});
|
||||
|
||||
// 将目录添加到面板
|
||||
tocContent.innerHTML = '';
|
||||
tocContent.appendChild(tocList);
|
||||
}
|
||||
|
||||
// 页面加载时生成目录
|
||||
try {
|
||||
generateTableOfContents();
|
||||
|
||||
// 添加滚动监听,更新目录高亮
|
||||
function updateActiveHeading() {
|
||||
const article = document.querySelector('article');
|
||||
if (!article || !tocContent) return;
|
||||
|
||||
const headings = Array.from(article.querySelectorAll('h1, h2, h3, h4, h5, h6'));
|
||||
if (headings.length === 0) return;
|
||||
|
||||
// 获取所有目录链接
|
||||
const tocLinks = Array.from(tocContent.querySelectorAll('a'));
|
||||
if (tocLinks.length === 0) return;
|
||||
|
||||
// 移除所有活跃状态
|
||||
tocLinks.forEach(link => {
|
||||
link.classList.remove('text-primary-600', 'dark:text-primary-400', 'font-medium');
|
||||
});
|
||||
|
||||
// 找到当前视口中最靠近顶部的标题
|
||||
let currentHeading = null;
|
||||
const scrollPosition = window.scrollY + 150; // 添加一些偏移量
|
||||
|
||||
for (const heading of headings) {
|
||||
const headingTop = heading.getBoundingClientRect().top + window.scrollY;
|
||||
if (headingTop <= scrollPosition) {
|
||||
currentHeading = heading;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果找到当前标题,高亮对应的目录项
|
||||
if (currentHeading) {
|
||||
const activeLink = tocLinks.find(link => link.getAttribute('href') === `#${currentHeading.id}`);
|
||||
if (activeLink) {
|
||||
activeLink.classList.add('text-primary-600', 'dark:text-primary-400', 'font-medium');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听滚动事件,使用节流函数优化性能
|
||||
let ticking = false;
|
||||
window.addEventListener('scroll', () => {
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(() => {
|
||||
updateActiveHeading();
|
||||
ticking = false;
|
||||
});
|
||||
ticking = true;
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化高亮
|
||||
updateActiveHeading();
|
||||
|
||||
} catch (error) {
|
||||
console.error('生成目录时发生错误:', error);
|
||||
if (tocContent) {
|
||||
tocContent.innerHTML = '<p class="text-secondary-500 dark:text-secondary-400 italic">生成目录时发生错误</p>';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 代码块增强功能
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 处理所有代码块
|
||||
|
@ -5,6 +5,20 @@ import { contentStructure, getRelativePath, getBasename, getDirPath } from '../.
|
||||
import Layout from '@/components/Layout.astro';
|
||||
import Breadcrumb from '@/components/Breadcrumb.astro';
|
||||
import ArticleTimeline from '@/components/ArticleTimeline.astro';
|
||||
export function extractSummary(content: string, length = 150) {
|
||||
// 移除 Markdown 标记
|
||||
const plainText = content
|
||||
.replace(/---[\s\S]*?---/, '') // 移除 frontmatter
|
||||
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // 将链接转换为纯文本
|
||||
.replace(/[#*`~>]/g, '') // 移除特殊字符
|
||||
.replace(/\n+/g, ' ') // 将换行转换为空格
|
||||
.trim();
|
||||
|
||||
// 提取指定长度的文本
|
||||
return plainText.length > length
|
||||
? plainText.slice(0, length).trim() + '...'
|
||||
: plainText;
|
||||
}
|
||||
|
||||
// 预渲染页面,但允许客户端导航
|
||||
export const prerender = false;
|
||||
@ -100,10 +114,10 @@ interface Breadcrumb {
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<div class="bg-gradient-to-b from-primary-50 to-white dark:from-gray-900 dark:to-gray-800 min-h-screen">
|
||||
<main class={`mx-auto px-4 py-6 ${viewMode === 'grid' ? 'max-w-6xl' : 'max-w-4xl'}`}>
|
||||
<div class="bg-gray-50 dark:bg-dark-bg min-h-screen">
|
||||
<main class={`mx-auto px-4 sm:px-6 lg:px-8 py-6 ${viewMode === 'grid' ? 'max-w-7xl' : 'max-w-5xl'}`}>
|
||||
<!-- 导航栏 -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl mb-4">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl mb-4 shadow-lg border border-gray-200 dark:border-gray-700">
|
||||
<div class="px-4 py-3">
|
||||
<div class="flex items-center justify-between !h-10">
|
||||
<Breadcrumb
|
||||
@ -146,7 +160,7 @@ interface Breadcrumb {
|
||||
{/* 上一级目录卡片 - 仅在浏览目录时显示 */}
|
||||
{!tagFilter && pathSegments.length > 0 && (
|
||||
<a href={`/articles${pathSegments.length > 1 ? `?path=${encodeURIComponent(pathSegments.slice(0, -1).join('/'))}` : ''}`}
|
||||
class="group flex flex-col h-full p-4 border border-primary-100 dark:border-gray-700 rounded-xl bg-white dark:bg-gray-800 hover:shadow-xl transition-all duration-300 hover:border-primary-300 dark:hover:border-primary-600">
|
||||
class="group flex flex-col h-full p-5 border border-gray-200 dark:border-gray-700 rounded-xl bg-white dark:bg-gray-800 hover:shadow-xl hover:-translate-y-1 transition-all duration-300 shadow-lg">
|
||||
<div class="flex items-center">
|
||||
<div class="w-10 h-10 flex items-center justify-center rounded-lg bg-primary-100 text-primary-600 group-hover:bg-primary-200 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
@ -173,7 +187,7 @@ interface Breadcrumb {
|
||||
|
||||
return (
|
||||
<a href={`/articles?path=${encodeURIComponent(dirLink)}`}
|
||||
class="group flex flex-col h-full p-4 border border-primary-100 dark:border-gray-700 rounded-xl bg-white dark:bg-gray-800 hover:shadow-xl transition-all duration-300 hover:border-primary-300 dark:hover:border-primary-600">
|
||||
class="group flex flex-col h-full p-5 border border-gray-200 dark:border-gray-700 rounded-xl bg-white dark:bg-gray-800 hover:shadow-xl hover:-translate-y-1 transition-all duration-300 shadow-lg">
|
||||
<div class="flex items-center">
|
||||
<div class="w-10 h-10 flex items-center justify-center rounded-lg bg-primary-100 text-primary-600 group-hover:bg-primary-200 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
@ -216,7 +230,7 @@ interface Breadcrumb {
|
||||
// 显示标签过滤后的文章
|
||||
filteredArticles.map(article => (
|
||||
<a href={`/articles/${article.id}`}
|
||||
class="group flex flex-col h-full p-4 border border-primary-100 dark:border-gray-700 rounded-xl bg-white dark:bg-gray-800 hover:shadow-xl transition-all duration-300 hover:border-primary-300 dark:hover:border-primary-600">
|
||||
class="group flex flex-col h-full p-5 border border-gray-200 dark:border-gray-700 rounded-xl bg-white dark:bg-gray-800 hover:shadow-xl hover:-translate-y-1 transition-all duration-300 shadow-lg">
|
||||
<div class="flex items-start">
|
||||
<div class="w-10 h-10 flex-shrink-0 flex items-center justify-center rounded-lg bg-primary-100 text-primary-600 group-hover:bg-primary-200 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
@ -225,8 +239,10 @@ interface Breadcrumb {
|
||||
</div>
|
||||
<div class="ml-3 flex-1">
|
||||
<h3 class="font-bold text-base text-gray-800 dark:text-gray-100 group-hover:text-primary-700 dark:group-hover:text-primary-300 transition-colors line-clamp-2">{article.data.title}</h3>
|
||||
{article.data.summary && (
|
||||
<p class="text-xs text-gray-600 mt-1 line-clamp-2">{article.data.summary}</p>
|
||||
{article.body && (
|
||||
<p class="text-xs text-gray-600 mt-1 line-clamp-2">
|
||||
{extractSummary(article.body)}
|
||||
</p>
|
||||
)}
|
||||
<div class="text-xs text-gray-500 mt-2 flex items-center justify-between">
|
||||
<time datetime={article.data.date.toISOString()}>
|
||||
@ -292,7 +308,7 @@ interface Breadcrumb {
|
||||
|
||||
if (!article) {
|
||||
return (
|
||||
<div class="flex flex-col h-full p-4 border border-red-200 rounded-xl bg-red-50">
|
||||
<div class="flex flex-col h-full p-5 border border-red-200 rounded-xl bg-red-50 shadow-lg">
|
||||
<div class="flex items-start">
|
||||
<div class="w-10 h-10 flex-shrink-0 flex items-center justify-center rounded-lg bg-red-100 text-red-600">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
@ -313,7 +329,7 @@ interface Breadcrumb {
|
||||
|
||||
return (
|
||||
<a href={`/articles/${article.id}`}
|
||||
class="group flex flex-col h-full p-4 border border-primary-100 dark:border-gray-700 rounded-xl bg-white dark:bg-gray-800 hover:shadow-xl transition-all duration-300 hover:border-primary-300 dark:hover:border-primary-600">
|
||||
class="group flex flex-col h-full p-5 border border-gray-200 dark:border-gray-700 rounded-xl bg-white dark:bg-gray-800 hover:shadow-xl hover:-translate-y-1 transition-all duration-300 shadow-lg">
|
||||
<div class="flex items-start">
|
||||
<div class="w-10 h-10 flex-shrink-0 flex items-center justify-center rounded-lg bg-primary-100 text-primary-600 group-hover:bg-primary-200 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
@ -322,8 +338,10 @@ interface Breadcrumb {
|
||||
</div>
|
||||
<div class="ml-3 flex-1">
|
||||
<h3 class="font-bold text-base text-gray-800 dark:text-gray-100 group-hover:text-primary-700 dark:group-hover:text-primary-300 transition-colors line-clamp-2">{article.data.title}</h3>
|
||||
{article.data.summary && (
|
||||
<p class="text-xs text-gray-600 mt-1 line-clamp-2">{article.data.summary}</p>
|
||||
{article.body && (
|
||||
<p class="text-xs text-gray-600 mt-1 line-clamp-2">
|
||||
{extractSummary(article.body)}
|
||||
</p>
|
||||
)}
|
||||
<div class="text-xs text-gray-500 mt-2 flex items-center justify-between">
|
||||
<time datetime={article.data.date.toISOString()}>
|
||||
@ -341,7 +359,7 @@ interface Breadcrumb {
|
||||
|
||||
{/* 空内容提示 */}
|
||||
{((tagFilter && filteredArticles.length === 0) || (!tagFilter && currentSections.length === 0 && currentArticles.length === 0)) && (
|
||||
<div class="text-center py-16 bg-white rounded-xl shadow-sm mb-12">
|
||||
<div class="text-center py-16 bg-white rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 mb-12">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 mx-auto text-primary-200 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" />
|
||||
</svg>
|
||||
@ -355,7 +373,7 @@ interface Breadcrumb {
|
||||
)}
|
||||
|
||||
<!-- 标签过滤器 -->
|
||||
<div class="bg-white dark:bg-gray-800 p-8 rounded-xl shadow-sm">
|
||||
<div class="bg-white dark:bg-gray-800 p-8 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700">
|
||||
<h2 class="text-2xl font-bold mb-6 text-primary-900 dark:text-primary-100 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
|
||||
@ -368,10 +386,10 @@ interface Breadcrumb {
|
||||
const isActive = tag === tagFilter;
|
||||
return (
|
||||
<a href={`/articles?tag=${tag}`}
|
||||
class={`py-2 px-4 rounded-full text-sm font-medium transition-all duration-200 ${
|
||||
class={`py-2 px-4 rounded-full text-sm font-medium transition-all duration-300 ${
|
||||
isActive
|
||||
? 'bg-primary-600 text-white dark:bg-primary-500 dark:text-gray-900 hover:bg-primary-700 dark:hover:bg-primary-400'
|
||||
: 'bg-primary-50 dark:bg-gray-700 text-primary-700 dark:text-primary-200 hover:bg-primary-100 dark:hover:bg-gray-600'
|
||||
? 'bg-primary-600 text-white dark:bg-primary-500 dark:text-gray-100 hover:bg-primary-700 dark:hover:bg-primary-600 shadow-md hover:shadow-lg'
|
||||
: 'bg-primary-50 dark:bg-gray-700/50 text-primary-600 dark:text-gray-300 hover:bg-primary-100 dark:hover:bg-gray-700 hover:text-primary-700 dark:hover:text-primary-400'
|
||||
}`}>
|
||||
{tag}
|
||||
</a>
|
||||
|
@ -1,10 +1,17 @@
|
||||
---
|
||||
import Layout from '@/components/Layout.astro';
|
||||
import { ThemeToggle } from '@/components/ThemeToggle';
|
||||
import { SITE_NAME } from '@/consts';
|
||||
---
|
||||
|
||||
<Layout>
|
||||
|
||||
|
||||
|
||||
<div class="min-h-[calc(100vh-4rem)] flex items-center justify-center">
|
||||
<div class="text-center px-4">
|
||||
<h1 class="text-6xl md:text-8xl font-bold bg-gradient-to-r from-primary-600 to-primary-400 dark:from-primary-400 dark:to-primary-200 text-transparent bg-clip-text mb-6">
|
||||
{SITE_NAME}
|
||||
</h1>
|
||||
<p class="text-xl md:text-2xl text-secondary-600 dark:text-secondary-400 max-w-2xl mx-auto leading-relaxed">
|
||||
探索技术与思维的边界
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
|
@ -0,0 +1,22 @@
|
||||
---
|
||||
import Layout from '@/components/Layout.astro';
|
||||
import { GitPlatform } from '@/components/GitProjectCollection';
|
||||
import GitProjectCollection from '@/components/GitProjectCollection';
|
||||
---
|
||||
|
||||
<Layout title="项目 | echoes">
|
||||
<main class="container mx-auto px-4 py-8">
|
||||
|
||||
<div class="space-y-12">
|
||||
<!-- Gitea 项目集合 -->
|
||||
<GitProjectCollection
|
||||
platform={GitPlatform.GITEA}
|
||||
username="lsy"
|
||||
title="Gitea 个人项目"
|
||||
client:load
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
@ -1,42 +1,186 @@
|
||||
/* 增强表格样式 */
|
||||
.prose table {
|
||||
border-collapse: collapse;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 1.5em;
|
||||
border-radius: 0.5rem;
|
||||
margin: 1.5em 0;
|
||||
border-radius: 0.75rem;
|
||||
overflow: hidden;
|
||||
box-shadow: none;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
border: 2px solid var(--color-secondary-500);
|
||||
}
|
||||
|
||||
/* 确保表格边框在所有浏览器中都能正确显示 */
|
||||
.prose table * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.prose thead {
|
||||
background-color: var(--color-secondary-50);
|
||||
background-color: var(--color-primary-50);
|
||||
}
|
||||
|
||||
.prose thead th {
|
||||
padding: 0.75rem 1rem;
|
||||
padding: 1rem 1.25rem;
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
border-bottom: 2px solid var(--color-secondary-200);
|
||||
color: var(--color-primary-900);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.5rem;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
border-bottom: 2px solid var(--color-secondary-500);
|
||||
border-right: 2px solid var(--color-secondary-500);
|
||||
}
|
||||
|
||||
.prose thead th:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.prose tbody tr {
|
||||
border-bottom: 1px solid var(--color-secondary-200);
|
||||
border-bottom: 2px solid var(--color-secondary-500);
|
||||
}
|
||||
|
||||
.prose tbody tr:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.prose tbody tr:hover {
|
||||
background-color: var(--color-secondary-50);
|
||||
}
|
||||
|
||||
.prose tbody td {
|
||||
padding: 0.75rem 1rem;
|
||||
vertical-align: top;
|
||||
padding: 0.875rem 1.25rem;
|
||||
color: var(--color-secondary-800);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.5rem;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
border-right: 2px solid var(--color-secondary-500);
|
||||
border-bottom: 2px solid var(--color-secondary-500);
|
||||
}
|
||||
|
||||
.prose tbody td:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.prose tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.prose tbody tr:nth-child(even) {
|
||||
background-color: var(--color-secondary-50/80);
|
||||
}
|
||||
|
||||
.prose tbody tr:nth-child(even):hover {
|
||||
background-color: var(--color-secondary-50);
|
||||
}
|
||||
|
||||
/* 表格内容对齐样式 */
|
||||
.prose th[align="center"],
|
||||
.prose td[align="center"] {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.prose th[align="right"],
|
||||
.prose td[align="right"] {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.prose th[align="left"],
|
||||
.prose td[align="left"] {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* 表格内容样式增强 */
|
||||
.prose td code {
|
||||
background-color: var(--color-secondary-100);
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 0.25em;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose td code {
|
||||
background-color: var(--color-secondary-800);
|
||||
}
|
||||
|
||||
/* 表格内的链接样式 */
|
||||
.prose td a {
|
||||
color: var(--color-primary-600);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid var(--color-primary-300);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.prose td a:hover {
|
||||
color: var(--color-primary-700);
|
||||
border-bottom-color: var(--color-primary-500);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose td a {
|
||||
color: var(--color-primary-400);
|
||||
border-bottom-color: var(--color-primary-700);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose td a:hover {
|
||||
color: var(--color-primary-300);
|
||||
border-bottom-color: var(--color-primary-500);
|
||||
}
|
||||
|
||||
/* 表格内复选框样式 */
|
||||
.prose td input[type="checkbox"] {
|
||||
width: 1.25em;
|
||||
height: 1.25em;
|
||||
margin: 0;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
accent-color: var(--color-primary-500);
|
||||
}
|
||||
|
||||
/* 表格内图标样式 */
|
||||
.prose td svg,
|
||||
.prose td img:not(.emoji) {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: 1.25em;
|
||||
margin: 0 0.25em;
|
||||
}
|
||||
|
||||
/* 表格内的状态标记 */
|
||||
.prose td .status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.25em 0.75em;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.75em;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.prose td .status-success {
|
||||
background-color: rgba(16, 185, 129, 0.1);
|
||||
color: rgb(6, 95, 70);
|
||||
}
|
||||
|
||||
.prose td .status-warning {
|
||||
background-color: rgba(245, 158, 11, 0.1);
|
||||
color: rgb(146, 64, 14);
|
||||
}
|
||||
|
||||
.prose td .status-error {
|
||||
background-color: rgba(239, 68, 68, 0.1);
|
||||
color: rgb(153, 27, 27);
|
||||
}
|
||||
|
||||
.prose td .status-info {
|
||||
background-color: rgba(59, 130, 246, 0.1);
|
||||
color: rgb(30, 64, 175);
|
||||
}
|
||||
|
||||
/* 增强列表样式 */
|
||||
.prose ul {
|
||||
list-style-type: disc;
|
||||
@ -239,15 +383,374 @@
|
||||
color: var(--color-secondary-300);
|
||||
}
|
||||
|
||||
/* 引用样式 */
|
||||
.prose blockquote {
|
||||
margin: 1.5em 0;
|
||||
padding: 1em 1.5em;
|
||||
border-left: 4px solid var(--color-primary-500);
|
||||
background-color: var(--color-gray-100);
|
||||
border-radius: 0.5rem;
|
||||
font-style: italic;
|
||||
color: var(--color-secondary-700);
|
||||
}
|
||||
|
||||
.prose blockquote p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.prose blockquote p + p {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
/* 链接样式 */
|
||||
.prose a {
|
||||
color: var(--color-primary-600);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid var(--color-primary-300);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.prose a:hover {
|
||||
color: var(--color-primary-800);
|
||||
border-bottom-color: var(--color-primary-600);
|
||||
}
|
||||
|
||||
/* 暗色模式适配 */
|
||||
[data-theme="dark"] .prose blockquote {
|
||||
background-color: var(--color-dark-surface);
|
||||
border-left-color: var(--color-primary-400);
|
||||
color: var(--color-secondary-300);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose a {
|
||||
color: var(--color-primary-400);
|
||||
border-bottom-color: var(--color-primary-600);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose a:hover {
|
||||
color: var(--color-primary-300);
|
||||
border-bottom-color: var(--color-primary-400);
|
||||
}
|
||||
|
||||
/* 响应式表格样式 */
|
||||
@media (max-width: 640px) {
|
||||
.prose table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.prose thead th {
|
||||
white-space: nowrap;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.prose tbody td {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 表格内容溢出处理 */
|
||||
.prose td p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.prose td > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.prose td > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.prose td.truncate {
|
||||
max-width: 20rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* 确保表格边框在所有情况下都能正确显示 */
|
||||
.prose table tr td:last-child {
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
.prose table tr th:last-child {
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
.prose table tr:last-child td {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
/* 暗色模式下的边框颜色 */
|
||||
[data-theme="dark"] .prose table {
|
||||
--table-border-color: var(--color-dark-border);
|
||||
}
|
||||
|
||||
/* 增强列表样式 */
|
||||
.prose ul {
|
||||
list-style-type: disc;
|
||||
margin-top: 1.25em;
|
||||
margin-bottom: 1.25em;
|
||||
padding-left: 1.625em;
|
||||
}
|
||||
|
||||
.prose ul li {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
padding-left: 0.375em;
|
||||
}
|
||||
|
||||
.prose ul li::marker {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.prose ul li ul {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.prose ol {
|
||||
list-style-type: decimal;
|
||||
margin-top: 1.25em;
|
||||
margin-bottom: 1.25em;
|
||||
padding-left: 1.625em;
|
||||
}
|
||||
|
||||
.prose ol li {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
padding-left: 0.375em;
|
||||
}
|
||||
|
||||
.prose ol li::marker {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
/* 添加明确的表格边框样式 */
|
||||
.prose table {
|
||||
border: 2px solid var(--color-secondary-500) !important;
|
||||
}
|
||||
|
||||
.prose th {
|
||||
border-right: 2px solid var(--color-secondary-500) !important;
|
||||
border-bottom: 2px solid var(--color-secondary-500) !important;
|
||||
}
|
||||
|
||||
.prose td {
|
||||
border-right: 2px solid var(--color-secondary-500) !important;
|
||||
border-bottom: 2px solid var(--color-secondary-500) !important;
|
||||
}
|
||||
|
||||
.prose th:last-child,
|
||||
.prose td:last-child {
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
.prose tr:last-child td {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose th,
|
||||
[data-theme="dark"] .prose td {
|
||||
border-color: var(--color-dark-border) !important;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose table {
|
||||
border-color: var(--color-dark-border) !important;
|
||||
}
|
||||
|
||||
/* 暗色模式表格样式 */
|
||||
[data-theme="dark"] .prose table {
|
||||
border-color: var(--color-dark-border);
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
|
||||
border-radius: 0.75rem;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose thead {
|
||||
background-color: var(--color-dark-card);
|
||||
background-color: var(--color-dark-surface);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose thead th {
|
||||
color: var(--color-primary-300);
|
||||
border-bottom: 2px solid var(--color-dark-border);
|
||||
border-right: 2px solid var(--color-dark-border);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose thead th:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose tbody tr {
|
||||
border-bottom: 2px solid var(--color-dark-border);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose tbody tr:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose tbody td {
|
||||
color: var(--color-secondary-200);
|
||||
border-right: 2px solid var(--color-dark-border);
|
||||
border-bottom: 2px solid var(--color-dark-border);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose tbody td:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose tbody tr:hover {
|
||||
background-color: var(--color-dark-surface/60);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose tbody tr:nth-child(even) {
|
||||
background-color: var(--color-dark-card);
|
||||
background-color: var(--color-dark-surface/40);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose thead th,
|
||||
[data-theme="dark"] .prose tbody tr {
|
||||
border-color: var(--color-dark-border);
|
||||
[data-theme="dark"] .prose tbody tr:nth-child(even):hover {
|
||||
background-color: var(--color-dark-surface/60);
|
||||
}
|
||||
|
||||
/* 暗色模式下的状态标记样式 */
|
||||
[data-theme="dark"] .prose td .status-success {
|
||||
background-color: rgba(16, 185, 129, 0.2);
|
||||
color: rgb(110, 231, 183);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose td .status-warning {
|
||||
background-color: rgba(245, 158, 11, 0.2);
|
||||
color: rgb(253, 186, 116);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose td .status-error {
|
||||
background-color: rgba(239, 68, 68, 0.2);
|
||||
color: rgb(252, 165, 165);
|
||||
}
|
||||
|
||||
/* 收纳内容样式 */
|
||||
.details-content {
|
||||
margin-left: 1.5em;
|
||||
padding: 1em;
|
||||
background-color: var(--color-gray-100);
|
||||
border-left: 4px solid var(--color-primary-500);
|
||||
margin-bottom: 1em;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .details-content {
|
||||
background-color: var(--color-dark-surface);
|
||||
border-left-color: var(--color-primary-400);
|
||||
}
|
||||
|
||||
/* 收纳标题样式 */
|
||||
.prose details {
|
||||
margin: 1.5em 0;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--color-secondary-200);
|
||||
background-color: var(--color-gray-50);
|
||||
transition: all 0.2s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.prose details summary {
|
||||
padding: 1em;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
font-weight: 500;
|
||||
color: var(--color-secondary-900);
|
||||
list-style: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75em;
|
||||
transition: all 0.2s ease;
|
||||
background: linear-gradient(to right, var(--color-primary-50), var(--color-gray-50));
|
||||
border-left: 4px solid var(--color-primary-100);
|
||||
}
|
||||
|
||||
.prose details summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.prose details summary::before {
|
||||
content: "";
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%234b6bff'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5l7 7-7 7'%3E%3C/path%3E%3C/svg%3E");
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
transition: transform 0.3s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.prose details[open] summary {
|
||||
border-left: 4px solid var(--color-primary-500);
|
||||
background: linear-gradient(to right, var(--color-primary-100), var(--color-gray-50));
|
||||
border-bottom: 1px solid var(--color-secondary-200);
|
||||
}
|
||||
|
||||
.prose details[open] summary::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.prose details summary:hover {
|
||||
background: linear-gradient(to right, var(--color-primary-100), var(--color-gray-100));
|
||||
color: var(--color-primary-700);
|
||||
}
|
||||
|
||||
.prose details > blockquote {
|
||||
margin: 0;
|
||||
padding: 1.5em;
|
||||
border-radius: 0;
|
||||
border-left: 4px solid var(--color-primary-500);
|
||||
background: linear-gradient(to right, var(--color-primary-50/50), var(--color-gray-50));
|
||||
}
|
||||
|
||||
.prose details > blockquote p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.prose details > blockquote p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 暗色模式适配 */
|
||||
[data-theme="dark"] .prose details {
|
||||
border-color: var(--color-dark-border);
|
||||
background-color: var(--color-dark-surface);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose details summary {
|
||||
color: var(--color-secondary-100);
|
||||
background: linear-gradient(to right, var(--color-dark-surface), var(--color-dark-card));
|
||||
border-left-color: var(--color-primary-800);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose details[open] summary {
|
||||
border-bottom-color: var(--color-dark-border);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose details summary::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%23839dff'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5l7 7-7 7'%3E%3C/path%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose details[open] summary {
|
||||
background: linear-gradient(to right, var(--color-dark-card), var(--color-dark-surface));
|
||||
border-left-color: var(--color-primary-400);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose details summary:hover {
|
||||
background: linear-gradient(to right, var(--color-primary-900/30), var(--color-dark-card));
|
||||
color: var(--color-primary-400);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .prose details > blockquote {
|
||||
background: linear-gradient(to right, var(--color-primary-900/10), var(--color-dark-surface));
|
||||
border-left-color: var(--color-primary-400);
|
||||
}
|
Loading…
Reference in New Issue
Block a user