将代码高亮改为astro集成配置,mermaid改为客户端生成,优化导航栏样式,将地图数据动态导入

This commit is contained in:
lsy 2025-05-03 19:50:03 +08:00
parent af0dee8d1d
commit d3e0eddff7
19 changed files with 1266 additions and 2852 deletions

View File

@ -14,7 +14,6 @@ import { SITE_URL } from "./src/consts";
import compressor from "astro-compressor"; import compressor from "astro-compressor";
import vercel from "@astrojs/vercel"; import vercel from "@astrojs/vercel";
import { articleIndexerIntegration } from "./src/plugins/build-article-index.js"; import { articleIndexerIntegration } from "./src/plugins/build-article-index.js";
import customCodeBlocksIntegration from "./src/plugins/custom-code-blocks.js";
function getArticleDate(articleId) { function getArticleDate(articleId) {
try { try {
@ -52,21 +51,15 @@ export default defineConfig({
}, },
integrations: [ integrations: [
// 使用我们自己的代码块集成替代expressiveCode // 使用Astro官方的MDX支持
customCodeBlocksIntegration(),
// MDX 集成配置
mdx(), mdx(),
swup({ swup({
cache: true, cache: true,
preload: true, preload: true,
} }),
),
react(), react(),
// 使用我们自己的文章索引生成器替换pagefind // 使用文章索引生成器
articleIndexerIntegration(), articleIndexerIntegration(),
sitemap({ sitemap({
filter: (page) => !page.includes("/api/"), filter: (page) => !page.includes("/api/"),
serialize(item) { serialize(item) {
@ -109,9 +102,24 @@ export default defineConfig({
compressor() compressor()
], ],
// Markdown 配置 // Markdown 配置 - 使用官方语法高亮
markdown: { markdown: {
syntaxHighlight: false, // 禁用默认的语法高亮,使用我们自定义的高亮 // 配置语法高亮
syntaxHighlight: {
// 使用shiki作为高亮器
type: 'shiki',
// 排除mermaid语言不进行高亮处理
excludeLangs: ['mermaid']
},
// Shiki主题配置
shikiConfig: {
theme: 'github-light',
themes: {
light: 'github-light',
dark: 'github-dark'
},
wrap: true
},
remarkPlugins: [ remarkPlugins: [
[remarkEmoji, { emoticon: false, padded: true }] [remarkEmoji, { emoticon: false, padded: true }]
], ],
@ -119,13 +127,6 @@ export default defineConfig({
[rehypeExternalLinks, { target: '_blank', rel: ['nofollow', 'noopener', 'noreferrer'] }] [rehypeExternalLinks, { target: '_blank', rel: ['nofollow', 'noopener', 'noreferrer'] }]
], ],
gfm: true, gfm: true,
// 设置 remark-rehype 选项以控制HTML处理
remarkRehype: {
// 保留原始HTML格式但仅在非代码块区域
allowDangerousHtml: true,
// 确保代码块内容不被解析
passThrough: ['code']
},
}, },
adapter: vercel(), adapter: vercel(),

11
package-lock.json generated
View File

@ -25,7 +25,7 @@
"astro": "^5.7.5", "astro": "^5.7.5",
"astro-expressive-code": "^0.41.2", "astro-expressive-code": "^0.41.2",
"cheerio": "^1.0.0", "cheerio": "^1.0.0",
"highlight.js": "^11.11.1", "mermaid": "^11.6.0",
"node-fetch": "^3.3.2", "node-fetch": "^3.3.2",
"octokit": "^4.1.3", "octokit": "^4.1.3",
"puppeteer": "^23.11.1", "puppeteer": "^23.11.1",
@ -9955,15 +9955,6 @@
"url": "https://opencollective.com/unified" "url": "https://opencollective.com/unified"
} }
}, },
"node_modules/highlight.js": {
"version": "11.11.1",
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz",
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/html-escaper": { "node_modules/html-escaper": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmmirror.com/html-escaper/-/html-escaper-3.0.3.tgz", "resolved": "https://registry.npmmirror.com/html-escaper/-/html-escaper-3.0.3.tgz",

View File

@ -26,7 +26,7 @@
"astro": "^5.7.5", "astro": "^5.7.5",
"astro-expressive-code": "^0.41.2", "astro-expressive-code": "^0.41.2",
"cheerio": "^1.0.0", "cheerio": "^1.0.0",
"highlight.js": "^11.11.1", "mermaid": "^11.6.0",
"node-fetch": "^3.3.2", "node-fetch": "^3.3.2",
"octokit": "^4.1.3", "octokit": "^4.1.3",
"puppeteer": "^23.11.1", "puppeteer": "^23.11.1",

View File

@ -29,7 +29,7 @@ const breadcrumbs: Breadcrumb[] = pathSegments
}); });
--- ---
<div class="flex items-center justify-between w-full flex-wrap sm:flex-nowrap" transition:persist transition:name="breadcrumb"> <div class="flex items-center justify-between w-full flex-wrap sm:flex-nowrap">
<div class="flex items-center text-sm overflow-hidden"> <div class="flex items-center text-sm overflow-hidden">
<!-- 文章列表链接 - 根据当前页面类型决定链接 --> <!-- 文章列表链接 - 根据当前页面类型决定链接 -->
<a href={'/articles/'} class="text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 flex items-center flex-shrink-0"> <a href={'/articles/'} class="text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 flex items-center flex-shrink-0">

View File

@ -57,7 +57,7 @@ let secondaryHighlightStyle = activeSubItem ? "opacity: 1;" : "opacity: 0;";
const activeClass = "font-medium"; const activeClass = "font-medium";
const itemClass = "px-4 py-2 text-sm font-medium"; const itemClass = "px-4 py-2 text-sm font-medium";
const primaryHighlightClass = "bg-primary-100 dark:bg-primary-800/30 rounded-xl shadow-md backdrop-blur-sm"; const primaryHighlightClass = "bg-primary-100 dark:bg-primary-800/30 rounded-xl shadow-md backdrop-blur-sm";
const secondaryHighlightClass = "bg-primary-300/80 dark:bg-primary-700/60 rounded-md shadow-md backdrop-blur-sm"; const secondaryHighlightClass = "bg-primary-300/80 dark:bg-primary-700/60 rounded-xl shadow-md backdrop-blur-sm";
const transitionDuration = 300; const transitionDuration = 300;
const navSelectorClassName = "mr-4"; const navSelectorClassName = "mr-4";
--- ---
@ -98,7 +98,7 @@ const navSelectorClassName = "mr-4";
class={`absolute z-0 hover:shadow-lg rounded-xl ${primaryHighlightClass}`} class={`absolute z-0 hover:shadow-lg rounded-xl ${primaryHighlightClass}`}
style={primaryHighlightStyle}></div> style={primaryHighlightStyle}></div>
<div id="nav-secondary-highlight" <div id="nav-secondary-highlight"
class={`absolute z-10 hover:shadow-lg rounded-md ${secondaryHighlightClass}`} class={`absolute z-10 hover:shadow-lg rounded-xl ${secondaryHighlightClass}`}
style={secondaryHighlightStyle}></div> style={secondaryHighlightStyle}></div>
<!-- 导航菜单项 --> <!-- 导航菜单项 -->
@ -130,7 +130,7 @@ const navSelectorClassName = "mr-4";
<!-- 二级菜单容器 --> <!-- 二级菜单容器 -->
<div class={`nav-group-items relative min-w-full ${activeGroupId === item.id ? 'menu-visible' : 'hidden'}`}> <div class={`nav-group-items relative min-w-full ${activeGroupId === item.id ? 'menu-visible' : 'hidden'}`}>
<div class="flex flex-row flex-wrap gap-2 justify-center items-center"> <div class="flex flex-row flex-wrap justify-center items-center">
{item.items.map((subItem) => ( {item.items.map((subItem) => (
<a <a
href={subItem.href} href={subItem.href}

View File

@ -165,8 +165,6 @@ const {
// 立即设置文档主题在DOM渲染前应用避免闪烁 // 立即设置文档主题在DOM渲染前应用避免闪烁
document.documentElement.dataset.theme = theme; document.documentElement.dataset.theme = theme;
// 确保同步classList提高兼容性
document.documentElement.classList.toggle('dark', theme === 'dark');
// 监听系统主题变化(只有当主题设为跟随系统时才响应) // 监听系统主题变化(只有当主题设为跟随系统时才响应)
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
@ -176,7 +174,6 @@ const {
if (!localStorage.getItem("theme")) { if (!localStorage.getItem("theme")) {
const newTheme = e.matches ? "dark" : "light"; const newTheme = e.matches ? "dark" : "light";
document.documentElement.dataset.theme = newTheme; document.documentElement.dataset.theme = newTheme;
document.documentElement.classList.toggle('dark', e.matches);
} }
}; };
@ -191,11 +188,9 @@ const {
// 重新初始化主题 // 重新初始化主题
if (storedTheme) { if (storedTheme) {
document.documentElement.dataset.theme = storedTheme; document.documentElement.dataset.theme = storedTheme;
document.documentElement.classList.toggle('dark', storedTheme === 'dark');
} else { } else {
const systemTheme = getSystemTheme(); const systemTheme = getSystemTheme();
document.documentElement.dataset.theme = systemTheme; document.documentElement.dataset.theme = systemTheme;
document.documentElement.classList.toggle('dark', systemTheme === 'dark');
} }
} }
@ -206,7 +201,6 @@ const {
} catch (error) { } catch (error) {
// 出错时应用默认浅色主题,确保页面正常显示 // 出错时应用默认浅色主题,确保页面正常显示
document.documentElement.dataset.theme = "light"; document.documentElement.dataset.theme = "light";
document.documentElement.classList.remove('dark');
} }
})(); })();
</script> </script>

View File

@ -454,7 +454,7 @@ const {
callback(); callback();
// 确保DOM已更新 // 确保DOM已更新
document.documentElement.classList.toggle('dark', toTheme === 'dark'); document.documentElement.dataset.theme = toTheme;
}); });
// 生成动画需要的SVG资源 // 生成动画需要的SVG资源
@ -632,10 +632,6 @@ const {
} else { } else {
document.documentElement.dataset.theme = "light"; document.documentElement.dataset.theme = "light";
} }
// 确保同步类名
document.documentElement.classList.toggle('dark',
document.documentElement.dataset.theme === 'dark');
}; };
// 切换主题 // 切换主题
@ -714,7 +710,6 @@ const {
if (!localStorage.getItem("theme")) { if (!localStorage.getItem("theme")) {
const newTheme = e.matches ? "dark" : "light"; const newTheme = e.matches ? "dark" : "light";
document.documentElement.dataset.theme = newTheme; document.documentElement.dataset.theme = newTheme;
document.documentElement.classList.toggle('dark', e.matches);
} }
}; };

