Compare commits

..

No commits in common. "main" and "4.3" have entirely different histories.
main ... 4.3

245 changed files with 1092 additions and 171766 deletions

View File

@ -1,7 +0,0 @@
{
"summary": "本文记录了2025年网站更新包括优化流畅度、修复显示问题、设计改进和教程更新等旨在提升用户体验和网站性能。",
"service": "glm",
"page_title": "2025网站更新记录",
"timestamp": "2025-07-09T19:59:54.371013",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "本文介绍了Git的基本操作和实用技巧包括新建仓库、克隆、提交、分支管理、合并冲突处理等并探讨了临时保存、灵活合并、 cherry-pick、修改提交等高级操作旨在帮助开发者提升Git使用效率。",
"service": "glm",
"page_title": "Git 实用技巧",
"timestamp": "2025-07-09T20:00:14.257325",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "MkDocs支持自定义页脚通过编辑`footer.html`文件可添加自定义代码,实现个性化页脚设置。",
"service": "glm",
"page_title": "页脚设置",
"timestamp": "2025-07-09T20:00:02.047017",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "Tidio免费版提供便捷的在线聊天功能只需创建账户并粘贴一行代码即可安装适用于各类网站。",
"service": "glm",
"page_title": "添加在线聊天",
"timestamp": "2025-07-09T20:00:10.797953",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "本文介绍了网页圆角化设计的方法包括通过CSS文件引入和md文件使用实现图片和边框的圆角效果并展示了如何利用内置的grid cards和按钮样式提升网页视觉效果。",
"service": "glm",
"page_title": "网页圆角化设计",
"timestamp": "2025-07-09T20:00:12.611857",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "通过git插件实现MKdocs文章修订时间戳功能支持本地化日期显示优化渲染速度提升文档更新追踪效率。",
"service": "glm",
"page_title": "为MKdocs添加文章修订时间戳",
"timestamp": "2025-07-09T20:00:09.608392",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "麻省理工学院许可证允许用户无限制使用、复制、修改和分发软件,但需保留版权声明和许可声明。软件提供“原样”,不保证适销性或无侵权性,使用风险自担。",
"service": "glm",
"page_title": "许可声明",
"timestamp": "2025-07-09T19:59:37.331858",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "在Github Pages部署mkdocs时通过在/docs目录下创建无后缀的CNAME文件并填写域名可解决自定义域名失效问题。原因在于每次添加自定义域名后系统会生成新的CNAME文件但未同步到本地导致push后CNAME信息丢失。",
"service": "glm",
"page_title": "3.解决Github Pages部署mkdocs自定义域名失效的问题",
"timestamp": "2025-07-09T19:59:46.806034",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "本文针对MKdocs中相对地址引用问题进行探讨涵盖图片、PDF、Markdown页面等资源引用的写法、注意事项及常见问题强调统一资源管理目录结构的重要性确保本地和线上文档正确访问。",
"service": "glm",
"page_title": "相对地址的一些问题",
"timestamp": "2025-07-09T19:59:57.281649",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "唐·诺曼提出情感设计分为本能层、行为层和反思层,分别影响产品外观、使用体验和用户思考。本能层关注产品外观,行为层关注使用体验,反思层关注用户对产品的思考。这三个层次共同构成产品整体体验。",
"service": "glm",
"page_title": "唐·诺曼—情感设计的三个层次",
"timestamp": "2025-07-09T20:00:21.378650",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "Mkdocs博客插件易于配置通过添加特定行至mkdocs.yml文件并配置作者信息即可快速搭建个人博客。",
"service": "glm",
"page_title": "添加Mkdocs博客",
"timestamp": "2025-07-09T19:59:47.865754",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "霞鹜文楷是一款基于Klee One开源日文字体衍生的高质量中文字体支持简繁日韩三字重适用于网站、手机系统等具有美观、可读性强等特点。",
"service": "glm",
"page_title": "修改网站字体",
"timestamp": "2025-07-09T20:00:05.955545",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "本文介绍了如何通过JavaScript和LocalStorage实现网页背景特效包括雪花、樱花和粒子效果通过设置标志控制是否显示适用于个性化网页设计。",
"service": "glm",
"page_title": "背景特效",
"timestamp": "2025-07-09T20:00:00.962646",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "本文介绍了使用MkDocs和GitHub Pages部署静态网页的详细步骤包括准备工作、创建网站、配置完善和参考资料帮助读者快速搭建个人博客或网站。",
"service": "glm",
"page_title": "1.利用Mkdocs部署静态网页",
"timestamp": "2025-07-09T19:59:41.333874",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "MWeb Pro是一款macOS上的Markdown写作与笔记应用支持GFM语法、多种输出格式和强大的笔记功能适用于博客写作、知识管理和文档输出。",
"service": "glm",
"page_title": "MWeb Pro",
"timestamp": "2025-07-09T20:00:17.187140",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "本文介绍了如何在网站中添加顶部公告栏,通过在`docs/overrides`目录下新建`main.html`文件并修改其内容实现,适用于自定义网站布局。",
"service": "glm",
"page_title": "添加顶部公告栏",
"timestamp": "2025-07-09T20:00:03.273178",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "本文介绍了Mkdocs文档教程涵盖官方教程、Material for MkDocs插件、中文教程和视频教程指导用户快速部署静态网页至GitHub pages并配置Mkdocs博客。",
"service": "glm",
"page_title": "0.Mkdocs教程前言",
"timestamp": "2025-07-09T19:59:49.407883",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "优化网站访问速度可采取图片格式转换、使用CDN加速资源、调整本地渲染配置等措施并利用Lighthouse进行性能测试。",
"service": "glm",
"page_title": "加速网站访问的一些心得",
"timestamp": "2025-07-09T19:59:59.882438",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "Markdown是一种轻量级标记语言便于纯文本格式编写文档支持多种格式导出。本文介绍了Markdown的基本语法包括标题、列表、表格、链接、图片、代码块等并提供了一些文档创作工具推荐。",
"service": "glm",
"page_title": "Markdown指南",
"timestamp": "2025-07-09T20:00:19.000594",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "本文介绍了如何为Markdown文档添加阅读时间统计功能。通过配置mkdocs.yml文件和编写Python脚本实现自动计算并展示阅读时间、中文字符数和代码行数等统计信息。",
"service": "glm",
"page_title": "添加阅读信息统计",
"timestamp": "2025-07-09T20:00:07.898964",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "MkDocs AI Hooks是一款集成AI摘要和智能阅读统计功能的插件支持多AI服务自动生成80-120字摘要并提供多种配置选项提升文档智能化和用户体验。",
"service": "glm",
"page_title": "MkDocs文档AI摘要",
"timestamp": "2025-07-09T19:59:55.922690",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "通过简单复制代码至MD文件即可在MKdocs中添加友链。采用极简设计支持卡片样式和响应式布局适用于展示技术博客或资源分享网站。",
"service": "glm",
"page_title": "如何给MKdocs添加友链",
"timestamp": "2025-07-09T19:59:39.765402",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "本文介绍了使用JavaScript和CSS自定义鼠标样式的实现方法包括尺寸、颜色等参数设置并提供了代码示例和配置步骤。",
"service": "glm",
"page_title": "JS实现鼠标样式",
"timestamp": "2025-07-09T20:00:08.755133",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "本文记录了2024年网站更新包括优化网站流畅度、启用CDN加速、美化Blog页面、更新教程、修复bugs等旨在提升用户体验。",
"service": "glm",
"page_title": "2024网站更新记录",
"timestamp": "2025-07-09T19:59:53.114480",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "本文详细介绍了MkDocs配置文件mkdocs.yml的设置方法包括主题颜色、模式切换、功能启用、导航设置、搜索功能、语言设置、图标、导航栏、标签配置、Markdown扩展、自定义CSS/JS等旨在帮助用户更好地自定义和优化MkDocs网站。",
"service": "glm",
"page_title": "2.Mkdocs配置说明(mkdocs.yml)",
"timestamp": "2025-07-09T19:59:44.268400",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "设计应简洁精炼,追求优雅美感,以用户体验为核心,注重细节,鼓励创新与情感共鸣,打造令人愉悦且富有创意的体验。",
"service": "glm",
"page_title": "我对设计的一些观点",
"timestamp": "2025-07-09T20:00:22.728567",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "Lighthouse作为浏览器扩展可快速评估网站性能通过谷歌或Edge浏览器即可使用助力优化网页加载速度和用户体验。",
"service": "glm",
"page_title": "利用Lighthouse测试网站性能",
"timestamp": "2025-07-09T20:00:15.859700",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "网站持续优化,包括流畅度提升、教程更新、修复显示问题,引入新材料设计规范和插件,提升用户体验。",
"service": "glm",
"page_title": "2025网站更新记录",
"timestamp": "2025-07-09T23:18:47.552565",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "2022年网站更新记录建立Github仓库简化打开流程新增反馈收集购买域名取消cookie确认保障隐私研究网站建设初步确定框架为MKdocs。",
"service": "glm",
"page_title": "2022网站更新记录",
"timestamp": "2025-07-09T19:59:50.455583",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "giscus是一款基于GitHub Discussions的评论系统开源、无跟踪广告支持自定义主题和多种语言自动同步评论可自建服务适用于网站评论功能。",
"service": "glm",
"page_title": "添加评论系统(giscus为例)",
"timestamp": "2025-07-09T20:00:04.475556",
"language": "zh"
}

View File

@ -1,7 +0,0 @@
{
"summary": "本文记录了2023年网站更新包括优化流畅度、引入人工智能问答机器人、新增多语言支持、修复Bug、增加友链版块、引入个性化标签等功能旨在提升用户体验和网站性能。",
"service": "glm",
"page_title": "2023网站更新记录",
"timestamp": "2025-07-09T19:59:51.720080",
"language": "zh"
}

View File

@ -1,10 +0,0 @@
{
"default_service": "glm",
"available_services": [
"glm",
"openai",
"gemini"
],
"summary_language": "zh",
"check_time": "2025-07-16T07:55:31.381146"
}

View File

@ -1,9 +1 @@
<<<<<<< Updated upstream
<<<<<<< Updated upstream
{"cache_date": "2025-07-15", "page_authors": {}}
=======
{"cache_date": "2025-07-12", "page_authors": {}}
>>>>>>> Stashed changes
=======
{"cache_date": "2025-07-12", "page_authors": {}}
>>>>>>> Stashed changes
{"cache_date": "2024-07-22", "page_authors": {}}

View File

@ -4,10 +4,6 @@ on:
branches:
- master
- main
# 禁止从 fork 仓库访问 secrets
pull_request:
types: [closed]
branches: [main, master]
permissions:
contents: write
jobs:
@ -17,60 +13,27 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
sparse-checkout: |
docs
includes
.ai_cache
- uses: actions/setup-python@v4
with:
python-version: 3.x
- name: Set cache ID
run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
- uses: actions/cache@v3
with:
key: mkdocs-material-${{ github.run_number }}
key: mkdocs-material-${ env.cache_id }
path: .cache
restore-keys: |
mkdocs-material-
- run: pip install mkdocs-git-revision-date-localized-plugin
- run: pip install mkdocs-git-authors-plugin
- run: pip install mkdocs-git-committers-plugin-2
- run: pip install markdown-callouts
- run: pip install mkdocs-rss-plugin
- run: pip install requests>=2.25.0
- run: pip install python-dateutil>=2.8.0
- run: pip install cachetools>=4.2.0
- run: pip install python-dotenv>=0.19.0
- run: pip install pymdown-extensions
# - run: pip install jieba
- run: pip install mkdocs-markdownextradata-plugin
# - run: pip install mkdocs-glightbox
# - run: pip install "mkdocs-material[imaging]"
# - run: pip install mkdocs-statistics-plugin
# - run: pip install mkdocs-rss-plugin
- run: pip install mkdocs-material
- run: pip install --upgrade --force-reinstall mkdocs-material
- name: Deploy with AI Summary
env:
# AI摘要开关控制
AI_SUMMARY_CI_ENABLED: 'true' # CI部署环境启用AI摘要 (true=在CI中为文章生成AI摘要)
AI_SUMMARY_CI_ONLY_CACHE: 'true' # CI部署不生成新摘要 (true=使用本地部署过的摘要缓存不再重复调用API)
AI_SUMMARY_CI_FALLBACK: 'true' # CI部署启用备用摘要 (true=API失败时生成离线基础摘要)
# AI_SUMMARY_LOCAL_ENABLED: 'false' # 本地部署环境禁用AI摘要 (true=本地开发时也生成摘要)(不需要管这条)
# AI_SUMMARY_CACHE_ENABLED: 'true' # 本地启用缓存功能 (true=缓存摘要避免重复生成)(不需要管这条)
# API密钥配置
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: mkdocs gh-deploy --force
- run: mkdocs gh-deploy --force
# 自动提交新生成的AI缓存文件
- name: Auto-commit AI cache (if any new files)
run: |
if [ -d ".ai_cache" ] && [ "$(ls -A .ai_cache 2>/dev/null)" ]; then
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add .ai_cache/
if ! git diff --cached --quiet; then
git commit -m "🤖 Auto-update AI summary cache [skip ci]"
git push
echo "✅ 自动提交了新的 AI 缓存文件"
else
echo " 没有新的缓存文件需要提交"
fi
else
echo " 没有找到缓存目录或缓存为空"
fi

11
.gitignore vendored
View File

@ -1,11 +0,0 @@
# 环境变量文件(敏感信息)
.env
.env.local
.env.*.local
*.key
# MkDocs 构建输出目录
site/
# AI 摘要缓存目录(项目根目录)- 需要被提交
!.ai_cache/

View File

@ -1,4 +1,3 @@
{
"bitoAI.codeCompletion.enableAutoCompletion": false,
"Codegeex.RepoIndex": true
"bitoAI.codeCompletion.enableAutoCompletion": false
}

View File

