将页面搜索和切换主题,代码块配色改为用别人的组件,添加seo和压缩代码组件,优化代码块样式,移除冗余样式和组件
This commit is contained in:
parent
6e96b59245
commit
25f4a89594
@ -11,9 +11,15 @@ import fs from "node:fs";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import swup from "@swup/astro"
|
import swup from "@swup/astro"
|
||||||
import { SITE_URL } from "./src/consts";
|
import { SITE_URL } from "./src/consts";
|
||||||
|
import pagefind from "astro-pagefind";
|
||||||
|
import compressor from "astro-compressor";
|
||||||
|
|
||||||
import vercel from "@astrojs/vercel";
|
import vercel from "@astrojs/vercel";
|
||||||
|
|
||||||
|
import expressiveCode from "astro-expressive-code";
|
||||||
|
import { pluginLineNumbers } from "@expressive-code/plugin-line-numbers";
|
||||||
|
import { pluginCollapsibleSections } from "@expressive-code/plugin-collapsible-sections";
|
||||||
|
|
||||||
function getArticleDate(articleId) {
|
function getArticleDate(articleId) {
|
||||||
try {
|
try {
|
||||||
// 处理多级目录的文章路径
|
// 处理多级目录的文章路径
|
||||||
@ -47,50 +53,55 @@ export default defineConfig({
|
|||||||
|
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [tailwindcss()],
|
plugins: [tailwindcss()],
|
||||||
build: {
|
|
||||||
rollupOptions: {
|
|
||||||
output: {
|
|
||||||
// 手动分块配置
|
|
||||||
manualChunks: {
|
|
||||||
// 将地图组件单独打包
|
|
||||||
"world-heatmap": ["./src/components/WorldHeatmap.tsx"],
|
|
||||||
// 将 React 相关库单独打包
|
|
||||||
"react-vendor": ["react", "react-dom"],
|
|
||||||
// 其他大型依赖也可以单独打包
|
|
||||||
"chart-vendor": ["chart.js"],
|
|
||||||
// 将 ECharts 单独打包
|
|
||||||
"echarts-vendor": ["echarts"],
|
|
||||||
// 将其他组件打包到一起
|
|
||||||
components: ["./src/components"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// 提高警告阈值,避免不必要的警告
|
|
||||||
chunkSizeWarningLimit: 1000,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
integrations: [
|
integrations: [
|
||||||
// MDX 集成配置
|
expressiveCode({
|
||||||
mdx({
|
themes: ['github-light', 'dracula'],
|
||||||
// 不使用共享的 markdown 配置
|
themeCssSelector: (theme) =>
|
||||||
extendMarkdownConfig: false,
|
theme.name === 'dracula' ? '[data-theme=dark]' : '[data-theme=light]',
|
||||||
// 为 MDX 单独配置所需功能
|
useDarkModeMediaQuery: false,
|
||||||
remarkPlugins: [
|
|
||||||
// 添加表情符号支持
|
plugins: [
|
||||||
[remarkEmoji, { emoticon: false, padded: true }]
|
pluginLineNumbers(),
|
||||||
|
pluginCollapsibleSections(),
|
||||||
],
|
],
|
||||||
rehypePlugins: [
|
defaultProps: {
|
||||||
[rehypeExternalLinks, { target: '_blank', rel: ['nofollow', 'noopener', 'noreferrer'] }]
|
showLineNumbers: true,
|
||||||
],
|
collapseStyle: 'collapsible-auto',
|
||||||
// 设置代码块处理行为
|
},
|
||||||
remarkRehype: {
|
frames: {
|
||||||
allowDangerousHtml: false // 不解析 HTML
|
extractFileNameFromCode: true,
|
||||||
|
},
|
||||||
|
styleOverrides: {
|
||||||
|
// 核心样式设置
|
||||||
|
borderRadius: '0.5rem',
|
||||||
|
borderWidth: '0.5px',
|
||||||
|
codeFontSize: '0.9rem',
|
||||||
|
codeFontFamily: "'JetBrains Mono', Menlo, Monaco, Consolas, 'Courier New', monospace",
|
||||||
|
codeLineHeight: '1.5',
|
||||||
|
codePaddingInline: '1.5rem',
|
||||||
|
codePaddingBlock: '1.2rem',
|
||||||
|
// 框架样式设置
|
||||||
|
frames: {
|
||||||
|
shadowColor: 'rgba(0, 0, 0, 0.12)',
|
||||||
|
editorActiveTabBackground: '#ffffff',
|
||||||
|
editorTabBarBackground: '#f5f5f5',
|
||||||
|
terminalBackground: '#1a1a1a',
|
||||||
|
terminalTitlebarBackground: '#333333',
|
||||||
|
},
|
||||||
|
// 文本标记样式
|
||||||
|
textMarkers: {
|
||||||
|
defaultChroma: 'rgba(255, 255, 0, 0.2)',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
gfm: true
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// MDX 集成配置
|
||||||
|
mdx(),
|
||||||
swup(),
|
swup(),
|
||||||
react(),
|
react(),
|
||||||
|
pagefind(),
|
||||||
sitemap({
|
sitemap({
|
||||||
filter: (page) => !page.includes("/api/"),
|
filter: (page) => !page.includes("/api/"),
|
||||||
serialize(item) {
|
serialize(item) {
|
||||||
@ -129,11 +140,13 @@ export default defineConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
// 添加压缩插件 (必须放在最后位置)
|
||||||
|
compressor()
|
||||||
],
|
],
|
||||||
|
|
||||||
// Markdown 配置
|
// Markdown 配置
|
||||||
markdown: {
|
markdown: {
|
||||||
syntaxHighlight: 'prism',
|
syntaxHighlight: false, // 禁用默认的语法高亮,使用expressiveCode代替
|
||||||
remarkPlugins: [
|
remarkPlugins: [
|
||||||
[remarkEmoji, { emoticon: false, padded: true }]
|
[remarkEmoji, { emoticon: false, padded: true }]
|
||||||
],
|
],
|
||||||
@ -148,11 +161,6 @@ export default defineConfig({
|
|||||||
// 确保代码块内容不被解析
|
// 确保代码块内容不被解析
|
||||||
passThrough: ['code']
|
passThrough: ['code']
|
||||||
},
|
},
|
||||||
shikiConfig: {
|
|
||||||
theme: "github-dark",
|
|
||||||
langs: [],
|
|
||||||
wrap: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
adapter: vercel(),
|
adapter: vercel(),
|
||||||
|
1745
package-lock.json
generated
1745
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,12 +14,18 @@
|
|||||||
"@astrojs/react": "^4.2.4",
|
"@astrojs/react": "^4.2.4",
|
||||||
"@astrojs/sitemap": "^3.3.0",
|
"@astrojs/sitemap": "^3.3.0",
|
||||||
"@astrojs/vercel": "^8.1.3",
|
"@astrojs/vercel": "^8.1.3",
|
||||||
|
"@astrolib/seo": "^1.0.0-beta.8",
|
||||||
|
"@expressive-code/plugin-collapsible-sections": "^0.41.2",
|
||||||
|
"@expressive-code/plugin-line-numbers": "^0.41.2",
|
||||||
"@swup/astro": "^1.6.0",
|
"@swup/astro": "^1.6.0",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
"@types/react": "^19.1.2",
|
"@types/react": "^19.1.2",
|
||||||
"@types/react-dom": "^19.1.2",
|
"@types/react-dom": "^19.1.2",
|
||||||
"@types/three": "^0.174.0",
|
"@types/three": "^0.174.0",
|
||||||
"astro": "^5.7.4",
|
"astro": "^5.7.4",
|
||||||
|
"astro-expressive-code": "^0.41.2",
|
||||||
|
"astro-pagefind": "^1.8.3",
|
||||||
|
"astro-theme-toggle": "^0.6.0",
|
||||||
"cheerio": "^1.0.0",
|
"cheerio": "^1.0.0",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"octokit": "^3.2.1",
|
"octokit": "^3.2.1",
|
||||||
@ -31,6 +37,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/typography": "^0.5.16",
|
"@tailwindcss/typography": "^0.5.16",
|
||||||
|
"astro-compressor": "^1.0.0",
|
||||||
"rehype-external-links": "^3.0.0",
|
"rehype-external-links": "^3.0.0",
|
||||||
"remark-emoji": "^5.0.1"
|
"remark-emoji": "^5.0.1"
|
||||||
}
|
}
|
||||||
|
26908
src/assets/china.json
26908
src/assets/china.json
File diff suppressed because one or more lines are too long
@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
import { SITE_NAME, NAV_LINKS } from "@/consts.ts";
|
import { SITE_NAME, NAV_LINKS } from "@/consts.ts";
|
||||||
import ThemeToggle from "./ThemeToggle.astro";
|
import { Toggle } from "astro-theme-toggle";
|
||||||
|
import Search from "astro-pagefind/components/Search";
|
||||||
|
|
||||||
// 获取当前路径
|
// 获取当前路径
|
||||||
const currentPath = Astro.url.pathname;
|
const currentPath = Astro.url.pathname;
|
||||||
@ -41,52 +42,24 @@ const normalizedPath =
|
|||||||
<!-- 导航链接 -->
|
<!-- 导航链接 -->
|
||||||
<div class="hidden md:flex md:items-center md:space-x-8">
|
<div class="hidden md:flex md:items-center md:space-x-8">
|
||||||
<!-- 桌面端搜索框 -->
|
<!-- 桌面端搜索框 -->
|
||||||
<div class="relative">
|
<Search
|
||||||
<input
|
className="pagefind-ui"
|
||||||
type="text"
|
uiOptions={{
|
||||||
id="desktop-search"
|
showImages: false,
|
||||||
class="w-48 pl-10 pr-4 py-1.5 rounded-full text-sm text-gray-700 dark:text-gray-200 placeholder-gray-500 dark:placeholder-gray-400 bg-gray-50/80 dark:bg-gray-800/60 border border-gray-200/60 dark:border-gray-700/40 focus:outline-none focus:ring-1 focus:ring-primary-400 dark:focus:ring-primary-500 focus:bg-white dark:focus:bg-gray-800 focus:border-primary-300 dark:focus:border-primary-600"
|
resetStyles: false,
|
||||||
placeholder="搜索文章..."
|
showSubResults: true,
|
||||||
/>
|
translations: {
|
||||||
<div
|
placeholder: "搜索文章...",
|
||||||
class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"
|
clear_search: "清除",
|
||||||
>
|
load_more: "加载更多结果",
|
||||||
<svg
|
search_label: "搜索网站",
|
||||||
class="h-4 w-4 text-gray-400 dark:text-gray-500"
|
filters_label: "筛选",
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
zero_results: "未找到 [SEARCH_TERM] 的结果",
|
||||||
fill="none"
|
many_results: "找到 [COUNT] 个 [SEARCH_TERM] 的结果",
|
||||||
viewBox="0 0 24 24"
|
one_result: "找到 [COUNT] 个 [SEARCH_TERM] 的结果",
|
||||||
stroke="currentColor"
|
},
|
||||||
aria-hidden="true"
|
}}
|
||||||
>
|
/>
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 搜索结果容器(默认隐藏) -->
|
|
||||||
<div
|
|
||||||
id="desktop-search-results"
|
|
||||||
class="absolute top-full left-0 right-0 mt-2 max-h-80 overflow-y-auto rounded-lg bg-white/95 dark:bg-gray-800/95 shadow-md border border-gray-200/70 dark:border-gray-700/70 backdrop-blur-sm z-50 hidden"
|
|
||||||
>
|
|
||||||
<!-- 结果将通过JS动态填充 -->
|
|
||||||
<div
|
|
||||||
class="p-4 text-center text-gray-500 dark:text-gray-400"
|
|
||||||
id="desktop-search-message"
|
|
||||||
>
|
|
||||||
<p>输入关键词开始搜索</p>
|
|
||||||
</div>
|
|
||||||
<ul
|
|
||||||
class="divide-y divide-gray-200/70 dark:divide-gray-700/70"
|
|
||||||
id="desktop-search-list"
|
|
||||||
>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
{
|
||||||
NAV_LINKS.map((link) => (
|
NAV_LINKS.map((link) => (
|
||||||
@ -103,7 +76,40 @@ const normalizedPath =
|
|||||||
</a>
|
</a>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
<ThemeToggle />
|
<div
|
||||||
|
class="inline-flex items-center justify-center cursor-pointer rounded-md hover:bg-gray-100 dark:hover:bg-gray-700/50 group relative mt-1.5 w-8 h-8"
|
||||||
|
>
|
||||||
|
<Toggle class="flex items-center justify-center h-full w-full">
|
||||||
|
<Fragment
|
||||||
|
slot="icon-light"
|
||||||
|
class="flex items-center justify-center h-full w-full"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
class="w-4 h-4 text-gray-900 dark:text-white group-hover:text-primary-600 dark:group-hover:text-primary-400 fill-current flex-shrink-0 m-auto absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<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"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</Fragment>
|
||||||
|
<Fragment
|
||||||
|
slot="icon-dark"
|
||||||
|
class="flex items-center justify-center h-full w-full"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
class="w-4 h-4 text-gray-900 dark:text-white group-hover:text-primary-600 dark:group-hover:text-primary-400 fill-current flex-shrink-0 m-auto absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<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"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</Fragment>
|
||||||
|
</Toggle>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 移动端菜单按钮 -->
|
<!-- 移动端菜单按钮 -->
|
||||||
@ -181,70 +187,25 @@ const normalizedPath =
|
|||||||
id="mobile-search-panel"
|
id="mobile-search-panel"
|
||||||
class="hidden md:hidden fixed inset-x-0 top-16 p-4 bg-white dark:bg-gray-800 shadow-md z-50 border-t border-gray-200 dark:border-gray-700"
|
class="hidden md:hidden fixed inset-x-0 top-16 p-4 bg-white dark:bg-gray-800 shadow-md z-50 border-t border-gray-200 dark:border-gray-700"
|
||||||
>
|
>
|
||||||
<div class="relative">
|
<div>
|
||||||
<input
|
<Search
|
||||||
type="text"
|
className="pagefind-ui"
|
||||||
id="mobile-search"
|
uiOptions={{
|
||||||
class="w-full pl-10 pr-10 py-2 rounded-full text-sm text-gray-700 dark:text-gray-200 placeholder-gray-500 dark:placeholder-gray-400 bg-gray-50/80 dark:bg-gray-800/60 border border-gray-200/60 dark:border-gray-700/40 focus:outline-none focus:ring-1 focus:ring-primary-400 dark:focus:ring-primary-500 focus:bg-white dark:focus:bg-gray-800 focus:border-primary-300 dark:focus:border-primary-600"
|
showImages: false,
|
||||||
placeholder="搜索文章..."
|
resetStyles: false,
|
||||||
|
showSubResults: true,
|
||||||
|
translations: {
|
||||||
|
placeholder: "搜索文章...",
|
||||||
|
clear_search: "清除",
|
||||||
|
load_more: "加载更多结果",
|
||||||
|
search_label: "搜索网站",
|
||||||
|
filters_label: "筛选",
|
||||||
|
zero_results: "未找到 [SEARCH_TERM] 的结果",
|
||||||
|
many_results: "找到 [COUNT] 个 [SEARCH_TERM] 的结果",
|
||||||
|
one_result: "找到 [COUNT] 个 [SEARCH_TERM] 的结果",
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<div
|
|
||||||
class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
class="h-5 w-5 text-gray-400 dark:text-gray-500"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
id="mobile-search-close"
|
|
||||||
class="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
|
||||||
aria-label="关闭搜索"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
class="h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M6 18L18 6M6 6l12 12"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<!-- 移动端搜索结果 -->
|
|
||||||
<div
|
|
||||||
id="mobile-search-results"
|
|
||||||
class="mt-3 max-h-80 overflow-y-auto rounded-lg bg-white/95 dark:bg-gray-800/95 shadow-md border border-gray-200/70 dark:border-gray-700/70 backdrop-blur-sm hidden"
|
|
||||||
>
|
|
||||||
<!-- 结果将通过JS动态填充 -->
|
|
||||||
<div
|
|
||||||
class="p-4 text-center text-gray-500 dark:text-gray-400"
|
|
||||||
id="mobile-search-message"
|
|
||||||
>
|
|
||||||
<p>输入关键词开始搜索</p>
|
|
||||||
</div>
|
|
||||||
<ul
|
|
||||||
class="divide-y divide-gray-200/70 dark:divide-gray-700/70"
|
|
||||||
id="mobile-search-list"
|
|
||||||
>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -281,7 +242,40 @@ const normalizedPath =
|
|||||||
<span class="text-sm font-medium text-gray-600 dark:text-gray-300"
|
<span class="text-sm font-medium text-gray-600 dark:text-gray-300"
|
||||||
>切换主题</span
|
>切换主题</span
|
||||||
>
|
>
|
||||||
<ThemeToggle />
|
<div
|
||||||
|
class="group relative w-7 h-7 mt-1 flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<Toggle class="flex items-center justify-center h-full w-full">
|
||||||
|
<Fragment
|
||||||
|
slot="icon-light"
|
||||||
|
class="flex items-center justify-center h-full w-full"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
class="w-3.5 h-3.5 text-gray-900 dark:text-white group-hover:text-primary-600 dark:group-hover:text-primary-400 fill-current flex-shrink-0 m-auto absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<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"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</Fragment>
|
||||||
|
<Fragment
|
||||||
|
slot="icon-dark"
|
||||||
|
class="flex items-center justify-center h-full w-full"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
class="w-3.5 h-3.5 text-gray-900 dark:text-white group-hover:text-primary-600 dark:group-hover:text-primary-400 fill-current flex-shrink-0 m-auto absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<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"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</Fragment>
|
||||||
|
</Toggle>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -300,20 +294,6 @@ const normalizedPath =
|
|||||||
handler: EventListenerOrEventListenerObject;
|
handler: EventListenerOrEventListenerObject;
|
||||||
}> = [];
|
}> = [];
|
||||||
|
|
||||||
// 记录是否已加载文章数据
|
|
||||||
interface Article {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
date: string | Date;
|
|
||||||
summary?: string;
|
|
||||||
tags?: string[];
|
|
||||||
image?: string;
|
|
||||||
content?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
let articles: Article[] = [];
|
|
||||||
let isArticlesLoaded = false;
|
|
||||||
|
|
||||||
// 添加事件监听器并记录,方便后续统一清理
|
// 添加事件监听器并记录,方便后续统一清理
|
||||||
function addListener<K extends keyof HTMLElementEventMap>(
|
function addListener<K extends keyof HTMLElementEventMap>(
|
||||||
element: EventTarget | null,
|
element: EventTarget | null,
|
||||||
@ -437,7 +417,6 @@ const normalizedPath =
|
|||||||
"mobile-search-button",
|
"mobile-search-button",
|
||||||
);
|
);
|
||||||
const mobileSearchPanel = document.getElementById("mobile-search-panel");
|
const mobileSearchPanel = document.getElementById("mobile-search-panel");
|
||||||
const mobileSearch = document.getElementById("mobile-search");
|
|
||||||
const mobileSearchClose = document.getElementById("mobile-search-close");
|
const mobileSearchClose = document.getElementById("mobile-search-close");
|
||||||
|
|
||||||
// 关闭移动端菜单的函数
|
// 关闭移动端菜单的函数
|
||||||
@ -457,6 +436,130 @@ const normalizedPath =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 清空桌面搜索框的函数
|
||||||
|
function clearDesktopSearch(): void {
|
||||||
|
// 获取关键元素
|
||||||
|
const searchInput = document.querySelector('.pagefind-ui__search-input');
|
||||||
|
const clearButton = document.querySelector('.pagefind-ui__search-clear');
|
||||||
|
const resultsContainer = document.querySelector('.pagefind-ui__results');
|
||||||
|
|
||||||
|
// 隐藏搜索结果面板
|
||||||
|
if (resultsContainer) {
|
||||||
|
// 直接隐藏结果容器
|
||||||
|
resultsContainer.setAttribute('style', 'display: none !important');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有清除按钮,点击它清空输入框
|
||||||
|
if (clearButton) {
|
||||||
|
(clearButton as HTMLElement).click();
|
||||||
|
} else if (searchInput) {
|
||||||
|
// 备选方案,设置输入框为空
|
||||||
|
(searchInput as HTMLInputElement).value = '';
|
||||||
|
searchInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听搜索结果链接点击事件
|
||||||
|
function setupSearchResultListeners(): void {
|
||||||
|
// 使用事件委托,监听整个搜索容器
|
||||||
|
const searchContainer = document.querySelector(".pagefind-ui");
|
||||||
|
if (searchContainer) {
|
||||||
|
// 移除之前可能存在的事件监听器
|
||||||
|
const existingHandler = (searchContainer as any)._clickHandler;
|
||||||
|
if (existingHandler) {
|
||||||
|
searchContainer.removeEventListener("click", existingHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加新的事件监听器
|
||||||
|
const clickHandler = (e: Event) => {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
// 检查是否点击了搜索结果链接或其父元素
|
||||||
|
const linkElement = target.closest(
|
||||||
|
".pagefind-ui__result-link, .pagefind-ui__result-title, .pagefind-ui__results a",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (linkElement && linkElement.tagName === "A") {
|
||||||
|
// 不做任何处理,避免干扰导航
|
||||||
|
// 让路由变化后的处理函数来隐藏搜索面板
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 存储处理函数以便后续可能的移除
|
||||||
|
(searchContainer as any)._clickHandler = clickHandler;
|
||||||
|
|
||||||
|
// 添加事件监听
|
||||||
|
addListener(searchContainer, "click", clickHandler, {
|
||||||
|
capture: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn("[Header] 未找到搜索容器,无法添加点击事件");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接为文档添加事件监听,确保能捕获到所有点击
|
||||||
|
addListener(
|
||||||
|
document.body,
|
||||||
|
"click",
|
||||||
|
(e) => {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
const isSearchResult = target.closest(
|
||||||
|
".pagefind-ui__result-link, .pagefind-ui__result-title, .pagefind-ui__results a",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isSearchResult && isSearchResult.tagName === "A") {
|
||||||
|
// 不做任何处理,避免干扰导航
|
||||||
|
// 让路由变化后的处理函数来隐藏搜索面板
|
||||||
|
|
||||||
|
// 只关闭移动端搜索面板,因为它不影响导航
|
||||||
|
closeMobileSearch();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ capture: true },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化搜索功能并设置页面加载后的变化监听
|
||||||
|
function initSearch(): void {
|
||||||
|
setupSearchResultListeners();
|
||||||
|
|
||||||
|
// Pagefind可能在页面加载后动态添加内容,添加MutationObserver以监视这些变化
|
||||||
|
const pageObserver = new MutationObserver((mutations) => {
|
||||||
|
for (const mutation of mutations) {
|
||||||
|
if (mutation.addedNodes.length > 0) {
|
||||||
|
// 检查是否有新添加的搜索容器或结果
|
||||||
|
const hasNewSearchElements = Array.from(mutation.addedNodes).some(
|
||||||
|
(node) => {
|
||||||
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
const element = node as HTMLElement;
|
||||||
|
return (
|
||||||
|
element.classList?.contains("pagefind-ui") ||
|
||||||
|
element.querySelector(".pagefind-ui") !== null ||
|
||||||
|
element.classList?.contains("pagefind-ui__results") ||
|
||||||
|
element.querySelector(".pagefind-ui__results") !== null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasNewSearchElements) {
|
||||||
|
setupSearchResultListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 观察整个body以捕获所有变化
|
||||||
|
pageObserver.observe(document.body, { childList: true, subtree: true });
|
||||||
|
|
||||||
|
// 添加导航后清空搜索框的监听
|
||||||
|
addListener(document, "astro:page-load", () => {
|
||||||
|
// 页面加载后清空搜索框和隐藏结果面板
|
||||||
|
setTimeout(() => {
|
||||||
|
clearDesktopSearch();
|
||||||
|
}, 100); // 短暂延迟确保搜索组件已加载
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (mobileMenuButton && mobileMenu && menuOpenIcon && menuCloseIcon) {
|
if (mobileMenuButton && mobileMenu && menuOpenIcon && menuCloseIcon) {
|
||||||
// 移动端菜单按钮点击事件
|
// 移动端菜单按钮点击事件
|
||||||
(mobileMenuButton as HTMLElement).style.pointerEvents = "auto";
|
(mobileMenuButton as HTMLElement).style.pointerEvents = "auto";
|
||||||
@ -526,7 +629,6 @@ const normalizedPath =
|
|||||||
} else {
|
} else {
|
||||||
closeMobileMenu();
|
closeMobileMenu();
|
||||||
mobileSearchPanel.classList.remove("hidden");
|
mobileSearchPanel.classList.remove("hidden");
|
||||||
if (mobileSearch) mobileSearch.focus();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ capture: true },
|
{ capture: true },
|
||||||
@ -548,6 +650,9 @@ const normalizedPath =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化搜索功能
|
||||||
|
initSearch();
|
||||||
|
|
||||||
// 处理移动端主题切换容器
|
// 处理移动端主题切换容器
|
||||||
const themeToggleContainer = document.getElementById(
|
const themeToggleContainer = document.getElementById(
|
||||||
"theme-toggle-container",
|
"theme-toggle-container",
|
||||||
@ -560,13 +665,12 @@ const normalizedPath =
|
|||||||
(e: Event) => {
|
(e: Event) => {
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
if (
|
if (
|
||||||
target.id !== "theme-toggle-button" &&
|
target.tagName !== "ASTRO-THEME-TOGGLE" &&
|
||||||
!target.closest("#theme-toggle-button")
|
!target.closest("astro-theme-toggle")
|
||||||
) {
|
) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const toggleButton = themeToggleContainer.querySelector(
|
const toggleButton =
|
||||||
"#theme-toggle-button",
|
themeToggleContainer.querySelector("astro-theme-toggle");
|
||||||
);
|
|
||||||
if (toggleButton) {
|
if (toggleButton) {
|
||||||
(toggleButton as HTMLElement).click();
|
(toggleButton as HTMLElement).click();
|
||||||
}
|
}
|
||||||
@ -575,309 +679,47 @@ const normalizedPath =
|
|||||||
{ capture: true },
|
{ capture: true },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索功能逻辑
|
// 路由变化时处理
|
||||||
function initSearch(): void {
|
const routeEvents = [
|
||||||
// 搜索节流函数
|
"astro:page-load", // Astro路由导航完成
|
||||||
function debounce<T extends (...args: any[]) => void>(
|
"astro:after-swap" // Astro视图变化后
|
||||||
func: T,
|
];
|
||||||
wait: number
|
|
||||||
): (...args: Parameters<T>) => void {
|
routeEvents.forEach(eventName => {
|
||||||
let timeout: ReturnType<typeof setTimeout> | undefined;
|
addListener(document, eventName, () => {
|
||||||
return function(this: any, ...args: Parameters<T>): void {
|
// 页面加载后清空搜索框和隐藏结果面板
|
||||||
clearTimeout(timeout);
|
clearDesktopSearch();
|
||||||
timeout = setTimeout(() => func.apply(this, args), wait);
|
closeMobileSearch();
|
||||||
};
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
// 获取DOM元素
|
// 直接监听popstate和pushstate事件
|
||||||
const desktopSearch = document.getElementById("desktop-search");
|
addListener(window, "popstate", () => {
|
||||||
const desktopResults = document.getElementById("desktop-search-results");
|
clearDesktopSearch();
|
||||||
const desktopList = document.getElementById("desktop-search-list");
|
closeMobileSearch();
|
||||||
const desktopMessage = document.getElementById("desktop-search-message");
|
});
|
||||||
|
|
||||||
const mobileSearch = document.getElementById("mobile-search");
|
// 添加自定义监听处理history API
|
||||||
const mobileResults = document.getElementById("mobile-search-results");
|
const originalPushState = history.pushState;
|
||||||
const mobileList = document.getElementById("mobile-search-list");
|
history.pushState = function(...args) {
|
||||||
const mobileMessage = document.getElementById("mobile-search-message");
|
// 调用原始方法
|
||||||
|
const result = originalPushState.apply(this, args as [any, string, string | URL | null]);
|
||||||
// 获取文章数据
|
|
||||||
async function fetchArticles(): Promise<void> {
|
|
||||||
if (isArticlesLoaded && articles.length > 0) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch("/api/search");
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error("获取文章数据失败");
|
|
||||||
}
|
|
||||||
articles = await response.json();
|
|
||||||
isArticlesLoaded = true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("获取文章失败:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 高亮文本中的匹配部分
|
|
||||||
function highlightText(text: string, query: string): string {
|
|
||||||
if (!text || !query.trim()) return text;
|
|
||||||
|
|
||||||
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
||||||
const regex = new RegExp(`(${escapedQuery})`, "gi");
|
|
||||||
|
|
||||||
return text.replace(
|
|
||||||
regex,
|
|
||||||
'<mark class="bg-yellow-100 dark:bg-yellow-900/30 text-gray-900 dark:text-gray-100 px-0.5 rounded">$1</mark>',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索文章
|
|
||||||
function searchArticles(
|
|
||||||
query: string,
|
|
||||||
resultsList: HTMLElement,
|
|
||||||
resultsMessage: HTMLElement
|
|
||||||
): void {
|
|
||||||
if (!query.trim()) {
|
|
||||||
resultsList.innerHTML = "";
|
|
||||||
resultsMessage.textContent = "输入关键词开始搜索";
|
|
||||||
resultsMessage.style.display = "block";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (articles.length === 0) {
|
|
||||||
resultsMessage.textContent = "正在加载数据...";
|
|
||||||
resultsMessage.style.display = "block";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lowerQuery = query.toLowerCase();
|
|
||||||
|
|
||||||
// 过滤并排序结果
|
|
||||||
const filteredArticles = articles
|
|
||||||
.filter((article: Article) => {
|
|
||||||
const title = article.title.toLowerCase();
|
|
||||||
const tags = article.tags
|
|
||||||
? article.tags.map((tag: string) => tag.toLowerCase())
|
|
||||||
: [];
|
|
||||||
const summary = article.summary ? article.summary.toLowerCase() : "";
|
|
||||||
const content = article.content ? article.content.toLowerCase() : "";
|
|
||||||
|
|
||||||
return (
|
|
||||||
title.includes(lowerQuery) ||
|
|
||||||
tags.some((tag: string) => tag.includes(lowerQuery)) ||
|
|
||||||
summary.includes(lowerQuery) ||
|
|
||||||
content.includes(lowerQuery)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.sort((a: Article, b: Article) => {
|
|
||||||
// 标题匹配优先
|
|
||||||
const aTitle = a.title.toLowerCase();
|
|
||||||
const bTitle = b.title.toLowerCase();
|
|
||||||
|
|
||||||
if (aTitle.includes(lowerQuery) && !bTitle.includes(lowerQuery)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (!aTitle.includes(lowerQuery) && bTitle.includes(lowerQuery)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 内容匹配次之
|
|
||||||
const aContent = a.content ? a.content.toLowerCase() : "";
|
|
||||||
const bContent = b.content ? b.content.toLowerCase() : "";
|
|
||||||
|
|
||||||
if (aContent.includes(lowerQuery) && !bContent.includes(lowerQuery)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (!aContent.includes(lowerQuery) && bContent.includes(lowerQuery)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 日期排序
|
|
||||||
return new Date(b.date).getTime() - new Date(a.date).getTime();
|
|
||||||
})
|
|
||||||
.slice(0, 10); // 限制结果数量
|
|
||||||
|
|
||||||
if (filteredArticles.length === 0) {
|
|
||||||
resultsList.innerHTML = "";
|
|
||||||
resultsMessage.textContent = "没有找到相关内容";
|
|
||||||
resultsMessage.style.display = "block";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示结果
|
|
||||||
resultsMessage.style.display = "none";
|
|
||||||
resultsList.innerHTML = filteredArticles
|
|
||||||
.map((article: Article) => {
|
|
||||||
// 生成匹配的内容片段
|
|
||||||
let contentMatch = "";
|
|
||||||
if (
|
|
||||||
article.content &&
|
|
||||||
article.content.toLowerCase().includes(lowerQuery)
|
|
||||||
) {
|
|
||||||
// 找到匹配文本在内容中的位置
|
|
||||||
const matchIndex = article.content
|
|
||||||
.toLowerCase()
|
|
||||||
.indexOf(lowerQuery);
|
|
||||||
// 计算片段的起始和结束位置
|
|
||||||
const startPos = Math.max(0, matchIndex - 50);
|
|
||||||
const endPos = Math.min(article.content.length, matchIndex + 100);
|
|
||||||
// 提取片段
|
|
||||||
let snippet = article.content.substring(startPos, endPos);
|
|
||||||
if (startPos > 0) {
|
|
||||||
snippet = "..." + snippet;
|
|
||||||
}
|
|
||||||
if (endPos < article.content.length) {
|
|
||||||
snippet = snippet + "...";
|
|
||||||
}
|
|
||||||
snippet = highlightText(snippet, query);
|
|
||||||
contentMatch = `<p class="text-xs text-gray-500 dark:text-gray-400 mt-1 line-clamp-2">${snippet}</p>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 高亮标题和摘要中的匹配文本
|
|
||||||
const highlightedTitle = highlightText(article.title, query);
|
|
||||||
const highlightedSummary = article.summary
|
|
||||||
? highlightText(article.summary, query)
|
|
||||||
: "";
|
|
||||||
|
|
||||||
return `
|
|
||||||
<li>
|
|
||||||
<a href="/articles/${article.id}" class="block px-4 py-3 hover:bg-gray-100 dark:hover:bg-gray-700/70 search-result-link">
|
|
||||||
<h3 class="text-sm font-medium text-gray-800 dark:text-gray-200 truncate">${highlightedTitle}</h3>
|
|
||||||
${article.summary ? `<p class="text-xs text-gray-500 dark:text-gray-400 mt-1 truncate">${highlightedSummary}</p>` : ""}
|
|
||||||
${contentMatch}
|
|
||||||
${
|
|
||||||
article.tags && article.tags.length > 0
|
|
||||||
? `
|
|
||||||
<div class="flex flex-wrap gap-1 mt-1.5">
|
|
||||||
${article.tags
|
|
||||||
.slice(0, 3)
|
|
||||||
.map(
|
|
||||||
(tag: string) => `
|
|
||||||
<span class="inline-block text-xs bg-primary-50/50 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400 py-0.5 px-1.5 rounded-full">#${tag}</span>
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.join("")}
|
|
||||||
${article.tags.length > 3 ? `<span class="text-xs text-gray-400 dark:text-gray-500">+${article.tags.length - 3}</span>` : ""}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
`;
|
|
||||||
})
|
|
||||||
.join("");
|
|
||||||
|
|
||||||
// 为搜索结果链接添加点击事件,点击后关闭搜索面板
|
// 触发自定义事件
|
||||||
addSearchResultsClickListeners();
|
const event = new Event("pushstate");
|
||||||
}
|
window.dispatchEvent(event);
|
||||||
|
|
||||||
// 节流搜索
|
// 在pushstate时清空搜索面板
|
||||||
const debouncedDesktopSearch = debounce((value: string) => {
|
clearDesktopSearch();
|
||||||
if (desktopList && desktopMessage) {
|
closeMobileSearch();
|
||||||
searchArticles(
|
|
||||||
value,
|
return result;
|
||||||
desktopList as HTMLElement,
|
};
|
||||||
desktopMessage as HTMLElement,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
|
|
||||||
const debouncedMobileSearch = debounce((value: string) => {
|
|
||||||
if (mobileList && mobileMessage) {
|
|
||||||
searchArticles(
|
|
||||||
value,
|
|
||||||
mobileList as HTMLElement,
|
|
||||||
mobileMessage as HTMLElement,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
|
|
||||||
// 桌面端搜索逻辑
|
|
||||||
if (desktopSearch && desktopResults) {
|
|
||||||
// 聚焦时显示结果
|
|
||||||
addListener(desktopSearch, "focus", () => {
|
|
||||||
desktopResults.classList.remove("hidden");
|
|
||||||
if (!isArticlesLoaded) fetchArticles();
|
|
||||||
});
|
|
||||||
|
|
||||||
// 输入时更新搜索结果
|
|
||||||
addListener(desktopSearch, "input", (e: Event) => {
|
|
||||||
const target = e.target as HTMLInputElement;
|
|
||||||
if (target && target.value !== undefined) {
|
|
||||||
debouncedDesktopSearch(target.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 点击外部关闭结果
|
|
||||||
const documentClickHandler = (e: MouseEvent) => {
|
|
||||||
const target = e.target as Node;
|
|
||||||
if (
|
|
||||||
desktopSearch &&
|
|
||||||
!desktopSearch.contains(target) &&
|
|
||||||
!desktopResults.contains(target)
|
|
||||||
) {
|
|
||||||
desktopResults.classList.add("hidden");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
addListener(document, "click", documentClickHandler as EventListener);
|
|
||||||
|
|
||||||
// ESC键关闭结果
|
|
||||||
addListener(desktopSearch, "keydown", ((e: KeyboardEvent) => {
|
|
||||||
if (e.key === "Escape") {
|
|
||||||
desktopResults.classList.add("hidden");
|
|
||||||
}
|
|
||||||
}) as EventListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移动端搜索逻辑
|
|
||||||
if (mobileSearch && mobileResults) {
|
|
||||||
// 输入时更新搜索结果
|
|
||||||
addListener(mobileSearch, "input", (e: Event) => {
|
|
||||||
mobileResults.classList.remove("hidden");
|
|
||||||
const target = e.target as HTMLInputElement;
|
|
||||||
if (target && target.value !== undefined) {
|
|
||||||
debouncedMobileSearch(target.value);
|
|
||||||
if (!isArticlesLoaded) fetchArticles();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ESC键关闭搜索面板
|
|
||||||
addListener(mobileSearch, "keydown", ((e: KeyboardEvent) => {
|
|
||||||
if (e.key === "Escape") {
|
|
||||||
const mobileSearchPanel = document.getElementById(
|
|
||||||
"mobile-search-panel",
|
|
||||||
);
|
|
||||||
if (mobileSearchPanel) {
|
|
||||||
mobileSearchPanel.classList.add("hidden");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}) as EventListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭所有搜索面板的函数
|
|
||||||
function closeSearchPanels(): void {
|
|
||||||
const desktopSearchResults = document.getElementById("desktop-search-results");
|
|
||||||
if (desktopSearchResults) {
|
|
||||||
desktopSearchResults.classList.add("hidden");
|
|
||||||
}
|
|
||||||
|
|
||||||
const mobileSearchPanel = document.getElementById("mobile-search-panel");
|
addListener(window, "pushstate", () => {
|
||||||
if (mobileSearchPanel) {
|
clearDesktopSearch();
|
||||||
mobileSearchPanel.classList.add("hidden");
|
closeMobileSearch();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 为搜索结果链接添加点击事件
|
|
||||||
function addSearchResultsClickListeners(): void {
|
|
||||||
const searchResultLinks = document.querySelectorAll('.search-result-link');
|
|
||||||
|
|
||||||
searchResultLinks.forEach(link => {
|
|
||||||
addListener(link, 'click', () => {
|
|
||||||
closeSearchPanels();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -897,12 +739,7 @@ const normalizedPath =
|
|||||||
function setupHeader(): void {
|
function setupHeader(): void {
|
||||||
cleanup();
|
cleanup();
|
||||||
initHeader();
|
initHeader();
|
||||||
initSearch();
|
|
||||||
registerCleanup();
|
registerCleanup();
|
||||||
|
|
||||||
// 在页面路由变化时关闭搜索面板
|
|
||||||
addListener(document, 'astro:page-load', closeSearchPanels);
|
|
||||||
addListener(document, 'astro:after-swap', closeSearchPanels);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在页面加载时初始化
|
// 在页面加载时初始化
|
||||||
|
@ -3,6 +3,8 @@ import "@/styles/global.css";
|
|||||||
import Header from "@/components/Header.astro";
|
import Header from "@/components/Header.astro";
|
||||||
import Footer from "@/components/Footer.astro";
|
import Footer from "@/components/Footer.astro";
|
||||||
import { ICP, PSB_ICP, PSB_ICP_URL, SITE_NAME, SITE_DESCRIPTION } from "@/consts";
|
import { ICP, PSB_ICP, PSB_ICP_URL, SITE_NAME, SITE_DESCRIPTION } from "@/consts";
|
||||||
|
import { ThemeScript } from 'astro-theme-toggle';
|
||||||
|
import { AstroSeo } from '@astrolib/seo';
|
||||||
|
|
||||||
// 定义Props接口
|
// 定义Props接口
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -27,72 +29,41 @@ const { title = SITE_NAME, description = SITE_DESCRIPTION, date, tags } = Astro.
|
|||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
|
|
||||||
<!-- 基本元数据 -->
|
<!-- 使用 AstroSeo 组件替换原有的 SEO 标签 -->
|
||||||
<title>{title}</title>
|
<AstroSeo
|
||||||
<meta name="description" content={description || `${SITE_NAME} - 个人博客`} />
|
title={title}
|
||||||
<link rel="canonical" href={canonicalURL} />
|
description={description || `${SITE_NAME} - 个人博客`}
|
||||||
|
canonical={canonicalURL.toString()}
|
||||||
|
openGraph={{
|
||||||
|
type: 'article',
|
||||||
|
url: canonicalURL.toString(),
|
||||||
|
title: title,
|
||||||
|
description: description || `${SITE_NAME} - 个人博客`,
|
||||||
|
site_name: SITE_NAME,
|
||||||
|
...(date && { article: {
|
||||||
|
publishedTime: date.toISOString(),
|
||||||
|
tags: tags || []
|
||||||
|
}}),
|
||||||
|
}}
|
||||||
|
twitter={{
|
||||||
|
cardType: 'summary_large_image',
|
||||||
|
site: SITE_NAME,
|
||||||
|
handle: SITE_NAME,
|
||||||
|
}}
|
||||||
|
additionalMetaTags={[
|
||||||
|
{
|
||||||
|
property: 'article:published_time',
|
||||||
|
content: date ? date.toISOString() : '',
|
||||||
|
},
|
||||||
|
...(tags?.map(tag => ({
|
||||||
|
property: 'article:tag',
|
||||||
|
content: tag,
|
||||||
|
})) || []),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Open Graph / Facebook -->
|
<!-- 主题切换脚本 -->
|
||||||
<meta property="og:type" content="article" />
|
<ThemeScript />
|
||||||
<meta property="og:url" content={canonicalURL} />
|
|
||||||
<meta property="og:title" content={title} />
|
|
||||||
<meta property="og:description" content={description || `${SITE_NAME} - 个人博客`} />
|
|
||||||
|
|
||||||
<!-- 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} - 个人博客`} />
|
|
||||||
|
|
||||||
<!-- 文章特定元数据 -->
|
|
||||||
{date && <meta property="article:published_time" content={date.toISOString()} />}
|
|
||||||
{tags && tags.map(tag => (
|
|
||||||
<meta property="article:tag" content={tag} />
|
|
||||||
))}
|
|
||||||
<script is:inline>
|
|
||||||
// 立即执行主题初始化,采用"无闪烁"加载方式
|
|
||||||
(function() {
|
|
||||||
try {
|
|
||||||
// 获取系统首选主题
|
|
||||||
const getSystemTheme = () => {
|
|
||||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
||||||
};
|
|
||||||
|
|
||||||
const storedTheme = typeof localStorage !== 'undefined' ? localStorage.getItem('theme') : null;
|
|
||||||
const systemTheme = getSystemTheme();
|
|
||||||
let theme = 'light'; // 默认浅色主题
|
|
||||||
|
|
||||||
// 按照逻辑优先级应用主题
|
|
||||||
if (storedTheme) {
|
|
||||||
// 如果有存储的主题设置,则应用它
|
|
||||||
theme = storedTheme;
|
|
||||||
} else if (systemTheme) {
|
|
||||||
// 如果没有存储的设置,检查系统主题
|
|
||||||
theme = systemTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 立即设置文档主题,在DOM渲染前应用,避免闪烁
|
|
||||||
document.documentElement.dataset.theme = theme;
|
|
||||||
|
|
||||||
// 监听系统主题变化(只有当主题设为跟随系统时才响应)
|
|
||||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
||||||
|
|
||||||
const handleMediaChange = (e) => {
|
|
||||||
// 只有当主题设置为跟随系统时才更新主题
|
|
||||||
if (!localStorage.getItem('theme')) {
|
|
||||||
const newTheme = e.matches ? 'dark' : 'light';
|
|
||||||
document.documentElement.dataset.theme = newTheme;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 添加系统主题变化监听
|
|
||||||
mediaQuery.addEventListener('change', handleMediaChange);
|
|
||||||
} catch (error) {
|
|
||||||
// 出错时应用默认浅色主题,确保页面正常显示
|
|
||||||
document.documentElement.dataset.theme = 'light';
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
</head>
|
</head>
|
||||||
<body class="m-0 w-full h-full bg-gray-50 dark:bg-dark-bg flex flex-col min-h-screen">
|
<body class="m-0 w-full h-full bg-gray-50 dark:bg-dark-bg flex flex-col min-h-screen">
|
||||||
<Header />
|
<Header />
|
||||||
|
@ -1,434 +0,0 @@
|
|||||||
---
|
|
||||||
interface Props {
|
|
||||||
height?: number;
|
|
||||||
width?: number;
|
|
||||||
fill?: string;
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
height = 16,
|
|
||||||
width = 16,
|
|
||||||
fill = "currentColor",
|
|
||||||
className = ""
|
|
||||||
} = Astro.props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<button
|
|
||||||
id="theme-toggle-button"
|
|
||||||
class={`inline-flex items-center justify-center h-8 w-8 cursor-pointer rounded-md 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 ${className}`}
|
|
||||||
aria-label="切换主题"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<!-- 月亮图标 (暗色模式) -->
|
|
||||||
<svg
|
|
||||||
id="dark-icon"
|
|
||||||
style={`height: ${height}px; width: ${width}px;`}
|
|
||||||
fill={fill}
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
class="hover:scale-110 hidden dark:block"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<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
|
|
||||||
id="light-icon"
|
|
||||||
style={`height: ${height}px; width: ${width}px;`}
|
|
||||||
fill={fill}
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
class="hover:scale-110 block dark:hidden"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<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>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<script is:inline>
|
|
||||||
// 主题切换逻辑
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
// 页面导航计数器(跟踪页面跳转次数)
|
|
||||||
let pageNavigationCount = 0;
|
|
||||||
|
|
||||||
// 存储事件监听器,便于统一清理
|
|
||||||
const listeners = [];
|
|
||||||
|
|
||||||
// 定时器
|
|
||||||
let transitionTimeout = null;
|
|
||||||
|
|
||||||
// 直接从按钮移除事件监听器
|
|
||||||
function cleanupButtonListeners() {
|
|
||||||
// 查找所有主题切换按钮
|
|
||||||
const buttons = document.querySelectorAll('#theme-toggle-button');
|
|
||||||
|
|
||||||
buttons.forEach(button => {
|
|
||||||
// 移除所有可能的事件
|
|
||||||
if (button._clickHandler) {
|
|
||||||
button.removeEventListener('click', button._clickHandler, { capture: true });
|
|
||||||
delete button._clickHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button._keydownHandler) {
|
|
||||||
button.removeEventListener('keydown', button._keydownHandler);
|
|
||||||
delete button._keydownHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清除其他可能的事件
|
|
||||||
const otherClickHandlers = button.__themeToggleClickHandlers || [];
|
|
||||||
otherClickHandlers.forEach(handler => {
|
|
||||||
try {
|
|
||||||
button.removeEventListener('click', handler, { capture: true });
|
|
||||||
} catch (e) {
|
|
||||||
// 忽略错误
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const otherKeydownHandlers = button.__themeToggleKeydownHandlers || [];
|
|
||||||
otherKeydownHandlers.forEach(handler => {
|
|
||||||
try {
|
|
||||||
button.removeEventListener('keydown', handler);
|
|
||||||
} catch (e) {
|
|
||||||
// 忽略错误
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 重置处理函数数组
|
|
||||||
button.__themeToggleClickHandlers = [];
|
|
||||||
button.__themeToggleKeydownHandlers = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
// 清理容器
|
|
||||||
const container = document.getElementById('theme-toggle-container');
|
|
||||||
if (container) {
|
|
||||||
if (container._clickHandler) {
|
|
||||||
container.removeEventListener('click', container._clickHandler);
|
|
||||||
delete container._clickHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清除其他可能的事件
|
|
||||||
const otherClickHandlers = container.__themeToggleClickHandlers || [];
|
|
||||||
otherClickHandlers.forEach(handler => {
|
|
||||||
try {
|
|
||||||
container.removeEventListener('click', handler);
|
|
||||||
} catch (e) {
|
|
||||||
// 忽略错误
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 重置处理函数数组
|
|
||||||
container.__themeToggleClickHandlers = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加事件监听器并记录,方便后续统一清理
|
|
||||||
function addListener(element, eventType, handler, options) {
|
|
||||||
if (!element) return null;
|
|
||||||
|
|
||||||
// 确保先移除可能已存在的同类型事件处理函数
|
|
||||||
if (eventType === 'click' && element.id === 'theme-toggle-button') {
|
|
||||||
if (element._clickHandler) {
|
|
||||||
element.removeEventListener('click', element._clickHandler, { capture: true });
|
|
||||||
}
|
|
||||||
element._clickHandler = handler;
|
|
||||||
|
|
||||||
// 保存到数组中以便清理
|
|
||||||
if (!element.__themeToggleClickHandlers) {
|
|
||||||
element.__themeToggleClickHandlers = [];
|
|
||||||
}
|
|
||||||
element.__themeToggleClickHandlers.push(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType === 'keydown' && element.id === 'theme-toggle-button') {
|
|
||||||
if (element._keydownHandler) {
|
|
||||||
element.removeEventListener('keydown', element._keydownHandler);
|
|
||||||
}
|
|
||||||
element._keydownHandler = handler;
|
|
||||||
|
|
||||||
// 保存到数组中以便清理
|
|
||||||
if (!element.__themeToggleKeydownHandlers) {
|
|
||||||
element.__themeToggleKeydownHandlers = [];
|
|
||||||
}
|
|
||||||
element.__themeToggleKeydownHandlers.push(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType === 'click' && element.id === 'theme-toggle-container') {
|
|
||||||
if (element._clickHandler) {
|
|
||||||
element.removeEventListener('click', element._clickHandler);
|
|
||||||
}
|
|
||||||
element._clickHandler = handler;
|
|
||||||
|
|
||||||
// 保存到数组中以便清理
|
|
||||||
if (!element.__themeToggleClickHandlers) {
|
|
||||||
element.__themeToggleClickHandlers = [];
|
|
||||||
}
|
|
||||||
element.__themeToggleClickHandlers.push(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
element.addEventListener(eventType, handler, options);
|
|
||||||
listeners.push({ element, eventType, handler, options });
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清理函数 - 移除所有事件监听器
|
|
||||||
function cleanup() {
|
|
||||||
// 先直接从按钮清理事件
|
|
||||||
cleanupButtonListeners();
|
|
||||||
|
|
||||||
// 移除所有监听器
|
|
||||||
listeners.forEach(({ element, eventType, handler, options }) => {
|
|
||||||
try {
|
|
||||||
element.removeEventListener(eventType, handler, options);
|
|
||||||
} catch (err) {
|
|
||||||
// 忽略错误
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 清空数组
|
|
||||||
listeners.length = 0;
|
|
||||||
|
|
||||||
// 清理任何定时器
|
|
||||||
if (transitionTimeout) {
|
|
||||||
clearTimeout(transitionTimeout);
|
|
||||||
transitionTimeout = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化主题切换功能
|
|
||||||
function setupThemeToggle() {
|
|
||||||
// 确保当前没有活动的主题切换按钮事件
|
|
||||||
cleanup();
|
|
||||||
|
|
||||||
// 获取所有主题切换按钮
|
|
||||||
const themeToggleButtons = document.querySelectorAll('#theme-toggle-button');
|
|
||||||
|
|
||||||
if (!themeToggleButtons.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let transitioning = false;
|
|
||||||
|
|
||||||
// 获取系统首选主题
|
|
||||||
const getSystemTheme = () => {
|
|
||||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
||||||
};
|
|
||||||
|
|
||||||
// 初始化主题
|
|
||||||
const initializeTheme = () => {
|
|
||||||
const storedTheme = localStorage.getItem('theme');
|
|
||||||
const systemTheme = getSystemTheme();
|
|
||||||
|
|
||||||
// 按照逻辑优先级应用主题
|
|
||||||
if (storedTheme) {
|
|
||||||
document.documentElement.dataset.theme = storedTheme;
|
|
||||||
} else if (systemTheme) {
|
|
||||||
document.documentElement.dataset.theme = systemTheme;
|
|
||||||
} else {
|
|
||||||
document.documentElement.dataset.theme = 'light';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 切换主题
|
|
||||||
const toggleTheme = () => {
|
|
||||||
if (transitioning) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
transitioning = true;
|
|
||||||
|
|
||||||
// 获取当前主题
|
|
||||||
const currentTheme = document.documentElement.dataset.theme;
|
|
||||||
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
|
||||||
|
|
||||||
// 更新 HTML 属性
|
|
||||||
document.documentElement.dataset.theme = newTheme;
|
|
||||||
|
|
||||||
// 更新本地存储
|
|
||||||
const systemTheme = getSystemTheme();
|
|
||||||
|
|
||||||
if (newTheme === systemTheme) {
|
|
||||||
localStorage.removeItem('theme');
|
|
||||||
} else {
|
|
||||||
localStorage.setItem('theme', newTheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加防抖
|
|
||||||
if (transitionTimeout) {
|
|
||||||
clearTimeout(transitionTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
transitionTimeout = setTimeout(() => {
|
|
||||||
transitioning = false;
|
|
||||||
}, 300);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 监听系统主题变化
|
|
||||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
||||||
|
|
||||||
const handleMediaChange = (e) => {
|
|
||||||
if (!localStorage.getItem('theme')) {
|
|
||||||
const newTheme = e.matches ? 'dark' : 'light';
|
|
||||||
document.documentElement.dataset.theme = newTheme;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 添加系统主题变化监听
|
|
||||||
addListener(mediaQuery, 'change', handleMediaChange);
|
|
||||||
|
|
||||||
// 为每个按钮添加事件
|
|
||||||
themeToggleButtons.forEach((button, index) => {
|
|
||||||
// 确保移除旧的事件监听
|
|
||||||
if (button._clickHandler) {
|
|
||||||
button.removeEventListener('click', button._clickHandler, { capture: true });
|
|
||||||
}
|
|
||||||
if (button._keydownHandler) {
|
|
||||||
button.removeEventListener('keydown', button._keydownHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
button.style.pointerEvents = 'auto';
|
|
||||||
} catch (e) {
|
|
||||||
// 忽略样式错误
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建点击处理函数
|
|
||||||
const clickHandler = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
toggleTheme();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 点击事件 - 使用捕获模式并保存引用
|
|
||||||
addListener(button, 'click', clickHandler, { capture: true });
|
|
||||||
|
|
||||||
// 键盘事件
|
|
||||||
const keydownHandler = (e) => {
|
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
|
||||||
e.preventDefault();
|
|
||||||
toggleTheme();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
addListener(button, 'keydown', keydownHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 处理移动端主题切换容器
|
|
||||||
const themeToggleContainer = document.getElementById('theme-toggle-container');
|
|
||||||
if (themeToggleContainer) {
|
|
||||||
// 确保移除旧的事件监听
|
|
||||||
if (themeToggleContainer._clickHandler) {
|
|
||||||
themeToggleContainer.removeEventListener('click', themeToggleContainer._clickHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
const containerClickHandler = (e) => {
|
|
||||||
const target = e.target;
|
|
||||||
if (target.id !== 'theme-toggle-button' && !target.closest('#theme-toggle-button')) {
|
|
||||||
e.stopPropagation();
|
|
||||||
toggleTheme();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
addListener(themeToggleContainer, 'click', containerClickHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化主题
|
|
||||||
initializeTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注册清理函数 - 确保在每次页面转换前清理事件
|
|
||||||
function registerCleanup() {
|
|
||||||
const cleanupEvents = [
|
|
||||||
'astro:before-preparation',
|
|
||||||
'astro:before-swap',
|
|
||||||
'swup:willReplaceContent'
|
|
||||||
];
|
|
||||||
|
|
||||||
// 为每个事件注册一次性清理函数
|
|
||||||
cleanupEvents.forEach(eventName => {
|
|
||||||
const handler = () => {
|
|
||||||
cleanup();
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener(eventName, handler, { once: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
// 页面卸载时清理
|
|
||||||
window.addEventListener('beforeunload', () => {
|
|
||||||
cleanup();
|
|
||||||
}, { once: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化函数
|
|
||||||
function init() {
|
|
||||||
pageNavigationCount++;
|
|
||||||
setupThemeToggle();
|
|
||||||
registerCleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听页面转换事件
|
|
||||||
function setupPageTransitionEvents() {
|
|
||||||
// 确保事件处理程序唯一性的函数
|
|
||||||
function setupUniqueEvent(eventName, callback) {
|
|
||||||
const eventKey = `__theme_toggle_event_${eventName.replace(/:/g, '_')}`;
|
|
||||||
|
|
||||||
// 移除可能存在的旧处理函数
|
|
||||||
if (window[eventKey]) {
|
|
||||||
document.removeEventListener(eventName, window[eventKey]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存新处理函数并注册
|
|
||||||
window[eventKey] = callback;
|
|
||||||
document.addEventListener(eventName, window[eventKey]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 页面转换后事件
|
|
||||||
const pageTransitionEvents = [
|
|
||||||
{ name: 'astro:after-swap', delay: 10 },
|
|
||||||
{ name: 'astro:page-load', delay: 10 },
|
|
||||||
{ name: 'swup:contentReplaced', delay: 10 }
|
|
||||||
];
|
|
||||||
|
|
||||||
// 设置每个页面转换事件
|
|
||||||
pageTransitionEvents.forEach(({ name, delay }) => {
|
|
||||||
setupUniqueEvent(name, () => {
|
|
||||||
cleanupButtonListeners(); // 立即清理按钮上的事件
|
|
||||||
|
|
||||||
// 延迟初始化,确保DOM完全更新
|
|
||||||
setTimeout(() => {
|
|
||||||
cleanupButtonListeners(); // 再次清理,确保没有遗漏
|
|
||||||
init();
|
|
||||||
}, delay);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 特别处理 swup:pageView 事件
|
|
||||||
setupUniqueEvent('swup:pageView', () => {
|
|
||||||
// 对于偶数次页面跳转,特别确保事件被正确重新绑定
|
|
||||||
if (pageNavigationCount % 2 === 0) {
|
|
||||||
setTimeout(() => {
|
|
||||||
const buttons = document.querySelectorAll('#theme-toggle-button');
|
|
||||||
if (buttons.length > 0) {
|
|
||||||
cleanupButtonListeners();
|
|
||||||
setupThemeToggle();
|
|
||||||
}
|
|
||||||
}, 50);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置页面转换事件监听
|
|
||||||
setupPageTransitionEvents();
|
|
||||||
|
|
||||||
// 在页面加载后初始化
|
|
||||||
if (document.readyState === 'loading') {
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
init();
|
|
||||||
}, { once: true });
|
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
init();
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -4,8 +4,8 @@ export const SITE_DESCRIPTION = "记录生活,分享所思";
|
|||||||
|
|
||||||
export const NAV_LINKS = [
|
export const NAV_LINKS = [
|
||||||
{ href: '/', text: '首页' },
|
{ href: '/', text: '首页' },
|
||||||
{ href: '/articles', text: '文章' },
|
|
||||||
{ href: '/filtered', text: '筛选' },
|
{ href: '/filtered', text: '筛选' },
|
||||||
|
{ href: '/articles', text: '文章' },
|
||||||
{ href: '/movies', text: '观影' },
|
{ href: '/movies', text: '观影' },
|
||||||
{ href: '/books', text: '读书' },
|
{ href: '/books', text: '读书' },
|
||||||
{ href: '/projects', text: '项目' },
|
{ href: '/projects', text: '项目' },
|
||||||
|
@ -582,75 +582,11 @@ function getArticleUrl(articleId: string) {
|
|||||||
updateActiveHeading();
|
updateActiveHeading();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupCodeBlocks() {
|
|
||||||
const codeBlocks = document.querySelectorAll("pre");
|
|
||||||
if (!codeBlocks.length) return;
|
|
||||||
|
|
||||||
codeBlocks.forEach(pre => {
|
|
||||||
const code = pre.querySelector("code");
|
|
||||||
if (!code || pre.querySelector('.code-header')) return;
|
|
||||||
|
|
||||||
const className = code.className;
|
|
||||||
const languageMatch = className.match(/language-(\w+)/);
|
|
||||||
const language = languageMatch ? languageMatch[1] : "text";
|
|
||||||
|
|
||||||
const header = document.createElement("div");
|
|
||||||
header.className = "code-header flex justify-between items-center text-xs px-4 py-2 bg-secondary-800 dark:bg-gray-900 text-secondary-300 dark:text-secondary-400 rounded-t-lg";
|
|
||||||
|
|
||||||
const languageLabel = document.createElement("span");
|
|
||||||
languageLabel.className = "code-language font-mono";
|
|
||||||
languageLabel.textContent = language;
|
|
||||||
|
|
||||||
const copyButton = document.createElement("button");
|
|
||||||
copyButton.className = "code-copy-button flex items-center gap-1 hover:text-white dark:hover:text-primary-400 transition-colors";
|
|
||||||
|
|
||||||
const copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;
|
|
||||||
const successIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4"><path d="M20 6L9 17l-5-5"></path></svg>`;
|
|
||||||
const errorIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>`;
|
|
||||||
|
|
||||||
copyButton.innerHTML = `${copyIcon}<span>复制</span>`;
|
|
||||||
copyButton.setAttribute("aria-label", "复制代码");
|
|
||||||
copyButton.setAttribute("title", "复制代码到剪贴板");
|
|
||||||
|
|
||||||
addListener(copyButton, "click", (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
navigator.clipboard.writeText(code.textContent || "")
|
|
||||||
.then(() => {
|
|
||||||
copyButton.innerHTML = `${successIcon}<span>已复制</span>`;
|
|
||||||
copyButton.classList.add("text-green-400");
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
copyButton.innerHTML = `${copyIcon}<span>复制</span>`;
|
|
||||||
copyButton.classList.remove("text-green-400");
|
|
||||||
}, 2000);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
copyButton.innerHTML = `${errorIcon}<span>失败</span>`;
|
|
||||||
copyButton.classList.add("text-red-400");
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
copyButton.innerHTML = `${copyIcon}<span>复制</span>`;
|
|
||||||
copyButton.classList.remove("text-red-400");
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
header.appendChild(languageLabel);
|
|
||||||
header.appendChild(copyButton);
|
|
||||||
pre.insertBefore(header, pre.firstChild);
|
|
||||||
|
|
||||||
pre.classList.add("rounded-b-lg", "mt-0");
|
|
||||||
pre.style.marginTop = "0";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
if (!document.querySelector("article")) return;
|
if (!document.querySelector("article")) return;
|
||||||
|
|
||||||
setupProgressBar();
|
setupProgressBar();
|
||||||
setupTableOfContents();
|
setupTableOfContents();
|
||||||
setupCodeBlocks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.readyState === "loading") {
|
if (document.readyState === "loading") {
|
||||||
|
@ -309,12 +309,12 @@ function getArticleUrl(articleId: string) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 无结果提示 -->
|
<!-- 无结果提示 -->
|
||||||
<div id="noResultsMessage" class="hidden text-center py-16 bg-white rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 mb-12">
|
<div id="noResultsMessage" class="hidden text-center py-16 bg-white dark:bg-gray-800 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">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 mx-auto text-primary-200 dark:text-primary-700 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" />
|
<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>
|
</svg>
|
||||||
<h3 class="text-2xl font-bold text-gray-700 mb-2">没有找到符合条件的文章</h3>
|
<h3 class="text-2xl font-bold text-gray-700 dark:text-gray-200 mb-2">没有找到符合条件的文章</h3>
|
||||||
<p class="text-gray-500 max-w-md mx-auto">请尝试调整筛选条件或者清除筛选条件</p>
|
<p class="text-gray-500 dark:text-gray-400 max-w-md mx-auto">请尝试调整筛选条件或者清除筛选条件</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 筛选功能的客户端脚本 -->
|
<!-- 筛选功能的客户端脚本 -->
|
||||||
|
1
src/pages/table-test.md
Normal file
1
src/pages/table-test.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
@ -1,175 +0,0 @@
|
|||||||
/* 代码块容器样式 */
|
|
||||||
.prose pre {
|
|
||||||
position: relative;
|
|
||||||
margin: 1.5em 0;
|
|
||||||
padding: 0;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
/* 调整背景色和边框 */
|
|
||||||
background-color: #282c34 !important;
|
|
||||||
border: 1px solid #374151;
|
|
||||||
overflow: hidden;
|
|
||||||
overflow-x: auto;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* 代码块顶部栏 - 调整颜色使其更突出 */
|
|
||||||
.code-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
/* 修改背景色为更深的颜色 */
|
|
||||||
background-color: #21252b;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
/* 调整边框颜色 */
|
|
||||||
border-bottom: 1px solid #374151;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 语言标签 */
|
|
||||||
.code-language {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 600;
|
|
||||||
/* 调整颜色使其更明显 */
|
|
||||||
color: #d1d5db;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.05em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 复制按钮 - 增强视觉效果 */
|
|
||||||
.code-copy-button {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 6px;
|
|
||||||
padding: 0.35rem 0.75rem;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
/* 调整按钮颜色 */
|
|
||||||
color: #d1d5db;
|
|
||||||
background-color: #2c313a;
|
|
||||||
border: 1px solid #4b5563;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-copy-button svg {
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
stroke: currentColor;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-copy-button span {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-copy-button:hover {
|
|
||||||
background-color: #374151;
|
|
||||||
color: #f3f4f6;
|
|
||||||
border-color: #6b7280;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-copy-button:active {
|
|
||||||
transform: scale(0.95);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 代码内容区域 */
|
|
||||||
.prose pre code {
|
|
||||||
display: block;
|
|
||||||
padding: 1.5em 1.25em;
|
|
||||||
overflow-x: auto;
|
|
||||||
font-family: "JetBrains Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
||||||
font-size: 0.9em;
|
|
||||||
line-height: 1.6;
|
|
||||||
tab-size: 2;
|
|
||||||
/* 调整文字颜色 */
|
|
||||||
color: #abb2bf !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 自定义滚动条 */
|
|
||||||
.prose pre code::-webkit-scrollbar {
|
|
||||||
height: 8px;
|
|
||||||
background-color: #282c34;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prose pre code::-webkit-scrollbar-thumb {
|
|
||||||
background-color: #4b5363;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prose pre code::-webkit-scrollbar-thumb:hover {
|
|
||||||
background-color: #5a6377;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prose pre code::-webkit-scrollbar-track {
|
|
||||||
background-color: #21252b;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Firefox滚动条 */
|
|
||||||
.prose pre code {
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: #4b5363 #282c34;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 代码行高亮 */
|
|
||||||
.prose pre .highlight-line {
|
|
||||||
background-color: rgba(171, 178, 191, 0.1);
|
|
||||||
display: block;
|
|
||||||
margin: 0 -1em;
|
|
||||||
padding: 0 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 行号 */
|
|
||||||
.prose pre code {
|
|
||||||
counter-reset: line;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prose pre code .line {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 内联代码样式 */
|
|
||||||
.prose :not(pre) > code {
|
|
||||||
background-color: rgba(171, 178, 191, 0.1);
|
|
||||||
padding: 0.2em 0.4em;
|
|
||||||
border-radius: 0.25em;
|
|
||||||
font-weight: 400;
|
|
||||||
white-space: nowrap;
|
|
||||||
border: 1px solid rgba(171, 178, 191, 0.2);
|
|
||||||
font-family: "JetBrains Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
||||||
font-size: 0.85em;
|
|
||||||
color: #e06c75;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 暗色模式适配 */
|
|
||||||
[data-theme="dark"] .prose pre,
|
|
||||||
[data-theme="dark"] .code-header {
|
|
||||||
background-color: #282c34 !important;
|
|
||||||
border-color: #374151;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .code-copy-button {
|
|
||||||
background-color: #2c313a;
|
|
||||||
border-color: #4b5563;
|
|
||||||
color: #d1d5db;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .code-copy-button:hover {
|
|
||||||
background-color: #374151;
|
|
||||||
color: #f3f4f6;
|
|
||||||
border-color: #6b7280;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .prose pre code {
|
|
||||||
/* 调整暗色模式文字颜色 */
|
|
||||||
color: #abb2bf;
|
|
||||||
}
|
|
@ -1,186 +1,3 @@
|
|||||||
/* 增强表格样式 */
|
|
||||||
.prose table {
|
|
||||||
border-collapse: separate;
|
|
||||||
border-spacing: 0;
|
|
||||||
width: 100%;
|
|
||||||
margin: 1.5em 0;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
overflow: hidden;
|
|
||||||
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-primary-50);
|
|
||||||
}
|
|
||||||
|
|
||||||
.prose thead th {
|
|
||||||
padding: 1rem 1.25rem;
|
|
||||||
font-weight: 600;
|
|
||||||
text-align: left;
|
|
||||||
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: 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.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 {
|
.prose ul {
|
||||||
list-style-type: disc;
|
list-style-type: disc;
|
||||||
@ -432,206 +249,9 @@
|
|||||||
border-bottom-color: var(--color-primary-400);
|
border-bottom-color: var(--color-primary-400);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 响应式表格样式 */
|
/* 响应式样式 */
|
||||||
@media (max-width: 640px) {
|
@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-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-surface/40);
|
|
||||||
}
|
|
||||||
|
|
||||||
[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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 收纳内容样式 */
|
/* 收纳内容样式 */
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@import "./prism.css";
|
|
||||||
@import "./content-styles.css";
|
@import "./content-styles.css";
|
||||||
|
@import "./table-styles.css";
|
||||||
@import "./emoji.css";
|
@import "./emoji.css";
|
||||||
@import "./code-blocks.css";
|
@import "./header.css";
|
||||||
|
|
||||||
/* 定义深色模式选择器 */
|
/* 定义深色模式选择器 */
|
||||||
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
||||||
@ -12,25 +12,6 @@
|
|||||||
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
|
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 专门为卡片元素添加过渡属性,修复悬停问题 */
|
|
||||||
.recent-article,
|
|
||||||
[class*="hover:-translate-y"] {
|
|
||||||
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
|
|
||||||
/* 添加transform-style属性以优化渲染 */
|
|
||||||
transform-style: preserve-3d;
|
|
||||||
/* 添加will-change提示浏览器将使用GPU加速 */
|
|
||||||
will-change: transform;
|
|
||||||
/* 确保初始状态是稳定的 */
|
|
||||||
transform: translateY(0);
|
|
||||||
/* 防止鼠标在卡片内移动时触发重新计算 */
|
|
||||||
backface-visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 使用更具体的选择器优先级来控制悬停行为 */
|
|
||||||
.recent-article:hover,
|
|
||||||
[class*="hover:-translate-y"]:hover {
|
|
||||||
transform: translateY(-0.25rem) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
/* 主色调 - 使用更现代的蓝紫色 */
|
/* 主色调 - 使用更现代的蓝紫色 */
|
||||||
|
@ -4,14 +4,185 @@
|
|||||||
#header-bg.scrolled {
|
#header-bg.scrolled {
|
||||||
backdrop-filter: blur(6px);
|
backdrop-filter: blur(6px);
|
||||||
background: rgba(249, 250, 251, 0.8);
|
background: rgba(249, 250, 251, 0.8);
|
||||||
box-shadow:
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 2px 4px rgba(0, 0, 0, 0.04),
|
||||||
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);
|
0 4px 8px rgba(0, 0, 0, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 黑暗模式样式 */
|
/* 黑暗模式样式 */
|
||||||
[data-theme="dark"] #header-bg.scrolled {
|
[data-theme="dark"] #header-bg.scrolled {
|
||||||
background: rgba(15, 23, 42, 0.8);
|
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);
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3),
|
||||||
}
|
0 2px 4px -1px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 通用搜索框样式 */
|
||||||
|
.pagefind-ui .pagefind-ui__form {
|
||||||
|
margin: 0;
|
||||||
|
max-width: none;
|
||||||
|
position: relative; /* 确保定位上下文 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagefind-ui .pagefind-ui__search-input {
|
||||||
|
border-radius: 9999px !important;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
height: 32px !important;
|
||||||
|
width: 12rem !important;
|
||||||
|
padding-top: 0.25rem !important;
|
||||||
|
padding-bottom: 0.25rem !important;
|
||||||
|
padding-left: 35px !important;
|
||||||
|
padding-right: 47px !important;
|
||||||
|
border-color: var(--color-gray-200) !important;
|
||||||
|
border-width: 1px !important;
|
||||||
|
color: var(--color-gray-700) !important;
|
||||||
|
font-weight: 400 !important;
|
||||||
|
background-color: var(--color-gray-50) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端搜索框样式 */
|
||||||
|
#mobile-search-panel .pagefind-ui .pagefind-ui__search-input {
|
||||||
|
width: 100% !important;
|
||||||
|
height:35px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#mobile-search-panel .pagefind-ui__search-input {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .pagefind-ui .pagefind-ui__search-input {
|
||||||
|
color: var(--color-gray-200) !important;
|
||||||
|
border-color: var(--color-gray-700) !important;
|
||||||
|
background-color: var(--color-gray-800) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagefind-ui .pagefind-ui__search-input::placeholder {
|
||||||
|
color: var(--color-gray-500) !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .pagefind-ui .pagefind-ui__search-input::placeholder {
|
||||||
|
color: var(--color-gray-400) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagefind-ui .pagefind-ui__search-input:hover {
|
||||||
|
border-color: var(--color-primary-300) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .pagefind-ui .pagefind-ui__search-input:hover {
|
||||||
|
border-color: var(--color-primary-600) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.pagefind-ui *:focus-visible {
|
||||||
|
outline:0 transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagefind-ui .pagefind-ui__search-input:focus {
|
||||||
|
background-color: white !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
border-width: 2px !important;
|
||||||
|
border-color: var(--color-primary-300) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .pagefind-ui .pagefind-ui__search-input:focus {
|
||||||
|
background-color: var(--color-gray-800) !important;
|
||||||
|
border-color: var(--color-primary-600) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 修复搜索图标位置 */
|
||||||
|
.pagefind-ui .pagefind-ui__form::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 50% !important;
|
||||||
|
transform: translateY(-50%) !important;
|
||||||
|
left: 12px !important;
|
||||||
|
z-index: 10;
|
||||||
|
background-color: var(--color-gray-400) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 搜索框清除按钮垂直居中 */
|
||||||
|
.pagefind-ui .pagefind-ui__search-clear {
|
||||||
|
transform: translateY(-20%);
|
||||||
|
background: none !important;
|
||||||
|
font-size: 0.8125rem !important;
|
||||||
|
color: var(--color-gray-400) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* 搜索结果抽屉 */
|
||||||
|
.pagefind-ui .pagefind-ui__drawer{
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
z-index: 50;
|
||||||
|
padding: 0.75rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 70vh;
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||||
|
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--color-gray-200);
|
||||||
|
background-color: var(--color-gray-50);
|
||||||
|
width: 100%; /* 确保抽屉宽度不超过容器 */
|
||||||
|
max-width: 100%; /* 限制最大宽度 */
|
||||||
|
overflow-x: hidden; /* 防止横向滚动 */
|
||||||
|
color:var(--color-gray-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .pagefind-ui .pagefind-ui__drawer{
|
||||||
|
border: 1px solid var(--color-gray-700);
|
||||||
|
background-color: var(--color-gray-800);
|
||||||
|
color:var(--color-gray-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagefind-ui .pagefind-ui__results-area {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 搜索结果消息文本 */
|
||||||
|
.pagefind-ui .pagefind-ui__message {
|
||||||
|
font-size: 0.55rem;
|
||||||
|
word-wrap: break-word; /* 允许长消息文本换行 */
|
||||||
|
padding-top: 0 !important;
|
||||||
|
padding-bottom: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 搜索结果内容 */
|
||||||
|
.pagefind-ui .pagefind-ui__drawer ol,
|
||||||
|
.pagefind-ui .pagefind-ui__drawer div,
|
||||||
|
.pagefind-ui .pagefind-ui__drawer p,
|
||||||
|
.pagefind-ui .pagefind-ui__drawer mark {
|
||||||
|
word-wrap: break-word;
|
||||||
|
min-width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagefind-ui .pagefind-ui__result {
|
||||||
|
padding: 2px 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagefind-ui .pagefind-ui__result-link{
|
||||||
|
color:var(--color-gray-800) !important;
|
||||||
|
font-size: 1rem; /* 等于 14px */
|
||||||
|
line-height: 1.5rem; /* 等于 20px */
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .pagefind-ui .pagefind-ui__result-link{
|
||||||
|
color:var(--color-gray-200) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagefind-ui .pagefind-ui__result-title{
|
||||||
|
color:var(--color-gray-600) !important;
|
||||||
|
font-size: 0.875rem; /* 等于 14px */
|
||||||
|
line-height: 1.25rem; /* 等于 20px */
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .pagefind-ui .pagefind-ui__result-title{
|
||||||
|
color:var(--color-gray-400) !important;
|
||||||
|
}
|
@ -1,137 +0,0 @@
|
|||||||
/* PrismJS 1.29.0
|
|
||||||
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript+bash+jsx+tsx+typescript */
|
|
||||||
/**
|
|
||||||
* prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
|
|
||||||
* Based on https://github.com/chriskempson/tomorrow-theme
|
|
||||||
* @author Rose Pritchard
|
|
||||||
*/
|
|
||||||
|
|
||||||
code[class*="language-"],
|
|
||||||
pre[class*="language-"] {
|
|
||||||
color: #abb2bf !important;
|
|
||||||
background: none;
|
|
||||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
|
||||||
font-size: 1em;
|
|
||||||
text-align: left;
|
|
||||||
white-space: pre;
|
|
||||||
word-spacing: normal;
|
|
||||||
word-break: normal;
|
|
||||||
word-wrap: normal;
|
|
||||||
line-height: 1.5;
|
|
||||||
border: 1px solid #374151;
|
|
||||||
|
|
||||||
-moz-tab-size: 4;
|
|
||||||
-o-tab-size: 4;
|
|
||||||
tab-size: 4;
|
|
||||||
|
|
||||||
-webkit-hyphens: none;
|
|
||||||
-moz-hyphens: none;
|
|
||||||
-ms-hyphens: none;
|
|
||||||
hyphens: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Code blocks */
|
|
||||||
pre[class*="language-"] {
|
|
||||||
padding: 1em;
|
|
||||||
margin: .5em 0;
|
|
||||||
overflow: auto;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
background: #282c34 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:not(pre) > code[class*="language-"],
|
|
||||||
pre[class*="language-"] {
|
|
||||||
background: #282c34 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inline code */
|
|
||||||
:not(pre) > code[class*="language-"] {
|
|
||||||
padding: .1em;
|
|
||||||
border-radius: .3em;
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.comment,
|
|
||||||
.token.block-comment,
|
|
||||||
.token.prolog,
|
|
||||||
.token.doctype,
|
|
||||||
.token.cdata {
|
|
||||||
color: #7f848e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.punctuation {
|
|
||||||
color: #abb2bf;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.tag,
|
|
||||||
.token.attr-name,
|
|
||||||
.token.namespace,
|
|
||||||
.token.deleted {
|
|
||||||
color: #e06c75;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.function-name {
|
|
||||||
color: #61afef;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.boolean,
|
|
||||||
.token.number,
|
|
||||||
.token.function {
|
|
||||||
color: #d19a66;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.property,
|
|
||||||
.token.class-name,
|
|
||||||
.token.constant,
|
|
||||||
.token.symbol {
|
|
||||||
color: #e5c07b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.selector,
|
|
||||||
.token.important,
|
|
||||||
.token.atrule,
|
|
||||||
.token.keyword,
|
|
||||||
.token.builtin {
|
|
||||||
color: #c678dd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.string,
|
|
||||||
.token.char,
|
|
||||||
.token.attr-value,
|
|
||||||
.token.regex,
|
|
||||||
.token.variable {
|
|
||||||
color: #98c379;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.operator,
|
|
||||||
.token.entity,
|
|
||||||
.token.url {
|
|
||||||
color: #56b6c2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.important,
|
|
||||||
.token.bold {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.token.italic {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.entity {
|
|
||||||
cursor: help;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.inserted {
|
|
||||||
color: green;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] code[class*="language-"],
|
|
||||||
[data-theme="dark"] pre[class*="language-"] {
|
|
||||||
color: #abb2bf !important;
|
|
||||||
background: #282c34 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] :not(pre) > code[class*="language-"],
|
|
||||||
[data-theme="dark"] pre[class*="language-"] {
|
|
||||||
background: #282c34 !important;
|
|
||||||
}
|
|
665
src/styles/table-styles.css
Normal file
665
src/styles/table-styles.css
Normal file
@ -0,0 +1,665 @@
|
|||||||
|
/* 表格基础样式 */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 1.5em 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
box-shadow: 0 4px 10px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -1px rgba(0, 0, 0, 0.04);
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格边框样式 */
|
||||||
|
table, th, td {
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表头样式 */
|
||||||
|
thead {
|
||||||
|
background-color: #f0f5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
padding: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-bottom: 2px solid #d6e0ff;
|
||||||
|
color: #3451db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格单元格样式 */
|
||||||
|
td {
|
||||||
|
padding: 0.875rem 1rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
color: #1e293b;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 内容对齐方式 */
|
||||||
|
th[align="center"],
|
||||||
|
td[align="center"] {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
th[align="right"],
|
||||||
|
td[align="right"] {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
th[align="left"],
|
||||||
|
td[align="left"] {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格行交替颜色 */
|
||||||
|
tbody tr:nth-child(even) {
|
||||||
|
background-color: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:nth-child(odd) {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 鼠标悬停效果 */
|
||||||
|
tbody tr:hover {
|
||||||
|
background-color: #f5f7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式表格 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
table {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 特定针对于内联样式的表格 */
|
||||||
|
table[style] th,
|
||||||
|
table[style] td {
|
||||||
|
border: 1px solid #e2e8f0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 处理嵌套样式的情况 */
|
||||||
|
.prose table {
|
||||||
|
border-collapse: collapse !important;
|
||||||
|
border-radius: 0.5rem !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose table th,
|
||||||
|
.prose table td {
|
||||||
|
border: 1px solid #e2e8f0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 勾号和叉号样式 - 通用方式 */
|
||||||
|
.tick, .yes, .true {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
color: #059669;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross, .no, .false {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
color: #dc2626;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加内容 */
|
||||||
|
.tick::before, .yes::before, .true::before {
|
||||||
|
content: "✓";
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross::before, .no::before, .false::before {
|
||||||
|
content: "✗";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 直接使用✓或✗的样式 */
|
||||||
|
.check-mark {
|
||||||
|
color: #059669;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross-mark {
|
||||||
|
color: #dc2626;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 状态标识符号样式 */
|
||||||
|
td:has(> :is(svg[aria-label="yes"], .yes, .tick, .true)) {
|
||||||
|
color: #047857;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:has(> :is(svg[aria-label="no"], .no, .cross, .false)) {
|
||||||
|
color: #b91c1c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 对表格中直接使用的✓和✗符号进行样式处理 */
|
||||||
|
td:only-child {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 检查整个表格的样式 */
|
||||||
|
table.feature-comparison td {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.feature-comparison td:first-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加全局样式识别 */
|
||||||
|
.text-success,
|
||||||
|
.text-green,
|
||||||
|
.success-mark {
|
||||||
|
color: #059669 !important;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-danger,
|
||||||
|
.text-red,
|
||||||
|
.danger-mark {
|
||||||
|
color: #dc2626 !important;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 直接针对表格中的✓和✗符号 - 移除不兼容的:contains选择器 */
|
||||||
|
/* 改用更通用的选择器和类 */
|
||||||
|
|
||||||
|
/* 针对行内样式的表格 */
|
||||||
|
table[style] td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 使用属性选择器 */
|
||||||
|
td[align="center"] {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 为常见的功能对比表格添加专用样式 */
|
||||||
|
.feature-table th,
|
||||||
|
.function-table th,
|
||||||
|
.comparison-table th {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-table th:first-child,
|
||||||
|
.function-table th:first-child,
|
||||||
|
.comparison-table th:first-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-table td,
|
||||||
|
.function-table td,
|
||||||
|
.comparison-table td {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-table td:first-child,
|
||||||
|
.function-table td:first-child,
|
||||||
|
.comparison-table td:first-child {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 直接样式化 ✓ 和 ✗ 字符 */
|
||||||
|
.checkmark, .check, .✓ {
|
||||||
|
color: #059669;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crossmark, .cross-symbol, .✗ {
|
||||||
|
color: #dc2626;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 暗色模式 - 只使用 [data-theme="dark"] 选择器 */
|
||||||
|
[data-theme="dark"] {
|
||||||
|
table {
|
||||||
|
box-shadow: 0 4px 12px -1px rgba(0, 0, 0, 0.3), 0 2px 6px -1px rgba(0, 0, 0, 0.2);
|
||||||
|
border-color: #475569;
|
||||||
|
background-color: #0f172a;
|
||||||
|
}
|
||||||
|
|
||||||
|
table, th, td {
|
||||||
|
border-color: #475569;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[style] th,
|
||||||
|
table[style] td,
|
||||||
|
.prose table th,
|
||||||
|
.prose table td {
|
||||||
|
border-color: #475569 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead {
|
||||||
|
background-color: #1a2e6a;
|
||||||
|
border-bottom: 2px solid #4b6bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
border-bottom-color: #4b6bff;
|
||||||
|
color: #adc2ff;
|
||||||
|
background-color: transparent;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th:not(:last-child) {
|
||||||
|
border-right-color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
color: #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:not(:last-child) {
|
||||||
|
border-right-color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:nth-child(even) {
|
||||||
|
background-color: #1e293b;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:nth-child(odd) {
|
||||||
|
background-color: #111827;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:hover {
|
||||||
|
background-color: #272f45;
|
||||||
|
box-shadow: inset 0 0 0 2px rgba(75, 107, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:last-child td {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格圆角样式在暗色模式下的处理 */
|
||||||
|
table {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 勾号和叉号颜色调整 */
|
||||||
|
.tick, .yes, .true, .check-mark {
|
||||||
|
color: #10b981;
|
||||||
|
text-shadow: 0 0 5px rgba(16, 185, 129, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross, .no, .false, .cross-mark {
|
||||||
|
color: #ef4444;
|
||||||
|
text-shadow: 0 0 5px rgba(239, 68, 68, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-success,
|
||||||
|
.text-green,
|
||||||
|
.success-mark {
|
||||||
|
color: #10b981 !important;
|
||||||
|
text-shadow: 0 0 5px rgba(16, 185, 129, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-danger,
|
||||||
|
.text-red,
|
||||||
|
.danger-mark {
|
||||||
|
color: #ef4444 !important;
|
||||||
|
text-shadow: 0 0 5px rgba(239, 68, 68, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkmark, .check, .✓ {
|
||||||
|
color: #10b981;
|
||||||
|
text-shadow: 0 0 5px rgba(16, 185, 129, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.crossmark, .cross-symbol, .✗ {
|
||||||
|
color: #ef4444;
|
||||||
|
text-shadow: 0 0 5px rgba(239, 68, 68, 0.3);
|
||||||
|
}
|
||||||
|
}/* 表格基础样式 */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 1.5em 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
box-shadow: 0 4px 10px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -1px rgba(0, 0, 0, 0.04);
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格边框样式 */
|
||||||
|
table, th, td {
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表头样式 */
|
||||||
|
thead {
|
||||||
|
background-color: #f0f5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
padding: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-bottom: 2px solid #d6e0ff;
|
||||||
|
color: #3451db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格单元格样式 */
|
||||||
|
td {
|
||||||
|
padding: 0.875rem 1rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
color: #1e293b;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 内容对齐方式 */
|
||||||
|
th[align="center"],
|
||||||
|
td[align="center"] {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
th[align="right"],
|
||||||
|
td[align="right"] {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
th[align="left"],
|
||||||
|
td[align="left"] {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格行交替颜色 */
|
||||||
|
tbody tr:nth-child(even) {
|
||||||
|
background-color: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:nth-child(odd) {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 鼠标悬停效果 */
|
||||||
|
tbody tr:hover {
|
||||||
|
background-color: #f5f7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式表格 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
table {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 特定针对于内联样式的表格 */
|
||||||
|
table[style] th,
|
||||||
|
table[style] td {
|
||||||
|
border: 1px solid #e2e8f0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 处理嵌套样式的情况 */
|
||||||
|
.prose table {
|
||||||
|
border-collapse: collapse !important;
|
||||||
|
border-radius: 0.5rem !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose table th,
|
||||||
|
.prose table td {
|
||||||
|
border: 1px solid #e2e8f0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 勾号和叉号样式 - 通用方式 */
|
||||||
|
.tick, .yes, .true {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
color: #059669;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross, .no, .false {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
color: #dc2626;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加内容 */
|
||||||
|
.tick::before, .yes::before, .true::before {
|
||||||
|
content: "✓";
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross::before, .no::before, .false::before {
|
||||||
|
content: "✗";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 直接使用✓或✗的样式 */
|
||||||
|
.check-mark {
|
||||||
|
color: #059669;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross-mark {
|
||||||
|
color: #dc2626;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 状态标识符号样式 */
|
||||||
|
td:has(> :is(svg[aria-label="yes"], .yes, .tick, .true)) {
|
||||||
|
color: #047857;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:has(> :is(svg[aria-label="no"], .no, .cross, .false)) {
|
||||||
|
color: #b91c1c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 对表格中直接使用的✓和✗符号进行样式处理 */
|
||||||
|
td:only-child {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 检查整个表格的样式 */
|
||||||
|
table.feature-comparison td {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.feature-comparison td:first-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加全局样式识别 */
|
||||||
|
.text-success,
|
||||||
|
.text-green,
|
||||||
|
.success-mark {
|
||||||
|
color: #059669 !important;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-danger,
|
||||||
|
.text-red,
|
||||||
|
.danger-mark {
|
||||||
|
color: #dc2626 !important;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 直接针对表格中的✓和✗符号 - 移除不兼容的:contains选择器 */
|
||||||
|
/* 改用更通用的选择器和类 */
|
||||||
|
|
||||||
|
/* 针对行内样式的表格 */
|
||||||
|
table[style] td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 使用属性选择器 */
|
||||||
|
td[align="center"] {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 为常见的功能对比表格添加专用样式 */
|
||||||
|
.feature-table th,
|
||||||
|
.function-table th,
|
||||||
|
.comparison-table th {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-table th:first-child,
|
||||||
|
.function-table th:first-child,
|
||||||
|
.comparison-table th:first-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-table td,
|
||||||
|
.function-table td,
|
||||||
|
.comparison-table td {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-table td:first-child,
|
||||||
|
.function-table td:first-child,
|
||||||
|
.comparison-table td:first-child {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 直接样式化 ✓ 和 ✗ 字符 */
|
||||||
|
.checkmark, .check, .✓ {
|
||||||
|
color: #059669;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crossmark, .cross-symbol, .✗ {
|
||||||
|
color: #dc2626;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 暗色模式 - 只使用 [data-theme="dark"] 选择器 */
|
||||||
|
[data-theme="dark"] {
|
||||||
|
table {
|
||||||
|
box-shadow: 0 4px 12px -1px rgba(0, 0, 0, 0.3), 0 2px 6px -1px rgba(0, 0, 0, 0.2);
|
||||||
|
border-color: #475569;
|
||||||
|
background-color: #0f172a;
|
||||||
|
}
|
||||||
|
|
||||||
|
table, th, td {
|
||||||
|
border-color: #475569;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[style] th,
|
||||||
|
table[style] td,
|
||||||
|
.prose table th,
|
||||||
|
.prose table td {
|
||||||
|
border-color: #475569 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead {
|
||||||
|
background-color: #1e293b;
|
||||||
|
border-bottom: 2px solid #4b6bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
border-bottom-color: #4b6bff;
|
||||||
|
color: #adc2ff;
|
||||||
|
background-color: transparent;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th:not(:last-child) {
|
||||||
|
border-right-color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
color: #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:not(:last-child) {
|
||||||
|
border-right-color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:nth-child(even) {
|
||||||
|
background-color: #1e293b;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:nth-child(odd) {
|
||||||
|
background-color: #111827;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:hover {
|
||||||
|
background-color: #272f45;
|
||||||
|
box-shadow: inset 0 0 0 2px rgba(75, 107, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:last-child td {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格圆角样式在暗色模式下的处理 */
|
||||||
|
table {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 勾号和叉号颜色调整 */
|
||||||
|
.tick, .yes, .true, .check-mark {
|
||||||
|
color: #10b981;
|
||||||
|
text-shadow: 0 0 5px rgba(16, 185, 129, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross, .no, .false, .cross-mark {
|
||||||
|
color: #ef4444;
|
||||||
|
text-shadow: 0 0 5px rgba(239, 68, 68, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-success,
|
||||||
|
.text-green,
|
||||||
|
.success-mark {
|
||||||
|
color: #10b981 !important;
|
||||||
|
text-shadow: 0 0 5px rgba(16, 185, 129, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-danger,
|
||||||
|
.text-red,
|
||||||
|
.danger-mark {
|
||||||
|
color: #ef4444 !important;
|
||||||
|
text-shadow: 0 0 5px rgba(239, 68, 68, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkmark, .check, .✓ {
|
||||||
|
color: #10b981;
|
||||||
|
text-shadow: 0 0 5px rgba(16, 185, 129, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.crossmark, .cross-symbol, .✗ {
|
||||||
|
color: #ef4444;
|
||||||
|
text-shadow: 0 0 5px rgba(239, 68, 68, 0.3);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user