View File

@ -1,7 +1,29 @@
import React, { useEffect, useRef, useState } from "react"; import React, { useEffect, useRef, useState } from "react";
import * as THREE from "three"; import {
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; Scene,
import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer.js"; PerspectiveCamera,
WebGLRenderer,
SphereGeometry,
MeshBasicMaterial,
Mesh,
AmbientLight,
DirectionalLight,
Vector2,
Vector3,
Raycaster,
Group,
BufferGeometry,
LineBasicMaterial,
Line,
FrontSide
} from "three";
import type { Side } from "three";
// 需要懒加载的模块
const loadControlsAndRenderers = () => Promise.all([
import("three/examples/jsm/controls/OrbitControls.js"),
import("three/examples/jsm/renderers/CSS2DRenderer.js")
]);
// WASM模块接口 // WASM模块接口
interface GeoWasmModule { interface GeoWasmModule {
@ -45,25 +67,26 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
const [mapLoading, setMapLoading] = useState(true); const [mapLoading, setMapLoading] = useState(true);
const [mapError, setMapError] = useState<string | null>(null); const [mapError, setMapError] = useState<string | null>(null);
// 修改场景引用类型以适应新的导入方式
const sceneRef = useRef<{ const sceneRef = useRef<{
scene: THREE.Scene; scene: Scene;
camera: THREE.PerspectiveCamera; camera: PerspectiveCamera;
renderer: THREE.WebGLRenderer; renderer: WebGLRenderer;
labelRenderer: CSS2DRenderer; labelRenderer: any; // 后面会动态设置具体类型
controls: OrbitControls; controls: any; // 后面会动态设置具体类型
earth: THREE.Mesh; earth: Mesh;
countries: Map<string, THREE.Object3D>; countries: Map<string, Group>;
raycaster: THREE.Raycaster; raycaster: Raycaster;
mouse: THREE.Vector2; mouse: Vector2;
animationId: number | null; animationId: number | null;
lastCameraPosition: THREE.Vector3 | null; lastCameraPosition: Vector3 | null;
lastMouseEvent: MouseEvent | null; lastMouseEvent: MouseEvent | null;
lastClickedCountry: string | null; lastClickedCountry: string | null;
lastMouseX: number | null; lastMouseX: number | null;
lastMouseY: number | null; lastMouseY: number | null;
lastHoverTime: number | null; lastHoverTime: number | null;
lineToCountryMap: Map<THREE.Line, string>; lineToCountryMap: Map<Line, string>;
allLineObjects: THREE.Line[]; allLineObjects: Line[];
} | null>(null); } | null>(null);
// 监听主题变化 // 监听主题变化
@ -104,22 +127,29 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
}; };
}, []); }, []);
// 动态加载地图数据 // 从公共目录加载地图数据
useEffect(() => { useEffect(() => {
const loadMapData = async () => { const loadMapData = async () => {
try { try {
setMapLoading(true); setMapLoading(true);
setMapError(null); setMapError(null);
// 并行加载两个地图数据 // 从公共目录加载地图数据
const [worldDataModule, chinaDataModule] = await Promise.all([ const [worldDataResponse, chinaDataResponse] = await Promise.all([
import("@/assets/map/world.zh.json"), fetch('/maps/world.zh.json'),
import("@/assets/map/china.json") fetch('/maps/china.json')
]); ]);
if (!worldDataResponse.ok || !chinaDataResponse.ok) {
throw new Error('无法获取地图数据');
}
const worldData = await worldDataResponse.json();
const chinaData = await chinaDataResponse.json();
setMapData({ setMapData({
worldData: worldDataModule.default || worldDataModule, worldData,
chinaData: chinaDataModule.default || chinaDataModule chinaData
}); });
setMapLoading(false); setMapLoading(false);
@ -196,6 +226,19 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
containerRef.current.innerHTML = ""; containerRef.current.innerHTML = "";
} }
// 创建一个引用,用于清理函数
let mounted = true;
let cleanupFunctions: Array<() => void> = [];
// 动态加载Three.js控制器和渲染器
const initThreeScene = async () => {
try {
// 加载OrbitControls和CSS2DRenderer
const [{ OrbitControls }, { CSS2DRenderer }] = await loadControlsAndRenderers();
// 如果组件已卸载,退出初始化
if (!mounted || !containerRef.current) return;
// 检查当前是否为暗色模式 // 检查当前是否为暗色模式
const isDarkMode = const isDarkMode =
document.documentElement.classList.contains("dark") || document.documentElement.classList.contains("dark") ||
@ -217,16 +260,16 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
const colors = getColors(); const colors = getColors();
// 创建场景 // 创建场景
const scene = new THREE.Scene(); const scene = new Scene();
scene.background = null; scene.background = null;
// 创建材质的辅助函数 // 创建材质的辅助函数
const createMaterial = ( const createMaterial = (
color: string, color: string,
side: THREE.Side = THREE.FrontSide, side: Side = FrontSide,
opacity: number = 1.0, opacity: number = 1.0,
) => { ) => {
return new THREE.MeshBasicMaterial({ return new MeshBasicMaterial({
color: color, color: color,
side: side, side: side,
transparent: true, transparent: true,
@ -235,24 +278,24 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
}; };
// 创建地球几何体 // 创建地球几何体
const earthGeometry = new THREE.SphereGeometry(2.0, 64, 64); const earthGeometry = new SphereGeometry(2.0, 64, 64);
const earthMaterial = createMaterial( const earthMaterial = createMaterial(
colors.earthBase, colors.earthBase,
THREE.FrontSide, FrontSide,
isDarkMode ? 0.9 : 0.9, // 调整明亮模式下的不透明度 isDarkMode ? 0.9 : 0.9, // 调整明亮模式下的不透明度
); );
const earth = new THREE.Mesh(earthGeometry, earthMaterial); const earth = new Mesh(earthGeometry, earthMaterial);
earth.renderOrder = 1; earth.renderOrder = 1;
scene.add(earth); scene.add(earth);
// 添加光源 // 添加光源
const ambientLight = new THREE.AmbientLight( const ambientLight = new AmbientLight(
0xffffff, 0xffffff,
isDarkMode ? 0.7 : 0.85, // 微调明亮模式下的光照强度 isDarkMode ? 0.7 : 0.85, // 微调明亮模式下的光照强度
); );
scene.add(ambientLight); scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight( const directionalLight = new DirectionalLight(
isDarkMode ? 0xeeeeff : 0xffffff, // 恢复明亮模式下的纯白光源 isDarkMode ? 0xeeeeff : 0xffffff, // 恢复明亮模式下的纯白光源
isDarkMode ? 0.6 : 0.65, // 微调明亮模式下的定向光强度 isDarkMode ? 0.6 : 0.65, // 微调明亮模式下的定向光强度
); );
@ -260,7 +303,7 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
scene.add(directionalLight); scene.add(directionalLight);
// 创建相机 // 创建相机
const camera = new THREE.PerspectiveCamera( const camera = new PerspectiveCamera(
45, 45,
containerRef.current.clientWidth / containerRef.current.clientHeight, containerRef.current.clientWidth / containerRef.current.clientHeight,
0.1, 0.1,
@ -269,7 +312,7 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
camera.position.z = 8; camera.position.z = 8;
// 创建渲染器 // 创建渲染器
const renderer = new THREE.WebGLRenderer({ const renderer = new WebGLRenderer({
antialias: true, antialias: true,
alpha: true, alpha: true,
logarithmicDepthBuffer: true, logarithmicDepthBuffer: true,
@ -317,15 +360,15 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
}); });
// 保存所有线条对象的引用,用于快速检测 // 保存所有线条对象的引用,用于快速检测
const allLineObjects: THREE.Line[] = []; const allLineObjects: Line[] = [];
const lineToCountryMap = new Map<THREE.Line, string>(); const lineToCountryMap = new Map<Line, string>();
// 创建国家边界组 // 创建国家边界组
const countryGroup = new THREE.Group(); const countryGroup = new Group();
earth.add(countryGroup); earth.add(countryGroup);
// 创建国家边界 // 创建国家边界
const countries = new Map<string, THREE.Object3D>(); const countries = new Map<string, Group>();
// 从WASM获取边界线数据 // 从WASM获取边界线数据
const boundaryLines = geoProcessor.get_boundary_lines(); const boundaryLines = geoProcessor.get_boundary_lines();
@ -337,15 +380,15 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
const { points, region_name, is_visited } = boundaryLine; const { points, region_name, is_visited } = boundaryLine;
// 创建区域组 // 创建区域组
const regionObject = new THREE.Group(); const regionObject = new Group();
regionObject.userData = { name: region_name, isVisited: is_visited }; regionObject.userData = { name: region_name, isVisited: is_visited };
// 转换点数组为THREE.Vector3数组 // 转换点数组为Vector3数组
const threePoints = points.map((p: { x: number; y: number; z: number }) => new THREE.Vector3(p.x, p.y, p.z)); const threePoints = points.map((p: { x: number; y: number; z: number }) => new Vector3(p.x, p.y, p.z));
// 创建边界线 // 创建边界线
if (threePoints.length > 1) { if (threePoints.length > 1) {
const lineGeometry = new THREE.BufferGeometry().setFromPoints(threePoints); const lineGeometry = new BufferGeometry().setFromPoints(threePoints);
// 确定线条颜色 // 确定线条颜色
const isChina = region_name === "中国" || region_name.startsWith("中国-"); const isChina = region_name === "中国" || region_name.startsWith("中国-");
@ -362,14 +405,14 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
borderColor = colors.border; borderColor = colors.border;
} }
const lineMaterial = new THREE.LineBasicMaterial({ const lineMaterial = new LineBasicMaterial({
color: borderColor, color: borderColor,
linewidth: is_visited ? 1.8 : 1.2, // 微调线条宽度,保持已访问区域更明显 linewidth: is_visited ? 1.8 : 1.2, // 微调线条宽度,保持已访问区域更明显
transparent: true, transparent: true,
opacity: is_visited ? 0.95 : 0.85, // 调整不透明度,使边界明显但不突兀 opacity: is_visited ? 0.95 : 0.85, // 调整不透明度,使边界明显但不突兀
}); });
const line = new THREE.Line(lineGeometry, lineMaterial); const line = new Line(lineGeometry, lineMaterial);
line.userData = { line.userData = {
name: region_name, name: region_name,
isVisited: is_visited, isVisited: is_visited,
@ -402,10 +445,10 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
let fixedPosition; let fixedPosition;
if (isSmallScreen) { if (isSmallScreen) {
// 小屏幕显示距离更远,以便看到更多地球 // 小屏幕显示距离更远,以便看到更多地球
fixedPosition = new THREE.Vector3(-2.1, 3.41, -8.0); fixedPosition = new Vector3(-2.1, 3.41, -8.0);
} else { } else {
// 大屏幕使用原来的位置 // 大屏幕使用原来的位置
fixedPosition = new THREE.Vector3(-2.1, 3.41, -6.5); fixedPosition = new Vector3(-2.1, 3.41, -6.5);
} }
// 应用位置 // 应用位置
@ -425,8 +468,8 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
positionCameraToFaceChina(); positionCameraToFaceChina();
// 创建射线投射器用于鼠标交互 // 创建射线投射器用于鼠标交互
const raycaster = new THREE.Raycaster(); const raycaster = new Raycaster();
const mouse = new THREE.Vector2(); const mouse = new Vector2();
// 添加节流函数,限制鼠标移动事件的触发频率 // 添加节流函数,限制鼠标移动事件的触发频率
const throttle = (func: Function, limit: number) => { const throttle = (func: Function, limit: number) => {
@ -474,7 +517,7 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
// 重置所有线条颜色 // 重置所有线条颜色
allLineObjects.forEach((line) => { allLineObjects.forEach((line) => {
if (line.material instanceof THREE.LineBasicMaterial) { if (line.material instanceof LineBasicMaterial) {
line.material.color.set(line.userData.originalColor); line.material.color.set(line.userData.originalColor);
} }
}); });
@ -485,7 +528,7 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
allLineObjects.forEach((line) => { allLineObjects.forEach((line) => {
if ( if (
lineToCountryMap.get(line) === result.countryName && lineToCountryMap.get(line) === result.countryName &&
line.material instanceof THREE.LineBasicMaterial line.material instanceof LineBasicMaterial
) { ) {
line.material.color.set(line.userData.highlightColor); line.material.color.set(line.userData.highlightColor);
} }
@ -515,7 +558,7 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
const clearSelection = () => { const clearSelection = () => {
// 恢复所有线条的原始颜色 // 恢复所有线条的原始颜色
allLineObjects.forEach((line) => { allLineObjects.forEach((line) => {
if (line.material instanceof THREE.LineBasicMaterial) { if (line.material instanceof LineBasicMaterial) {
line.material.color.set(line.userData.originalColor); line.material.color.set(line.userData.originalColor);
} }
}); });
@ -547,7 +590,7 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
if (result && result.countryName) { if (result && result.countryName) {
// 重置所有线条颜色 // 重置所有线条颜色
allLineObjects.forEach((line) => { allLineObjects.forEach((line) => {
if (line.material instanceof THREE.LineBasicMaterial) { if (line.material instanceof LineBasicMaterial) {
line.material.color.set(line.userData.originalColor); line.material.color.set(line.userData.originalColor);
} }
}); });
@ -556,7 +599,7 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
allLineObjects.forEach((line) => { allLineObjects.forEach((line) => {
if ( if (
lineToCountryMap.get(line) === result.countryName && lineToCountryMap.get(line) === result.countryName &&
line.material instanceof THREE.LineBasicMaterial line.material instanceof LineBasicMaterial
) { ) {
line.material.color.set(line.userData.highlightColor); line.material.color.set(line.userData.highlightColor);
} }
@ -589,21 +632,100 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
containerRef.current.addEventListener("click", onClick, { passive: false }); containerRef.current.addEventListener("click", onClick, { passive: false });
containerRef.current.addEventListener("dblclick", onDoubleClick, { passive: false }); containerRef.current.addEventListener("dblclick", onDoubleClick, { passive: false });
// 保存清理函数
cleanupFunctions.push(() => {
if (containerRef.current) {
containerRef.current.removeEventListener("mousemove", onMouseMove);
containerRef.current.removeEventListener("click", onClick);
containerRef.current.removeEventListener("dblclick", onDoubleClick);
}
});
// 获取球面上的点对应的国家/地区
const getPointOnSphere = (
mouseX: number,
mouseY: number,
camera: PerspectiveCamera,
radius: number,
): { point: Vector3, countryName: string | null } | null => {
// 计算鼠标在画布中的归一化坐标
const rect = containerRef.current!.getBoundingClientRect();
const x = ((mouseX - rect.left) / rect.width) * 2 - 1;
const y = -((mouseY - rect.top) / rect.height) * 2 + 1;
// 创建射线
const ray = new Raycaster();
ray.setFromCamera(new Vector2(x, y), camera);
// 检测射线与实际地球模型的相交
const earthIntersects = ray.intersectObject(earth, false);
if (earthIntersects.length > 0) {
const point = earthIntersects[0].point;
// 使用WASM查找最近的国家/地区
const countryName = geoProcessor.find_nearest_country(
point.x, point.y, point.z, radius
);
return { point, countryName };
}
// 如果没有直接相交,使用球体辅助检测
const sphereGeom = new SphereGeometry(radius, 32, 32);
const sphereMesh = new Mesh(sphereGeom);
const intersects = ray.intersectObject(sphereMesh);
if (intersects.length > 0) {
const point = intersects[0].point;
// 使用WASM查找最近的国家/地区
const countryName = geoProcessor.find_nearest_country(
point.x, point.y, point.z, radius
);
return { point, countryName };
}
return null;
};
// 简化的动画循环函数 // 简化的动画循环函数
const animate = () => { const animate = () => {
if (!sceneRef.current) return; if (!sceneRef.current) return;
// 更新控制器 // 更新控制器
sceneRef.current.controls.update(); controls.update();
// 渲染 // 渲染
sceneRef.current.renderer.render(scene, camera); renderer.render(scene, camera);
sceneRef.current.labelRenderer.render(scene, camera); labelRenderer.render(scene, camera);
// 请求下一帧 // 请求下一帧
sceneRef.current.animationId = requestAnimationFrame(animate); sceneRef.current.animationId = requestAnimationFrame(animate);
}; };
// 处理窗口大小变化
const handleResize = () => {
if (!containerRef.current || !sceneRef.current) return;
const width = containerRef.current.clientWidth;
const height = containerRef.current.clientHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
labelRenderer.setSize(width, height);
// 立即渲染一次
renderer.render(scene, camera);
labelRenderer.render(scene, camera);
};
window.addEventListener("resize", handleResize, { passive: true });
cleanupFunctions.push(() => {
window.removeEventListener("resize", handleResize);
});
// 保存场景引用 // 保存场景引用
sceneRef.current = { sceneRef.current = {
scene, scene,
@ -629,76 +751,21 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
// 开始动画 // 开始动画
sceneRef.current.animationId = requestAnimationFrame(animate); sceneRef.current.animationId = requestAnimationFrame(animate);
// 获取球面上的点对应的国家/地区 } catch (error) {
const getPointOnSphere = ( console.error("Three.js初始化失败:", error);
mouseX: number,
mouseY: number,
camera: THREE.Camera,
radius: number,
): { point: THREE.Vector3, countryName: string | null } | null => {
// 计算鼠标在画布中的归一化坐标
const rect = containerRef.current!.getBoundingClientRect();
const x = ((mouseX - rect.left) / rect.width) * 2 - 1;
const y = -((mouseY - rect.top) / rect.height) * 2 + 1;
// 创建射线
const ray = new THREE.Raycaster();
ray.setFromCamera(new THREE.Vector2(x, y), camera);
// 检测射线与实际地球模型的相交
const earthIntersects = ray.intersectObject(earth, false);
if (earthIntersects.length > 0) {
const point = earthIntersects[0].point;
// 使用WASM查找最近的国家/地区
const countryName = geoProcessor.find_nearest_country(
point.x, point.y, point.z, radius
);
return { point, countryName };
} }
// 如果没有直接相交,使用球体辅助检测
const sphereGeom = new THREE.SphereGeometry(radius, 32, 32);
const sphereMesh = new THREE.Mesh(sphereGeom);
const intersects = ray.intersectObject(sphereMesh);
if (intersects.length > 0) {
const point = intersects[0].point;
// 使用WASM查找最近的国家/地区
const countryName = geoProcessor.find_nearest_country(
point.x, point.y, point.z, radius
);
return { point, countryName };
}
return null;
}; };
// 处理窗口大小变化 // 执行初始化
const handleResize = () => { initThreeScene();
if (!containerRef.current || !sceneRef.current) return;
const { camera, renderer, labelRenderer } = sceneRef.current;
const width = containerRef.current.clientWidth;
const height = containerRef.current.clientHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
labelRenderer.setSize(width, height);
// 立即渲染一次
renderer.render(sceneRef.current.scene, camera);
labelRenderer.render(sceneRef.current.scene, camera);
};
window.addEventListener("resize", handleResize, { passive: true });
// 清理函数 // 清理函数
return () => { return () => {
mounted = false;
// 执行所有保存的清理函数
cleanupFunctions.forEach(fn => fn());
// 清理资源和事件监听器 // 清理资源和事件监听器
if (sceneRef.current) { if (sceneRef.current) {
// 取消动画帧 // 取消动画帧
@ -721,16 +788,6 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
sceneRef.current.controls.dispose(); sceneRef.current.controls.dispose();
} }
} }
// 移除事件监听器
if (containerRef.current) {
containerRef.current.removeEventListener("mousemove", onMouseMove);
containerRef.current.removeEventListener("click", onClick);
containerRef.current.removeEventListener("dblclick", onDoubleClick);
}
// 移除窗口事件监听器
window.removeEventListener("resize", handleResize);
}; };
}, [visitedPlaces, theme, wasmReady, geoProcessor]); // 添加geoProcessor依赖 }, [visitedPlaces, theme, wasmReady, geoProcessor]); // 添加geoProcessor依赖

View File

@ -14,7 +14,10 @@ tags: []
3. 如果变量没有使用可以前置下划线,消除警告 3. 如果变量没有使用可以前置下划线,消除警告
4. 强制类型转换 4. 强制类型转换
```rust
let a = 3.1;let b = a as i32 let a = 3.1;let b = a as i32
```
5. 打印变量 5. 打印变量
```rust ```rust

View File

@ -4,7 +4,7 @@ import { getSpecialPath } from "@/content.config";
import Layout from "@/components/Layout.astro"; import Layout from "@/components/Layout.astro";
import Breadcrumb from "@/components/Breadcrumb.astro"; import Breadcrumb from "@/components/Breadcrumb.astro";
import { ARTICLE_EXPIRY_CONFIG } from "@/consts"; import { ARTICLE_EXPIRY_CONFIG } from "@/consts";
import "@/styles/content-styles.css"; import "@/styles/articles.css";
// 定义文章类型 // 定义文章类型
interface ArticleEntry { interface ArticleEntry {
@ -500,105 +500,196 @@ const tableOfContents = generateTableOfContents(headings);
listeners.length = 0; listeners.length = 0;
} }
// 1. 应用高亮代码 // 1. 增强代码块功能 - 添加标题栏、语言显示和复制按钮
function applyCodeHighlighting() { function enhanceCodeBlocks() {
const codeElements = document.querySelectorAll('code[data-highlighted]'); // 查找所有代码块元素
const codeBlocks = document.querySelectorAll('pre > code');
if (codeBlocks.length === 0) return;
codeElements.forEach(codeElement => { codeBlocks.forEach(codeBlock => {
const highlightedCode = codeElement.getAttribute('data-highlighted'); const pre = codeBlock.parentElement;
if (highlightedCode) { // 如果已经处理过,跳过
codeElement.innerHTML = highlightedCode; if (pre.parentElement.classList.contains('code-block-container')) return;
codeElement.removeAttribute('data-highlighted');
} // 获取语言类 - 简化语言提取逻辑
}); let language = '';
// 从data-language属性中提取(最常见的方式)
language = pre.getAttribute('data-language') || codeBlock.getAttribute('data-language') || '';
// 如果没有找到语言,则默认为 'text'
if (!language || language === 'mermaid') {
// 跳过 mermaid 图表
if (language === 'mermaid') return;
language = 'text';
} }
// 2. 加载Mermaid SVG内容 // 获取原始代码文本,保留换行和格式
function loadMermaidSvg() { let originalCode = '';
const mermaidContainers = document.querySelectorAll('.mermaid-figure [data-mermaid-src], .mermaid-svg-container[data-mermaid-src]'); const hasLineElements = codeBlock.querySelectorAll('.line').length > 0 ||
codeBlock.innerHTML.includes('<span class="line">');
mermaidContainers.forEach(container => { if (hasLineElements) {
const svgSrc = container.getAttribute('data-mermaid-src'); // 从行元素中提取文本
if (svgSrc) { const lines = codeBlock.querySelectorAll('.line');
fetch(svgSrc) if (lines.length > 0) {
.then(response => { originalCode = Array.from(lines)
if (!response.ok) { .map(line => line.textContent)
throw new Error('SVG加载失败: ' + response.status); .join('\n');
}
return response.text();
})
.then(svgContent => {
// 直接插入SVG内容而不是作为图像
container.innerHTML = svgContent;
// 确保SVG元素正确应用样式
const svgElement = container.querySelector('svg');
if (svgElement) {
// 设置高度为auto以允许CSS控制
svgElement.setAttribute('height', 'auto');
// 确保viewBox属性存在并正确设置
if (!svgElement.hasAttribute('viewBox') ||
!svgElement.getAttribute('viewBox')?.match(/^\d+\s+\d+\s+\d+\s+\d+$/)) {
// 如果没有有效的viewBox尝试从宽高创建一个
const width = svgElement.getAttribute('width');
const height = svgElement.getAttribute('height');
if (width && height && !isNaN(parseFloat(width)) && !isNaN(parseFloat(height))) {
svgElement.setAttribute('viewBox', `0 0 ${parseFloat(width)} ${parseFloat(height)}`);
} else { } else {
// 使用默认viewBox // 尝试解析HTML中的行
svgElement.setAttribute('viewBox', '0 0 100 100'); const tempDiv = document.createElement('div');
tempDiv.innerHTML = codeBlock.innerHTML;
const lineSpans = tempDiv.querySelectorAll('.line');
originalCode = Array.from(lineSpans)
.map(span => span.textContent)
.join('\n');
} }
} else {
// 直接使用textContent (这种情况下可能没有特殊格式化)
originalCode = codeBlock.textContent || '';
} }
// 确保具有mermaid-svg类 // 创建代码块容器
svgElement.classList.add('mermaid-svg'); const container = document.createElement('div');
} container.className = 'code-block-container';
// 移除data属性避免重复加载 // 创建标题栏
container.removeAttribute('data-mermaid-src'); const header = document.createElement('div');
}) header.className = 'code-block-header';
.catch(error => {
console.error('加载SVG失败:', error);
container.innerHTML = '<div class="mermaid-error-message">图表加载失败</div>';
});
}
});
}
// 3. 处理代码复制功能 // 语言标签
function setupCodeCopy() { const langDiv = document.createElement('div');
const handleCopyClick = (e) => { langDiv.className = 'code-block-lang';
const target = e.target; langDiv.innerHTML = `
const copyButton = target.closest('[data-copy]'); <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">
if (!copyButton) return; <polyline points="16 18 22 12 16 6"></polyline>
<polyline points="8 6 2 12 8 18"></polyline>
</svg>
${language.toUpperCase()}
`;
const container = copyButton.closest('.code-block-container'); // 复制按钮
if (!container) return; const copyButton = document.createElement('button');
copyButton.className = 'code-block-copy';
copyButton.innerHTML = `
<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 codeElement = container.querySelector('pre code'); // 代码内容容器
if (!codeElement) return; const codeContent = document.createElement('div');
codeContent.className = 'code-block-content';
const code = codeElement.textContent || ''; // 添加复制功能
addListener(copyButton, 'click', async () => {
navigator.clipboard.writeText(code) try {
.then(() => { // 使用优化后的方法获取代码文本
const originalText = copyButton.textContent || ''; await navigator.clipboard.writeText(originalCode);
copyButton.textContent = '已复制!'; copyButton.classList.add('copied');
copyButton.disabled = true; copyButton.innerHTML = `
<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>
已复制
`;
setTimeout(() => { setTimeout(() => {
copyButton.textContent = originalText; copyButton.classList.remove('copied');
copyButton.disabled = false; copyButton.innerHTML = `
<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>
复制
`;
}, 2000); }, 2000);
}) } catch (err) {
.catch(err => {
console.error('复制失败:', err); console.error('复制失败:', err);
alert('复制失败,请手动复制'); copyButton.innerHTML = `
<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="12" y1="8" x2="12" y2="12"></line>
<line x1="12" y1="16" x2="12.01" y2="16"></line>
</svg>
失败
`;
setTimeout(() => {
copyButton.innerHTML = `
<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>
复制
`;
}, 2000);
}
}); });
};
addListener(document, 'click', handleCopyClick); // 组装标题栏
header.appendChild(langDiv);
header.appendChild(copyButton);
// 添加行号
const preHasLineNumbers = pre.classList.contains('line-numbers') || pre.classList.contains('has-line-numbers');
// 不改变原有结构,只在外层包装
// 保留原有的代码块,将其移入新容器
pre.parentNode.insertBefore(container, pre);
container.appendChild(header);
container.appendChild(codeContent);
codeContent.appendChild(pre);
// 始终添加行号 - 修改此部分,确保所有代码块都有行号
if (!preHasLineNumbers) {
pre.classList.add('line-numbers');
// 如果代码块内部没有行号结构,则添加
const hasLineElements = codeBlock.querySelectorAll('.line').length > 0 ||
codeBlock.innerHTML.includes('<span class="line">');
if (!hasLineElements) {
// 特殊处理已经高亮的代码,防止破坏高亮效果
const isPreHighlighted = codeBlock.innerHTML.includes('span style=') ||
pre.classList.contains('shiki') ||
pre.classList.contains('astro-code');
if (isPreHighlighted) {
// 已经高亮的代码,只添加行号类,不修改结构
// 在这种情况下依赖CSS来显示行号而不是修改DOM结构
return;
}
// 只处理未高亮的代码块
// 将代码按行分割
const lines = originalCode.split('\n');
let newHtml = '';
// 优化行处理,确保正确处理行内容
lines.forEach((line, index) => {
// 避免末尾空行
if (index === lines.length - 1 && line.trim() === '') return;
// 处理HTML实体防止XSS并保留格式
const escapedLine = line
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
// 添加行标记(不添加换行符)
newHtml += `<span class="line">${escapedLine}</span>`;
});
// 更新代码块内容,仅在有内容时更新
if (newHtml) {
codeBlock.innerHTML = newHtml;
}
}
}
});
} }
// 4. 设置阅读进度条 // 4. 设置阅读进度条
@ -773,24 +864,137 @@ const tableOfContents = generateTableOfContents(headings);
updateActiveHeading(); updateActiveHeading();
} }
// 6. 处理Mermaid图表渲染
function setupMermaid() {
// 查找所有mermaid代码块 - 支持多种可能的类名和选择器
const mermaidBlocks = document.querySelectorAll(
'pre.language-mermaid, pre > code.language-mermaid, .mermaid'
);
if (mermaidBlocks.length === 0) return;
console.log('找到Mermaid代码块:', mermaidBlocks.length);
// 动态加载mermaid库
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js';
script.onload = function() {
console.log('Mermaid库加载完成开始渲染图表');
// 初始化mermaid配置 - 始终使用默认主题通过CSS控制样式
window.mermaid.initialize({
startOnLoad: false,
theme: 'default',
securityLevel: 'loose'
});
// 将所有mermaid代码块转换为可渲染的格式
mermaidBlocks.forEach((block, index) => {
// 获取mermaid代码
let code = '';
// 检查元素类型并相应处理
if (block.tagName === 'CODE' && block.classList.contains('language-mermaid')) {
// 处理 code.language-mermaid 元素
code = block.textContent || '';
const pre = block.closest('pre');
if (pre) {
// 创建新的div元素替换整个pre
const div = document.createElement('div');
div.className = 'mermaid';
div.id = 'mermaid-diagram-' + index;
div.textContent = code;
pre.parentNode.replaceChild(div, pre);
}
} else if (block.tagName === 'PRE' && block.classList.contains('language-mermaid')) {
// 处理 pre.language-mermaid 元素
code = block.textContent || '';
const div = document.createElement('div');
div.className = 'mermaid';
div.id = 'mermaid-diagram-' + index;
div.textContent = code;
block.parentNode.replaceChild(div, block);
} else if (block.classList.contains('mermaid') && block.tagName !== 'DIV') {
// 如果是其他带mermaid类的元素但不是div转换为div
code = block.textContent || '';
const div = document.createElement('div');
div.className = 'mermaid';
div.id = 'mermaid-diagram-' + index;
div.textContent = code;
block.parentNode.replaceChild(div, block);
}
});
// 初始化渲染
try {
console.log('开始渲染Mermaid图表');
window.mermaid.run().catch(err => {
console.error('Mermaid渲染出错:', err);
});
} catch (error) {
console.error('初始化Mermaid渲染失败:', error);
}
};
script.onerror = function() {
console.error('加载Mermaid库失败');
// 显示错误信息
mermaidBlocks.forEach(block => {
if (block.tagName === 'CODE') block = block.closest('pre');
if (block) {
block.innerHTML = '<div class="mermaid-error-message">无法加载Mermaid图表库</div>';
}
});
};
document.head.appendChild(script);
// 添加到清理列表,确保后续页面跳转时能删除脚本
listeners.push({
element: script,
eventType: 'remove',
handler: () => {
if (script.parentNode) {
script.parentNode.removeChild(script);
}
// 清除全局mermaid对象
if (window.mermaid) {
window.mermaid = undefined;
}
// 移除页面上可能留下的mermaid相关元素
try {
// 移除所有可能的mermaid样式和元素
const mermaidElements = [
'#mermaid-style',
'#mermaid-cloned-styles',
'.mermaid-svg-reference',
'style[id^="mermaid-"]'
];
document.querySelectorAll(mermaidElements.join(', ')).forEach(el => {
if (el && el.parentNode) {
el.parentNode.removeChild(el);
}
});
} catch (e) {
console.error('清理Mermaid元素时出错:', e);
}
},
options: null
});
}
// 初始化所有功能 // 初始化所有功能
function init() { function init() {
if (!document.querySelector("article")) return; if (!document.querySelector("article")) return;
// 应用代码高亮 enhanceCodeBlocks(); // 添加新的代码块增强函数
applyCodeHighlighting();
// 加载Mermaid SVG图表
loadMermaidSvg();
// 设置代码复制功能
setupCodeCopy();
// 设置阅读进度条
setupProgressBar(); setupProgressBar();
// 设置目录交互
setupTableOfContents(); setupTableOfContents();
setupMermaid();
} }
// 注册清理函数 // 注册清理函数

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,200 @@
/* 代码块容器样式 - 简化背景和阴影 */
.code-block-container {
margin: 1rem 0;
border-radius: 0.5rem;
overflow: hidden;
border: 1px solid #e2e8f0;
background-color: transparent;
position: relative;
}
/* 代码块标题栏 */
.code-block-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.4rem 0.8rem;
background-color: #f1f5f9;
border-bottom: 1px solid #e2e8f0;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 0.875rem;
}
/* 代码语言标签 */
.code-block-lang {
font-weight: 600;
color: #475569;
display: flex;
align-items: center;
gap: 0.5rem;
}
.code-block-lang svg {
width: 1rem;
height: 1rem;
}
/* 复制按钮 */
.code-block-copy {
background: transparent;
border: none;
cursor: pointer;
color: #475569;
display: flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
transition: all 0.2s ease;
}
.code-block-copy:hover {
background-color: #e2e8f0;
}
.code-block-copy.copied {
color: #10b981;
}
/* 代码内容容器 - 移除背景 */
.code-block-content {
position: relative;
overflow-x: auto;
background-color: transparent;
}
/* 基础代码块样式 - 减小内边距 */
pre {
margin: 0;
padding: 0.2rem 0;
overflow-x: auto;
}
pre code {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1.05rem;
line-height: 1.5rem;
padding: 0;
display: block;
}
/* 行号样式 - 缩小间距 */
.line-numbers {
counter-reset: line;
position: relative;
}
/* 行号背景条 - 减小宽度 */
.line-numbers::before {
content: "";
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3rem;
background-color: #f1f5f9;
border-right: 1px solid #e2e8f0;
z-index: 1;
}
/* 行样式 - 进一步减小行间距和缩进 */
.line-numbers .line {
position: relative;
counter-increment: line;
padding-left: 3.5rem;
padding-right: 0.5rem;
min-height: 1.5rem;
white-space: pre;
}
/* 行号 - 调整位置 */
.line-numbers .line::before {
content: counter(line);
position: absolute;
left: 0;
top: 0;
width: 3rem;
height: 100%;
text-align: center;
color: #94a3b8;
font-size: 0.95rem;
user-select: none;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
}
/* 暗色模式 */
[data-theme='dark'] .code-block-container {
border-color: #334155;
background-color: transparent;
}
[data-theme='dark'] .code-block-header {
background-color: #1e293b;
border-bottom-color: #334155;
}
[data-theme='dark'] .code-block-lang {
color: #e2e8f0;
}
[data-theme='dark'] .code-block-copy {
color: #e2e8f0;
}
[data-theme='dark'] .code-block-copy:hover {
background-color: #334155;
}
/* 移除代码内容区域的黑暗模式背景 */
[data-theme='dark'] .code-block-content {
background-color: transparent;
}
/* 暗色模式行号样式 */
[data-theme='dark'] .line-numbers::before {
background-color: #1e293b;
border-right-color: #334155;
}
[data-theme='dark'] .line-numbers .line::before {
color: #64748b;
}
/* 兼容原有高亮 - 确保不添加背景 */
[data-theme='dark'] .shiki,
[data-theme='dark'] .shiki span {
color: var(--shiki-dark) !important;
background-color: transparent !important;
}
[data-theme='dark'] .astro-code,
[data-theme='dark'] .astro-code span {
color: var(--shiki-dark) !important;
background-color: transparent !important;
}
/* 确保所有代码元素没有背景 */
code, pre, .code-block-content,
.code-block-content pre.shiki,
.code-block-content pre.astro-code,
.code-block-content pre code,
.code-block-content pre code span,
pre.shiki, pre.astro-code,
.line, .line span {
background-color: transparent !important;
}
/* 高亮行样式 - 仅保留边框而不添加背景 */
.line.highlighted {
border-left: 2px solid #eab308;
background-color: transparent !important;
}
[data-theme='dark'] .line.highlighted {
border-left: 2px solid #fbbf24;
background-color: transparent !important;
}

View File

@ -0,0 +1,150 @@
:root {
--mermaid-text-color: #1e293b;
--mermaid-bg-color: #ffffff;
--mermaid-primary-color: #4f46e5;
--mermaid-secondary-color: #6366f1;
--mermaid-border-color: #9ca3af;
--mermaid-line-color: #4b5563;
--mermaid-edge-label-bg: #ffffff;
--mermaid-title-color: #0f172a;
--mermaid-section-bg: #f8fafc;
--mermaid-node-bg: #f1f5f9;
}
/* 暗色模式样式变量 */
[data-theme='dark'], .dark {
--mermaid-text-color: #e2e8f0;
--mermaid-bg-color: #1e293b;
--mermaid-primary-color: #818cf8;
--mermaid-secondary-color: #a5b4fc;
--mermaid-border-color: #64748b;
--mermaid-line-color: #94a3b8;
--mermaid-edge-label-bg: #1e293b;
--mermaid-title-color: #f8fafc;
--mermaid-section-bg: #334155;
--mermaid-node-bg: #475569;
}
/* 基础Mermaid样式覆盖 */
.mermaid {
background-color: transparent !important;
transition: color 0.3s ease, fill 0.3s ease, stroke 0.3s ease;
}
/* Mermaid文本颜色统一 */
.mermaid .label,
.mermaid text,
.mermaid span,
.mermaid .messageText,
.mermaid .loopText,
.mermaid .noteText,
.mermaid .taskText {
color: var(--mermaid-text-color) !important;
fill: var(--mermaid-text-color) !important;
font-family: inherit !important;
transition: color 0.3s ease, fill 0.3s ease;
}
/* 节点样式设置 */
.mermaid .node rect,
.mermaid .node circle,
.mermaid .node ellipse,
.mermaid .node polygon,
.mermaid .node path {
fill: var(--mermaid-node-bg) !important;
stroke: var(--mermaid-border-color) !important;
stroke-width: 1px !important;
transition: fill 0.3s ease, stroke 0.3s ease;
}
/* 连接线样式 */
.mermaid .edgePath .path,
.mermaid .flowchart-link,
.mermaid line,
.mermaid .messageLine0,
.mermaid .messageLine1 {
stroke: var(--mermaid-line-color) !important;
stroke-width: 1px !important;
transition: stroke 0.3s ease;
}
/* 箭头填充 */
.mermaid .arrowheadPath,
.mermaid marker path {
fill: var(--mermaid-line-color) !important;
stroke: none !important;
transition: fill 0.3s ease;
}
/* 文本标签背景 */
.mermaid .edgeLabel rect,
.mermaid .labelBox {
fill: var(--mermaid-edge-label-bg) !important;
background-color: var(--mermaid-edge-label-bg) !important;
transition: fill 0.3s ease, background-color 0.3s ease;
}
/* 标题样式 */
.mermaid .titleText,
.mermaid .classTitle,
.mermaid .cluster-label text {
fill: var(--mermaid-title-color) !important;
color: var(--mermaid-title-color) !important;
font-weight: bold !important;
transition: fill 0.3s ease, color 0.3s ease;
}
/* 集群/子图样式 */
.mermaid .cluster rect,
.mermaid .cluster polygon {
fill: var(--mermaid-section-bg) !important;
stroke: var(--mermaid-border-color) !important;
stroke-width: 1px !important;
opacity: 0.8 !important;
transition: fill 0.3s ease, stroke 0.3s ease;
}
/* 序列图特殊样式 */
.mermaid .actor {
fill: var(--mermaid-node-bg) !important;
stroke: var(--mermaid-border-color) !important;
stroke-width: 1px !important;
transition: fill 0.3s ease, stroke 0.3s ease;
}
.mermaid .note {
fill: var(--mermaid-secondary-color) !important;
opacity: 0.7 !important;
transition: fill 0.3s ease;
}
/* 甘特图特殊样式 */
.mermaid .section0,
.mermaid .section2 {
fill: var(--mermaid-section-bg) !important;
opacity: 0.5 !important;
transition: fill 0.3s ease;
}
.mermaid .section1,
.mermaid .section3 {
fill: var(--mermaid-node-bg) !important;
opacity: 0.4 !important;
transition: fill 0.3s ease;
}
.mermaid .task0,
.mermaid .task1,
.mermaid .task2,
.mermaid .task3 {
fill: var(--mermaid-primary-color) !important;
stroke: var(--mermaid-border-color) !important;
transition: fill 0.3s ease, stroke 0.3s ease;
}
/* 类图特殊样式 */
.mermaid .classLabel .label {
fill: var(--mermaid-text-color) !important;
color: var(--mermaid-text-color) !important;
transition: fill 0.3s ease, color 0.3s ease;
}

View File

@ -1,6 +1,6 @@
@import "./table-styles.css"; @import "./articles-table.css";
@import "./code-blocks.css"; @import "./articles-mermaid.css";
@import "./mermaid-themes.css"; @import "./articles-code.css";
/* 增强列表样式 */ /* 增强列表样式 */
.prose ul { .prose ul {
@ -42,41 +42,6 @@
color: #6b7280; color: #6b7280;
} }
/* 行内代码样式 */
.prose :not(pre) > code {
background-color: rgba(0, 0, 0, 0.05);
color: var(--color-primary-700);
padding: 0.2em 0.4em;
border-radius: 0.375em;
font-weight: 500;
font-family: "JetBrains Mono", Menlo, Monaco, Consolas, "Courier New", monospace;
font-size: 0.875em;
white-space: normal;
word-wrap: break-word;
overflow-wrap: break-word;
border: 1px solid rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
letter-spacing: -0.025em;
transition: all 0.2s ease;
}
.prose :not(pre) > code:hover {
background-color: rgba(var(--color-primary-600-rgb), 0.08);
border-color: rgba(var(--color-primary-400-rgb), 0.2);
}
[data-theme="dark"] .prose :not(pre) > code {
background-color: rgba(255, 255, 255, 0.07);
color: var(--color-primary-300);
border-color: rgba(255, 255, 255, 0.1);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
[data-theme="dark"] .prose :not(pre) > code:hover {
background-color: rgba(var(--color-primary-500-rgb), 0.15);
border-color: rgba(var(--color-primary-300-rgb), 0.3);
}
/* 标题样式 */ /* 标题样式 */
.prose h1 { .prose h1 {
font-size: 2.25rem; font-size: 2.25rem;
@ -294,8 +259,7 @@
.prose details > p, .prose details > p,
.prose details > ul, .prose details > ul,
.prose details > ol, .prose details > ol,
.prose details > div, .prose details > div {
.prose details > pre {
margin-top: 0; margin-top: 0;
margin-bottom: 1em; margin-bottom: 1em;
} }

View File

@ -1,537 +0,0 @@
/* 代码块容器 */
.code-block-container {
margin: 1.5rem 0;
border-radius: 0.5rem;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
background-color: #f5f7fa;
border: 1px solid rgba(0,0,0,0.1);
font-size: 0.9rem;
}
[data-theme="dark"] .code-block-container {
background-color: #282a36;
border-color: rgba(255,255,255,0.1);
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
}
/* 标题栏 */
.code-block-title {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 1rem;
background-color: #e2e8f0;
border-bottom: 1px solid #cbd5e0;
font-family: 'JetBrains Mono', Menlo, Monaco, Consolas, 'Courier New', monospace;
font-size: 0.85rem;
}
[data-theme="dark"] .code-block-title {
background-color: #343746;
border-bottom: 1px solid #44475a;
color: #f8f8f2;
}
/* 标题栏左侧 */
.code-title-left {
display: flex;
align-items: center;
gap: 0.5rem;
}
/* 标题栏右侧 */
.code-title-right {
display: flex;
align-items: center;
}
/* 语言标识 */
.code-language {
text-transform: uppercase;
font-size: 0.75rem;
font-weight: 600;
color: #4a5568;
}
[data-theme="dark"] .code-language {
color: #bd93f9;
}
/* 文件名 */
.code-filename {
margin-left: 0.5rem;
font-size: 0.75rem;
color: #4a5568;
font-weight: normal;
}
[data-theme="dark"] .code-filename {
color: #f1fa8c;
}
/* 复制按钮 */
.copy-button {
background: transparent;
border: 1px solid #a0aec0;
border-radius: 0.25rem;
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
cursor: pointer;
color: #4a5568;
}
.copy-button:hover {
background: #edf2f7;
color: #2d3748;
}
[data-theme="dark"] .copy-button {
border-color: #44475a;
color: #f8f8f2;
}
[data-theme="dark"] .copy-button:hover {
background: #44475a;
}
/* 代码内容区 */
.code-block-content {
padding: 1rem;
overflow: auto;
}
.code-block-content pre {
margin: 0;
padding: 0;
background: transparent;
overflow: visible;
font-family: 'JetBrains Mono', Menlo, Monaco, Consolas, 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.5;
}
.code-block-content code {
background: transparent;
padding: 0;
font-family: inherit;
color: inherit;
border-radius: 0;
}
/* ----------------------- */
/* 终端样式 */
/* ----------------------- */
.terminal-container {
background-color: #f8fafc;
border-radius: 0.5rem;
overflow: hidden;
border: 1px solid #adc2ff;
box-shadow: 0 8px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
position: relative;
}
[data-theme="dark"] .terminal-container {
background-color: #1a1e2a;
border-color: rgba(255,255,255,0.1);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
[data-theme="dark"] .terminal-container::before {
display: none;
}
/* 终端标题栏 */
.terminal-container .code-block-title {
background-color: #f1f5f9;
border-bottom: 1px solid #adc2ff;
padding: 0.4rem 1rem;
color: #334155;
font-weight: 500;
}
[data-theme="dark"] .terminal-container .code-block-title {
background-color: #111827;
border-bottom: 1px solid rgba(255,255,255,0.1);
color: #f8fafc;
}
/* 终端控制按钮 */
.terminal-controls {
display: flex;
gap: 6px;
margin-right: 10px;
}
.terminal-control {
width: 12px;
height: 12px;
border-radius: 50%;
display: block;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.terminal-close {
background-color: #f87171;
}
.terminal-minimize {
background-color: #fbbf24;
}
.terminal-maximize {
background-color: #34d399;
}
/* 终端内容区 */
.terminal-container .code-block-content {
background-color: #f8fafc;
color: #334155;
padding: 1rem;
}
[data-theme="dark"] .terminal-container .code-block-content {
background-color: #1a1e2a;
color: #f1f5f9;
}
.terminal-pre {
color: #334155;
}
[data-theme="dark"] .terminal-pre {
color: #f1f5f9;
}
/* 语言标签在终端样式中的颜色 */
.terminal-container .code-language {
color: #64748b;
font-weight: 500;
}
[data-theme="dark"] .terminal-container .code-language {
color: #a0aec0;
}
/* 终端中的复制按钮 */
.terminal-container .copy-button {
border-color: #adc2ff;
color: #4a5568;
transition: all 0.2s ease;
}
.terminal-container .copy-button:hover {
background: #ebf0ff;
color: #4b6bff;
border-color: #4b6bff;
}
[data-theme="dark"] .terminal-container .copy-button {
border-color: rgba(255,255,255,0.3);
color: #e2e8f0;
}
[data-theme="dark"] .terminal-container .copy-button:hover {
background: rgba(255,255,255,0.15);
color: #ffffff;
}
/* ----------------------- */
/* Mermaid 图表样式 */
/* ----------------------- */
.mermaid-figure {
margin: 2rem auto;
text-align: center;
max-width: 100%;
display: flex;
justify-content: center;
}
.mermaid-figure img {
max-width: 100%;
height: auto;
border-radius: 0.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
/* 亮色主题图表 */
.light-theme-diagram {
display: block;
}
[data-theme="dark"] .light-theme-diagram {
display: none;
}
/* 暗色主题图表 */
.dark-theme-diagram {
display: none;
}
[data-theme="dark"] .dark-theme-diagram {
display: block;
}
/* Mermaid 错误样式 */
.mermaid-error {
border: 1px solid #e53e3e;
background-color: #fff5f5;
padding: 1rem;
border-radius: 0.5rem;
margin: 1.5rem 0;
}
[data-theme="dark"] .mermaid-error {
background-color: #3b1a1a;
border-color: #fc8181;
}
.mermaid-error-message {
color: #c53030;
font-size: 0.875rem;
margin-top: 0.5rem;
}
[data-theme="dark"] .mermaid-error-message {
color: #fc8181;
}
/* ----------------------- */
/* Highlight.js 主题样式 */
/* ----------------------- */
/* 基本颜色方案 - 亮色 */
.hljs {
color: #1a202c;
background: transparent;
}
[data-theme="dark"] .hljs {
color: #f8f8f2;
background: transparent;
}
/* 注释 */
.hljs-comment,
.hljs-quote {
color: #718096;
font-style: italic;
}
[data-theme="dark"] .hljs-comment,
[data-theme="dark"] .hljs-quote {
color: #6272a4;
}
/* 关键字 */
.hljs-keyword,
.hljs-selector-tag,
.hljs-addition {
color: #805ad5;
}
[data-theme="dark"] .hljs-keyword,
[data-theme="dark"] .hljs-selector-tag,
[data-theme="dark"] .hljs-addition {
color: #ff79c6;
}
/* 变量名和属性 */
.hljs-variable,
.hljs-template-variable,
.hljs-literal,
.hljs-attr,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-meta {
color: #dd6b20;
}
[data-theme="dark"] .hljs-variable,
[data-theme="dark"] .hljs-template-variable,
[data-theme="dark"] .hljs-literal,
[data-theme="dark"] .hljs-attr,
[data-theme="dark"] .hljs-selector-attr,
[data-theme="dark"] .hljs-selector-pseudo,
[data-theme="dark"] .hljs-meta {
color: #f1fa8c;
}
/* 函数名 */
.hljs-title,
.hljs-function .hljs-title,
.hljs-section {
color: #3182ce;
}
[data-theme="dark"] .hljs-title,
[data-theme="dark"] .hljs-function .hljs-title,
[data-theme="dark"] .hljs-section {
color: #50fa7b;
}
/* 类名和ID */
.hljs-title.class_,
.hljs-type,
.hljs-class .hljs-title,
.hljs-selector-id,
.hljs-selector-class {
color: #d53f8c;
}
[data-theme="dark"] .hljs-title.class_,
[data-theme="dark"] .hljs-type,
[data-theme="dark"] .hljs-class .hljs-title,
[data-theme="dark"] .hljs-selector-id,
[data-theme="dark"] .hljs-selector-class {
color: #8be9fd;
}
/* 字符串 */
.hljs-string,
.hljs-regexp,
.hljs-attribute,
.hljs-doctag {
color: #38a169;
}
[data-theme="dark"] .hljs-string,
[data-theme="dark"] .hljs-regexp,
[data-theme="dark"] .hljs-attribute,
[data-theme="dark"] .hljs-doctag {
color: #f1fa8c;
}
/* 数字 */
.hljs-number,
.hljs-deletion,
.hljs-symbol,
.hljs-bullet,
.hljs-link,
.hljs-formula {
color: #e53e3e;
}
[data-theme="dark"] .hljs-number,
[data-theme="dark"] .hljs-deletion,
[data-theme="dark"] .hljs-symbol,
[data-theme="dark"] .hljs-bullet,
[data-theme="dark"] .hljs-link,
[data-theme="dark"] .hljs-formula {
color: #bd93f9;
}
/* HTML标签 */
.hljs-tag,
.hljs-name,
.hljs-selector-tag {
color: #2b6cb0;
}
[data-theme="dark"] .hljs-tag,
[data-theme="dark"] .hljs-name,
[data-theme="dark"] .hljs-selector-tag {
color: #ff79c6;
}
/* 内置对象 */
.hljs-built_in {
color: #c05621;
}
[data-theme="dark"] .hljs-built_in {
color: #8be9fd;
}
/* 高亮代码行 */
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
/* 终端命令特殊着色 */
.terminal-container .hljs-built_in,
.terminal-container .hljs-keyword {
color: #4b6bff;
font-weight: 500;
}
[data-theme="dark"] .terminal-container .hljs-built_in,
[data-theme="dark"] .terminal-container .hljs-keyword {
color: #9ae6b4;
}
/* 终端中的字符串 */
.terminal-container .hljs-string {
color: #ea580c;
}
[data-theme="dark"] .terminal-container .hljs-string {
color: #fbd38d;
}
/* 终端中的变量 */
.terminal-container .hljs-variable {
color: #8b5cf6;
}
[data-theme="dark"] .terminal-container .hljs-variable {
color: #b794f4;
}
/* 终端中的参数 */
.terminal-container .hljs-params {
color: #475569;
}
[data-theme="dark"] .terminal-container .hljs-params {
color: #cbd5e0;
}
/* 终端中的注释 */
.terminal-container .hljs-comment {
color: #64748b;
font-style: italic;
}
[data-theme="dark"] .terminal-container .hljs-comment {
color: #a0aec0;
}
/* 滚动条样式 */
.code-block-content::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.code-block-content::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
.code-block-content::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
.code-block-content::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.3);
}
[data-theme="dark"] .code-block-content::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
}
[data-theme="dark"] .code-block-content::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
}
[data-theme="dark"] .code-block-content::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.3);
}

View File

@ -1,231 +0,0 @@
/* Mermaid图表主题样式
* 支持亮色/暗色主题切换
*/
/* 图表容器样式 */
.mermaid-figure {
margin: 2rem auto;
display: flex;
justify-content: center;
width: 100%;
overflow-x: auto;
}
.mermaid-svg-container {
display: flex;
justify-content: center;
width: 100%;
max-height: 80vh; /* 限制容器最大高度为视窗高度的80% */
}
/* 错误信息样式 */
.mermaid-error-message {
color: #e53e3e;
padding: 1rem;
border: 1px dashed #e53e3e;
border-radius: 0.5rem;
margin: 1rem 0;
text-align: center;
}
/* 基础样式 - 亮色主题 */
.mermaid-svg {
/* 全局图表容器样式 */
max-width: 100%;
height: auto !important; /* 强制高度为自动,覆盖内联样式 */
max-height: 80vh; /* 限制最大高度为视窗高度的80% */
width: auto; /* 允许宽度自适应内容 */
overflow: visible;
/* 移除背景色,使用透明背景 */
background-color: transparent;
}
/* 节点样式 */
.mermaid-svg .node rect,
.mermaid-svg .node circle,
.mermaid-svg .node ellipse,
.mermaid-svg .node polygon,
.mermaid-svg .node path {
fill: #f8fafc;
stroke: #94a3b8;
stroke-width: 1px;
}
/* 基本填充和背景色 */
.mermaid-svg .basic {
fill: #f8fafc;
stroke: #94a3b8;
}
.mermaid-svg .labelBkg {
background-color: transparent;
}
/* 集群样式 */
.mermaid-svg .cluster rect {
fill: #f1f5f9;
stroke: #cbd5e1;
stroke-width: 1px;
}
/* 边标签样式 */
.mermaid-svg .edgeLabel rect {
fill: transparent;
background-color: transparent;
}
/* 文本样式 */
.mermaid-svg text {
fill: #334155;
font-family: var(--font-sans);
}
.mermaid-svg .label {
color: #334155;
background-color: transparent;
}
.mermaid-svg .nodeLabel {
color: #334155;
background-color: transparent;
}
.mermaid-svg .cluster text {
fill: #475569;
}
/* 连线样式 */
.mermaid-svg .edgePath .path {
stroke: #94a3b8;
stroke-width: 1.5px;
}
.mermaid-svg .flowchart-link {
stroke: #94a3b8;
fill: none;
}
.mermaid-svg marker {
fill: #94a3b8;
}
/* 箭头路径 */
.mermaid-svg .arrowMarkerPath {
fill: #94a3b8;
stroke: #94a3b8;
}
/* 特殊节点样式 */
.mermaid-svg .node.clickable {
cursor: pointer;
}
.mermaid-svg .node.clickable:hover rect,
.mermaid-svg .node.clickable:hover circle,
.mermaid-svg .node.clickable:hover ellipse,
.mermaid-svg .node.clickable:hover polygon {
stroke-width: 2px;
opacity: 0.9;
}
/* ======= 暗色主题样式 ======= */
[data-theme="dark"] .mermaid-svg {
background-color: transparent;
}
[data-theme="dark"] .mermaid-svg .node rect,
[data-theme="dark"] .mermaid-svg .node circle,
[data-theme="dark"] .mermaid-svg .node ellipse,
[data-theme="dark"] .mermaid-svg .node polygon,
[data-theme="dark"] .mermaid-svg .node path {
fill: #1e293b;
stroke: #475569;
}
[data-theme="dark"] .mermaid-svg .basic {
fill: #1e293b;
stroke: #475569;
}
[data-theme="dark"] .mermaid-svg .labelBkg {
background-color: transparent;
}
[data-theme="dark"] .mermaid-svg .cluster rect {
fill: #0f172a;
stroke: #334155;
}
[data-theme="dark"] .mermaid-svg .edgeLabel rect {
fill: transparent;
background-color: transparent;
}
[data-theme="dark"] .mermaid-svg text {
fill: #e2e8f0;
}
[data-theme="dark"] .mermaid-svg .label {
color: #e2e8f0;
background-color: transparent;
}
[data-theme="dark"] .mermaid-svg .nodeLabel {
color: #e2e8f0;
background-color: transparent;
}
[data-theme="dark"] .mermaid-svg .cluster text {
fill: #cbd5e1;
}
[data-theme="dark"] .mermaid-svg .edgePath .path {
stroke: #64748b;
}
[data-theme="dark"] .mermaid-svg .flowchart-link {
stroke: #64748b;
}
[data-theme="dark"] .mermaid-svg marker {
fill: #64748b;
}
[data-theme="dark"] .mermaid-svg .arrowMarkerPath {
fill: #64748b;
stroke: #64748b;
}
/* 序列图特殊样式 */
[data-theme="dark"] .mermaid-svg .actor {
fill: #334155 !important;
}
[data-theme="dark"] .mermaid-svg .messageLine0 {
stroke: #94a3b8 !important;
}
[data-theme="dark"] .mermaid-svg .messageLine1 {
stroke: #94a3b8 !important;
}
[data-theme="dark"] .mermaid-svg #arrowhead path {
fill: #94a3b8 !important;
}
/* 甘特图特殊样式 */
[data-theme="dark"] .mermaid-svg .section0 {
fill: rgba(255, 255, 255, 0.1) !important;
}
[data-theme="dark"] .mermaid-svg .section1,
[data-theme="dark"] .mermaid-svg .section2 {
fill: rgba(255, 255, 255, 0.07) !important;
}
[data-theme="dark"] .mermaid-svg .task0,
[data-theme="dark"] .mermaid-svg .task1,
[data-theme="dark"] .mermaid-svg .task2,
[data-theme="dark"] .mermaid-svg .task3 {
fill: #1e3a8a !important;
}