@ -1,16 +1,15 @@
# Wcowin for MkDocs主题
- **基于Material for MkDocs美化**
- **简洁美观,功能多元化**
- **可自定义样式,中文教程详细**
- **简单易上手,小白配置**
- **𝕙𝕒𝕧𝕖 𝕒 𝕘𝕠𝕠𝕕 𝕥𝕚𝕞𝕖 !**
**- 基于Material for MkDocs美化**
**- 简洁美观,功能多元化**
**- 可自定义样式,中文教程详细**
**- 简单易上手,小白配置**
**- 𝕙𝕒𝕧𝕖 𝕒 𝕘𝕠𝕠𝕕 𝕥𝕚𝕞𝕖 !**
## 目录
- [Wcowin for MkDocs主题](#wcowin-for-mkdocs主题)
- [目录](#目录)
- [展示](#展示)
- [来自Claude的肯定](#来自claude的肯定)
- [如何快速使用](#如何快速使用)
- [视频教程](#视频教程)
- [Connect with me](#connect-with-me)
@ -53,10 +52,6 @@
</center>
## 来自Claude的肯定
![image](https://s1.imagehub.cc/images/2025/07/12/509bbab32399e1b22942d259c1433d09.png)
## 如何快速使用
首先,建议在虚拟环境下安装 mkdocs-material

View File

@ -1 +0,0 @@
google.com, pub-2327435979273742, DIRECT, f08c47fec0942fa0

View File

@ -6,7 +6,6 @@ hide:
- footer
- feedback
comments: false
hide_reading_time: true
---
# 首頁
@ -159,6 +158,17 @@ hide_reading_time: true
[^see-how-much-I-love-you]:All problems in computer science can be solved by another level of indirection
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-29HZMNR0KG"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-29HZMNR0KG');
</script>
<!-- Start of Howxm client code snippet -->
<script>
function _howxm(){_howxmQueue.push(arguments)}

View File

@ -20,18 +20,17 @@ status: new
</center> -->
<div class="flip-container">
<div class="image-container">
<img src="https://pic4.zhimg.com/v2-a0456a5f527c1923f096759f2926012f_1440w.jpg" alt="Back Image">
<img src="https://picx.zhimg.com/v2-fb22186d2490043435a72876950492f5_1440w.jpg" alt="Front Image">
<img src="https://pic4.zhimg.com/v2-a0456a5f527c1923f096759f2926012f_1440w.jpg" alt="Back Image">
</div>
</div>
<style>
.flip-container {
position: relative;
width: 280px;
height: 280px;
width: 290px;
height: 290px;
margin: 10px auto;
display: flex;
align-items: flex-start;
@ -42,8 +41,8 @@ status: new
.image-container {
position: relative;
position: relative;
width: 280px;
height: 280px;
width: 290px;
height: 290px;
}
.image-container img {
position: absolute;
@ -51,12 +50,9 @@ status: new
left: 0;
width: 100%;
height: 100%;
object-fit: cover; /* 图片填满容器 */
border-radius: 50%;
border: 4px solid #ffffff; /* 白色边框 */
box-shadow: 0 8px 24px rgba(14, 30, 37, 0.15); /* 阴影 */
backface-visibility: hidden; /* 隐藏背面 */
transition: transform 0.6s ease-in-out; /* 仅对transform过渡 */
display: block;
transition: all 1s;
}
.image-container img:first-child {
z-index: 1;
@ -78,7 +74,6 @@ status: new
</style>
<!-- <center>
![](https://picx.zhimg.com/v2-fb22186d2490043435a72876950492f5_1440w.jpg#only-light){style="width: 270px; border-radius: 50%;"}
@ -88,42 +83,175 @@ status: new
</center> -->
<!-- <p style="text-align: center; font-size: 35px; "><strong>A college student in Chongqing</strong></p> -->
<br>
<center><font size=6rem color= #757575>
观史知今,当思进退,读书明志可识春秋
<center><font size=6 color= #757575>
观史知今,当思进退,读书明志可识春秋
<br>
——Wcowin </font></center>
---
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js"></script>
<link rel="stylesheet" href="../sty/portfolio.css">
</head>
<body>
<main class="main">
<section class="about section" id="about">
<div class="about__container container">
<div class="about__data">
<p style="text-align: center;"></p>
<div class="about__info">
<div>
<span class="about__info-title">1年+</span>
<span class="about__info-name">工作经验</span>
</div>
<div>
<a href="https://github.com/Wcowin" target="_blank">
<span class="about__info-title">3项+</span>
<span class="about__info-name">完成的项目</span>
</a>
</div>
<div>
<span class="about__info-title">2个+</span>
<span class="about__info-name">贡献的开源</span>
</div>
</div>
</div> <!-- 闭合 about__data -->
</div> <!-- 闭合 about__container -->
</section>
</main> <!-- 闭合 main -->
</body>
</html>
<!-- <center>
[下载简历 :fontawesome-solid-download:](个人简历2.pdf){.md-button target="_blank"}
</center> -->
<center>
<a href="../个人简历.pdf" target="_blank" class="md-button">下载简历</a>
<a href="../个人简历2.pdf" target="_blank" class="md-button">下载简历</a>
</center>
<!-- <div class="card2 file-block" markdown="1">
<div class="file-icon"><img src="https://pic4.zhimg.com/80/v2-98f918276ecbc6d549fa6a5d1238e713_1440w.webp" style="height: 3em;"></div>
<div class="file-body">
<div class="file-title">个人简历</div>
<div class="file-meta">2025-02-14</div>
</div>
<a class="down-button" target="_blank" href="../个人简历.pdf" markdown="1">:fontawesome-solid-download: 下载</a>
</div> -->
---
## 我的履历
<html lang="en">
<body>
<section class="qualification section">
<div class="qualification__container container">
<div class="qualification__tabs">
<div class="qualification__button button--flex qualification__active" data-target='#education'>
<iconify-icon icon="fluent:hat-graduation-12-regular" class="qualification__icon"></iconify-icon>
来时路
</div>
</div>
<div class="qualification__sections">
<!-- 教育经历时间线 -->
<div class="qualification__content qualification__active" data-content id="education">
<!-- 高中 -->
<div class="qualification__data">
<div>
<h3 class="qualification__title">漯河高中</h3>
<span class="qualification__subtitle">平凡的三年</span>
<div class="qualification__calendar">
<font color= #757575><iconify-icon icon="tabler:calendar"></iconify-icon>
2018 - 2021</font>
</div>
</div>
<div>
<span class="qualification__rounder"></span>
<span class="qualification__line"></span>
</div>
</div>
<div class="qualification__data">
<div></div>
<div>
<span class="qualification__rounder"></span>
<span class="qualification__line"></span>
</div>
<div>
<h3 class="qualification__title">CTBU</h3>
<span class="qualification__subtitle">电子信息工程专业学士</span>
<div class="qualification__calendar">
<font color= #757575><iconify-icon icon="tabler:calendar"></iconify-icon>
2021 - 2025</font>
</div>
</div>
</div>
<div class="qualification__data">
<div>
<h3 class="qualification__title">家里蹲大学</h3>
<span class="qualification__subtitle">密码学硕士研究生</span>
<div class="qualification__calendar">
<font color= #757575><iconify-icon icon="tabler:calendar"></iconify-icon>
2026 - 2029</font>
</div>
</div>
<div>
<span class="qualification__rounder"></span>
<span class="qualification__line"></span>
</div>
</div>
<div class="qualification__data">
<div></div>
<div>
<span class="qualification__rounder"></span>
<span class="qualification__line"></span>
</div>
<div>
<h3 class="qualification__title">未完待续</h3>
<span class="qualification__subtitle">于道各努力,千里自同风</span>
<div class="qualification__calendar">
<font color= #757575><iconify-icon icon="tabler:calendar"></iconify-icon>
Before - After</font>
</div>
</div>
</div>
</div> <!-- 闭合 qualification__content -->
</div> <!-- 闭合 qualification__sections -->
</div> <!-- 闭合 qualification__container -->
</section>
</body>
</html>
<HR style="FILTER: progid:DXImageTransform.Microsoft.Shadow(color:#608DBD,direction:145,strength:15)" width="100%" color=#608DBD SIZE=1>
## 个人简介
<!-- <p style="text-align: center; font-size: 25px; margin: 0px;"><strong>𝘿𝙤𝙣'𝙩 𝙘𝙖𝙧𝙚 𝙖𝙗𝙤𝙪𝙩 𝙬𝙤𝙧𝙡𝙙𝙡𝙮 𝙚𝙮𝙚𝙨 𝙩𝙤 𝙥𝙪𝙧𝙨𝙪𝙚 𝙮𝙤𝙪𝙧 𝙤𝙬𝙣 𝙡𝙞𝙜𝙝𝙩</strong></p> -->
<p style="text-align: center; font-size: 25px; margin: 0px;"><strong>𝘿𝙤𝙣'𝙩 𝙘𝙖𝙧𝙚 𝙖𝙗𝙤𝙪𝙩 𝙬𝙤𝙧𝙡𝙙𝙡𝙮 𝙚𝙮𝙚𝙨 𝙩𝙤 𝙥𝙪𝙧𝙨𝙪𝙚 𝙮𝙤𝙪𝙧 𝙤𝙬𝙣 𝙡𝙞𝙜𝙝𝙩</strong></p>
<!-- https://s1.imagehub.cc/images/2024/02/02/79cb7379982d1c7bb0ae7163985609c4.jpeg -->
!!! pied-piper1 "About me"
- [x] Hey, I'm [Wcowin](https://wcowin.work/VitePress/){target=“_blank”}~
- [x] 咖啡重度爱好者
- [x] 热爱(xiā)折腾技术/数学,目前的研究领域是[密码学](https://wcowin.work/blog/Cryptography/)
- [x] 读书明志;诗词爱好者;喜欢村上春树;擅长羽毛球
- [x] 清醒知趣,明得失,知进退
- [x] 热爱(xiā)折腾技术/Math目前的研究领域是[密码学](https://ctf-wiki.org/crypto/introduction/){target=“_blank”}
- [x] 读书明志可识春秋;诗词爱好者;喜欢村上春树;擅长羽毛球
- [x] 清醒知趣,明得失,知进退
## 人生态度
<p style="text-align: center; font-size: 25px; margin: 0px;"><strong>𝘿𝙤𝙣'𝙩 𝙘𝙖𝙧𝙚 𝙖𝙗𝙤𝙪𝙩 𝙬𝙤𝙧𝙡𝙙𝙡𝙮 𝙚𝙮𝙚𝙨 𝙩𝙤 𝙥𝙪𝙧𝙨𝙪𝙚 𝙮𝙤𝙪𝙧 𝙤𝙬𝙣 𝙡𝙞𝙜𝙝𝙩</strong></p>
<!-- <img class="img1" src="https://pic2.zhimg.com/80/v2-6cf497fc08da090bd53e4a5dc962d9d9_1440w.webp"> -->
<img class="img1" src="https://pic1.zhimg.com/80/v2-8030915c744322fb1e3a6ec0b8fed24c_1440w.webp">
<img class="img1" src="https://pic2.zhimg.com/80/v2-6cf497fc08da090bd53e4a5dc962d9d9_1440w.webp">
<!-- <head>
## 联系我
<head>
<style>
@media (min-width: 768px) {
.mobile-only {
@ -205,102 +333,16 @@ status: new
</div>
</div>
</div> -->
## 联系我
=== "💬 微信"
<center>
<img src="https://picx.zhimg.com/80/v2-540df18f16032fbe114dd960da21b467_1440w.webp" style="width: 280px; height: auto; border-radius: 12px; border: 3px solid white;">
<br>
扫一扫上面的二维码图案<br>
加我为朋友
</center>
=== "📧 邮箱"
<div style="text-align: center; padding: 5px 0px;">
<div style="margin-bottom: 15px;">
</div>
<a href="mailto:wangkewen821@gmail.com" class="md-button md-button--primary"
style="font-size: 16px; padding: 12px 30px; border-radius: 25px;">
:fontawesome-solid-paper-plane: &nbsp; 发送电子邮件
</a>
<div style="margin-top: 20px;">
<p style="font-size: 20px; color: #757575; margin-top: 5px;">
💡24小时内回复请耐心等待
</p>
</div>
</div>
=== "🌐 社交"
<div class="contact-tab-container">
<div class="contact-tab-content" style="text-align: center; padding: 0px 0;">
<div style="margin-bottom: 25px;">
<p style="font-size: 16px; color: var(--md-default-fg-color--light); margin-bottom: 20px;">
关注我的社交媒体,获取最新动态
</p>
</div>
<!-- 修改按钮布局 - 移动端也保持左右排列 -->
<div style="display: flex; justify-content: center; gap: 12px; flex-wrap: wrap; margin-bottom: 25px; min-height: 50px; align-items: center;">
<a href="https://t.me/Wcowin" class="md-button md-button--primary"
style="display: inline-flex; align-items: center; gap: 6px; padding: 10px 16px; border-radius: 25px; background: linear-gradient(135deg, #0088cc, #0066aa); color: white; text-decoration: none; font-size: 14px; min-width: 120px; justify-content: center;" target="_blank">
:fontawesome-brands-telegram: Telegram
</a>
<a href="https://twitter.com/wcowin_" class="md-button md-button--primary"
style="display: inline-flex; align-items: center; gap: 6px; padding: 10px 16px; border-radius: 25px; background: linear-gradient(135deg, #1da1f2, #0d8bd9); color: white; text-decoration: none; font-size: 14px; min-width: 120px; justify-content: center;" target="_blank">
:fontawesome-brands-twitter: Twitter
</a>
</div>
<div style="max-width: 500px; margin: 0 auto;">
<img src="https://s1.imagehub.cc/images/2025/01/04/ac7fda1814bb1e18714f9dd9f5d87636.png"
style="width: 100%; height: auto; border-radius: 10px;">
</div>
</div>
</div>
=== "📍 其他"
<div style="text-align: center; padding: 0px 0px;">
<div style="margin-bottom: 30px;">
<p style="font-size: 15px; color: var(--md-default-fg-color--light);">
通过下列平台了解我的更多工作和项目经历
</p>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 24px; max-width: 700px; margin: 0 auto;">
<!-- GitHub -->
<div style="padding: 20px; border-radius: 16px; background: var(--md-code-bg-color); border: 1px solid var(--md-default-fg-color--lightest); box-shadow: 0 4px 10px rgba(0,0,0,0.05); transition: transform 0.3s, box-shadow 0.3s;"
onmouseover="this.style.transform='translateY(-6px)'; this.style.boxShadow='0 10px 20px rgba(0,0,0,0.08)'"
onmouseout="this.style.transform='none'; this.style.boxShadow='0 4px 10px rgba(0,0,0,0.05)'">
<div style="font-size: 26px; margin-bottom: 12px;">🌟</div>
<h4 style="margin: 0 0 10px 0; color: var(--md-primary-fg-color); font-size: 17px;">GitHub</h4>
<a href="https://github.com/Wcowin" class="md-button" style="font-size: 14px;" target="_blank">
:fontawesome-brands-github: 查看 GitHub
</a>
</div>
<!-- LinkedIn -->
<div style="padding: 20px; border-radius: 16px; background: var(--md-code-bg-color); border: 1px solid var(--md-default-fg-color--lightest); box-shadow: 0 4px 10px rgba(0,0,0,0.05); transition: transform 0.3s, box-shadow 0.3s;"
onmouseover="this.style.transform='translateY(-6px)'; this.style.boxShadow='0 10px 20px rgba(0,0,0,0.08)'"
onmouseout="this.style.transform='none'; this.style.boxShadow='0 4px 10px rgba(0,0,0,0.05)'">
<div style="font-size: 26px; margin-bottom: 12px;">💼</div>
<h4 style="margin: 0 0 10px 0; color: var(--md-primary-fg-color); font-size: 17px;">LinkedIn</h4>
<a href="https://www.linkedin.com/in/wcowin/" class="md-button" style="font-size: 14px;" target="_blank">
:fontawesome-brands-linkedin: 查看档案
</a>
</div>
</div>
<!-- 底部强调卡片 -->
<div style="margin-top: 40px; padding: 20px; border-radius: 12px; background: linear-gradient(135deg, var(--md-primary-fg-color--light), var(--md-primary-fg-color)); color: white; box-shadow: 0 6px 15px rgba(0,0,0,0.1);">
<p style="margin: 0; font-size: 16px; font-weight: 600;">
随时欢迎联系我合作或交流!
</p>
<p style="margin: 10px 0 0 0; font-size: 14px; opacity: 0.9;">
无论是技术探讨、学习交流还是职业机会,我都乐意听见你的声音 😄
</p>
</div>
</div>
??? tip "公众号"
<figure markdown >
![Image title](https://s1.imagehub.cc/images/2025/01/04/ac7fda1814bb1e18714f9dd9f5d87636.png){.img1 }
<figcaption>公众号</figcaption>
</figure>
---
> 💬我电话号码的`MD5`7037F514864088F907CC921687B670EE破解有奖
@ -313,7 +355,14 @@ status: new
如果给我发[邮件](mailto:<wangkewen821@gmail.com>),或者通过右下角微信添加好友,请写上您的**真名实姓**。对于那些不知来路、上来就问问题的微信和邮件,我通常会**直接忽略**,谢谢。
---
<center>
[发送电子邮件 :fontawesome-solid-paper-plane:](mailto:<wcowin@qq.com>){.md-button}
</center>
<center>[发送电子邮件 :fontawesome-solid-paper-plane:](mailto:<wcowin@qq.com>){.md-button}</center>
<!-- 我的电话号码`SHA256`DEF633030D31F7ABE6213EE5B5EFDF0E4ADDFDA121695325660D82F15ED22946 -->
<!-- <chat-bot platform_id="d19a99ed-b684-4d64-8c70-7663d974af17" user_id="325b3ae2-0317-4c5f-9f9b-c4ce0e51e36b" chatbot_id="8eedef48-41ef-4f78-97d9-71e8197a452d"><a href="https://www.chatsimple.ai/?utm_source=widget&utm_medium=referral">[chatbot]</a></chat-bot><script src="https://cdn.chatsimple.ai/chat-bot-loader.js" defer></script> -->
<!-- <script src="//code.tidio.co/6jmawe9m5wy4ahvlhub2riyrnujz7xxi.js" async></script> -->

View File

@ -37,35 +37,15 @@ t.parentNode.insertBefore(e,t)}})();
<img class="ava" src="https://pic4.zhimg.com/80/v2-a0456a5f527c1923f096759f2926012f_1440w.webp" />
<div class="card-header">
<div>
<a href="https://wcowin.work/ ">Wcowin's Blog</a>
<a href="https://wcowin.work/ " target="_blank">Wcowin's Blog</a>
</div>
<div class="info">
“循此苦旅,以达星辰”
</div>
</div>
</div>
<div class="card">
<img class="ava" src="https://s1.imagehub.cc/images/2025/06/03/526b59b6a2e478f2ffa1629320e3e2ce.png" />
<div class="card-header">
<div>
<a href="https://wcowin.work/Mkdocs-AI-Summary-Plus/MkDocs-AI-Summary/">MkDocs AI Summary</a>
</div>
<div class="info">
AI驱动的摘要生成
</div>
</div>
</div>
<div class="card">
<img class="ava" src="https://s1.imagehub.cc/images/2025/06/06/a4584dbad4da3f87eb5c2f1e7ed14a74.png" />
<div class="card-header">
<div>
<a href="https://wcowin.work/mkdocs-reading-time/">MkDocs Reading Time</a>
</div>
<div class="info">
为MkDocs文档添加准确阅读时间统计功能
</div>
</div>
</div>
</div>
@ -79,7 +59,7 @@ t.parentNode.insertBefore(e,t)}})();
<h2>失联人员</h2>
</div> -->
<HR style="FILTER: progid:DXImageTransform.Microsoft.Shadow(color:#EEF3FE),direction:145,strength:15)" width="100%" color=#EEF3FE SIZE=1>
<HR style="FILTER: progid:DXImageTransform.Microsoft.Shadow(color:#608DBD,direction:145,strength:15)" width="100%" color=#608DBD SIZE=1>
<script>
document.addEventListener("DOMContentLoaded", function () {
@ -127,7 +107,7 @@ t.parentNode.insertBefore(e,t)}})();
</div>
</div>
</div>
<HR style="FILTER: progid:DXImageTransform.Microsoft.Shadow(color:#EEF3FE,direction:145,strength:15)" width="100%" color= #EEF3FE SIZE=1>
<HR style="FILTER: progid:DXImageTransform.Microsoft.Shadow(color:#608DBD,direction:145,strength:15)" width="100%" color=#608DBD SIZE=1>
</div>

View File

@ -28,6 +28,8 @@ comments: false
---
<iframe src="/assets/个人简历.pdf" width="100%" height="1250px" style="border: 1.5px solid #ccc; overflow: auto; border-radius: 18px; background: #fff;"></iframe>
<iframe src="../个人简历2.pdf" width="100%" height="1200px" style="border: 1px solid #ccc; overflow: auto;">
</iframe>
</div>

View File

@ -1,31 +1,54 @@
:root {
/* --header-height: 3rem; */
/*========== Colors ==========*/
--first-color: #608DBD;
--first-color-second: #608DBD;
--first-color-alt: #608DBD;
--first-color-lighter: #608DBD;
--title-color: #608DBD;
--text-color: #757575;
/* --text-color-light: #f5ab17; */
--input-color: #F5EEE3;
/* --body-color: #FDFBF8;
--container-color: #FFFFFF; */
/*========== Font and typography ==========*/
--big-font-size: 2rem;
--h1-font-size: 1.5rem;
--h2-font-size: 1.25rem;
--h3-font-size: 1.125rem;
--normal-font-size: .938rem;
--small-font-size: .813rem;
--smaller-font-size: .70rem;
/*========== Font weight ==========*/
--font-medium: 500;
--font-semi-bold: 600;
/*========== Margins ==========*/
/*========== Margenes Bottom ==========*/
--mb-0-25: .25rem;
--mb-0-5: .5rem;
--mb-0-75: .75rem;
--mb-1: 1rem;
--mb-1-5: 1.5rem;
--mb-2: 2rem;
--mb-2-5: 2.5rem;
--mb-3: 3rem;
/*========== z index ==========*/
/* --z-tooltip: 10;
--z-fixed: 100;
--z-modal: 1000; */
}
/* Font size for large devices */
@media screen and (min-width: 968px) {
:root {
--big-font-size: 3rem;
--h1-font-size: 2.25rem;
--h2-font-size: 1.5rem;
--h3-font-size: 1.25rem;
--normal-font-size: 1rem;
@ -35,14 +58,145 @@
}
/*==================== BASE ====================*/
html {
scroll-behavior: smooth;
}
h1, h2, h3, h4 {
color: var(--title-color);
font-weight: var(--font-semi-bold);
}
/*==================== REUSABLE CSS CLASSES ====================*/
.section__title {
font-size: var(--h1-font-size);
color: var(--title-color);
}
.section__subtitle {
display: block;
font-size: var(--small-font-size);
margin-bottom: var(--mb-3);
}
.section__title,
.section__subtitle {
text-align: center;
}
/*==================== LAYOUT ====================*/
.container {
max-width: 768px;
margin-left: var(--mb-1-5);
margin-right: var(--mb-1-5);
}
.grid {
display: grid;
gap: 1.5rem;
}
.header {
width: 100%;
position: fixed;
bottom: 0;
left: 0;
z-index: var(--z-fixed);
background-color: var(--body-color);
}
/*==================== HOME ====================*/
.home__container {
gap: 1rem;
}
.home__content {
grid-template-columns: .5fr 3fr;
padding-top: 3.5rem;
align-items: center;
}
.home__social {
grid-template-columns: max-content;
row-gap: 1rem;
}
.home__social-icon {
font-size: 1.25rem;
}
.home__social-icon:hover {
color: var(--first-color-alt);
}
.home__title {
font-size: var(--big-font-size);
}
.home__subtitle {
margin-bottom: var(--h3-font-size);
color: var(--text-color);
font-weight: var(--font-medium);
margin-bottom: var(--mb-0-75);
}
.home__description {
margin-bottom: var(--mb-2);
}
/*==================== BUTTONS ====================*/
.button {
display: inline-block;
background-color: var(--first-color-alt);
color: #fff;
padding: 1rem;
border-radius: .5rem;
font-weight: var(--font-medium);
}
.button:hover {
background-color: var(--first-color-alt);
}
.button__icon {
font-size: 1.25rem;
margin-right: var(--mb-0-5);
transition: .3s;
color: #FFF;
}
.button--flex {
display: inline-flex;
align-items: center;
}
.button--small {
padding: .75rem 1rem;
}
.button--link {
padding: 0;
background-color: transparent;
color: var(--first-color);
}
.button--link:hover {
background-color: transparent;
color: var(--first-color-alt);
}
/*==================== ABOUT ====================*/
.about__img {
width: 400px;
border-radius: .5rem;
justify-self: center;
align-self: center;
}
.about__description {
text-align: center;
margin-bottom: var(--mb-2-5);
}
.about__info {
display: flex;
@ -66,6 +220,142 @@ h1, h2, h3, h4 {
text-align: center;
}
.about__buttons {
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
}
/*==================== SKILLS ====================*/
.skills__container {
row-gap: 0;
}
.skills__header {
display: flex;
align-items: center;
margin-bottom: var(--mb-2-5);
cursor: pointer;
}
.skills__icon,
.skills__arrow {
font-size: 2rem;
color: #757575;
}
.skills__icon {
margin-right: var(--mb-0-75);
}
.skills__title {
font-size: var(--h3-font-size);
}
.skills_subtitle {
font-size: var(--small-font-size);
color: var(--text-color-light);
}
.skills__arrow {
margin-left: auto;
transition: .4s;
}
.skills__list {
row-gap: 1.5rem;
padding-left: 2.7rem;
}
.skills__titles {
display: flex;
justify-content: space-between;
margin-bottom: var(--mb-0-5);
}
.skills__name {
font-size: var(--normal-font-size);
font-weight: var(--font-medium);
}
.skills__number {
margin-top: var(--mb-2-5);
}
.skills__bar,
.skills__percentage {
height: 5px;
border-radius: .25rem;
}
.skills__bar {
background-color: var(--first-color-lighter);
}
.skills__percentage {
display: block;
background-color: var(--first-color);
}
.skills__fastapi {
width: 90%;
}
.skills__django {
width: 85%;
}
.skills__flask {
width: 75%;
}
.skills__spring {
width: 75%;
}
.skills__androidjava {
width: 70%;
}
.skills__androidkotlin {
width: 60%;
}
.skills__flutter {
width: 60%;
}
.skills__aws {
width: 80%;
}
.skills__azure {
width: 75%;
}
.skills__oc {
width: 70%;
}
.skills__cicd {
width: 75%;
}
.skills__close .skills__list {
height: 0;
overflow: hidden;
}
.skills__open .skills__list {
height: max-content;
margin-bottom: var(--mb-2-5);
}
.skills__open .skills__arrow {
transform: rotate(-180deg);
}
/*==================== QUALIFICATION ====================*/
.qualification__tabs {
display: flex;
@ -97,11 +387,11 @@ h1, h2, h3, h4 {
.qualification__data > div:first-child {
text-align: center;
}
.qualification__data > div:last-child {
text-align: center;
}
.qualification__title {
font-size: var(--normal-font-size);
font-weight: var(--font-medium);
@ -145,3 +435,162 @@ h1, h2, h3, h4 {
.qualification__button.qualification__active {
color: var(--first-color);
}
/*==================== MEDIA QUERIES ====================*/
/* For small devices */
@media screen and (max-width: 350px) {
.container {
margin-left: var(--mb-1);
margin-right: var(--mb-1);
}
.home__content {
grid-template-columns: .25fr 3fr;
}
.home__blob {
width: 180px;
}
.skills__title {
font-size: var(--normal-font-size);
}
.qualification__data {
gap: .5rem;
}
}
/* For medium devices */
@media screen and (min-width: 568px) {
.home__content {
grid-template-columns: max-content 1fr 1fr;
}
.home__data {
grid-column: initial;
}
.home__img {
order: 1;
justify-self: center;
}
.skills__container,
.about__container,
.portfolio__content,
.project__container {
grid-template-columns: repeat(2, 1fr);
}
.qualification__sections {
display: grid;
grid-template-columns: 6fr;
justify-content: center;
}
}
@media screen and (min-width: 768px) {
.container {
margin-left: auto;
margin-right: auto;
}
body {
margin: 0;
}
.section {
padding: 2rem 0 1rem;
}
.section__subtitle {
margin-bottom: 4rem;
}
.header {
top: 0;
bottom: initial;
}
.header,
.main {
padding: 0 1rem;
}
.home__container {
row-gap: 5rem;
}
.home__content {
padding-top: 5.5rem;
column-gap: 2rem;
}
.home__blob {
width: 270px;
}
.home__scroll {
display: block;
}
.home__scroll-button {
margin-left: 3rem;
}
.about__container {
column-gap: 5rem;
}
.about__img {
width: 350px;
}
.about__description {
text-align: initial;
}
.about__info {
justify-content: space-between;
}
.about__buttons {
justify-content: initial;
}
.qualification__tabs {
justify-content: center;
}
.qualification__button {
margin: 0 var(--mb-1);
}
.qualification__sections {
grid-template-columns: .5fr;
}
}
/* For large devices */
@media screen and (min-width: 1024px) {
.header,
.main {
padding: 0;
}
.home__blob {
width: 320px;
}
.home__social {
transform: translateX(-6rem);
}
.qualification__sections {
display: grid;
grid-template-columns: 6fr;
justify-content: center;
}
}

View File

@ -1,15 +1,6 @@
---
title: 测试小组件
# status: new
---
# 测试小组件
!!!info
测试中的小组件,可看源代码自行取用
<center><font color= #518FC1 size=6 class="ml3">“循此苦旅 以达星辰”</font></center>
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/2.0.2/anime.min.js"></script>

View File

@ -1 +0,0 @@
google.com, pub-2327435979273742, DIRECT, f08c47fec0942fa0

View File

@ -1,310 +0,0 @@
---
title: 如何给MKdocs添加友链
tags:
- Mkdocs
hide:
- feedback
---
复制后在需要添加友链的.md 文件页面粘贴即可
```html hl_lines="82-126"
<div class="post-body">
<div id="links">
<style>
/* 友链容器样式 */
.link-navigation {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 1rem;
max-width: 100%;
}
/* 通用卡片样式 */
.card {
width: 100%;
max-width: 320px;
height: 90px;
font-size: 1rem;
padding: 10px 20px;
border-radius: 25px;
transition: transform 0.15s, box-shadow 0.15s, background 0.15s;
display: flex;
align-items: center;
color: #333;
justify-self: center;
}
.card:hover {
transform: translateY(0px) scale(1.05);
background-color: rgba(68, 138, 255, 0.1);
color: #040000;
}
.card a {
border: none;
}
.card .ava {
width: 3rem !important;
height: 3rem !important;
margin: 0 !important;
margin-right: 1em !important;
border-radius: 50%;
}
.card .card-header {
font-style: italic;
overflow: hidden;
width: auto;
}
.card .card-header a {
font-style: normal;
color: #608DBD;
font-weight: bold;
text-decoration: none;
}
.card .card-header a:hover {
color: #d480aa;
text-decoration: none;
}
.card .card-header .info {
font-style: normal;
color: #706f6f;
font-size: 14px;
min-width: 0;
overflow: visible;
white-space: normal;
}
/* 小屏优化 */
@media (max-width: 768px) {
.link-navigation {
grid-template-columns: 1fr;
gap: 0.8rem;
}
.card {
width: 100%;
max-width: 100%;
height: auto;
min-height: 80px;
}
.card:hover {
background-color: rgba(68, 138, 255, 0.1);
}
}
</style>
<div class="links-content">
<div class="link-navigation">
<div class="card">
<img class="ava" src="https://avatars.githubusercontent.com/jaywhj" />
<div class="card-header">
<div>
<a href="https://jaywhj.netlify.app/" target="_blank">极简主义</a>
</div>
<div class="info">文档即产品</div>
</div>
</div>
<div class="card">
<img class="ava" src="https://i.stardots.io/wcowin/1750089315509.png" />
<div class="card-header">
<div>
<a href="https://wcowin.work/" target="_blank">Wcowins blog</a>
</div>
<div class="info">这是一个分享技术的小站。</div>
</div>
</div>
<div class="card">
<img class="ava" src="https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png" />
<div class="card-header">
<div>
<a href="https://twitter.com/" target="_blank">Twitter</a>
</div>
<div class="info">社交分享平台</div>
</div>
</div>
<div class="card">
<img class="ava" src="https://i.stardots.io/wcowin/1750220860750.jpg" />
<div class="card-header">
<div>
<a href="https://macapp.org.cn" target="_blank">Macapp</a>
</div>
<div class="info">一个专注于分享Mac资源的频道</div>
</div>
</div>
<div class="card">
<img class="ava" src="https://i.stardots.io/wcowin/1750221795613.jpeg" />
<div class="card-header">
<div>
<a href="{link}" target="_blank">{name}</a>
</div>
<div class="info">{description}</div>
</div>
</div>
</div>
</div>
</div>
</div>
```
## 如何加入友链
```html
<div class="card">
<img class="ava" src="{avatarurl}" />
<div class="card-header">
<div>
<a href="{link}" target="_blank">{name}</a>
</div>
<div class="info">{description}</div>
</div>
</div>
```
## 效果
<!-- <div>
<div class="links-content">
<div class="link-navigation">
<div class="card">
<img class="ava" src="https://pic4.zhimg.com/80/v2-a0456a5f527c1923f096759f2926012f_1440w.webp" />
<div class="card-header">
<div>
<a href="https://wcowin.work/ " target=“_blank”>Wcowins blog</a>
</div>
<div class="info">
这是一个分享技术的小站。
</div>
</div>
</div>
</div> -->
<div class="post-body">
<div id="links">
<style>
/* 友链容器样式 */
.link-navigation {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 1rem;
max-width: 100%;
}
/* 通用卡片样式 */
.card {
width: 100%;
max-width: 320px;
height: 90px;
font-size: 1rem;
padding: 10px 20px;
border-radius: 25px;
transition: transform 0.15s, box-shadow 0.15s, background 0.15s;
display: flex;
align-items: center;
color: #333;
justify-self: center;
}
.card:hover {
transform: translateY(0px) scale(1.05);
background-color: rgba(68, 138, 255, 0.1);
color: #040000;
}
.card a {
border: none;
}
.card .ava {
width: 3rem !important;
height: 3rem !important;
margin: 0 !important;
margin-right: 1em !important;
border-radius: 50%;
}
.card .card-header {
font-style: italic;
overflow: hidden;
width: auto;
}
.card .card-header a {
font-style: normal;
color: #608DBD;
font-weight: bold;
text-decoration: none;
}
.card .card-header a:hover {
color: #d480aa;
text-decoration: none;
}
.card .card-header .info {
font-style: normal;
color: #706f6f;
font-size: 14px;
min-width: 0;
overflow: visible;
white-space: normal;
}
/* 小屏优化 */
@media (max-width: 768px) {
.link-navigation {
grid-template-columns: 1fr;
gap: 0.8rem;
}
.card {
width: 100%;
max-width: 100%;
height: auto;
min-height: 80px;
}
.card:hover {
background-color: rgba(68, 138, 255, 0.1);
}
}
</style>
<div class="links-content">
<div class="link-navigation">
<div class="card">
<img class="ava" src="https://avatars.githubusercontent.com/jaywhj" />
<div class="card-header">
<div>
<a href="https://jaywhj.netlify.app/" target="_blank">极简主义</a>
</div>
<div class="info">文档即产品</div>
</div>
</div>
<div class="card">
<img class="ava" src="https://i.stardots.io/wcowin/1750089315509.png" />
<div class="card-header">
<div>
<a href="https://wcowin.work/" target="_blank">Wcowins blog</a>
</div>
<div class="info">这是一个分享技术的小站。</div>
</div>
</div>
<div class="card">
<img class="ava" src="https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png" />
<div class="card-header">
<div>
<a href="https://twitter.com/" target="_blank">Twitter</a>
</div>
<div class="info">社交分享平台</div>
</div>
</div>
<div class="card">
<img class="ava" src="https://i.stardots.io/wcowin/1750220860750.jpg" />
<div class="card-header">
<div>
<a href="https://macapp.org.cn" target="_blank">Macapp</a>
</div>
<div class="info">一个专注于分享Mac资源的频道</div>
</div>
</div>
<div class="card">
<img class="ava" src="https://i.stardots.io/wcowin/1750221795613.jpeg" />
<div class="card-header">
<div>
<a href="{link}" target="_blank">{name}</a>
</div>
<div class="info">{description}</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -83,7 +83,7 @@ Mkdocs Materials 教程:
## 一点请求
<font color= #518FC1 size=6 class="ml3">创作不易。如果您参考了本教程/借鉴了网页设计,如果可以的话,烦请在您的网站注明教程来源!感谢!</font>
<font color= #518FC1 size=6 class="ml3">如果您参考了本教程,如果可以的话,烦请在您的网站注明教程来源!感谢!</font>
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/2.0.2/anime.min.js"></script>
示例:

View File

@ -6,7 +6,6 @@ hide:
# - feedback
# comments: false
# icon: octicons/home-fill-24
hide_reading_time: true
---
# MyBlog

View File

@ -7,7 +7,6 @@ description: >
a blog alongside your documentation or standalone
categories:
- Hello World
hide_reading_time: true
---

View File

@ -5,7 +5,6 @@ date: 2022-06-06
categories:
- 网站更新记录
readtime: 2
hide_reading_time: true
---
## </p><h1 id="01" name="01"><strong>2022-10-20</strong></h1><p>

View File

@ -5,7 +5,6 @@ date: 2023-12-21
categories:
- 网站更新记录
readtime: 2
hide_reading_time: true
---
## </p><h1 id="01" name="01"><strong>2023-12-21</strong></h1><p>

View File

@ -5,7 +5,6 @@ date: 2024-01-01
categories:
- 网站更新记录
readtime: 2
hide_reading_time: true
---
## </p><h1 id="01" name="01"><strong>2024-12-24</strong></h1><p>
* 优化网站流畅度(玄学)

View File

@ -5,22 +5,7 @@ date: 2025-01-01
categories:
- 网站更新记录
readtime: 2
hide_reading_time: true
---
## </p><h1 id="01" name="01"><strong>2025-07-08</strong></h1><p>
* 优化网站流畅度(玄学)
* AI摘要全局更换为智谱清言GLM
## </p><h1 id="01" name="01"><strong>2025-07-08</strong></h1><p>
* 优化网站流畅度(玄学)
* 修复[友链教程](../Mkdocs/linktech.md)的一些显示问题(感谢[Arron](https://github.com/jaywhj)
* 重新设计了页脚样式
## </p><h1 id="01" name="01"><strong>2025-06-15</strong></h1><p>
* 优化网站流畅度(玄学)
* 优化CDN配置
* 引入全局圆角化设计,参考[Material Design](https://material.io/design)设计规范
## </p><h1 id="01" name="01"><strong>2025-04-30</strong></h1><p>
* 优化网站流畅度(玄学)
* 修复[关于](../../about/geren.md)页面的显示问题
@ -35,7 +20,7 @@ hide_reading_time: true
## </p><h1 id="01" name="01"><strong>2025-04-10</strong></h1><p>
* 优化网站流畅度(玄学)
* 优化[友链](../../link.md)统计方式,更加准确计数
* 优化[友链](../../about/link.md)统计方式,更加准确计数
* 修复评论区重定位的bug防止他人网站测试评论区导致评论区覆盖到我的网站
## </p><h1 id="01" name="01"><strong>2025-03-30</strong></h1><p>

View File

@ -1,589 +0,0 @@
---
title: MkDocs文档AI摘要
tags:
- Mkdocs
status: new
---
# MkDocs AI Hooks
![logo 2](https://s1.imagehub.cc/images/2025/06/06/ee327dc2912fd2f31d38ee8a16a1e1ff.png){.img1}
仓库地址:[https://github.com/Wcowin/Mkdocs-AI-Summary](https://github.com/Wcowin/Mkdocs-AI-Summary)
🌐 **在线演示**:[https://wcowin.work/Mkdocs-AI-Summary-Plus/](https://wcowin.work/Mkdocs-AI-Summary-Plus/)
<p align="center">
<img src="https://img.shields.io/badge/MkDocs-Hooks-526CFE?style=for-the-badge&logo=MaterialForMkDocs&logoColor=white" alt="MkDocs Hooks">
<img src="https://img.shields.io/badge/AI_Powered-DeepSeek-FF6B35?style=for-the-badge&logo=openai&logoColor=white" alt="AI Powered">
<img src="https://img.shields.io/badge/Python-3.7+-3776AB?style=for-the-badge&logo=python&logoColor=white" alt="Python 3.7+">
</p>
<p align="center">
<a href="https://github.com/Wcowin/mkdocs-ai-hooks/blob/main/README.md">中文</a> | <a href="https://github.com/Wcowin/mkdocs-ai-hooks/blob/main/README-en.md">English</a>
</p>
🚀 **您的MkDocs文档首选智能摘要**
这个项目利用MkDocs hooks为您的技术文档和博客添加AI驱动的摘要生成和智能阅读统计功能。
![预览图1](https://s1.imagehub.cc/images/2025/06/03/d1563500263b22cfd0ffc3679993aa83.jpg)
![预览图2](https://s1.imagehub.cc/images/2025/06/03/526b59b6a2e478f2ffa1629320e3e2ce.png)
---
## ✨ 功能特性
### 🤖 AI智能摘要
- **多AI服务集成**: 支持DeepSeek、OpenAI、Claude、Gemini等主流AI服务
- **自动摘要生成**: 生成高质量的80-120字智能摘要
- **多语言支持**: 支持中文、英文、双语摘要生成
- **智能内容清理**: 自动过滤YAML、HTML、代码块等格式内容
- **备用摘要机制**: API失败时提供基于关键词的本地摘要
- **智能缓存系统**: 7天智能过期避免重复API调用
- **灵活配置**: 支持文件夹级别和页面级别的精确控制
### 📊 智能阅读统计(可选)
- **精准字符统计**: 专门优化的中英文内容识别
- **智能代码检测**: 识别30+编程语言和命令行代码
- **阅读时间估算**: 基于语言特性的智能计算中文400字/分钟英文200词/分钟)
- **美观信息展示**: 使用MkDocs Material风格的信息框
### 🚀 智能化特性
- **环境自适应**: 自动识别CI/本地环境,本地或者部署都可选启用/禁用
- **自动语言识别**: 支持30+编程语言和标记语言
- **内容类型检测**: 区分代码、配置、命令行等不同内容
- **LRU缓存优化**: 提升处理性能Todo
- **完善错误处理**: 异常处理和日志记录Todo
---
## 📦 快速安装
### 方法1: 直接下载(推荐)
**步骤1**: 下载文件
- 从 [Releases页面](https://github.com/Wcowin/mkdocs-ai-hooks/releases) 下载最新版本
- 或直接下载 `ai_summary.py`文件
**步骤2**: 创建目录并放置文件
```bash
# 在您的MkDocs项目根目录下执行
mkdir -p docs/overrides/hooks/
mv ai_summary.py docs/overrides/hooks/
```
**步骤3**: 配置MkDocs主题以及覆写路径
```yaml
# 在 mkdocs.yml 中添加
theme:
name: material
custom_dir: docs/overrides # 必需配置!!!
features:
- content.code.copy
- content.code.select
```
### 方法2: Git克隆
```bash
git clone https://github.com/Wcowin/mkdocs-ai-hooks.git
cd mkdocs-ai-hooks
pip install -r requirements.txt
```
### 依赖安装
```bash
pip install -r requirements.txt
```
---
## 🚀 快速开始
### 1. 基础配置
**步骤1**: 配置hooks
ai_summary.py务必放到docs/overrides/hooks目录下然后
```yaml
# 在 mkdocs.yml 中添加
hooks:
- docs/overrides/hooks/ai_summary.py # AI摘要hook
```
**步骤2**: 本地配置
根目录下创建 `.env` 文件存放密钥(记得添加到 `.gitignore`
```bash
# .env 文件内容
DEEPSEEK_API_KEY=your_deepseek_api_key_here
OPENAI_API_KEY=your_openai_api_key_here
```
```bash
#.gitignore 文件内容
# 环境变量文件(敏感信息)
.env
.env.local
.env.*.local
*.key
# MkDocs 构建输出目录
site/
# AI 摘要缓存目录(项目根目录)- 需要被提交
!.ai_cache/
```
到这里检查下目录树状图:
```
$ tree -a
文件名
├── .github
│ ├── .DS_Store
│ └── workflows
│ └── ci.yml
├── docs
│ └── index.md
| └── overrides
│ └── hooks
│ └── ai_summary.py
├── .env
├──.gitignore
├── README.md
└── mkdocs.yml
```
### 2. 配置AI服务
**选择AI服务提供商**
- 🌟 **DeepSeek**(推荐):性价比高,中文表现优秀
- 🔥 **OpenAI**:功能强大,广泛支持
- ⚡ **Claude**:逻辑清晰,文本理解佳
- 🧠 **Gemini**Google出品多语言支持
**获取API密钥**
- [DeepSeek](https://platform.deepseek.com/usage) - 注册获取API密钥
- [ChatAnywhere](https://github.com/chatanywhere/GPT_API_free) - 免费OpenAI额度
**获取的密钥存放于上一步创建的`.env` 文件中!!!**
### 3. 设置参数
`ai_summary.py` 中配置需要AI摘要的目录
```python
# 📂 启用AI摘要的文件夹
self.enabled_folders = [
'blog/', # 博客文章
# 添加更多文件夹...
]
```
### 4. 本地运行和测试
```bash
mkdocs serve # 本地预览
```
### 5. 部署配置
```yaml
#ci.yml
name: ci
on:
push:
branches:
- master
- main
# 禁止从 fork 仓库访问 secrets
pull_request:
types: [closed]
branches: [main, master]
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
sparse-checkout: |
docs
includes
requirements.txt
.ai_cache
- uses: actions/setup-python@v4
with:
python-version: 3.x
- name: Set cache ID
run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
- uses: actions/cache@v3
with:
key: mkdocs-material-${{ github.run_number }}
path: .cache
restore-keys: |
mkdocs-material-
- run: pip install mkdocs-git-revision-date-localized-plugin
- run: pip install mkdocs-git-authors-plugin
- run: pip install mkdocs-git-committers-plugin-2
- run: pip install markdown-callouts
- run: pip install mkdocs-rss-plugin
- run: pip install requests>=2.25.0
- run: pip install python-dateutil>=2.8.0
- run: pip install cachetools>=4.2.0
- run: pip install python-dotenv>=0.19.0
- run: pip install pymdown-extensions
- run: pip install mkdocs-material
- run: pip install --upgrade --force-reinstall mkdocs-material
- name: Deploy with AI Summary
env:
# AI摘要开关控制
AI_SUMMARY_CI_ENABLED: 'true' # CI部署环境启用AI摘要 (true=在CI中为文章生成AI摘要)
AI_SUMMARY_CI_ONLY_CACHE: 'true' # CI部署不生成新摘要 (true=使用本地部署过的摘要缓存不再重复调用API)
AI_SUMMARY_CI_FALLBACK: 'true' # CI部署启用备用摘要 (true=API失败时生成离线基础摘要)
# AI_SUMMARY_LOCAL_ENABLED: 'false' # 本地部署环境禁用AI摘要 (true=本地开发时也生成摘要)(不需要管这条)
# AI_SUMMARY_CACHE_ENABLED: 'true' # 本地启用缓存功能 (true=缓存摘要避免重复生成)(不需要管这条)
# API密钥配置
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: mkdocs gh-deploy --force
# 自动提交新生成的AI缓存文件
- name: Auto-commit AI cache (if any new files)
run: |
if [ -d ".ai_cache" ] && [ "$(ls -A .ai_cache 2>/dev/null)" ]; then
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add .ai_cache/
if ! git diff --cached --quiet; then
git commit -m "🤖 Auto-update AI summary cache [skip ci]"
git push
echo "✅ 自动提交了新的 AI 缓存文件"
else
echo " 没有新的缓存文件需要提交"
fi
else
echo " 没有找到缓存目录或缓存为空"
fi
```
```python
# ai_summary.py 中配置
# AI摘要本地环境配置
self.ci_config = {
# CI部署环境开关 (不用管只在ci.yml中设置有效)
'enabled_in_ci': os.getenv('AI_SUMMARY_CI_ENABLED', 'true').lower() == 'true',
# 本地部署环境开关 (true=本地开发时启用AI摘要)
'enabled_in_local': os.getenv('AI_SUMMARY_LOCAL_ENABLED', 'true').lower() == 'true',
# CI部署仅缓存模式(不用管只在ci.yml中设置有效)
'ci_only_cache': os.getenv('AI_SUMMARY_CI_ONLY_CACHE', 'false').lower() == 'true',
# 本地部署缓存功能开关 (true=启用缓存避免重复生成, false=总是生成新摘要)
'cache_enabled': os.getenv('AI_SUMMARY_CACHE_ENABLED', 'true').lower() == 'true',
# CI部署备用摘要开关 (不用管只在ci.yml中设置有效)
'ci_fallback_enabled': os.getenv('AI_SUMMARY_CI_FALLBACK', 'true').lower() == 'true',
}
```
**几种运行模式**
1. **完全禁用**: 本地和CI部署都不运行摘要生成
2. **仅CI部署启用**: 本地禁用CI部署生成新摘要
3. **缓存模式**本地已经生成过摘要CI部署使用缓存**推荐。上方配置项中已默认CI部署的缓存模式可自行搭配选择**
4. **完全启用**: 本地和CI部署都运行(API消耗会更多)
### 6. GitHub Secrets配置
**步骤1**: 设置Repository Secrets
1. 进入GitHub仓库 → **Settings****Secrets and variables** → **Actions**
2. 点击 **New repository secret** 添加:
```
DEEPSEEK_API_KEY=your_deepseek_api_key_here
OPENAI_API_KEY=your_openai_api_key_here
```
![image](https://s1.imagehub.cc/images/2025/06/04/b5fd63d839bb6443c8560a5f690d2c41.png)
---
然后部署到GitHub Pages或其他平台即可。
**有报错可以去问ChatGPT或者在Issues中提问。**
## 📖 使用指南
### AI摘要控制
#### 方法1: 页面级控制(推荐)
在Markdown文件最上面的yaml meta中
**启用AI摘要**
```yaml
---
title: 文章标题
ai_summary: true # 启用AI摘要
---
```
**禁用AI摘要**
```yaml
---
title: 文章标题
ai_summary: false # 禁用AI摘要
description: 自定义摘要内容 # 可选手动摘要
---
```
#### 方法2: 文件夹级控制
```python
# 在 ai_summary.py 中配置
# 📂 可自定义的文件夹配置
self.enabled_folders = [
'blog/', # blog文件夹
'index.md',
# 'develop/', # develop文件夹
# 'posts/', # posts文件夹
# 'trip/', # trip文件夹
# 'about/', # about文件夹
]
# 📋 Excluded files and folders
self.exclude_patterns = [
'404.md', 'tag.md', 'tags.md',
]
# 📋 Excluded specific files
self.exclude_files = [
'blog/index.md',
]
```
---
## 🎨 显示效果
### AI摘要显示
**实际效果预览**
![image](https://s1.imagehub.cc/images/2025/06/04/152205c10ef1bfd7658b383a3e5e6e9f.png)
### 💰 成本说明
- **单次费用**: 约0.03-0.05元(中大型文档)
- **月度预估**: 普通博客约1-5元
- **免费额度**: 多数AI服务商提供新用户免费额度
---
## ⚙️ 高级配置
### 自定义AI服务
```python
# 添加新的AI服务
self.ai_services = {
'your_service': {
'url': 'https://api.yourservice.com/v1/chat/completions',
'model': 'your-model',
'api_key': os.getenv('YOUR_API_KEY'),
'max_tokens': 150,
'temperature': 0.3
}
}
# 默认使用的AI服务
self.default_service = 'your_service'
# 服务优先级(按顺序尝试)
self.service_fallback_order = ['openai', 'deepseek', 'claude', 'gemini'] # 按顺序尝试
```
### 自定义提示词
```python
def generate_ai_summary(self, content, page_title=""):
prompt = f"""请为以下技术文档生成一个简洁的中文摘要80-120字
文章标题:{page_title}
文章内容:{content[:2500]}
要求:
1. 突出核心技术要点
2. 使用简洁专业的语言
3. 长度控制在80-120字
"""
```
### 缓存配置
```python
# 修改缓存过期时间
cache_time = datetime.fromisoformat(cache_data.get('timestamp', '1970-01-01'))
if (datetime.now() - cache_time).days < 30: # 改为30天
return cache_data
```
---
## 🌍 多语言支持
### 语言配置
```python
# 在 ai_summary.py 中设置
self.summary_language = 'zh' # 中文摘要
# self.summary_language = 'en' # 英文摘要
# self.summary_language = 'both' # 双语摘要
```
### 支持的语言
- **完全支持**: 中文、English
- **部分支持**: 日本語です、한글、Français、Deutsch
---
## 📊 性能优化
### 已实现优化
- **LRU缓存**: 函数级别缓存提升性能
- **正则预编译**: 提高文本处理速度
- **智能过滤**: 减少不必要的API调用
- **内容哈希**: 基于内容变化的智能缓存
### 性能建议
- 使用 `ci_only_cache: true` 在CI环境中仅使用缓存
- 合理设置 `enabled_folders` 避免处理不必要的文件
- 定期清理过期缓存文件
---
## 🤝 贡献指南
### 如何贡献
1. **Fork** 这个仓库
2. 创建特性分支
3. 提交更改
4. 推送分支
5. 创建 **Pull Request**
### 开发环境
```bash
git clone https://github.com/Wcowin/mkdocs-ai-hooks.git
cd mkdocs-ai-hooks
pip install -r requirements.txt
```
---
## 📝 更新日志
### [v1.3.0] (2025-06-04) - 最新版本
#### 核心改进
- **统一缓存架构**
- **缓存路径统一为项目根目录 .ai_cache**
- **本地和 CI 环境使用相同缓存策略**
- **增强 CI/CD 支持****支持 CI 仅缓存模式,大幅减少部署时间**
- **智能识别 15+ 部署平台GitHub Actions、GitLab CI 等)**
- **可配置备用摘要机制**
### [v1.2.0] (2025-06-03)
#### ✨ 主要新功能
- **多AI服务支持**: 集成DeepSeek、OpenAI、Gemini、Claude
- **环境自适应**: 自动识别CI/本地环境
- **智能缓存系统**: 内容哈希缓存7天自动过期
- **安全配置**: GitHub Secrets集成API密钥安全管理
#### 🔧 技术改进
- **统一API接口**: 自适配不同AI服务格式
- **错误处理增强**: 完善的异常处理机制
- **性能优化**: LRU缓存和正则预编译
### [v1.0.0] (2025-06-01) - 初始版本
- 🤖 **AI智能摘要功能**
- 📖 **阅读时间统计功能**
- 💾 **基础缓存系统**
- 🎯 **基本配置选项**
---
## 🐛 问题反馈
遇到问题?请在 [Issues](https://github.com/Wcowin/mkdocs-ai-hooks/issues) 中反馈。
**反馈时请包含**
- MkDocs版本
- Python版本
- 完整错误信息
- 复现步骤
- 配置文件(去除敏感信息)
---
## 📄 许可证
本项目采用 [MIT License](https://github.com/Wcowin/mkdocs-ai-hooks/blob/main/LICENSE) 开源协议。
---
## 🙏 致谢
感谢以下项目和服务:
- [MkDocs](https://www.mkdocs.org/) - 优秀的静态站点生成器
- [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) - 精美的主题
- [DeepSeek](https://deepseek.com/) - 高性价比的AI API服务
- 所有贡献者和使用者
---
# Connect with me
<center>
**Telegram**
<p align="center">
<a href="https://t.me/wecowin" target="_blank">
<img src="https://pica.zhimg.com/80/v2-d5876bc0c8c756ecbba8ff410ed29c14_1440w.webp" alt="个人名片" style="border-radius: 10px;" width="50%">
</a>
</p>
**Wechat**
<!-- ![](https://s1.imagehub.cc/images/2024/02/02/bb9ee71b03ee7a3b87caad5cc4bcebff.jpeg) -->
<p align="center">
<img src="https://pic3.zhimg.com/80/v2-5ef3dde831c9d0a41fe35fabb0cb8784_1440w.webp" style="border-radius: 10px;" width="50%">
</p>
</center>
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=Wcowin/mkdocs-ai-hooks&type=Date)](https://www.star-history.com/#Wcowin/mkdocs-ai-hooks&Date)
## 请作者喝杯咖啡
<p align="center">
<a href="https://s1.imagehub.cc/images/2025/05/11/36eb33bf18f9041667267605b6b99bd0.jpeg" target="_blank">
<center>
<img src="https://s1.imagehub.cc/images/2025/05/11/36eb33bf18f9041667267605b6b99bd0.jpeg" style="width: 450px; height: auto; border-radius: 25px;" >
</center>
</a>
</center>
<p align="center">
如果这个项目对您有帮助,请给它一个 ⭐ Star
</p>
<p align="center">
<a href="https://github.com/Wcowin/mkdocs-ai-hooks/stargazers">
<img src="https://img.shields.io/github/stars/Wcowin/mkdocs-ai-hooks?style=social" alt="Stars">
</a>
<a href="https://github.com/Wcowin/mkdocs-ai-hooks/network/members">
<img src="https://img.shields.io/github/forks/Wcowin/mkdocs-ai-hooks?style=social" alt="Forks">
</a>
</p>
📝 本项目致力于让MkDocs文档更加智能化和用户友好。如有建议或想法欢迎交流

View File

@ -11,7 +11,7 @@ tags:
<iframe src="../个人简历.pdf (相对地址)" width="100%" height="800px" style="border: 1px solid #ccc; overflow: auto;"></iframe>
```
<img width="1178" alt="image" src="https://s1.imagehub.cc/images/2025/05/11/af422a556586fa0ed42f7adcf5a694ae.png" />
<img width="1178" alt="image" src="https://github.com/user-attachments/assets/5b511fe9-9a3b-4bff-b54c-37d92f00306b" />
我的完整代码:

View File

@ -1,374 +0,0 @@
---
title: Mkdocs 阅读时间插件
tags:
- Mkdocs
status: new
---
## 1. 基础配置
### **步骤1**
创建reading_time.py
??? note "reading_time.py"
```python
import re
import threading
import time
from functools import lru_cache
from collections import OrderedDict
import hashlib
# 预编译正则表达式(性能优化版本)
EXCLUDE_PATTERNS = [
re.compile(r'^index\.md$'),
re.compile(r'^trip/index\.md$'),
re.compile(r'^relax/index\.md$'),
re.compile(r'^blog/indexblog\.md$'),
re.compile(r'^blog/posts\.md$'),
re.compile(r'^develop/index\.md$'),
re.compile(r'waline\.md$'),
re.compile(r'link\.md$'),
re.compile(r'404\.md$'),
]
# 高度优化的正则表达式(一次性编译)
CHINESE_CHARS_PATTERN = re.compile(r'[\u4e00-\u9fff\u3400-\u4dbf]')
CODE_BLOCK_PATTERN = re.compile(r'```.*?```', re.DOTALL)
INLINE_CODE_PATTERN = re.compile(r'`[^`]+`')
YAML_FRONT_PATTERN = re.compile(r'^---.*?---\s*', re.DOTALL)
HTML_TAG_PATTERN = re.compile(r'<[^>]+>')
IMAGE_PATTERN = re.compile(r'!\[.*?\]\([^)]+\)')
LINK_PATTERN = re.compile(r'\[([^\]]+)\]\([^)]+\)')
# 预定义排除类型
EXCLUDE_TYPES = frozenset({'landing', 'special', 'widget'})
# 扩展非编程行内代码词汇(更全面的过滤)
NON_CODE_WORDS = frozenset({
'markdown', 'target', 'blank', 'lg', 'middle', 'small', 'large',
'left', 'right', 'center', 'top', 'bottom', 'primary', 'secondary',
'success', 'warning', 'danger', 'info', 'light', 'dark', 'grid',
'cards', 'octicons', 'bookmark', 'div', 'class', 'img', 'src',
'alt', 'width', 'height', 'style', 'id', 'data', 'href', 'title'
})
# 支持的编程和标记语言(扩展版本)
PROGRAMMING_LANGUAGES = frozenset({
# 编程语言
'python', 'py', 'javascript', 'js', 'typescript', 'ts', 'java', 'cpp', 'c',
'go', 'rust', 'php', 'ruby', 'swift', 'kotlin', 'csharp', 'cs',
# 脚本语言
'bash', 'sh', 'powershell', 'ps1', 'zsh', 'fish', 'bat', 'cmd',
# 标记和配置语言
'html', 'css', 'scss', 'sass', 'less', 'yaml', 'yml', 'json', 'xml',
'toml', 'ini', 'conf', 'dockerfile', 'makefile',
# 数据库和查询
'sql', 'mysql', 'postgresql', 'sqlite', 'mongodb',
# 其他
'r', 'matlab', 'scala', 'perl', 'lua', 'dart', 'tex', 'latex',
# 数据格式
'csv', 'properties',
# 无标识符(空字符串也算作有效语言)
''
})
@lru_cache(maxsize=256)
def clean_markdown_content_for_chinese(content_hash, markdown):
"""清理Markdown内容只保留中文文本用于统计添加缓存"""
content = markdown
# 使用预编译的正则表达式
content = YAML_FRONT_PATTERN.sub('', content)
content = HTML_TAG_PATTERN.sub('', content)
content = IMAGE_PATTERN.sub('', content)
content = LINK_PATTERN.sub(r'\1', content)
content = CODE_BLOCK_PATTERN.sub('', content)
content = INLINE_CODE_PATTERN.sub('', content)
return content
def count_code_lines(markdown):
"""统计代码行数(修复版本 - 正确处理所有代码行)"""
code_blocks = CODE_BLOCK_PATTERN.findall(markdown)
total_code_lines = 0
for i, block in enumerate(code_blocks):
# 提取语言标识
lang_match = re.match(r'^```(\w*)', block)
language = lang_match.group(1).lower() if lang_match else ''
# 移除开头的语言标识和结尾的```
code_content = re.sub(r'^```\w*\n?', '', block)
code_content = re.sub(r'\n?```$', '', code_content)
# 过滤空代码块
if not code_content.strip():
continue
# 计算有效行数(包含所有非空行,包括注释行)
lines = [line for line in code_content.split('\n') if line.strip()]
line_count = len(lines)
# 如果有明确的编程语言标识,直接统计
if language and language in PROGRAMMING_LANGUAGES:
total_code_lines += line_count
continue
# 增强的检测策略 - 更宽松的判断
is_code = False
# 1. 命令行检测
command_indicators = [
'sudo ', 'npm ', 'pip ', 'git ', 'cd ', 'ls ', 'mkdir ', 'rm ', 'cp ', 'mv ',
'chmod ', 'chown ', 'grep ', 'find ', 'ps ', 'kill ', 'top ', 'cat ', 'echo ',
'wget ', 'curl ', 'tar ', 'zip ', 'unzip ', 'ssh ', 'scp ', 'rsync ',
'xattr ', 'codesign ', 'xcode-select ', 'spctl ', 'launchctl ',
'brew ', 'defaults ', 'ditto ', 'hdiutil ', 'diskutil ',
'dir ', 'copy ', 'xcopy ', 'del ', 'rd ', 'md ', 'type ', 'attrib ',
'$ ', '# ', '% ', '> ', 'C:\\>', 'PS>',
'--', '-r', '-d', '-f', '-v', '-h', '--help', '--version',
'--force', '--deep', '--sign', '--master-disable',
'/Applications/', '/usr/', '/etc/', '/var/', '/home/', '~/',
'C:\\', 'D:\\', '.app', '.exe', '.pkg', '.dmg', '.zip', '.tar',
'#!/',
]
if any(indicator in code_content for indicator in command_indicators):
is_code = True
# 2. 编程语法检测(增强版)
if not is_code:
programming_indicators = [
# Python语法特征
'def ', 'class ', 'import ', 'from ', 'return ', 'yield ', 'lambda ',
'with ', 'as ', 'try:', 'except:', 'finally:', 'elif ', 'if __name__',
'print(', '.append(', '.extend(', '.remove(', '.sort(', '.reverse(',
'range(', 'len(', 'str(', 'int(', 'float(', 'list(', 'dict(',
# JavaScript/TypeScript语法
'function', 'var ', 'let ', 'const ', 'async ', 'await ', '=>',
'console.log', 'document.', 'window.', 'require(',
# 通用编程语法
'public ', 'private ', 'protected ', 'static ', 'void ', 'int ',
'string ', 'boolean ', 'float ', 'double ', 'char ',
# 操作符和结构
'==', '!=', '<=', '>=', '&&', '||', '++', '--', '+=', '-=', '**',
# 特殊结构
'while ', 'for ', 'if ', 'else:', 'switch ', 'case ',
# HTML/XML语法
'<!DOCTYPE', '<html', '<head', '<body', '<div', '<span', '<p>',
'<style', '<script', '<link', '<meta', '<title', '<img',
# CSS语法
'display:', 'color:', 'background:', 'margin:', 'padding:',
'font-size:', 'width:', 'height:', 'position:', 'border:',
# YAML语法
'name:', 'version:', 'theme:', 'title:', 'description:',
# JSON语法
'{"', '"}', '":', '",', '[{', '}]', 'null', 'true', 'false',
# 配置文件语法
'[', ']', '//', '/*', '*/', '<!--', '-->',
# SQL语法
'SELECT ', 'FROM ', 'WHERE ', 'INSERT ', 'UPDATE ', 'DELETE ',
'CREATE ', 'ALTER ', 'DROP ', 'INDEX ', 'TABLE ',
# 数学公式和LaTeX
'\\', '$', '$$', '\\begin', '\\end', '\\frac', '\\sum',
]
if any(indicator in code_content for indicator in programming_indicators):
is_code = True
# 3. 结构化检测
if not is_code:
# 缩进结构检测
if len(lines) > 1 and any(line.startswith(' ') or line.startswith('\t') for line in lines):
is_code = True
# HTML标签结构
elif '<' in code_content and '>' in code_content:
is_code = True
# 包含特殊字符组合
elif any(char in code_content for char in ['{', '}', '(', ')', '[', ']']) and ('=' in code_content or ':' in code_content):
is_code = True
# 4. 模式匹配检测(宽松策略)
if not is_code and len(lines) >= 1:
special_patterns = [
r'\w+\(\)', r'\w+\[\]', r'\w+\{\}', r'\w+=\w+', r'\w+:\w+',
r'<\w+>', r'\$\w+', r'#\w+', r'@\w+', r'\w+\.\w+\(\)',
r'\d+\.\d+\.\d+', r'http[s]?://', r'ftp://', r'localhost',
r'def\s+\w+', r'class\s+\w+', r'import\s+\w+', r'from\s+\w+',
r'if\s+\w+', r'while\s+\w+', r'for\s+\w+', r'return\s+\w*',
r'\w+\s*=\s*\w+', r'\w+\.\w+', r'#.*输出', r'#.*结果'
]
if any(re.search(pattern, code_content) for pattern in special_patterns):
is_code = True
# 如果判断为代码,则统计行数
if is_code:
total_code_lines += line_count
return total_code_lines
def calculate_reading_stats(markdown):
"""计算中文字符数和代码行数"""
# 生成内容哈希用于缓存
content_hash = hash(markdown)
# 使用缓存的清理函数
clean_content = clean_markdown_content_for_chinese(content_hash, markdown)
chinese_chars = len(CHINESE_CHARS_PATTERN.findall(clean_content))
# 统计代码行数
code_lines = count_code_lines(markdown)
# 计算阅读时间中文400字/分钟)
reading_time = max(1, round(chinese_chars / 400))
return reading_time, chinese_chars, code_lines
def on_page_markdown(markdown, **kwargs):
page = kwargs['page']
# 快速排除检查
if page.meta.get('hide_reading_time', False):
return markdown
# 保持原有的EXCLUDE_PATTERNS循环检查方式
src_path = page.file.src_path
for pattern in EXCLUDE_PATTERNS:
if pattern.match(src_path):
return markdown
# 优化类型检查
page_type = page.meta.get('type', '')
if page_type in EXCLUDE_TYPES:
return markdown
# 快速预检查
if len(markdown) < 300:
return markdown
# 计算统计信息
reading_time, chinese_chars, code_lines = calculate_reading_stats(markdown)
# 过滤太短的内容
if chinese_chars < 50:
return markdown
# 生成阅读信息
if code_lines > 0:
reading_info = f"""!!! info "📖 阅读信息"
阅读时间:**{reading_time}** 分钟 | 中文字符:**{chinese_chars}** | 有效代码行数:**{code_lines}**
"""
else:
reading_info = f"""!!! info "📖 阅读信息"
阅读时间:**{reading_time}** 分钟 | 中文字符:**{chinese_chars}**
"""
return reading_info + markdown
```
### **步骤2**
把reading_time.py放到docs/overrides/hooks目录下然后在mkdocs.yml中添加
```yaml
# 在 mkdocs.yml 中添加
hooks:
- docs/overrides/hooks/reading_time.py # 阅读时间统计
```
### **步骤3**
配置MkDocs主题以及覆写路径custom_dir
```yaml
# 在 mkdocs.yml 中添加
theme:
name: material
custom_dir: docs/overrides # 必需配置!!!
features:
- content.code.copy
- content.code.select
```
到这里检查下目录树状图:
```
$ tree -a
文件名
├── .github
│ ├── .DS_Store
│ └── workflows
│ └── ci.yml
├── docs
│ └── index.md
| └── overrides
│ └── hooks
│ └── reading_time.py
│ └── ...
└── mkdocs.yml
```
### **步骤4**
```bash
mkdocs serve # 本地预览
```
## 2. 效果展示
![image](https://s1.imagehub.cc/images/2025/06/06/a4584dbad4da3f87eb5c2f1e7ed14a74.png)
## 3.高级配置
### 3.1 排除特定页面
如果有一些页面不想统计阅读时间,可以在页面的元数据中添加 `hide_reading_time: true`。例如:
```markdown
---
title: 不统计阅读时间的页面
hide_reading_time: true
---
```
或者直接在reading_time.py中添加
```python
# 你想排除的页面路径
EXCLUDE_PATTERNS = [
re.compile(r'^index\.md$'),
re.compile(r'^trip/index\.md$'),
re.compile(r'^relax/index\.md$'),
re.compile(r'^blog/indexblog\.md$'),
re.compile(r'^blog/posts\.md$'),
re.compile(r'^develop/index\.md$'),
re.compile(r'waline\.md$'),
re.compile(r'link\.md$'),
re.compile(r'404\.md$'),
]
```
### 3.2 自定义统计信息
如果需要自定义统计信息的格式可以修改reading_time.py中的calculate_reading_stats函数。例如
```python
def calculate_reading_stats(markdown):
# 计算统计信息
reading_time, chinese_chars, code_lines = calculate_reading_stats(markdown)
# 自定义统计信息格式
if code_lines > 0:
reading_info = f"""!!! info "📖 阅读信息"
阅读时间:**{reading_time}** 分钟 | 中文字符:**{chinese_chars}** | 有效代码行数:**{code_lines}**
"""
else:
reading_info = f"""!!! info "📖 阅读信息"
阅读时间:**{reading_time}** 分钟 | 中文字符:**{chinese_chars}**
"""
return reading_info + markdown
```

View File

@ -1,534 +0,0 @@
---
title: 为MKdocs页面添加相关文章推荐
tags:
- Mkdocs
status: new
---
# 为MKdocs页面添加相关文章推荐
## 步骤
`mkdocs.yml`中需要覆写文件夹overrides(没有的话新建一个)
```yaml
theme:
name: material
custom_dir: docs/overrides
```
在docs/overrides/hooks/下新建一个`related_posts.py`文件即可,内容如下:
具体配置根据自己仓库情况自行修改
```python
import os
import re
from collections import Counter, defaultdict
from textwrap import dedent
import hashlib
import yaml
from urllib.parse import urlparse
# 存储所有文章的信息和索引
article_index = {}
category_index = defaultdict(list)
keyword_index = defaultdict(set)
# 配置:需要索引的目录
INDEXED_DIRECTORIES = ['blog/', 'develop/']
# 配置:排除推荐的页面列表(支持精确匹配和模式匹配)
EXCLUDED_PAGES = {
# 精确路径匹配
'blog/index.md',
'develop/index.md',
# 可以添加更多排除的页面
# 'blog/special-page.md',
}
# 配置:排除推荐的页面模式(支持通配符)
EXCLUDED_PATTERNS = [
r'.*\/index\.md$', # 排除所有 index.md 文件
r'.*\/archive\.md$', # 排除所有 archive.md 文件
r'blog\/posts?\/.*', # 排除 blog/post/ 和 blog/posts/ 目录下的所有文章
# 可以添加更多模式
# r'blog\/draft\/.*', # 排除草稿目录
]
# 配置:相似度阈值和权重
SIMILARITY_CONFIG = {
'min_threshold': 0.15, # 提高最低相似度阈值
'weights': {
'keywords': 0.35, # 关键词权重
'tags': 0.30, # 标签权重
'categories': 0.20, # 分类权重
'path': 0.10, # 路径分类权重
'source_dir': 0.05 # 源目录权重
},
'title_similarity': 0.25 # 标题相似度权重
}
def is_page_excluded(file_path):
"""检查页面是否应该排除推荐"""
# 精确匹配检查
if file_path in EXCLUDED_PAGES:
return True
# 模式匹配检查
for pattern in EXCLUDED_PATTERNS:
if re.match(pattern, file_path):
return True
return False
def should_index_file(file_path):
"""检查文件是否应该被索引"""
if not file_path.endswith('.md'):
return False
# 先检查是否被排除
if is_page_excluded(file_path):
return False
# 检查是否在指定目录下
for directory in INDEXED_DIRECTORIES:
if file_path.startswith(directory):
return True
return False
def extract_keywords(content, title):
"""提取文章中的关键词,改进算法"""
# 移除YAML front matter
content = re.sub(r'^---\s*\n.*?\n---\s*\n', '', content, flags=re.DOTALL | re.MULTILINE)
# 移除代码块
content = re.sub(r'```.*?```', '', content, flags=re.DOTALL)
# 移除HTML标签
content = re.sub(r'<.*?>', '', content)
# 移除链接
content = re.sub(r'\[.*?\]\(.*?\)', '', content)
# 移除标题标记
content = re.sub(r'^#+\s+', '', content, flags=re.MULTILINE)
# 合并标题和内容,标题权重更高
title_words = re.findall(r'\b\w+\b', title.lower()) * 4 # 增加标题权重
content_words = re.findall(r'\b\w+\b', content.lower())
all_words = title_words + content_words
# 扩展停用词列表(包含中英文)
stopwords = {
# 英文停用词
'the', 'a', 'an', 'in', 'on', 'at', 'to', 'for', 'of', 'and', 'or', 'is', 'are', 'was', 'were',
'be', 'been', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should',
'this', 'that', 'these', 'those', 'with', 'from', 'by', 'as', 'can', 'but', 'not', 'if', 'it',
'they', 'them', 'their', 'you', 'your', 'we', 'our', 'my', 'me', 'i', 'he', 'she', 'him', 'her',
# 常见无意义词
'about', 'above', 'after', 'again', 'all', 'also', 'any', 'because', 'before', 'between',
'both', 'each', 'few', 'first', 'get', 'how', 'into', 'just', 'last', 'made', 'make', 'may',
'most', 'new', 'now', 'old', 'only', 'other', 'over', 'said', 'same', 'see', 'some', 'such',
'take', 'than', 'then', 'time', 'two', 'use', 'very', 'way', 'well', 'where', 'when', 'which',
'while', 'who', 'why', 'work', 'world', 'year', 'years',
# 中文停用词
'的', '了', '和', '是', '就', '都', '而', '及', '与', '这', '那', '有', '在', '中', '为', '对', '等',
'能', '会', '可以', '没有', '什么', '一个', '自己', '这个', '那个', '这些', '那些', '如果', '因为', '所以'
}
# 过滤单词:长度>=2不在停用词中不是纯数字
words = [w for w in all_words
if len(w) >= 2 and w not in stopwords and not w.isdigit()]
# 返回词频最高的15个词
return Counter(words).most_common(15)
def extract_metadata(content):
"""提取文章元数据支持YAML front matter"""
metadata = {
'title': "未命名",
'description': "",
'tags': [],
'categories': [],
'disable_related': False # 新增:是否禁用相关推荐
}
# 尝试解析YAML front matter
yaml_match = re.match(r'^---\s*\n(.*?)\n---\s*\n', content, re.DOTALL)
if yaml_match:
try:
yaml_content = yaml_match.group(1)
yaml_data = yaml.safe_load(yaml_content)
if yaml_data:
metadata['title'] = str(yaml_data.get('title', '未命名')).strip('"\'')
metadata['description'] = str(yaml_data.get('description', '')).strip('"\'')
metadata['disable_related'] = yaml_data.get('disable_related', False)
# 处理tags
tags = yaml_data.get('tags', [])
if isinstance(tags, list):
metadata['tags'] = [str(tag).strip() for tag in tags]
elif isinstance(tags, str):
metadata['tags'] = [tag.strip() for tag in tags.split(',') if tag.strip()]
# 处理categories
categories = yaml_data.get('categories', [])
if isinstance(categories, list):
metadata['categories'] = [str(cat).strip() for cat in categories]
elif isinstance(categories, str):
metadata['categories'] = [cat.strip() for cat in categories.split(',') if cat.strip()]
except yaml.YAMLError:
pass # 如果YAML解析失败使用默认值
# 如果YAML解析失败回退到正则表达式
if metadata['title'] == "未命名":
title_match = re.search(r'^title:\s*(.+)$', content, re.MULTILINE)
if title_match:
metadata['title'] = title_match.group(1).strip('"\'')
return metadata
def get_category_from_path(file_path):
"""从文件路径提取分类"""
parts = file_path.split('/')
if len(parts) > 2:
return parts[1] # blog/category/file.md 或 develop/category/file.md格式
elif len(parts) > 1:
return parts[0] # blog 或 develop
return "未分类"
def calculate_content_hash(content):
"""计算内容哈希,用于检测内容变化"""
return hashlib.md5(content.encode('utf-8')).hexdigest()
def on_files(files, config):
"""预处理所有文章,建立索引"""
global article_index, category_index, keyword_index
# 清空索引
article_index.clear()
category_index.clear()
keyword_index.clear()
processed_count = 0
excluded_count = 0
for file in files:
if should_index_file(file.src_path):
try:
with open(file.abs_src_path, 'r', encoding='utf-8') as f:
content = f.read()
# 提取元数据
metadata = extract_metadata(content)
# 检查是否禁用相关推荐
if metadata.get('disable_related', False):
excluded_count += 1
continue
# 再次检查是否在排除列表中(双重检查)
if is_page_excluded(file.src_path):
excluded_count += 1
continue
# 提取关键词
keywords = extract_keywords(content, metadata['title'])
# 获取分类
path_category = get_category_from_path(file.src_path)
# 构建文章信息
article_info = {
'title': metadata['title'],
'description': metadata['description'],
'tags': metadata['tags'],
'categories': metadata['categories'],
'path_category': path_category,
'keywords': keywords,
'url': file.url,
'path': file.src_path,
'content_hash': calculate_content_hash(content),
'source_dir': file.src_path.split('/')[0] # blog 或 develop
}
# 添加到主索引
article_index[file.src_path] = article_info
# 添加到分类索引
category_index[path_category].append(file.src_path)
for category in metadata['categories']:
if category: # 确保分类不为空
category_index[category].append(file.src_path)
# 添加到关键词索引
for keyword, _ in keywords:
keyword_index[keyword].add(file.src_path)
for tag in metadata['tags']:
if tag: # 确保标签不为空
keyword_index[tag.lower()].add(file.src_path)
processed_count += 1
except Exception as e:
print(f"❌ 处理文件 {file.src_path} 时出错: {e}")
print(f"✅ 已索引 {processed_count} 篇文章 (blog + develop)")
if excluded_count > 0:
print(f"📝 排除 {excluded_count} 篇禁用推荐或在排除列表中的文章")
print(f"📊 分类数量: {len(category_index)}")
print(f"🔤 关键词数量: {len(keyword_index)}")
return files
def on_page_markdown(markdown, **kwargs):
"""为每篇文章添加相关推荐"""
page = kwargs['page']
config = kwargs['config']
# 检查是否应该处理这个页面
if not should_index_file(page.file.src_path):
return markdown
# 检查是否被排除
if is_page_excluded(page.file.src_path):
return markdown
# 检查文章元数据是否禁用推荐
try:
with open(page.file.abs_src_path, 'r', encoding='utf-8') as f:
content = f.read()
metadata = extract_metadata(content)
if metadata.get('disable_related', False):
return markdown
except Exception:
pass # 如果读取失败,继续处理
# 获取相关文章
related_articles = get_related_articles(page.file.src_path, max_count=5)
if not related_articles:
return markdown
# 从 config 中获取 site_url 并解析出基本路径
site_url = config.get('site_url', '')
base_path = urlparse(site_url).path if site_url else '/'
if not base_path.endswith('/'):
base_path += '/'
# 构建推荐HTML - 针对Safari浏览器优化
recommendation_html = "\n"
# 添加CSS样式特别针对Safari浏览器优化
recommendation_html += """<style>
.related-posts {
margin-top: 1.5rem;
padding-top: 0.75rem;
border-top: 1px solid rgba(0,0,0,0.1);
max-height: none !important; /* 防止Safari错误计算高度 */
overflow: visible !important; /* 防止内容被截断 */
}
.related-posts h3 {
margin-top: 0;
margin-bottom: 0.5rem;
font-size: 1.2rem;
font-weight: 500;
line-height: 1.3;
}
.related-posts ul {
margin: 0 0 0.5rem 0 !important; /* 强制覆盖可能的冲突样式 */
padding-left: 1.5rem;
list-style-position: outside;
}
.related-posts li {
margin-bottom: 0.25rem;
line-height: 1.4;
}
/* 暗色模式适配 */
[data-md-color-scheme="slate"] .related-posts {
border-top-color: rgba(255,255,255,0.1);
}
/* Safari特定修复 */
@supports (-webkit-hyphens:none) {
.related-posts {
display: block;
position: relative;
height: auto !important;
}
.related-posts ul {
position: static;
}
}
</style>
"""
# 简化且兼容的HTML结构
recommendation_html += '<div class="related-posts">\n'
recommendation_html += '<h3>📚 相关文章推荐</h3>\n'
recommendation_html += '<ul>\n'
for score, article_info in related_articles:
title = article_info['title']
relative_url = article_info['url']
# 拼接基本路径和文章相对URL并确保路径分隔符正确
full_url = (base_path + relative_url).replace('//', '/')
recommendation_html += f'<li><a href="{full_url}">{title}</a></li>\n'
recommendation_html += '</ul>\n'
recommendation_html += '</div>\n'
# 确保没有多余的空行
return markdown.rstrip() + recommendation_html
def get_related_articles(current_path, max_count=5):
"""获取相关文章,使用改进的算法"""
if current_path not in article_index:
return []
current_article = article_index[current_path]
similarities = []
# 获取当前文章的关键信息
current_title = current_article['title'].lower()
current_tags = set(tag.lower() for tag in current_article['tags'] if tag)
current_categories = set(cat.lower() for cat in current_article['categories'] if cat)
for path, article_info in article_index.items():
if path == current_path:
continue
# 过滤掉标题为"未命名"的文章
if article_info['title'] == "未命名" or not article_info['title'].strip():
continue
# 再次检查是否在排除列表中(双重检查)
if is_page_excluded(path):
continue
# 计算相似度
score = calculate_similarity(current_article, article_info)
# 标题相似度加权
title_similarity = calculate_title_similarity(current_title, article_info['title'].lower())
if title_similarity > 0.3: # 标题有一定相似度
score += title_similarity * SIMILARITY_CONFIG['title_similarity']
# 应用最低阈值
if score > SIMILARITY_CONFIG['min_threshold']:
similarities.append((score, article_info))
# 按相似度排序
similarities.sort(key=lambda x: x[0], reverse=True)
# 多样性优化:确保不同分类的文章都有机会被推荐
if len(similarities) > max_count * 2:
# 按分类分组
category_groups = defaultdict(list)
for score, article in similarities:
for category in article['categories']:
if category:
category_groups[category.lower()].append((score, article))
# 从每个分类中选取最相关的文章
diverse_results = []
used_paths = set()
# 首先添加最相关的文章
if similarities:
top_score, top_article = similarities[0]
diverse_results.append((top_score, top_article))
used_paths.add(top_article['path'])
# 然后从每个分类中添加最相关的文章
for category in sorted(category_groups.keys()):
if len(diverse_results) >= max_count:
break
for score, article in category_groups[category]:
if article['path'] not in used_paths:
diverse_results.append((score, article))
used_paths.add(article['path'])
break
# 如果还有空位,从剩余的高分文章中填充
if len(diverse_results) < max_count:
for score, article in similarities:
if article['path'] not in used_paths and len(diverse_results) < max_count:
diverse_results.append((score, article))
used_paths.add(article['path'])
# 重新按相似度排序
diverse_results.sort(key=lambda x: x[0], reverse=True)
return diverse_results[:max_count]
return similarities[:max_count]
def calculate_title_similarity(title1, title2):
"""计算两个标题的相似度"""
# 分词
words1 = set(re.findall(r'\b\w+\b', title1))
words2 = set(re.findall(r'\b\w+\b', title2))
if not words1 or not words2:
return 0
# 计算Jaccard相似度
intersection = len(words1.intersection(words2))
union = len(words1.union(words2))
if union == 0:
return 0
return intersection / union
def calculate_similarity(article1, article2):
"""计算两篇文章的相似度"""
score = 0
weights = SIMILARITY_CONFIG['weights']
# 1. 关键词相似度
keywords1 = dict(article1['keywords'])
keywords2 = dict(article2['keywords'])
common_keywords = set(keywords1.keys()) & set(keywords2.keys())
if common_keywords:
# 考虑关键词的频率和重要性
keyword_score = sum(min(keywords1[kw], keywords2[kw]) for kw in common_keywords)
# 关键词匹配数量的奖励
keyword_count_bonus = len(common_keywords) / max(len(keywords1), 1) * 0.5
score += (keyword_score + keyword_count_bonus) * weights['keywords']
# 2. 标签相似度
tags1 = set(tag.lower() for tag in article1['tags'] if tag)
tags2 = set(tag.lower() for tag in article2['tags'] if tag)
if tags1 and tags2: # 确保两篇文章都有标签
tag_overlap = len(tags1 & tags2)
tag_ratio = tag_overlap / max(len(tags1), 1) # 相对重叠比例
tag_score = tag_overlap * 8 * (1 + tag_ratio) # 增加重叠比例奖励
score += tag_score * weights['tags']
# 3. 分类相似度
categories1 = set(cat.lower() for cat in article1['categories'] if cat)
categories2 = set(cat.lower() for cat in article2['categories'] if cat)
if categories1 and categories2: # 确保两篇文章都有分类
category_overlap = len(categories1 & categories2)
category_ratio = category_overlap / max(len(categories1), 1)
category_score = category_overlap * 12 * (1 + category_ratio)
score += category_score * weights['categories']
# 4. 路径分类相似度
if article1['path_category'] == article2['path_category']:
score += 3 * weights['path']
# 5. 同源目录加分
if article1.get('source_dir') == article2.get('source_dir'):
score += 2 * weights['source_dir']
return score
```
## 效果如下

View File

@ -20,26 +20,59 @@ comments: false
```css
:root {
--admonition-border-left-width: 0.2rem;
--base-border-radius: 1rem;
/* --card-hover-shadow: 0 0 0.2rem #ffffff40; */
--base-border-radius: 0.5rem;
}
/* 卡片圆角与悬浮阴影 */
.md-typeset .grid.cards > ul > li,
.md-typeset .md-button,
.md-typeset table:not([class]) {
/* Change font family of filename present on top of code block. */
/* .highlight span.filename {
border-bottom: none;
border-radius: var(--base-border-radius);
display: inline;
font-family: var(--md-code-font-family);
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
margin-bottom: 5px;
text-align: center;
}
.highlight span.filename + pre > code {
border-radius: var(--base-border-radius);
border-top-left-radius: 0;
}
.md-typeset pre > code {
border-radius: var(--base-border-radius);
} */
/* Customize admonition layout */
/* .md-typeset .admonition {
border-width: 0px;
border-left-width: var(--admonition-border-left-width);
}
[dir="ltr"] .md-typeset blockquote {
border-radius: 0.2rem;
border-left-width: var(--admonition-border-left-width);
} */
/* Grid Cards */
.md-typeset .grid.cards > ul > li {
border-radius: var(--base-border-radius);
}
.md-typeset .grid.cards > ul > li:hover {
box-shadow: var(--card-hover-shadow);
box-shadow: 0 0 0.2rem #ffffff40;
}
/* 页脚社交图标高度 */
/* Markdown Button */
.md-typeset .md-button {
border-radius: var(--base-border-radius);
}
/* Footer: Social Links */
.md-social__link svg {
max-height: 1rem;
}
/* 搜索框及下拉结果圆角 */
/* Forms */
.md-search__form {
border-radius: var(--base-border-radius);
}
@ -54,57 +87,185 @@ comments: false
border-bottom-left-radius: var(--base-border-radius);
}
/* 可选:如需恢复代码块、警告框等样式,取消注释即可 */
/*
.highlight span.filename {
border-bottom: none;
border-radius: var(--base-border-radius);
display: inline;
font-family: var(--md-code-font-family);
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
margin-bottom: 5px;
text-align: center;
/* Blog - index.md */
/* div.md-content header {
display: none;
}
.highlight span.filename + pre > code,
.md-typeset pre > code {
border-radius: var(--base-border-radius);
border-top-left-radius: 0;
}
.md-typeset .admonition {
border-width: 0px;
border-left-width: var(--admonition-border-left-width);
}
[dir="ltr"] .md-typeset blockquote {
border-radius: 0.2rem;
border-left-width: var(--admonition-border-left-width);
}
*/
/* 可选:博客相关样式,按需启用 */
/* .md-post--excerpt {
background-color: rgba(68,138,255,.1);
box-shadow: 0 0 0 1rem rgba(68,138,255,.1);
.md-post--excerpt {
background-color: var(--md-accent-fg-color--transparent);
box-shadow: 0 0 0 1rem var(--md-accent-fg-color--transparent);
border-radius: var(--base-border-radius);
}
.md-post--excerpt .md-post__header {
justify-content: left;
justify-content: center;
}
.md-post--excerpt .md-post__content > h2,
.md-post__action {
text-align: left;
text-align: center;
} */
/* 让所有admonition包括!!! tip圆角化且更自然 */
.md-typeset .admonition,
.md-typeset details {
border-radius: 1.5em;
box-shadow: 0 2px 12px 0 rgba(60,60,60,0.07);
transition: border-radius 0.4s cubic-bezier(.4,2,.6,1), box-shadow 0.3s;
overflow: hidden;
/* Table */
.md-typeset table:not([class]) {
border-radius: var(--base-border-radius);
}
.carousel {
width: 60%;
height: 100%;
border-radius: 0.4rem;
overflow: hidden;
position: relative;
/* 居中 */
margin-left: auto;
margin-right: auto;
border: 0.075rem solid #7b7b7b7a;
box-shadow: var(--md-shadow-z1);
}
.carousel-container {
width: 100%;
height: 100%;
position: relative;
left: 0;
display: flex;
/* 过渡动画 1s */
transition: all 1s;
}
.carousel-hover {
height: 100%;
width: 10%;
position: absolute;
top: 0;
/* 子元素垂直居中 */
display: flex;
flex-flow: column nowrap;
align-items: center;
justify-content: center;
}
.carousel-hover.left {
left: 0;
}
.carousel-hover.right {
right: 0;
}
.carousel-hover button {
background-color: var(--md-accent-fg-color);
border-radius: 50%;
cursor: pointer;
opacity: 0;
transition: opacity 0.3s;
}
.carousel-hover button::after {
display: block;
height: 1.5rem;
width: 1.5rem;
background-color: white;
content: "";
mask-position: center;
-webkit-mask-position: center;
}
.carousel-hover.left button::after {
mask-image: var(--md-tabbed-icon--prev);
-webkit-mask-image: var(--md-tabbed-icon--prev);
}
.carousel-hover.right button::after {
mask-image: var(--md-tabbed-icon--next);
-webkit-mask-image: var(--md-tabbed-icon--next);
}
/* hover 外层 */
.carousel-hover:hover button {
opacity: 0.5;
transition: opacity 0.3s;
}
/* hover 内层 */
.carousel-hover button:hover {
opacity: 0.8;
transition: opacity 0.3s;
}
.carousel-container a {
width: 100%;
height: 100%;
flex-shrink: 0;
}
.carousel-container img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.carousel-bottom {
position: absolute;
/* 宽度等同于内容宽度 */
width: 100%;
padding: 20px;
bottom: 0;
display: flex;
justify-content: center;
/* 指示器间距 */
gap: 10px;
opacity: 0;
transition: opacity 0.3s;
}
.carousel-bottom:hover {
opacity: 0.8;
transition: opacity 0.3s;
}
.carousel-bottom .indicator {
height: 5px;
width: 20px;
background-color: var(--md-accent-fg-color);
opacity: 0.5;
cursor: pointer;
}
.carousel:hover .bottom .indicator {
opacity: 1;
}
.carousel:hover .shift .btn {
opacity: 1;
}
@media screen and (max-width: 600px) {
.carousel {
width: 100%;
}
.carousel-hover button {
opacity: 1;
}
}
```
## 图片圆角化

View File

@ -19,11 +19,11 @@ Markdown 编写的文档可以导出 HTML 、Word、图像、PDF、Epub 等多
一些Markdown文档创作工具
<ul>
<li><strong>Mac:</strong> <a href="https://macdown.uranusjr.com/">MacDown</a>, <a href="https://ia.net/writer">iA Writer</a><a href="https://marked2.com/">Marked 2</a></li>
<li><strong>iOS / Android:</strong> <a href="https://ia.net/writer">iA Writer</a></li>
<li><strong>Mac:</strong> <a href="/tools/macdown/">MacDown</a>, <a href="/tools/ia-writer/">iA Writer</a><a href="/tools/marked-2/">Marked 2</a></li>
<li><strong>iOS / Android:</strong> <a href="/tools/ia-writer/">iA Writer</a></li>
<li><strong>Windows:</strong> <a href="https://wereturtle.github.io/ghostwriter/">ghostwriter</a><a href="https://markdownmonster.west-wind.com/">Markdown Monster</a></li>
<li><strong>Linux:</strong> <a href="https://github.com/retext-project/retext">ReText</a><a href="https://wereturtle.github.io/ghostwriter/">ghostwriter</a></li>
<li><strong>Web:</strong> <a href="https://dillinger.io/">Dillinger</a><a href="https://stackedit.io/">StackEdit</a></li>
<li><strong>Web:</strong> <a href="/tools/dillinger/">Dillinger</a><a href="/tools/stackedit/">StackEdit</a></li>
</ul>
## 最常用
@ -337,9 +337,7 @@ markdown文本内的连续两个或多个回车会被替换为一个回车
```
下面我们来认识一下二次函数$$y=ax^2+bx+c$$
```
下面我们来认识一下二次函数
$$y=ax^2+bx+c$$
下面我们来认识一下二次函数$$y=ax^2+bx+c$$
- - -

View File

@ -25,5 +25,4 @@ tags:
【⚡啊!设计,是什么呢?⚡】
<!-- <iframe src="//player.bilibili.com/player.html?aid=941663394&bvid=BV1pW4y1a7Zu&cid=824513742&p=1" scrolling="no" border="1" frameborder="no" framespacing="0" allowfullscreen="true" style="width: 640px; height: 430px; max-width: 100%"> </iframe> -->
<iframe width="560" height="315" src="//player.bilibili.com/player.html?aid=941663394&bvid=BV1pW4y1a7Zu&cid=824513742&p=1" title="YouTube video player" frameborder="0" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
<iframe src="//player.bilibili.com/player.html?aid=941663394&bvid=BV1pW4y1a7Zu&cid=824513742&p=1" scrolling="no" border="1" frameborder="no" framespacing="0" allowfullscreen="true" style="width: 640px; height: 430px; max-width: 100%"> </iframe>

View File

@ -157,6 +157,16 @@ Quick Chat(1) Contact Me(2)
[^see-how-much-I-love-you]:All problems in computer science can be solved by another level of indirection
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-29HZMNR0KG"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-29HZMNR0KG');
</script>
<!-- Start of Howxm client code snippet -->
<script>

View File

@ -7,7 +7,6 @@ hide:
comments: false
---
<!--
____ __ ____ ______ ______ ____ __ ____ __ .__ __.
\ \ / \ / / / | / __ \ \ \ / \ / / | | | \ | |
@ -18,7 +17,7 @@ ____ __ ____ ______ ______ ____ __ ____ __ .__ __.
-->
<center><font class="custom-font ml3">最好的MkDocs博客教程</font></center>
<center><font class="custom-font ml3">Wcowin for MkDocs博客教程</font></center>
<script src="https://cdn.statically.io/libs/animejs/2.0.2/anime.min.js"></script>
<style>
.custom-font {
@ -33,8 +32,6 @@ ____ __ ____ ______ ______ ____ __ ____ __ .__ __.
</style>
<!-- <div id="rcorners2" >
<div id="rcorners1" class="date-display">
<p class="p1"></p>
@ -116,8 +113,6 @@ ____ __ ____ ______ ______ ____ __ ____ __ .__ __.
</style>
>不同于市面上过时的MkDocs教程本站提供了最详细最便捷最前沿的MkDocs中文文字/视频教程,与[官方发布](https://squidfunk.github.io/mkdocs-material/changelog/)的教程版本同步。包含了MkDocs的安装、配置、主题美化、插件使用等内容。无论你是初学者还是有经验的用户都能在这里找到你需要的帮助。我们还提供了示例和实用的技巧帮助你更好地使用MkDocs。𝓳𝓾𝓼𝓽 𝓮𝓷𝓳𝓸𝔂 𝓲𝓽
<!-- - 基于Material for MkDocs美化
- 简洁美观,功能多元化
- 简单易上手,小白配置
@ -147,7 +142,7 @@ ____ __ ____ ______ ______ ____ __ ____ __ .__ __.
---
- [Mkdocs视频教程](https://space.bilibili.com/1407028951/lists/4566631?type=series){target=“_blank”}(Bilibili)
- [Mkdocs视频教程](https://space.bilibili.com/1407028951/lists/4566631?type=series){target=“_blank”}
- [部署静态网页至GitHub pages](blog/Mkdocs/mkdocs1.md)
- [Mkdocs部署配置说明(mkdocs.yml)](blog/Mkdocs/mkdocs2.md)
- [如何给MKdocs添加友链](blog/websitebeauty/linktech.md)
@ -171,7 +166,7 @@ ____ __ ____ ______ ______ ____ __ ____ __ .__ __.
[^Knowing-that-loving-you-has-no-ending]:太阳总是能温暖向日葵
[^see-how-much-I-love-you]:All-problems-in-computer-science-can-be-solved-by-another-level-of-indirection
<!-- <body>
<body>
<font color="#B9B9B9">
<p style="text-align: center; ">
<span>本站已经运行</span>
@ -198,7 +193,7 @@ ____ __ ____ ______ ______ ____ __ ____ __ .__ __.
},1000)
</script>
</font>
</body> -->
</body>
<!-- <script src="//code.tidio.co/6jmawe9m5wy4ahvlhub2riyrnujz7xxi.js" async></script> -->
@ -231,22 +226,9 @@ body::before {
z-index: -1;
}
@media (max-width: 768px) {
body::before {
display: none; /* 在手机端隐藏网格效果 */
}
}
</style>
<!-- <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-2327435979273742"
crossorigin="anonymous"></script>
<ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-2327435979273742"
data-ad-slot="3702206121"
data-ad-format="auto"
data-full-width-responsive="true"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script> -->

View File

@ -59,7 +59,7 @@ comments: false
width: 180px;
color: #999;
border-radius: 25px;
border: 2px solid #1F2635;
border: 2px solid #608DBD;
padding: 12px 24px;
text-align: center;
text-decoration: none;
@ -77,9 +77,9 @@ comments: false
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.buttonxuan.active {
background-color: #1F2635;
background-color: #608DBD;
color: white;
border-color: #1F2635;
border-color: #3498db;
}
@media (max-width: 768px) {
.button-container {

File diff suppressed because it is too large Load Diff

View File

@ -1,256 +0,0 @@
import re
from functools import lru_cache
# 预编译正则表达式(保持原有格式)
EXCLUDE_PATTERNS = [
re.compile(r'^index\.md$'),
re.compile(r'^about/'),
re.compile(r'^trip/index\.md$'),
re.compile(r'^relax/index\.md$'),
re.compile(r'^blog/indexblog\.md$'),
re.compile(r'^blog/posts\.md$'),
re.compile(r'^develop/index\.md$'),
re.compile(r'waline\.md$'),
re.compile(r'link\.md$'),
re.compile(r'404\.md$'),
re.compile(r'liuyanban\.md$'),
]
# 优化的字符统计正则表达式
CHINESE_CHARS_PATTERN = re.compile(r'[\u4e00-\u9fff\u3400-\u4dbf]')
CODE_BLOCK_PATTERN = re.compile(r'```.*?```', re.DOTALL)
INLINE_CODE_PATTERN = re.compile(r'`[^`]+`')
YAML_FRONT_PATTERN = re.compile(r'^---.*?---\s*', re.DOTALL)
HTML_TAG_PATTERN = re.compile(r'<[^>]+>')
IMAGE_PATTERN = re.compile(r'!\[.*?\]\([^)]+\)')
LINK_PATTERN = re.compile(r'\[([^\]]+)\]\([^)]+\)')
# 预定义排除类型
EXCLUDE_TYPES = frozenset({'landing', 'special', 'widget'})
# 扩展非编程行内代码词汇(更全面的过滤)
NON_CODE_WORDS = frozenset({
'markdown', 'target', 'blank', 'lg', 'middle', 'small', 'large',
'left', 'right', 'center', 'top', 'bottom', 'primary', 'secondary',
'success', 'warning', 'danger', 'info', 'light', 'dark', 'grid',
'cards', 'octicons', 'bookmark', 'div', 'class', 'img', 'src',
'alt', 'width', 'height', 'style', 'id', 'data', 'href', 'title'
})
# 支持的编程和标记语言(扩展版本)
PROGRAMMING_LANGUAGES = frozenset({
# 编程语言
'python', 'py', 'javascript', 'js', 'typescript', 'ts', 'java', 'cpp', 'c',
'go', 'rust', 'php', 'ruby', 'swift', 'kotlin', 'csharp', 'cs',
# 脚本语言
'bash', 'sh', 'powershell', 'ps1', 'zsh', 'fish', 'bat', 'cmd',
# 标记和配置语言
'html', 'css', 'scss', 'sass', 'less', 'yaml', 'yml', 'json', 'xml',
'toml', 'ini', 'conf', 'dockerfile', 'makefile',
# 数据库和查询
'sql', 'mysql', 'postgresql', 'sqlite', 'mongodb',
# 其他
'r', 'matlab', 'scala', 'perl', 'lua', 'dart', 'tex', 'latex',
# 数据格式
'csv', 'properties',
# 无标识符(空字符串也算作有效语言)
''
})
@lru_cache(maxsize=256)
def clean_markdown_content_for_chinese(content_hash, markdown):
"""清理Markdown内容只保留中文文本用于统计添加缓存"""
content = markdown
# 使用预编译的正则表达式
content = YAML_FRONT_PATTERN.sub('', content)
content = HTML_TAG_PATTERN.sub('', content)
content = IMAGE_PATTERN.sub('', content)
content = LINK_PATTERN.sub(r'\1', content)
content = CODE_BLOCK_PATTERN.sub('', content)
content = INLINE_CODE_PATTERN.sub('', content)
return content
def count_code_lines(markdown):
"""统计代码行数(修复版本 - 正确处理所有代码行)"""
code_blocks = CODE_BLOCK_PATTERN.findall(markdown)
total_code_lines = 0
for i, block in enumerate(code_blocks):
# 提取语言标识
lang_match = re.match(r'^```(\w*)', block)
language = lang_match.group(1).lower() if lang_match else ''
# 移除开头的语言标识和结尾的```
code_content = re.sub(r'^```\w*\n?', '', block)
code_content = re.sub(r'\n?```$', '', code_content)
# 过滤空代码块
if not code_content.strip():
continue
# 计算有效行数(包含所有非空行,包括注释行)
lines = [line for line in code_content.split('\n') if line.strip()]
line_count = len(lines)
# 如果有明确的编程语言标识,直接统计
if language and language in PROGRAMMING_LANGUAGES:
total_code_lines += line_count
continue
# 增强的检测策略 - 更宽松的判断
is_code = False
# 1. 命令行检测
command_indicators = [
'sudo ', 'npm ', 'pip ', 'git ', 'cd ', 'ls ', 'mkdir ', 'rm ', 'cp ', 'mv ',
'chmod ', 'chown ', 'grep ', 'find ', 'ps ', 'kill ', 'top ', 'cat ', 'echo ',
'wget ', 'curl ', 'tar ', 'zip ', 'unzip ', 'ssh ', 'scp ', 'rsync ',
'xattr ', 'codesign ', 'xcode-select ', 'spctl ', 'launchctl ',
'brew ', 'defaults ', 'ditto ', 'hdiutil ', 'diskutil ',
'dir ', 'copy ', 'xcopy ', 'del ', 'rd ', 'md ', 'type ', 'attrib ',
'$ ', '# ', '% ', '> ', 'C:\\>', 'PS>',
'--', '-r', '-d', '-f', '-v', '-h', '--help', '--version',
'--force', '--deep', '--sign', '--master-disable',
'/Applications/', '/usr/', '/etc/', '/var/', '/home/', '~/',
'C:\\', 'D:\\', '.app', '.exe', '.pkg', '.dmg', '.zip', '.tar',
'#!/',
]
if any(indicator in code_content for indicator in command_indicators):
is_code = True
# 2. 编程语法检测(增强版)
if not is_code:
programming_indicators = [
# Python语法特征
'def ', 'class ', 'import ', 'from ', 'return ', 'yield ', 'lambda ',
'with ', 'as ', 'try:', 'except:', 'finally:', 'elif ', 'if __name__',
'print(', '.append(', '.extend(', '.remove(', '.sort(', '.reverse(',
'range(', 'len(', 'str(', 'int(', 'float(', 'list(', 'dict(',
# JavaScript/TypeScript语法
'function', 'var ', 'let ', 'const ', 'async ', 'await ', '=>',
'console.log', 'document.', 'window.', 'require(',
# 通用编程语法
'public ', 'private ', 'protected ', 'static ', 'void ', 'int ',
'string ', 'boolean ', 'float ', 'double ', 'char ',
# 操作符和结构
'==', '!=', '<=', '>=', '&&', '||', '++', '--', '+=', '-=', '**',
# 特殊结构
'while ', 'for ', 'if ', 'else:', 'switch ', 'case ',
# HTML/XML语法
'<!DOCTYPE', '<html', '<head', '<body', '<div', '<span', '<p>',
'<style', '<script', '<link', '<meta', '<title', '<img',
# CSS语法
'display:', 'color:', 'background:', 'margin:', 'padding:',
'font-size:', 'width:', 'height:', 'position:', 'border:',
# YAML语法
'name:', 'version:', 'theme:', 'title:', 'description:',
# JSON语法
'{"', '"}', '":', '",', '[{', '}]', 'null', 'true', 'false',
# 配置文件语法
'[', ']', '//', '/*', '*/', '<!--', '-->',
# SQL语法
'SELECT ', 'FROM ', 'WHERE ', 'INSERT ', 'UPDATE ', 'DELETE ',
'CREATE ', 'ALTER ', 'DROP ', 'INDEX ', 'TABLE ',
# 数学公式和LaTeX
'\\', '$', '$$', '\\begin', '\\end', '\\frac', '\\sum',
]
if any(indicator in code_content for indicator in programming_indicators):
is_code = True
# 3. 结构化检测
if not is_code:
# 缩进结构检测
if len(lines) > 1 and any(line.startswith(' ') or line.startswith('\t') for line in lines):
is_code = True
# HTML标签结构
elif '<' in code_content and '>' in code_content:
is_code = True
# 包含特殊字符组合
elif any(char in code_content for char in ['{', '}', '(', ')', '[', ']']) and ('=' in code_content or ':' in code_content):
is_code = True
# 4. 模式匹配检测(宽松策略)
if not is_code and len(lines) >= 1:
special_patterns = [
r'\w+\(\)', r'\w+\[\]', r'\w+\{\}', r'\w+=\w+', r'\w+:\w+',
r'<\w+>', r'\$\w+', r'#\w+', r'@\w+', r'\w+\.\w+\(\)',
r'\d+\.\d+\.\d+', r'http[s]?://', r'ftp://', r'localhost',
r'def\s+\w+', r'class\s+\w+', r'import\s+\w+', r'from\s+\w+',
r'if\s+\w+', r'while\s+\w+', r'for\s+\w+', r'return\s+\w*',
r'\w+\s*=\s*\w+', r'\w+\.\w+', r'#.*输出', r'#.*结果'
]
if any(re.search(pattern, code_content) for pattern in special_patterns):
is_code = True
# 如果判断为代码,则统计行数
if is_code:
total_code_lines += line_count
return total_code_lines
def calculate_reading_stats(markdown):
"""计算中文字符数和代码行数"""
# 生成内容哈希用于缓存
content_hash = hash(markdown)
# 使用缓存的清理函数
clean_content = clean_markdown_content_for_chinese(content_hash, markdown)
chinese_chars = len(CHINESE_CHARS_PATTERN.findall(clean_content))
# 统计代码行数
code_lines = count_code_lines(markdown)
# 计算阅读时间中文400字/分钟)
reading_time = max(1, round(chinese_chars / 400))
return reading_time, chinese_chars, code_lines
def on_page_markdown(markdown, **kwargs):
page = kwargs['page']
# 快速排除检查
if page.meta.get('hide_reading_time', False):
return markdown
# 保持原有的EXCLUDE_PATTERNS循环检查方式
src_path = page.file.src_path
for pattern in EXCLUDE_PATTERNS:
if pattern.match(src_path):
return markdown
# 优化类型检查
page_type = page.meta.get('type', '')
if page_type in EXCLUDE_TYPES:
return markdown
# 快速预检查
if len(markdown) < 300:
return markdown
# 计算统计信息
reading_time, chinese_chars, code_lines = calculate_reading_stats(markdown)
# 过滤太短的内容
if chinese_chars < 30:
return markdown
# 生成阅读信息
if code_lines > 0:
reading_info = f"""!!! info "📖 阅读信息"
阅读时间**{reading_time}** 分钟 | 中文字符**{chinese_chars}** | 有效代码行数**{code_lines}**
"""
else:
reading_info = f"""!!! info "📖 阅读信息"
阅读时间**{reading_time}** 分钟 | 中文字符**{chinese_chars}**
"""
return reading_info + markdown

View File

@ -1,503 +0,0 @@
import os
import re
from collections import Counter, defaultdict
from textwrap import dedent
import hashlib
import yaml
from urllib.parse import urlparse
# 存储所有文章的信息和索引
article_index = {}
category_index = defaultdict(list)
keyword_index = defaultdict(set)
# 配置:需要索引的目录
INDEXED_DIRECTORIES = ['blog/', 'develop/']
# 配置:排除推荐的页面列表(支持精确匹配和模式匹配)
EXCLUDED_PAGES = {
# 精确路径匹配
'blog/index.md',
'develop/index.md',
# 可以添加更多排除的页面
# 'blog/special-page.md',
}
# 配置:排除推荐的页面模式(支持通配符)
EXCLUDED_PATTERNS = [
r'.*\/index\.md$', # 排除所有 index.md 文件
r'.*\/archive\.md$', # 排除所有 archive.md 文件
r'blog\/posts?\/.*', # 排除 blog/post/ 和 blog/posts/ 目录下的所有文章
# 可以添加更多模式
# r'blog\/draft\/.*', # 排除草稿目录
]
# 配置:相似度阈值和权重
SIMILARITY_CONFIG = {
'min_threshold': 0.15, # 提高最低相似度阈值
'weights': {
'keywords': 0.35, # 关键词权重
'tags': 0.30, # 标签权重
'categories': 0.20, # 分类权重
'path': 0.10, # 路径分类权重
'source_dir': 0.05 # 源目录权重
},
'title_similarity': 0.25 # 标题相似度权重
}
def is_page_excluded(file_path):
"""检查页面是否应该排除推荐"""
# 精确匹配检查
if file_path in EXCLUDED_PAGES:
return True
# 模式匹配检查
for pattern in EXCLUDED_PATTERNS:
if re.match(pattern, file_path):
return True
return False
def should_index_file(file_path):
"""检查文件是否应该被索引"""
if not file_path.endswith('.md'):
return False
# 先检查是否被排除
if is_page_excluded(file_path):
return False
# 检查是否在指定目录下
for directory in INDEXED_DIRECTORIES:
if file_path.startswith(directory):
return True
return False
def extract_keywords(content, title):
"""提取文章中的关键词,改进算法"""
# 移除YAML front matter
content = re.sub(r'^---\s*\n.*?\n---\s*\n', '', content, flags=re.DOTALL | re.MULTILINE)
# 移除代码块
content = re.sub(r'```.*?```', '', content, flags=re.DOTALL)
# 移除HTML标签
content = re.sub(r'<.*?>', '', content)
# 移除链接
content = re.sub(r'\[.*?\]\(.*?\)', '', content)
# 移除标题标记
content = re.sub(r'^#+\s+', '', content, flags=re.MULTILINE)
# 合并标题和内容,标题权重更高
title_words = re.findall(r'\b\w+\b', title.lower()) * 4 # 增加标题权重
content_words = re.findall(r'\b\w+\b', content.lower())
all_words = title_words + content_words
# 扩展停用词列表(包含中英文)
stopwords = {
# 英文停用词
'the', 'a', 'an', 'in', 'on', 'at', 'to', 'for', 'of', 'and', 'or', 'is', 'are', 'was', 'were',
'be', 'been', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should',
'this', 'that', 'these', 'those', 'with', 'from', 'by', 'as', 'can', 'but', 'not', 'if', 'it',
'they', 'them', 'their', 'you', 'your', 'we', 'our', 'my', 'me', 'i', 'he', 'she', 'him', 'her',
# 常见无意义词
'about', 'above', 'after', 'again', 'all', 'also', 'any', 'because', 'before', 'between',
'both', 'each', 'few', 'first', 'get', 'how', 'into', 'just', 'last', 'made', 'make', 'may',
'most', 'new', 'now', 'old', 'only', 'other', 'over', 'said', 'same', 'see', 'some', 'such',
'take', 'than', 'then', 'time', 'two', 'use', 'very', 'way', 'well', 'where', 'when', 'which',
'while', 'who', 'why', 'work', 'world', 'year', 'years',
# 中文停用词
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
'', '', '可以', '没有', '什么', '一个', '自己', '这个', '那个', '这些', '那些', '如果', '因为', '所以'
}
# 过滤单词:长度>=2不在停用词中不是纯数字
words = [w for w in all_words
if len(w) >= 2 and w not in stopwords and not w.isdigit()]
# 返回词频最高的15个词
return Counter(words).most_common(15)
def extract_metadata(content):
"""提取文章元数据支持YAML front matter"""
metadata = {
'title': "未命名",
'description': "",
'tags': [],
'categories': [],
'disable_related': False # 新增:是否禁用相关推荐
}
# 尝试解析YAML front matter
yaml_match = re.match(r'^---\s*\n(.*?)\n---\s*\n', content, re.DOTALL)
if yaml_match:
try:
yaml_content = yaml_match.group(1)
yaml_data = yaml.safe_load(yaml_content)
if yaml_data:
metadata['title'] = str(yaml_data.get('title', '未命名')).strip('"\'')
metadata['description'] = str(yaml_data.get('description', '')).strip('"\'')
metadata['disable_related'] = yaml_data.get('disable_related', False)
# 处理tags
tags = yaml_data.get('tags', [])
if isinstance(tags, list):
metadata['tags'] = [str(tag).strip() for tag in tags]
elif isinstance(tags, str):
metadata['tags'] = [tag.strip() for tag in tags.split(',') if tag.strip()]
# 处理categories
categories = yaml_data.get('categories', [])
if isinstance(categories, list):
metadata['categories'] = [str(cat).strip() for cat in categories]
elif isinstance(categories, str):
metadata['categories'] = [cat.strip() for cat in categories.split(',') if cat.strip()]
except yaml.YAMLError:
pass # 如果YAML解析失败使用默认值
# 如果YAML解析失败回退到正则表达式
if metadata['title'] == "未命名":
title_match = re.search(r'^title:\s*(.+)$', content, re.MULTILINE)
if title_match:
metadata['title'] = title_match.group(1).strip('"\'')
return metadata
def get_category_from_path(file_path):
"""从文件路径提取分类"""
parts = file_path.split('/')
if len(parts) > 2:
return parts[1] # blog/category/file.md 或 develop/category/file.md格式
elif len(parts) > 1:
return parts[0] # blog 或 develop
return "未分类"
def calculate_content_hash(content):
"""计算内容哈希,用于检测内容变化"""
return hashlib.md5(content.encode('utf-8')).hexdigest()
def on_files(files, config):
"""预处理所有文章,建立索引"""
global article_index, category_index, keyword_index
# 清空索引
article_index.clear()
category_index.clear()
keyword_index.clear()
processed_count = 0
excluded_count = 0
for file in files:
if should_index_file(file.src_path):
try:
with open(file.abs_src_path, 'r', encoding='utf-8') as f:
content = f.read()
# 提取元数据
metadata = extract_metadata(content)
# 检查是否禁用相关推荐
if metadata.get('disable_related', False):
excluded_count += 1
continue
# 再次检查是否在排除列表中(双重检查)
if is_page_excluded(file.src_path):
excluded_count += 1
continue
# 提取关键词
keywords = extract_keywords(content, metadata['title'])
# 获取分类
path_category = get_category_from_path(file.src_path)
# 构建文章信息
article_info = {
'title': metadata['title'],
'description': metadata['description'],
'tags': metadata['tags'],
'categories': metadata['categories'],
'path_category': path_category,
'keywords': keywords,
'url': file.url,
'path': file.src_path,
'content_hash': calculate_content_hash(content),
'source_dir': file.src_path.split('/')[0] # blog 或 develop
}
# 添加到主索引
article_index[file.src_path] = article_info
# 添加到分类索引
category_index[path_category].append(file.src_path)
for category in metadata['categories']:
if category: # 确保分类不为空
category_index[category].append(file.src_path)
# 添加到关键词索引
for keyword, _ in keywords:
keyword_index[keyword].add(file.src_path)
for tag in metadata['tags']:
if tag: # 确保标签不为空
keyword_index[tag.lower()].add(file.src_path)
processed_count += 1
except Exception as e:
print(f"❌ 处理文件 {file.src_path} 时出错: {e}")
print(f"✅ 已索引 {processed_count} 篇文章 (blog + develop)")
if excluded_count > 0:
print(f"📝 排除 {excluded_count} 篇禁用推荐或在排除列表中的文章")
print(f"📊 分类数量: {len(category_index)}")
print(f"🔤 关键词数量: {len(keyword_index)}")
return files
def on_page_markdown(markdown, **kwargs):
"""为每篇文章添加相关推荐"""
page = kwargs['page']
config = kwargs['config']
# 检查是否应该处理这个页面
if not should_index_file(page.file.src_path):
return markdown
# 检查是否被排除
if is_page_excluded(page.file.src_path):
return markdown
# 检查文章元数据是否禁用推荐
try:
with open(page.file.abs_src_path, 'r', encoding='utf-8') as f:
content = f.read()
metadata = extract_metadata(content)
if metadata.get('disable_related', False):
return markdown
except Exception:
pass # 如果读取失败,继续处理
# 获取相关文章
related_articles = get_related_articles(page.file.src_path, max_count=5)
if not related_articles:
return markdown
# 从 config 中获取 site_url 并解析出基本路径
site_url = config.get('site_url', '')
base_path = urlparse(site_url).path if site_url else '/'
if not base_path.endswith('/'):
base_path += '/'
# 构建推荐HTML - 针对Safari浏览器优化
recommendation_html = "\n"
# 添加CSS样式特别针对Safari浏览器优化
recommendation_html += """<style>
.related-posts {
margin-top: 1.5rem;
padding-top: 0.75rem;
border-top: 1px solid rgba(0,0,0,0.1);
max-height: none !important; /* 防止Safari错误计算高度 */
overflow: visible !important; /* 防止内容被截断 */
}
.related-posts h3 {
margin-top: 0;
margin-bottom: 0.5rem;
font-size: 1.2rem;
font-weight: 500;
line-height: 1.3;
}
.related-posts ul {
margin: 0 0 0.5rem 0 !important; /* 强制覆盖可能的冲突样式 */
padding-left: 1.5rem;
list-style-position: outside;
}
.related-posts li {
margin-bottom: 0.25rem;
line-height: 1.4;
}
/* 暗色模式适配 */
[data-md-color-scheme="slate"] .related-posts {
border-top-color: rgba(255,255,255,0.1);
}
/* Safari特定修复 */
@supports (-webkit-hyphens:none) {
.related-posts {
display: block;
position: relative;
height: auto !important;
}
.related-posts ul {
position: static;
}
}
</style>
"""
# 简化且兼容的HTML结构
recommendation_html += '<div class="related-posts">\n'
recommendation_html += '<h3>📚 相关文章推荐</h3>\n'
recommendation_html += '<ul>\n'
for score, article_info in related_articles:
title = article_info['title']
relative_url = article_info['url']
# 拼接基本路径和文章相对URL并确保路径分隔符正确
full_url = (base_path + relative_url).replace('//', '/')
recommendation_html += f'<li><a href="{full_url}">{title}</a></li>\n'
recommendation_html += '</ul>\n'
recommendation_html += '</div>\n'
# 确保没有多余的空行
return markdown.rstrip() + recommendation_html
def get_related_articles(current_path, max_count=5):
"""获取相关文章,使用改进的算法"""
if current_path not in article_index:
return []
current_article = article_index[current_path]
similarities = []
# 获取当前文章的关键信息
current_title = current_article['title'].lower()
current_tags = set(tag.lower() for tag in current_article['tags'] if tag)
current_categories = set(cat.lower() for cat in current_article['categories'] if cat)
for path, article_info in article_index.items():
if path == current_path:
continue
# 过滤掉标题为"未命名"的文章
if article_info['title'] == "未命名" or not article_info['title'].strip():
continue
# 再次检查是否在排除列表中(双重检查)
if is_page_excluded(path):
continue
# 计算相似度
score = calculate_similarity(current_article, article_info)
# 标题相似度加权
title_similarity = calculate_title_similarity(current_title, article_info['title'].lower())
if title_similarity > 0.3: # 标题有一定相似度
score += title_similarity * SIMILARITY_CONFIG['title_similarity']
# 应用最低阈值
if score > SIMILARITY_CONFIG['min_threshold']:
similarities.append((score, article_info))
# 按相似度排序
similarities.sort(key=lambda x: x[0], reverse=True)
# 多样性优化:确保不同分类的文章都有机会被推荐
if len(similarities) > max_count * 2:
# 按分类分组
category_groups = defaultdict(list)
for score, article in similarities:
for category in article['categories']:
if category:
category_groups[category.lower()].append((score, article))
# 从每个分类中选取最相关的文章
diverse_results = []
used_paths = set()
# 首先添加最相关的文章
if similarities:
top_score, top_article = similarities[0]
diverse_results.append((top_score, top_article))
used_paths.add(top_article['path'])
# 然后从每个分类中添加最相关的文章
for category in sorted(category_groups.keys()):
if len(diverse_results) >= max_count:
break
for score, article in category_groups[category]:
if article['path'] not in used_paths:
diverse_results.append((score, article))
used_paths.add(article['path'])
break
# 如果还有空位,从剩余的高分文章中填充
if len(diverse_results) < max_count:
for score, article in similarities:
if article['path'] not in used_paths and len(diverse_results) < max_count:
diverse_results.append((score, article))
used_paths.add(article['path'])
# 重新按相似度排序
diverse_results.sort(key=lambda x: x[0], reverse=True)
return diverse_results[:max_count]
return similarities[:max_count]
def calculate_title_similarity(title1, title2):
"""计算两个标题的相似度"""
# 分词
words1 = set(re.findall(r'\b\w+\b', title1))
words2 = set(re.findall(r'\b\w+\b', title2))
if not words1 or not words2:
return 0
# 计算Jaccard相似度
intersection = len(words1.intersection(words2))
union = len(words1.union(words2))
if union == 0:
return 0
return intersection / union
def calculate_similarity(article1, article2):
"""计算两篇文章的相似度"""
score = 0
weights = SIMILARITY_CONFIG['weights']
# 1. 关键词相似度
keywords1 = dict(article1['keywords'])
keywords2 = dict(article2['keywords'])
common_keywords = set(keywords1.keys()) & set(keywords2.keys())
if common_keywords:
# 考虑关键词的频率和重要性
keyword_score = sum(min(keywords1[kw], keywords2[kw]) for kw in common_keywords)
# 关键词匹配数量的奖励
keyword_count_bonus = len(common_keywords) / max(len(keywords1), 1) * 0.5
score += (keyword_score + keyword_count_bonus) * weights['keywords']
# 2. 标签相似度
tags1 = set(tag.lower() for tag in article1['tags'] if tag)
tags2 = set(tag.lower() for tag in article2['tags'] if tag)
if tags1 and tags2: # 确保两篇文章都有标签
tag_overlap = len(tags1 & tags2)
tag_ratio = tag_overlap / max(len(tags1), 1) # 相对重叠比例
tag_score = tag_overlap * 8 * (1 + tag_ratio) # 增加重叠比例奖励
score += tag_score * weights['tags']
# 3. 分类相似度
categories1 = set(cat.lower() for cat in article1['categories'] if cat)
categories2 = set(cat.lower() for cat in article2['categories'] if cat)
if categories1 and categories2: # 确保两篇文章都有分类
category_overlap = len(categories1 & categories2)
category_ratio = category_overlap / max(len(categories1), 1)
category_score = category_overlap * 12 * (1 + category_ratio)
score += category_score * weights['categories']
# 4. 路径分类相似度
if article1['path_category'] == article2['path_category']:
score += 3 * weights['path']
# 5. 同源目录加分
if article1.get('source_dir') == article2.get('source_dir'):
score += 2 * weights['source_dir']
return score

View File

@ -1,397 +0,0 @@
# 备份智能摘要代码
import re
import json
import hashlib
import requests
from pathlib import Path
from datetime import datetime
from functools import lru_cache
class AISummaryGenerator:
def __init__(self):
self.cache_dir = Path("site/.ai_cache")
self.cache_dir.mkdir(exist_ok=True)
# DeepSeek API配置
self.api_config = {
'url': 'https://api.deepseek.com/v1/chat/completions',
'model': 'deepseek-chat',
'headers': {
'Content-Type': 'application/json',
'Authorization': 'Bearer sk-'
}
}
# 📂 可自定义的文件夹配置
self.enabled_folders = [
'blog/', # blog文件夹
'develop/', # develop文件夹
# 'about/', # about文件夹
# 在这里添加您想要启用AI摘要的文件夹
]
# 📋 排除的文件和文件夹
self.exclude_patterns = [
'liuyanban.md', 'link.md', '404.md', 'tag.md', 'tags.md',
'/about/', '/search/', '/sitemap', 'index.md', # 根目录index.md
]
# 📋 排除的特定文件
self.exclude_files = [
'blog/index.md',
'blog/indexblog.md',
'docs/index.md',
'develop/index.md',
]
def configure_folders(self, folders=None, exclude_patterns=None, exclude_files=None):
"""
配置启用AI摘要的文件夹
Args:
folders: 启用AI摘要的文件夹列表
exclude_patterns: 排除的模式列表
exclude_files: 排除的特定文件列表
"""
if folders is not None:
self.enabled_folders = folders
if exclude_patterns is not None:
self.exclude_patterns = exclude_patterns
if exclude_files is not None:
self.exclude_files = exclude_files
def get_content_hash(self, content):
"""生成内容hash用于缓存"""
return hashlib.md5(content.encode('utf-8')).hexdigest()
def get_cached_summary(self, content_hash):
"""获取缓存的摘要"""
cache_file = self.cache_dir / f"{content_hash}.json"
if cache_file.exists():
try:
with open(cache_file, 'r', encoding='utf-8') as f:
cache_data = json.load(f)
# 检查缓存是否过期7天
cache_time = datetime.fromisoformat(cache_data.get('timestamp', '1970-01-01'))
if (datetime.now() - cache_time).days < 7:
return cache_data
except:
pass
return None
def save_summary_cache(self, content_hash, summary_data):
"""保存摘要到缓存"""
cache_file = self.cache_dir / f"{content_hash}.json"
try:
summary_data['timestamp'] = datetime.now().isoformat()
with open(cache_file, 'w', encoding='utf-8') as f:
json.dump(summary_data, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"保存摘要缓存失败: {e}")
def clean_content_for_ai(self, markdown):
"""清理内容提取主要文本用于AI处理"""
content = markdown
# 移除YAML front matter
content = re.sub(r'^---.*?---\s*', '', content, flags=re.DOTALL)
# 移除已存在的阅读信息块和AI摘要块
content = re.sub(r'!!! info "📖 阅读信息".*?(?=\n\n|\n#|\Z)', '', content, flags=re.DOTALL)
content = re.sub(r'!!! info "🤖 AI智能摘要".*?(?=\n\n|\n#|\Z)', '', content, flags=re.DOTALL)
content = re.sub(r'!!! tip "📝 自动摘要".*?(?=\n\n|\n#|\Z)', '', content, flags=re.DOTALL)
# 移除HTML标签
content = re.sub(r'<[^>]+>', '', content)
# 移除图片保留alt文本作为内容提示
content = re.sub(r'!\[([^\]]*)\]\([^)]+\)', r'[图片:\1]', content)
# 移除链接,保留文本
content = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', content)
# 移除代码块,但保留关键信息
content = re.sub(r'```(\w+)?\n(.*?)\n```', r'[代码示例]', content, flags=re.DOTALL)
# 移除行内代码
content = re.sub(r'`[^`]+`', '[代码]', content)
# 移除表格格式但保留内容
content = re.sub(r'\|[^\n]+\|', '', content)
content = re.sub(r'^[-|:\s]+$', '', content, flags=re.MULTILINE)
# 清理格式符号
content = re.sub(r'\*\*([^*]+)\*\*', r'\1', content) # 粗体
content = re.sub(r'\*([^*]+)\*', r'\1', content) # 斜体
content = re.sub(r'^#+\s*', '', content, flags=re.MULTILINE) # 标题符号
# 移除多余的空行和空格
content = re.sub(r'\n\s*\n', '\n\n', content)
content = re.sub(r'^[ \t]+', '', content, flags=re.MULTILINE)
content = content.strip()
return content
def generate_ai_summary(self, content, page_title=""):
"""使用DeepSeek生成摘要"""
# 优化的提示词
prompt = f"""请为以下技术文章生成一个高质量的摘要,要求:
1. **长度控制**严格控制在80-120字以内
2. **内容要求**
- 准确概括文章的核心主题和关键要点
- 突出技术特点应用场景或解决的问题
- 使用专业但易懂的语言
- 避免重复文章标题的内容
3. **格式要求**
- 直接返回摘要内容无需任何前缀或后缀
- 使用简洁的陈述句
- 可以适当使用技术术语
文章标题{page_title}
文章内容
{content[:2500]}
请生成摘要"""
try:
payload = {
"model": self.api_config['model'],
"messages": [
{
"role": "system",
"content": "你是一个专业的技术文档摘要专家,擅长提取文章核心要点并生成简洁准确的摘要。"
},
{
"role": "user",
"content": prompt
}
],
"max_tokens": 150,
"temperature": 0.3, # 降低随机性,提高准确性
"top_p": 0.9
}
response = requests.post(
self.api_config['url'],
headers=self.api_config['headers'],
json=payload,
timeout=30
)
if response.status_code == 200:
result = response.json()
summary = result['choices'][0]['message']['content'].strip()
# 清理可能的格式问题
summary = re.sub(r'^["""''`]+|["""''`]+$', '', summary)
summary = re.sub(r'^\s*摘要[:]\s*', '', summary)
summary = re.sub(r'^\s*总结[:]\s*', '', summary)
return summary
else:
print(f"DeepSeek API请求失败: {response.status_code} - {response.text}")
return None
except requests.exceptions.RequestException as e:
print(f"DeepSeek API请求异常: {e}")
return None
except Exception as e:
print(f"AI摘要生成异常: {e}")
return None
def generate_fallback_summary(self, content, page_title=""):
"""生成备用摘要(基于规则的智能摘要)"""
# 移除格式符号
clean_text = re.sub(r'^#+\s*', '', content, flags=re.MULTILINE)
clean_text = re.sub(r'\*\*([^*]+)\*\*', r'\1', clean_text)
clean_text = re.sub(r'\*([^*]+)\*', r'\1', clean_text)
# 分割成句子
sentences = re.split(r'[\u3002\uff01\uff1f.!?]', clean_text)
sentences = [s.strip() for s in sentences if len(s.strip()) > 15]
# 优先选择包含关键词的句子
key_indicators = [
'介绍', '讲解', '说明', '分析', '探讨', '研究', '实现', '应用',
'方法', '技术', '算法', '原理', '概念', '特点', '优势', '解决',
'教程', '指南', '配置', '安装', '部署', '开发', '设计', '构建'
]
priority_sentences = []
normal_sentences = []
for sentence in sentences[:10]: # 处理前10句
if any(keyword in sentence for keyword in key_indicators):
priority_sentences.append(sentence)
else:
normal_sentences.append(sentence)
# 组合摘要
selected_sentences = []
total_length = 0
# 优先使用关键句子
for sentence in priority_sentences:
if total_length + len(sentence) > 100:
break
selected_sentences.append(sentence)
total_length += len(sentence)
# 如果还有空间,添加普通句子
if total_length < 80:
for sentence in normal_sentences:
if total_length + len(sentence) > 100:
break
selected_sentences.append(sentence)
total_length += len(sentence)
if selected_sentences:
summary = '.'.join(selected_sentences) + '.'
# 简化冗长的摘要
if len(summary) > 120:
summary = selected_sentences[0] + '.'
return summary
else:
# 根据标题生成通用摘要
if any(keyword in page_title for keyword in ['教程', '指南', 'Tutorial']):
return '本文提供了详细的教程指南,通过实例演示帮助读者掌握相关技术要点。'
elif any(keyword in page_title for keyword in ['配置', '设置', '安装', 'Config']):
return '本文介绍了系统配置的方法和步骤,提供实用的设置建议和最佳实践。'
elif any(keyword in page_title for keyword in ['开发', '编程', 'Development']):
return '本文分享了开发经验和技术实践,提供了实用的代码示例和解决方案。'
else:
return '本文深入探讨了相关技术内容,提供了实用的方法和解决方案。'
def process_page(self, markdown, page, config):
"""处理页面生成AI摘要"""
if not self.should_generate_summary(page, markdown):
return markdown
clean_content = self.clean_content_for_ai(markdown)
# 内容长度检查
if len(clean_content) < 200:
print(f"📄 内容太短,跳过摘要生成: {page.file.src_path}")
return markdown
content_hash = self.get_content_hash(clean_content)
page_title = getattr(page, 'title', '')
# 检查缓存
cached_summary = self.get_cached_summary(content_hash)
if cached_summary:
summary = cached_summary.get('summary', '')
ai_service = 'cached'
print(f"✅ 使用缓存摘要: {page.file.src_path}")
else:
# 生成新摘要
print(f"🤖 正在生成AI摘要: {page.file.src_path}")
summary = self.generate_ai_summary(clean_content, page_title)
if not summary:
summary = self.generate_fallback_summary(clean_content, page_title)
ai_service = 'fallback'
print(f"📝 使用备用摘要: {page.file.src_path}")
else:
ai_service = 'deepseek'
print(f"✅ AI摘要生成成功: {page.file.src_path}")
# 保存到缓存
self.save_summary_cache(content_hash, {
'summary': summary,
'service': ai_service,
'page_title': page_title
})
# 添加摘要到页面最上面
summary_html = self.format_summary(summary, ai_service)
return summary_html + '\n\n' + markdown
def should_generate_summary(self, page, markdown):
"""判断是否应该生成摘要 - 可自定义文件夹"""
# 检查页面元数据
if hasattr(page, 'meta'):
# 明确禁用
if page.meta.get('ai_summary') == False:
return False
# 强制启用
if page.meta.get('ai_summary') == True:
return True
# 获取文件路径
src_path = page.file.src_path.replace('\\', '/') # 统一路径分隔符
# 检查排除模式
if any(pattern in src_path for pattern in self.exclude_patterns):
return False
# 检查排除的特定文件
if src_path in self.exclude_files:
return False
# 检查是否在启用的文件夹中
for folder in self.enabled_folders:
if src_path.startswith(folder) or f'/{folder}' in src_path:
folder_name = folder.rstrip('/')
print(f"🎯 {folder_name}文件夹文章检测到启用AI摘要: {src_path}")
return True
# 默认不生成摘要
return False
def format_summary(self, summary, ai_service):
"""格式化摘要显示"""
service_config = {
'deepseek': {
'icon': '🤖',
'name': 'AI智能摘要',
'color': 'info'
},
'fallback': {
'icon': '📝',
'name': '自动摘要',
'color': 'tip'
},
'cached': {
'icon': '💾',
'name': 'AI智能摘要',
'color': 'info'
}
}
config = service_config.get(ai_service, service_config['deepseek'])
return f'''??? {config['color']} "{config['icon']} {config['name']}"
{summary}
'''
# 创建全局实例
ai_summary_generator = AISummaryGenerator()
# 🔧 自定义配置函数
def configure_ai_summary(enabled_folders=None, exclude_patterns=None, exclude_files=None):
"""
配置AI摘要功能
Args:
enabled_folders: 启用AI摘要的文件夹列表例如 ['blog/', 'docs/', 'posts/']
exclude_patterns: 排除的模式列表例如 ['404.md', '/admin/']
exclude_files: 排除的特定文件列表例如 ['blog/index.md']
Example:
# 只在blog和docs文件夹启用
configure_ai_summary(['blog/', 'docs/'])
# 在所有文件夹启用,但排除特定文件
configure_ai_summary([''], exclude_files=['index.md', 'about.md'])
"""
ai_summary_generator.configure_folders(enabled_folders, exclude_patterns, exclude_files)
def on_page_markdown(markdown, page, config, files):
"""MkDocs hook入口点"""
return ai_summary_generator.process_page(markdown, page, config)

File diff suppressed because it is too large Load Diff

View File

@ -1,532 +1,92 @@
<!-- Footer -->
<footer class="md-footer">
<!-- Link to previous and/or next page - 移到最上面 -->
{% if "navigation.footer" in features %} {% if page.previous_page or
page.next_page %} {% if page.meta and page.meta.hide %} {% set hidden =
"hidden" if "footer" in page.meta.hide %} {% endif %}
<!-- Link to previous and/or next page -->
{% if "navigation.footer" in features %}
{% if page.previous_page or page.next_page %}
{% if page.meta and page.meta.hide %}
{% set hidden = "hidden" if "footer" in page.meta.hide %}
{% endif %}
<nav
class="md-footer__inner md-grid"
aria-label="{{ lang.t('footer') }}"
{{ hidden }}
>
<!-- Link to previous page -->
{% if page.previous_page %} {% set direction = lang.t("footer.previous") %}
{% if page.previous_page %}
{% set direction = lang.t("footer.previous") %}
<a
href="{{ page.previous_page.url | url }}"
class="md-footer__link md-footer__link--prev"
aria-label="{{ direction }}: {{ page.previous_page.title | e }}"
>
<div class="md-footer__button md-icon">
{% set icon = config.theme.icon.previous or "material/arrow-left" %} {%
include ".icons/" ~ icon ~ ".svg" %}
{% set icon = config.theme.icon.previous or "material/arrow-left" %}
{% include ".icons/" ~ icon ~ ".svg" %}
</div>
<div class="md-footer__title">
<span class="md-footer__direction"> {{ direction }} </span>
<div class="md-ellipsis">{{ page.previous_page.title }}</div>
<span class="md-footer__direction">
{{ direction }}
</span>
<div class="md-ellipsis">
{{ page.previous_page.title }}
</div>
</div>
</a>
{% endif %}
<!-- Link to next page -->
{% if page.next_page %} {% set direction = lang.t("footer.next") %}
{% if page.next_page %}
{% set direction = lang.t("footer.next") %}
<a
href="{{ page.next_page.url | url }}"
class="md-footer__link md-footer__link--next"
aria-label="{{ direction }}: {{ page.next_page.title | e }}"
>
<div class="md-footer__title">
<span class="md-footer__direction"> {{ direction }} </span>
<div class="md-ellipsis">{{ page.next_page.title }}</div>
<span class="md-footer__direction">
{{ direction }}
</span>
<div class="md-ellipsis">
{{ page.next_page.title }}
</div>
</div>
<div class="md-footer__button md-icon">
{% set icon = config.theme.icon.next or "material/arrow-right" %} {%
include ".icons/" ~ icon ~ ".svg" %}
{% set icon = config.theme.icon.next or "material/arrow-right" %}
{% include ".icons/" ~ icon ~ ".svg" %}
</div>
</a>
{% endif %}
</nav>
{% endif %} {% endif %}
{% endif %}
{% endif %}
<!-- Further information -->
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="footer-wrapper">
<!-- 访问统计区域 -->
<div class="footer-content">
<div class="footer-visit-count">
<div class="footer-item">
<!-- <span class="footer-icon">👀</span> -->
<span>本站访问量:</span>
<span id="finicount_views" class="footer-highlight"></span>
</div>
{% include "partials/copyright.html" %}
<div class="footer-item">
<!-- <span class="footer-icon">📝</span> -->
<a
href="https://icp.gov.moe/?keyword=20230640"
target="_blank"
rel="noopener noreferrer"
class="icp-link"
>萌ICP备20230640号</a>
</div>
<div class="footer-item runtime-info">
<!-- <span class="footer-icon">⏱️</span> -->
<span>本站已经运行</span>
<span id="box1" class="footer-highlight"></span>
</div>
</div>
</div>
<!-- 移动端简洁布局 (仿 footercopy.html) -->
<div class="footer-visit-count-mobile">
<span>本站访问量:</span>
<span id="finicount_views_mobile" class="footer-highlight"></span>
|
<a
href="https://icp.gov.moe/?keyword=20230640"
target="_blank"
rel="noopener noreferrer"
class="icp-link"
>萌ICP备20230640号</a>
<!-- |&nbsp; -->
<span class="runtime-info">
<span>本站已经运行</span>
<span id="box1_mobile"></span>
</span>
</div>
<!-- 版权信息和社交媒体水平布局 -->
<div class="footer-bottom-section">
<div class="md-footer-copyright">
<p>Copyright © 2022-2025 Wcowin</p>
<p>Made with <a href="https://squidfunk.github.io/mkdocs-material/" style="color: #518FC1; text-decoration: none;">Material for MkDocs</a></p>
<font color="#B9B9B9">
<div class="footer-visit-count" style="display: flex; justify-content: center; align-items: center;">
本站访问量:<script async src="//finicounter.eu.org/finicounter.js"></script>
<span id="finicount_views"></span> &nbsp;|&nbsp;
<footer>
<a href="https://icp.gov.moe/?keyword=20230640" target="_blank">萌ICP备20230640号</a>
</footer>
</div>
</font>
<style>
.footer-visit-count {
height: fit-content;
min-height: 55px; /* 根据实际情况调整此高度 */
}
</style>
{% if config.extra.social %}
<div class="footer-social">
{% include "partials/social.html" %}
</div>
{% endif %}
</div>
</div>
<script>
(function() {
// 避免变量冲突,将所有代码包装在立即执行函数中
function timingTime() {
const start = "2023-10-14T00:00:00";
const startTime = new Date(start).getTime();
const now = Date.now();
let diff = Math.floor((now - startTime) / 1000);
const days = Math.floor(diff / 86400);
diff %= 86400;
const hours = Math.floor(diff / 3600);
diff %= 3600;
const minutes = Math.floor(diff / 60);
const seconds = diff % 60;
return `${days}天${hours}时${minutes}分${seconds}秒`;
}
// 简化的计时器更新函数
function updateTime() {
const el = document.getElementById("box1");
const elMobile = document.getElementById("box1_mobile");
const time = timingTime();
if (el) el.textContent = time;
if (elMobile) elMobile.textContent = time;
}
// 加载访问量计数器
async function loadVisitCounter() {
try {
const script = document.createElement('script');
script.src = '//finicounter.eu.org/finicounter.js';
script.async = true;
script.onerror = function() {
console.log('访问量计数器加载失败,使用备用显示');
const desktop = document.getElementById("finicount_views");
const mobile = document.getElementById("finicount_views_mobile");
if (desktop) desktop.textContent = '统计中...';
if (mobile) mobile.textContent = '统计中...';
};
document.head.appendChild(script);
} catch (e) {
console.log('访问量计数器初始化失败:', e);
}
}
// 同步访问量显示
function syncVisitCount() {
const desktop = document.getElementById("finicount_views");
const mobile = document.getElementById("finicount_views_mobile");
if (desktop && mobile && desktop.textContent && desktop.textContent !== '加载中...') {
mobile.textContent = desktop.textContent;
}
}
// 监听访问量更新
function setupVisitCountObserver() {
try {
const observer = new MutationObserver(syncVisitCount);
const visitCountTarget = document.getElementById("finicount_views");
if (visitCountTarget) {
observer.observe(visitCountTarget, { childList: true, subtree: true });
}
} catch (e) {
console.log('访问量监听器设置失败:', e);
}
}
// 初始化所有功能
function init() {
// 启动计时器
updateTime();
setInterval(updateTime, 1000);
// 加载访问量计数器
loadVisitCounter();
// 设置访问量同步
setupVisitCountObserver();
// 调试信息
console.log('页脚功能已启动');
console.log('当前时间:', timingTime());
}
// 确保DOM加载完成后再初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>
<style>
/* 页脚背景配色方案 - 可根据喜好选择 */
/* 方案1: 现代深蓝灰色调 (当前使用) */
.md-footer {
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
}
.md-footer-meta {
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
}
/* 方案2: 优雅紫色调 (取消注释使用)
.md-footer {
background: linear-gradient(135deg, #2d1b69 0%, #4c1d95 100%);
}
.md-footer-meta {
background: linear-gradient(135deg, #1e1b4b 0%, #2d1b69 100%);
}
*/
/* 方案3: 温暖深绿色调 (取消注释使用)
.md-footer {
background: linear-gradient(135deg, #14532d 0%, #166534 100%);
}
.md-footer-meta {
background: linear-gradient(135deg, #052e16 0%, #14532d 100%);
}
*/
/* 方案4: 经典深灰色调 (取消注释使用)
.md-footer {
background: linear-gradient(135deg, #374151 0%, #4b5563 100%);
}
.md-footer-meta {
background: linear-gradient(135deg, #1f2937 0%, #374151 100%);
}
*/
/* 整体页脚容器 */
.footer-wrapper {
width: 100%;
padding: 0.2rem 0;
text-align: center;
}
/* 版权信息和社交媒体水平布局 */
.footer-bottom-section {
max-width: 900px;
margin: 0.4rem auto 0;
padding: 0.4rem 1rem;
border-top: 1px solid rgba(255, 255, 255, 0.15);
display: flex;
justify-content: space-between;
align-items: center;
}
/* 版权信息 */
.md-footer-copyright {
text-align: left;
font-size: 0.75rem;
opacity: 0.8;
line-height: 1.2;
}
/* 社交媒体图标 */
.footer-social {
display: -webkit-flex;
display: flex;
gap: 0.3rem;
-webkit-align-items: center;
align-items: center;
}
/* Safari兼容性修复 */
.footer-social .md-social__link svg {
-webkit-transform: translateZ(0);
transform: translateZ(0);
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
opacity: 1 !important;
visibility: visible !important;
pointer-events: auto;
}
/* 强制Safari显示SVG图标 */
.footer-social .md-social__link {
opacity: 1 !important;
visibility: visible !important;
}
/* 内容区域样式 */
.footer-content {
max-width: 900px;
margin: 0 auto;
padding: 0.4rem 0 0;
}
.footer-visit-count {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.3rem;
font-size: 0.75rem;
color: var(--md-footer-fg-color--light);
}
.footer-item {
display: flex;
align-items: center;
justify-content: center;
padding: 0.4rem 0.8rem;
border-radius: 16px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10px);
min-width: 160px;
font-size: 0.8rem;
transition: all 0.2s ease;
}
.footer-item:hover {
background: rgba(255, 255, 255, 0.15);
border-color: rgba(81, 143, 193, 0.4);
transform: translateY(-1px);
}
.footer-icon {
margin-right: 0.5rem;
font-size: 1rem;
}
.footer-highlight {
color: #939ba2;
font-weight: 600;
margin-left: 0.3rem;
}
.icp-link {
color: inherit;
text-decoration: none;
}
.icp-link:hover {
color: #518FC1;
}
/* 社交媒体图标样式 - 仅桌面端 */
@media (min-width: 76.1875em) {
.footer-social .md-social__link {
padding: 0.3rem;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.1);
width: 2rem;
height: 2rem;
display: -webkit-inline-flex;
display: inline-flex;
-webkit-align-items: center;
align-items: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-transition: all 0.2s ease;
transition: all 0.2s ease;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.footer-social .md-social__link svg {
width: 1.2rem;
height: 1.2rem;
fill: currentColor;
-webkit-flex-shrink: 0;
flex-shrink: 0;
}
.footer-social .md-social__link:hover {
background: rgba(81, 143, 193, 0.2);
border-color: rgba(81, 143, 193, 0.4);
}
}
/* 移动端社交媒体图标样式 - 恢复默认样式 */
@media (max-width: 76.1875em) {
.footer-social .md-social__link {
padding: 0.4rem;
background: transparent;
border: none;
width: auto;
height: auto;
display: inline-block;
border-radius: 0;
-webkit-transition: color 0.2s ease;
transition: color 0.2s ease;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.footer-social .md-social__link:hover {
background: transparent;
border: none;
color: var(--md-accent-fg-color);
}
.footer-social .md-social__link svg {
width: 1.6rem;
height: 1.6rem;
fill: currentColor;
-webkit-flex-shrink: 0;
flex-shrink: 0;
display: block;
}
}
/* 导航链接样式 */
.md-footer__link {
transition: opacity 0.2s ease;
}
.md-footer__link:hover {
opacity: 1;
}
/* 移动端响应式 - 使用 footercopy.html 样式 */
@media (max-width: 76.1875em) {
/* 隐藏桌面端的卡片式布局 */
.footer-visit-count {
display: none;
}
.footer-bottom-section {
flex-direction: column;
gap: 0.6rem;
text-align: center;
}
.md-footer-copyright {
text-align: center;
font-size: 0.75rem;
}
.footer-social .md-social__link {
width: 1.8rem;
height: 1.8rem;
}
/* 显示移动端的简洁布局 */
.footer-visit-count-mobile {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 0.4em 0.8em;
padding: 0.2em 0;
font-size: 0.75rem;
color: var(--md-footer-fg-color--light);
text-align: center;
}
.footer-visit-count-mobile > * {
white-space: nowrap;
}
.footer-visit-count-mobile .icp-link {
color: var(--md-footer-fg-color--light);
text-decoration: none;
}
.footer-visit-count-mobile .icp-link:hover {
color: #596875;
text-decoration: underline;
}
/* .runtime-info {
display: none; 移动端隐藏运行时间
} */
}
/* 桌面端隐藏移动端布局 */
@media (min-width: 76.1875em) {
.footer-visit-count-mobile {
display: none;
}
}
@media (min-width: 768px) {
.footer-visit-count {
flex-direction: row;
justify-content: center;
gap: 2rem;
}
}
/* 深色模式适配 */
[data-md-color-scheme="slate"] .md-footer {
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
}
[data-md-color-scheme="slate"] .md-footer-meta {
background: linear-gradient(135deg, #020617 0%, #0f172a 100%);
}
[data-md-color-scheme="slate"] .footer-item {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.12);
}
[data-md-color-scheme="slate"] .footer-item:hover {
background: rgba(255, 255, 255, 0.12);
border-color: rgba(81, 143, 193, 0.3);
}
[data-md-color-scheme="slate"] .footer-social .md-social__link {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.12);
}
</style>
</div>
</div>
</footer>

View File

@ -1,7 +1,7 @@
:root {
--admonition-border-left-width: 0.2rem;
--base-border-radius: 1rem;
/* --card-hover-shadow: 0 0 0.2rem #ffffff40; */
--base-border-radius: 0.5rem;
--card-hover-shadow: 0 0 0.2rem #ffffff40;
}
/* 卡片圆角与悬浮阴影 */
@ -20,18 +20,10 @@
}
/* 搜索框及下拉结果圆角 */
.md-search__form {
border-radius: var(--base-border-radius);
}
[data-md-toggle="search"]:checked ~ .md-header .md-search__form {
border-top-right-radius: var(--base-border-radius);
border-top-left-radius: var(--base-border-radius);
}
.md-search__form,
[data-md-toggle="search"]:checked ~ .md-header .md-search__form,
[dir="ltr"] .md-search__output {
border-bottom-right-radius: var(--base-border-radius);
border-bottom-left-radius: var(--base-border-radius);
border-radius: var(--base-border-radius);
}
/* 可选:如需恢复代码块、警告框等样式,取消注释即可 */
@ -62,10 +54,10 @@
*/
/* 可选:博客相关样式,按需启用 */
/* .md-post--excerpt {
background-color: rgba(68,138,255,.1);
box-shadow: 0 0 0 1rem rgba(68,138,255,.1);
/*
.md-post--excerpt {
background-color: var(--md-accent-fg-color--transparent);
box-shadow: 0 0 0 1rem var(--md-accent-fg-color--transparent);
border-radius: var(--base-border-radius);
}
.md-post--excerpt .md-post__header {
@ -74,14 +66,5 @@
.md-post--excerpt .md-post__content > h2,
.md-post__action {
text-align: left;
} */
/* 让所有admonition包括!!! tip圆角化且更自然 */
.md-typeset .admonition,
.md-typeset details {
border-radius: 1.5em;
box-shadow: 0 2px 12px 0 rgba(60,60,60,0.07);
transition: border-radius 0.4s cubic-bezier(.4,2,.6,1), box-shadow 0.3s;
overflow: hidden;
}
*/

View File

@ -1,7 +1,7 @@
:root > * {
--md-primary-fg-color: #1D2636;
--md-primary-fg-color--light:#1D2636;
--md-primary-fg-color--dark: #1D2636;
--md-primary-fg-color: #4c94cb;
--md-primary-fg-color--light:#4c94cb;
--md-primary-fg-color--dark: #4c94cb;
}
/* 给所有元素加上边框 */
@ -15,7 +15,7 @@ button.md-top {
font-family: LXGW WenKai; /* 修改字体 */
font-size: 16px; /* 修改字体大小 */
font-weight: lighter;/* 修改字体粗细 */
/* color: #1D2636; 修改字体颜色 */
color: #518FC1; /* 修改字体颜色 */
}
:root {
@ -271,8 +271,8 @@ height: 100%;
}
#rcorners4 {
border-radius: 32px;
border: 2px solid rgba(68,138,255,.1);
border-radius: 25px;
border: 2px solid #518FC1;
padding: 20px;
width: 110%;
height: 100%;
@ -604,27 +604,3 @@ filter: progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);
height: 30rem;
}
/* 仅针对文本链接设置红色 */
.md-typeset p a:not(.md-button):not([class*="md-"]):not([style*="background"]),
.md-typeset li a:not(.md-button):not([class*="md-"]):not([style*="background"]),
.md-typeset td a:not(.md-button):not([class*="md-"]):not([style*="background"]),
.md-typeset blockquote a:not(.md-button):not([class*="md-"]):not([style*="background"]) {
color: #5688F7 !important; /* 红色文本链接 */
text-decoration: none;
}
.md-typeset p a:not(.md-button):not([class*="md-"]):not([style*="background"]):hover,
.md-typeset li a:not(.md-button):not([class*="md-"]):not([style*="background"]):hover,
.md-typeset td a:not(.md-button):not([class*="md-"]):not([style*="background"]):hover,
.md-typeset blockquote a:not(.md-button):not([class*="md-"]):not([style*="background"]):hover {
color: #43d2e8 !important; /* 悬停时深红色 */
text-decoration: underline;
}
/* 访问过的文本链接 */
/* .md-typeset p a:not(.md-button):not([class*="md-"]):not([style*="background"]):visited,
.md-typeset li a:not(.md-button):not([class*="md-"]):not([style*="background"]):visited,
.md-typeset td a:not(.md-button):not([class*="md-"]):not([style*="background"]):visited,
.md-typeset blockquote a:not(.md-button):not([class*="md-"]):not([style*="background"]):visited {
color: #b83280 !important;
} */

View File

@ -1,48 +0,0 @@
/* hooks.css */
.version-info {
margin-top: 2rem;
font-size: 0.9rem;
color: #666;
}
.video-container {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 16:9 */
}
.video-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.runnable-code {
border: 1px solid #ddd;
border-radius: 8px;
margin: 1rem 0;
}
.code-header {
background: #f5f5f5;
padding: 0.5rem 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.run-btn {
background: #007acc;
color: white;
border: none;
padding: 0.25rem 0.5rem;
border-radius: 4px;
cursor: pointer;
}
/* 目录导航的缩进参考线 */
nav.md-nav--secondary ul {
border-left: 1px solid lightblue;
}

View File

@ -1,5 +1,5 @@
---
title: 标签🏷
title: 分类
hide:
#- navigation # 显示右
#- toc #显示左
@ -7,10 +7,13 @@ hide:
- feedback
---
<!-- # Tags -->
# Tags
<div class="grid cards" markdown>
- :material-clock-fast:{ .lg .middle } __Tips__
- :material-clock-fast:{ .lg .middle } __tips__
---
@ -19,3 +22,5 @@ hide:
</div>
<!-- material/tags -->

View File

@ -75,11 +75,7 @@ nav:
- 2.Mkdocs配置说明(mkdocs.yml): blog/Mkdocs/mkdocs2.md
- 3.解决Github Pages部署mkdocs自定义域名失效的问题: blog/Mkdocs/mkdocs3.md
- Mkdocs美化/补充:
- MkDocs文档AI摘要: blog/websitebeauty/Mkdocs-AI-Summary.md
- 添加相关推荐文章: blog/websitebeauty/recommend.md
- 添加阅读信息统计: blog/websitebeauty/reading_time.md
- 添加Mkdocs博客: blog/Mkdocs/mkdocsblog.md
- 如何给MKdocs添加友链: blog/Mkdocs/linktech.md
- 网页圆角化设计: blog/websitebeauty/yuanjiaohua.md
- 添加评论系统(giscus为例): blog/websitebeauty/mkcomments.md
- 添加在线聊天: blog/websitebeauty/webtalknow.md
@ -110,15 +106,13 @@ nav:
- 博客:
- index: blog/index.md
- 友链:
- 友链: link.md
- 友链: about/link.md
- 关于:
- 作者个人简介: about/geren.md
- 个人简历: about/resume.md
- 支持作者: about/zcw.md
- 功能测试: about/test.md
- 个人博客: https://wcowin.work
# - 个人简介: https://wcowin.work/Personal-Profile/#
# https://personal-story-web-display.lovable.app/
- 使用本主题: https://github.com/new?template_name=Mkdocs-Wcowin&template_owner=Wcowin
copyright: Copyright &copy; 2022~2025 Wcowin # 左下角的版权声明
@ -215,7 +209,7 @@ plugins:
type: iso_date
enable_creation_date: false
exclude:
# - index.md
- index.md
- tag.md
- liuyanban.md
- blog/posts/update.md
@ -283,9 +277,8 @@ extra_javascript:
# - javascripts/extra.js #自定义javascript
- https://cdn.jsdelivr.net/gh/Wcowin/Wcowin.github.io@main/docs/javascripts/extra.js # extra的cdn
# - javascripts/mathjax.js #Latex支持
- javascripts/mathjax.js #Latex支持的CDN
# - https://polyfill.io/v3/polyfill.min.js?features=es6 #Latex支持
- https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js
- https://github.com/Wcowin/Wcowin.github.io/blob/main/docs/javascripts/mathjax.js #Latex支持的CDN
- https://polyfill.io/v3/polyfill.min.js?features=es6 #Latex支持
- https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js #Latex支持
# - ckplayer/js/ckplayer.js #播放器配置
# - https://cdn.jsdelivr.net/npm/gitalk@latest/dist/gitalk.min.js #gitalk支持
@ -293,13 +286,12 @@ extra_javascript:
- https://cdn.jsdelivr.net/gh/Wcowin/Wcowin.github.io@main/docs/javascripts/view.js
extra_css:
- stylesheets/extra.css # 自定义CSS
- stylesheets/extra2.css
# - https://cdn.jsdelivr.net/gh/Wcowin/Mkdocs-Wcowin@main/docs/stylesheets/extra.css
# - stylesheets/extra.css # 自定义CSS
- https://cdn.jsdelivr.net/gh/Wcowin/Mkdocs-Wcowin@main/docs/stylesheets/extra.css
# - stylesheets/link.css #友链配置
- https://cdn.jsdelivr.net/gh/Wcowin/Wcowin.github.io@main/docs/stylesheets/link.css #友链CDN
- stylesheets/customize.css # 搜索圆角优化
# - https://cdn.jsdelivr.net/gh/Wcowin/Wcowin.github.io@main/docs/stylesheets/customize.css #按钮圆角化CDN
# - stylesheets/customize.css # 搜索圆角优化
- https://cdn.jsdelivr.net/gh/Wcowin/Wcowin.github.io@main/docs/stylesheets/customize.css #按钮圆角化CDN
# - assets/stylesheets/portfolio.css
# - stylesheets/video.css #播放器可选配置
# - https://cdn.jsdelivr.net/npm/gitalk@latest/dist/gitalk.css #gitalk支持
@ -318,6 +310,3 @@ extra_css:
hooks:
- docs/overrides/hooks/socialmedia.py
- docs/overrides/hooks/reading_time.py
- docs/overrides/hooks/ai_summary.py
- docs/overrides/hooks/related_posts.py

View File

@ -1,6 +0,0 @@
{
"summary": "giscus是一款基于GitHub Discussions的开源评论系统无需数据库数据直接存储在GitHub中。它具有无跟踪、无广告、永久免费的特点支持多语言、自定义主题和高可配置性。系统能自动同步GitHub的评论更新适用于静态网站集成提供轻量级、隐私友好的评论解决方案同时允许用户自建服务。",
"service": "deepseek",
"page_title": "添加评论系统(giscus为例)",
"timestamp": "2025-06-03T16:16:38.038207"
}

View File

@ -1,6 +0,0 @@
{
"summary": "该项目为MkDocs文档系统提供AI驱动的智能摘要生成和阅读统计功能支持OpenAI、DeepSeek等多API服务。核心特性包括自动生成80-120字高质量摘要、多语言支持、智能内容清理和高效缓存系统同时提供精准的中文阅读统计和阅读时间估算。通过灵活的配置选项用户可实现文件夹或页面级别的精确控制并自定义API服务和提示词。该工具显著提升了技术文档的可读性和管理效率。",
"service": "deepseek",
"page_title": "MkDocs文档AI摘要",
"timestamp": "2025-06-03T20:49:35.847514"
}

View File

@ -1,6 +0,0 @@
{
"summary": "2022年网站更新主要包括技术框架搭建和用户体验优化。6月选定MKdocs作为建站框架10月完成Github仓库创建并启用独立域名。关键改进包括取消cookie确认弹窗以简化访问流程、增强隐私保护并新增首页反馈功能。更新体现了从技术选型到交互优化的完整建设路径重点关注用户友好性和数据隐私。",
"service": "deepseek",
"page_title": "2022网站更新记录",
"timestamp": "2025-06-03T16:15:36.310549"
}

View File

@ -1,6 +0,0 @@
{
"summary": "霞鹜文楷是一款基于日本FONTWORKS公司Klee One衍生的开源中文字体兼具仿宋和楷体特点适合诗词和注释排版。提供多个版本屏幕阅读版、轻便版、GB/TC规范版等支持简繁日韩字符。该字体解决了商业字体侵权风险问题填补了开源中文字体中楷体类的空白。用户可通过GitHub、Gitee等平台获取支持Windows/macOS系统安装。字体采用SIL Open Font License 1.1授权,适合网站和移动设备使用。",
"service": "deepseek",
"page_title": "修改网站字体",
"timestamp": "2025-06-03T16:16:50.880853"
}

View File

@ -1,6 +0,0 @@
{
"summary": "唐·诺曼提出的情感设计三层次理论包括本能层、行为层和反思层。本能层关注产品外观引发的即时情感反应,行为层强调使用过程中的功能性和效率,反思层涉及用户对产品的理性认知和自我形象关联。这三个层次相互影响,共同塑造用户对产品的整体体验。该理论为设计提供了系统框架,解释了为何某些产品即使存在功能缺陷仍能获得市场成功。",
"service": "deepseek",
"page_title": "唐·诺曼—情感设计的三个层次",
"timestamp": "2025-06-03T16:18:11.355466"
}

View File

@ -1,6 +0,0 @@
{
"summary": "该技术文档展示了一个多功能测试组件包含代码片段、数学公式、流程图及视频播放器配置等元素。支持HTML、CSS、JavaScript技术栈提供Markdown内容管理和快速部署功能5分钟搭建。具备自定义主题、MIT开源协议特性适用于构建响应式静态网站。核心组件包括天气插件、极限公式演示及CKPlayer视频播放器初始化方案。",
"service": "deepseek",
"page_title": "功能测试",
"timestamp": "2025-06-03T16:14:38.445737"
}

View File

@ -1,6 +0,0 @@
{
"summary": "该文章介绍了通过JavaScript和CSS自定义网页鼠标样式的实现方法重点说明了关键参数配置包括鼠标尺寸、颜色支持RGB值和颜色名称以及圆形跟随效果。提供了具体的代码示例和配置路径并强调需在mkdocs.yml中引入相关文件。适用于需要个性化鼠标交互效果的网页开发场景。",
"service": "deepseek",
"page_title": "JS实现鼠标样式",
"timestamp": "2025-06-03T16:16:59.645819"
}

View File

@ -1,6 +0,0 @@
{
"summary": "该技术方案通过git-revision-date-localized插件为MKdocs文档系统添加文章修订时间戳功能。核心实现是在CI工作流中优化git历史检查策略既提升了本地渲染速度又确保发布时准确显示最后更新时间。该方法适用于需要展示文档时效性的场景通过修改ci.yml配置文件即可快速部署兼顾了开发效率和功能完整性。",
"service": "deepseek",
"page_title": "为MKdocs添加文章修订时间戳",
"timestamp": "2025-06-03T16:17:09.116396"
}

View File

@ -1,6 +0,0 @@
{
"summary": "该项目为MkDocs文档系统提供AI驱动的智能摘要生成和阅读统计功能支持多语言和多种AI服务API。核心特性包括自动生成80-120字高质量摘要、智能内容清理、高效缓存系统以及精准的阅读时间估算。通过灵活的配置选项用户可实现文件夹或页面级别的控制并自定义API服务和提示词。该方案具有经济高效、性能优化等特点适用于技术文档和博客的自动化摘要需求。",
"service": "deepseek",
"page_title": "MkDocs文档AI摘要",
"timestamp": "2025-06-03T20:37:34.409741"
}

View File

@ -1,6 +0,0 @@
{
"summary": "本文提供了多种支持作者的方式包括通过邮箱联系、访问GitHub仓库以及通过支付宝、微信支付和Ko-fi等平台进行赞助。文章列出了具体的赞助渠道和按钮链接并附有近期赞助者的名单及时间记录方便读者选择合适的方式支持作者的工作。",
"service": "deepseek",
"page_title": "支持作者",
"timestamp": "2025-06-03T16:14:46.358819"
}

View File

@ -1,6 +0,0 @@
{
"summary": "本文详细介绍了Mkdocs配置文件mkdocs.yml的核心配置项包括站点基本信息设置、Material主题配色方案支持明暗模式切换、导航功能优化如目录跟踪、返回顶部按钮、搜索增强功能以及Markdown扩展支持。重点讲解了标签系统实现方法、自定义CSS/JS引入方式并提供了扩展网站功能的实践建议如添加数学公式支持、鼠标样式和评论系统等。配置文件采用YAML语法通过灵活组合各项参数可快速构建功能完善的文档网站。",
"service": "deepseek",
"page_title": "2.Mkdocs配置说明(mkdocs.yml)",
"timestamp": "2025-06-03T16:15:17.312523"
}

View File

@ -1,6 +0,0 @@
{
"summary": "MWeb Pro是一款专为macOS设计的专业Markdown写作与笔记应用支持GFM语法扩展含表格、LaTeX、代码块等及多种图表库mermaid/PlantUML等。提供高效的图文混排功能支持拖拽插入图片并调整宽度可导出为PDF/HTML/Epub等格式或直接发布至WordPress/印象笔记等平台。其文档库支持分类树、标签管理及静态网站生成,兼具快速笔记收集和全局搜索功能,适合技术写作、知识管理及博客发布场景。",
"service": "deepseek",
"page_title": "MWeb Pro",
"timestamp": "2025-06-03T16:17:50.584572"
}

View File

@ -1,6 +0,0 @@
{
"summary": "本文探讨了MkDocs中使用相对地址引用资源时的常见问题与解决方案涵盖图片、PDF、跨页面及静态资源的引用方法。重点分析了路径规范、大小写敏感性和目录结构管理强调以当前Markdown文件为基准的相对路径写法确保本地预览与线上部署一致性。提供了针对路径错误、资源加载失败等问题的排查建议推荐统一资源目录结构以提升维护性。",
"service": "deepseek",
"page_title": "相对地址的一些问题",
"timestamp": "2025-06-03T16:16:12.235861"
}

View File

@ -1,6 +0,0 @@
{
"summary": "本文介绍了使用MkDocs构建静态网站并部署到GitHub Pages的完整流程。重点讲解了通过GitHub Desktop管理仓库、配置mkdocs.yml文件、设置GitHub Actions自动化部署等关键步骤并提供了Material主题的配置建议。该方法适合快速搭建技术文档或博客网站具有版本控制友好、部署简单的特点最后还提供了实时预览和自定义域名的实现方式。",
"service": "deepseek",
"page_title": "1.利用Mkdocs部署静态网页",
"timestamp": "2025-06-03T16:15:07.175758"
}

View File

@ -1,6 +0,0 @@
{
"summary": "MIT许可证授予用户自由使用、修改、分发软件的广泛权利仅要求保留版权声明和许可条款。该许可证明确声明软件不提供任何担保作者不对使用后果承担责任。作为宽松开源协议MIT允许商业用途和私有修改是开发者广泛采用的标准许可之一。",
"service": "deepseek",
"page_title": "许可声明",
"timestamp": "2025-06-03T16:14:56.187919"
}

View File

@ -1,6 +0,0 @@
{
"summary": "本文介绍了使用MkDocs构建网站时的性能优化方法包括采用WebP等高效图片格式压缩资源、通过CDN分发静态文件减少延迟以及优化git插件配置加速本地渲染。建议开发时禁用非必要插件使用Lighthouse工具测试优化效果。这些措施能显著提升网站加载速度和用户体验。",
"service": "deepseek",
"page_title": "加速网站访问的一些心得",
"timestamp": "2025-06-03T16:16:20.040248"
}

View File

@ -1,6 +0,0 @@
{
"summary": "该技术文档介绍了三种网页背景特效的实现方法雪花、樱花和粒子效果。核心是通过JavaScript动态加载特效脚本并利用LocalStorage控制特效的显示状态。雪花特效通过检测LocalStorage标志决定是否加载外部JS文件其他特效也采用类似实现方式。这些轻量级特效可增强网页视觉体验适用于节日主题或装饰性页面需求。",
"service": "deepseek",
"page_title": "背景特效",
"timestamp": "2025-06-03T16:16:30.348375"
}

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