mirror of
https://github.com/Wcowin/Mkdocs-Wcowin.git
synced 2025-07-20 08:56:35 +00:00
Compare commits
84 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0575c05be3 | ||
a8ca1caa1c | |||
|
f12ee99693 | ||
a3efc6ccb7 | |||
9d455d6b81 | |||
|
367b8205d4 | ||
85a392130b | |||
d7cd2e9703 | |||
739737bd32 | |||
|
e2465b9581 | ||
6f793041b1 | |||
9f85f687fb | |||
8c1c983ac6 | |||
|
8612aea755 | ||
43e053bd8e | |||
68f840e0f3 | |||
|
252c5516a0 | ||
36492898d4 | |||
1639ed459f | |||
|
60f25f8f09 | ||
1f32f608ff | |||
|
428fbe0aeb | ||
addfa1289c | |||
41d75cd2a8 | |||
|
d2c462b046 | ||
0dabee6a18 | |||
62e7666ea1 | |||
a3758c36c7 | |||
|
4628abb9f5 | ||
018fcdd60f | |||
|
c31daf4ade | ||
017b804ecb | |||
59f0cddc96 | |||
|
c300e78a94 | ||
5b827b273e | |||
ada5c410fb | |||
|
304e1de3a5 | ||
819b4c1d39 | |||
7cc59fd0ab | |||
|
117193dac3 | ||
ae9ee6cb5b | |||
367a4bbe11 | |||
|
b7f73c5171 | ||
ab05aa5765 | |||
|
bc2a5dfac6 | ||
1f9a166488 | |||
ab0a755eeb | |||
|
613be6f668 | ||
c3b4772c50 | |||
a1279dff44 | |||
|
955b097dac | ||
0b1f8e3349 | |||
f7fb6314cd | |||
|
fd0f0283d1 | ||
fb5143a95c | |||
|
865e5e5ac9 | ||
a5fca67215 | |||
|
63de83f68e | ||
1082654942 | |||
94628b57c3 | |||
c4540d868e | |||
ae13ab78f4 | |||
|
9e8af81916 | ||
74812525a2 | |||
19a99d9e30 | |||
b71afb6257 | |||
27fa014569 | |||
7d33dcf50d | |||
c32ed1ac9f | |||
8e6f69e268 | |||
dd23049a5f | |||
1ab86a517f | |||
08e8327c7b | |||
85f609502e | |||
b5a36c5ce3 | |||
335518ce0d | |||
3fb2db6506 | |||
63464c6d67 | |||
7fc407847f | |||
22c0a8add2 | |||
ce08e8dc4a | |||
17c317b771 | |||
e4928c4b1a | |||
2ab539c78d |
7
.ai_cache/0027722f277bde02f38323d73b77a3d3.json
Normal file
7
.ai_cache/0027722f277bde02f38323d73b77a3d3.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "本文记录了2025年网站更新,包括优化流畅度、修复显示问题、设计改进和教程更新等,旨在提升用户体验和网站性能。",
|
||||
"service": "glm",
|
||||
"page_title": "2025网站更新记录",
|
||||
"timestamp": "2025-07-09T19:59:54.371013",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/008bdcfed0b0b9b589c54c03e8783d0d.json
Normal file
7
.ai_cache/008bdcfed0b0b9b589c54c03e8783d0d.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "本文介绍了Git的基本操作和实用技巧,包括新建仓库、克隆、提交、分支管理、合并冲突处理等,并探讨了临时保存、灵活合并、 cherry-pick、修改提交等高级操作,旨在帮助开发者提升Git使用效率。",
|
||||
"service": "glm",
|
||||
"page_title": "Git 实用技巧",
|
||||
"timestamp": "2025-07-09T20:00:14.257325",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/0b6176fc36485a4b7648d96ffd6b7282.json
Normal file
7
.ai_cache/0b6176fc36485a4b7648d96ffd6b7282.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "MkDocs支持自定义页脚,通过编辑`footer.html`文件可添加自定义代码,实现个性化页脚设置。",
|
||||
"service": "glm",
|
||||
"page_title": "页脚设置",
|
||||
"timestamp": "2025-07-09T20:00:02.047017",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/0d3e95ce9ad53d469c8aaae47719525f.json
Normal file
7
.ai_cache/0d3e95ce9ad53d469c8aaae47719525f.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "Tidio免费版提供便捷的在线聊天功能,只需创建账户并粘贴一行代码即可安装,适用于各类网站。",
|
||||
"service": "glm",
|
||||
"page_title": "添加在线聊天",
|
||||
"timestamp": "2025-07-09T20:00:10.797953",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/0dcbc37ab3e6963f1b3741d81c32b9c9.json
Normal file
7
.ai_cache/0dcbc37ab3e6963f1b3741d81c32b9c9.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "本文介绍了网页圆角化设计的方法,包括通过CSS文件引入和md文件使用,实现图片和边框的圆角效果,并展示了如何利用内置的grid cards和按钮样式,提升网页视觉效果。",
|
||||
"service": "glm",
|
||||
"page_title": "网页圆角化设计",
|
||||
"timestamp": "2025-07-09T20:00:12.611857",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/0ec4ff66c09bcbc1f189ff70391a7d5d.json
Normal file
7
.ai_cache/0ec4ff66c09bcbc1f189ff70391a7d5d.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "通过git插件实现MKdocs文章修订时间戳功能,支持本地化日期显示,优化渲染速度,提升文档更新追踪效率。",
|
||||
"service": "glm",
|
||||
"page_title": "为MKdocs添加文章修订时间戳",
|
||||
"timestamp": "2025-07-09T20:00:09.608392",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/0eeeda15a44fd8926aaf5ff9812ea5ea.json
Normal file
7
.ai_cache/0eeeda15a44fd8926aaf5ff9812ea5ea.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "麻省理工学院许可证允许用户无限制使用、复制、修改和分发软件,但需保留版权声明和许可声明。软件提供“原样”,不保证适销性或无侵权性,使用风险自担。",
|
||||
"service": "glm",
|
||||
"page_title": "许可声明",
|
||||
"timestamp": "2025-07-09T19:59:37.331858",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/11703e885f66213ef55252aa0203d1f8.json
Normal file
7
.ai_cache/11703e885f66213ef55252aa0203d1f8.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"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"
|
||||
}
|
7
.ai_cache/2159251c4cc57ddc1a2e5a4d6ce49b0f.json
Normal file
7
.ai_cache/2159251c4cc57ddc1a2e5a4d6ce49b0f.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "本文针对MKdocs中相对地址引用问题进行探讨,涵盖图片、PDF、Markdown页面等资源引用的写法、注意事项及常见问题,强调统一资源管理目录结构的重要性,确保本地和线上文档正确访问。",
|
||||
"service": "glm",
|
||||
"page_title": "相对地址的一些问题",
|
||||
"timestamp": "2025-07-09T19:59:57.281649",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/220350a8f8e51d3b7455257138a808af.json
Normal file
7
.ai_cache/220350a8f8e51d3b7455257138a808af.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "唐·诺曼提出情感设计分为本能层、行为层和反思层,分别影响产品外观、使用体验和用户思考。本能层关注产品外观,行为层关注使用体验,反思层关注用户对产品的思考。这三个层次共同构成产品整体体验。",
|
||||
"service": "glm",
|
||||
"page_title": "唐·诺曼—情感设计的三个层次",
|
||||
"timestamp": "2025-07-09T20:00:21.378650",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/2d86ca064a44a260d18cfb9cb515d7a0.json
Normal file
7
.ai_cache/2d86ca064a44a260d18cfb9cb515d7a0.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "Mkdocs博客插件易于配置,通过添加特定行至mkdocs.yml文件,并配置作者信息,即可快速搭建个人博客。",
|
||||
"service": "glm",
|
||||
"page_title": "添加Mkdocs博客",
|
||||
"timestamp": "2025-07-09T19:59:47.865754",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/30abfae7f16ecb7e41855d7ee59fe151.json
Normal file
7
.ai_cache/30abfae7f16ecb7e41855d7ee59fe151.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "霞鹜文楷是一款基于Klee One开源日文字体衍生的高质量中文字体,支持简繁日韩三字重,适用于网站、手机系统等,具有美观、可读性强等特点。",
|
||||
"service": "glm",
|
||||
"page_title": "修改网站字体",
|
||||
"timestamp": "2025-07-09T20:00:05.955545",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/3309c39267871635526f6fed179bdcc7.json
Normal file
7
.ai_cache/3309c39267871635526f6fed179bdcc7.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "本文介绍了如何通过JavaScript和LocalStorage实现网页背景特效,包括雪花、樱花和粒子效果,通过设置标志控制是否显示,适用于个性化网页设计。",
|
||||
"service": "glm",
|
||||
"page_title": "背景特效",
|
||||
"timestamp": "2025-07-09T20:00:00.962646",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/3b92e621cadfddb1f9d06e307c5f7ccf.json
Normal file
7
.ai_cache/3b92e621cadfddb1f9d06e307c5f7ccf.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "本文介绍了使用MkDocs和GitHub Pages部署静态网页的详细步骤,包括准备工作、创建网站、配置完善和参考资料,帮助读者快速搭建个人博客或网站。",
|
||||
"service": "glm",
|
||||
"page_title": "1.利用Mkdocs部署静态网页",
|
||||
"timestamp": "2025-07-09T19:59:41.333874",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/3d3b50606857af8f7e5e972fcf31b31f.json
Normal file
7
.ai_cache/3d3b50606857af8f7e5e972fcf31b31f.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "MWeb Pro是一款macOS上的Markdown写作与笔记应用,支持GFM语法、多种输出格式和强大的笔记功能,适用于博客写作、知识管理和文档输出。",
|
||||
"service": "glm",
|
||||
"page_title": "MWeb Pro",
|
||||
"timestamp": "2025-07-09T20:00:17.187140",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/42b524e7ffe869ec1cdd013899773fe9.json
Normal file
7
.ai_cache/42b524e7ffe869ec1cdd013899773fe9.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "本文介绍了如何在网站中添加顶部公告栏,通过在`docs/overrides`目录下新建`main.html`文件并修改其内容实现,适用于自定义网站布局。",
|
||||
"service": "glm",
|
||||
"page_title": "添加顶部公告栏",
|
||||
"timestamp": "2025-07-09T20:00:03.273178",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/4d823b90bb4a370baba6d254759e8951.json
Normal file
7
.ai_cache/4d823b90bb4a370baba6d254759e8951.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "本文介绍了Mkdocs文档教程,涵盖官方教程、Material for MkDocs插件、中文教程和视频教程,指导用户快速部署静态网页至GitHub pages,并配置Mkdocs博客。",
|
||||
"service": "glm",
|
||||
"page_title": "0.Mkdocs教程前言",
|
||||
"timestamp": "2025-07-09T19:59:49.407883",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/5560f97450f87a5fc516715dca59124e.json
Normal file
7
.ai_cache/5560f97450f87a5fc516715dca59124e.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "优化网站访问速度,可采取图片格式转换、使用CDN加速资源、调整本地渲染配置等措施,并利用Lighthouse进行性能测试。",
|
||||
"service": "glm",
|
||||
"page_title": "加速网站访问的一些心得",
|
||||
"timestamp": "2025-07-09T19:59:59.882438",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/59c551361d3a6a9dc416cfcb9a16b6d0.json
Normal file
7
.ai_cache/59c551361d3a6a9dc416cfcb9a16b6d0.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "Markdown是一种轻量级标记语言,便于纯文本格式编写文档,支持多种格式导出。本文介绍了Markdown的基本语法,包括标题、列表、表格、链接、图片、代码块等,并提供了一些文档创作工具推荐。",
|
||||
"service": "glm",
|
||||
"page_title": "Markdown指南",
|
||||
"timestamp": "2025-07-09T20:00:19.000594",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/632dbb8b4be93cc5aee43beaa9640dd7.json
Normal file
7
.ai_cache/632dbb8b4be93cc5aee43beaa9640dd7.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "本文介绍了如何为Markdown文档添加阅读时间统计功能。通过配置mkdocs.yml文件和编写Python脚本,实现自动计算并展示阅读时间、中文字符数和代码行数等统计信息。",
|
||||
"service": "glm",
|
||||
"page_title": "添加阅读信息统计",
|
||||
"timestamp": "2025-07-09T20:00:07.898964",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/815b3ebd5170fde5593a4a0c67813d41.json
Normal file
7
.ai_cache/815b3ebd5170fde5593a4a0c67813d41.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "MkDocs AI Hooks是一款集成AI摘要和智能阅读统计功能的插件,支持多AI服务,自动生成80-120字摘要,并提供多种配置选项,提升文档智能化和用户体验。",
|
||||
"service": "glm",
|
||||
"page_title": "MkDocs文档AI摘要",
|
||||
"timestamp": "2025-07-09T19:59:55.922690",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/8c834bb008a9ea4f0259db65d38d8891.json
Normal file
7
.ai_cache/8c834bb008a9ea4f0259db65d38d8891.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "通过简单复制代码至MD文件,即可在MKdocs中添加友链。采用极简设计,支持卡片样式和响应式布局,适用于展示技术博客或资源分享网站。",
|
||||
"service": "glm",
|
||||
"page_title": "如何给MKdocs添加友链",
|
||||
"timestamp": "2025-07-09T19:59:39.765402",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/9135036bb2cee912073c79448a507199.json
Normal file
7
.ai_cache/9135036bb2cee912073c79448a507199.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "本文介绍了使用JavaScript和CSS自定义鼠标样式的实现方法,包括尺寸、颜色等参数设置,并提供了代码示例和配置步骤。",
|
||||
"service": "glm",
|
||||
"page_title": "JS实现鼠标样式",
|
||||
"timestamp": "2025-07-09T20:00:08.755133",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/941b898d6a2fb8331109e1dbec8a087c.json
Normal file
7
.ai_cache/941b898d6a2fb8331109e1dbec8a087c.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "本文记录了2024年网站更新,包括优化网站流畅度、启用CDN加速、美化Blog页面、更新教程、修复bugs等,旨在提升用户体验。",
|
||||
"service": "glm",
|
||||
"page_title": "2024网站更新记录",
|
||||
"timestamp": "2025-07-09T19:59:53.114480",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/d3b41a11e5859b15b2707e47aae0c888.json
Normal file
7
.ai_cache/d3b41a11e5859b15b2707e47aae0c888.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"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"
|
||||
}
|
7
.ai_cache/d8d8f54e1bc344c8d6175d7ff48d59e5.json
Normal file
7
.ai_cache/d8d8f54e1bc344c8d6175d7ff48d59e5.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "设计应简洁精炼,追求优雅美感,以用户体验为核心,注重细节,鼓励创新与情感共鸣,打造令人愉悦且富有创意的体验。",
|
||||
"service": "glm",
|
||||
"page_title": "我对设计的一些观点",
|
||||
"timestamp": "2025-07-09T20:00:22.728567",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/d9cacc5ca0e343bad0c95d22720949e9.json
Normal file
7
.ai_cache/d9cacc5ca0e343bad0c95d22720949e9.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "Lighthouse作为浏览器扩展,可快速评估网站性能,通过谷歌或Edge浏览器即可使用,助力优化网页加载速度和用户体验。",
|
||||
"service": "glm",
|
||||
"page_title": "利用Lighthouse测试网站性能",
|
||||
"timestamp": "2025-07-09T20:00:15.859700",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/e6502b1e54edcc91caf57b6386aa914e.json
Normal file
7
.ai_cache/e6502b1e54edcc91caf57b6386aa914e.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "网站持续优化,包括流畅度提升、教程更新、修复显示问题,引入新材料设计规范和插件,提升用户体验。",
|
||||
"service": "glm",
|
||||
"page_title": "2025网站更新记录",
|
||||
"timestamp": "2025-07-09T23:18:47.552565",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/e7267ce6a4cd1f5054768befa04b1173.json
Normal file
7
.ai_cache/e7267ce6a4cd1f5054768befa04b1173.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "2022年网站更新记录:建立Github仓库,简化打开流程,新增反馈收集;购买域名,取消cookie确认,保障隐私;研究网站建设,初步确定框架为MKdocs。",
|
||||
"service": "glm",
|
||||
"page_title": "2022网站更新记录",
|
||||
"timestamp": "2025-07-09T19:59:50.455583",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/eb9262abe9f1173610b1de79da25b5dd.json
Normal file
7
.ai_cache/eb9262abe9f1173610b1de79da25b5dd.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "giscus是一款基于GitHub Discussions的评论系统,开源、无跟踪广告,支持自定义主题和多种语言,自动同步评论,可自建服务,适用于网站评论功能。",
|
||||
"service": "glm",
|
||||
"page_title": "添加评论系统(giscus为例)",
|
||||
"timestamp": "2025-07-09T20:00:04.475556",
|
||||
"language": "zh"
|
||||
}
|
7
.ai_cache/ee2f15a1a2f326e50b283acbba050ccf.json
Normal file
7
.ai_cache/ee2f15a1a2f326e50b283acbba050ccf.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"summary": "本文记录了2023年网站更新,包括优化流畅度、引入人工智能问答机器人、新增多语言支持、修复Bug、增加友链版块、引入个性化标签等功能,旨在提升用户体验和网站性能。",
|
||||
"service": "glm",
|
||||
"page_title": "2023网站更新记录",
|
||||
"timestamp": "2025-07-09T19:59:51.720080",
|
||||
"language": "zh"
|
||||
}
|
10
.ai_cache/service_config.json
Normal file
10
.ai_cache/service_config.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"default_service": "glm",
|
||||
"available_services": [
|
||||
"glm",
|
||||
"openai",
|
||||
"gemini"
|
||||
],
|
||||
"summary_language": "zh",
|
||||
"check_time": "2025-07-16T07:55:31.381146"
|
||||
}
|
@ -1 +1,9 @@
|
||||
{"cache_date": "2024-07-22", "page_authors": {}}
|
||||
<<<<<<< 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
|
||||
|
55
.github/workflows/ci.yml
vendored
55
.github/workflows/ci.yml
vendored
@ -4,6 +4,10 @@ on:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
# 禁止从 fork 仓库访问 secrets
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches: [main, master]
|
||||
permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
@ -13,27 +17,60 @@ 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
|
||||
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
|
||||
- name: Set cache ID
|
||||
run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
key: mkdocs-material-${ env.cache_id }
|
||||
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 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 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
|
||||
- run: mkdocs gh-deploy --force
|
||||
- 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
|
||||
|
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# 环境变量文件(敏感信息)
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
*.key
|
||||
|
||||
# MkDocs 构建输出目录
|
||||
site/
|
||||
|
||||
# AI 摘要缓存目录(项目根目录)- 需要被提交
|
||||
!.ai_cache/
|
68
.ignore
68
.ignore
@ -1,68 +0,0 @@
|
||||
# Copyright (c) 2016-2024 Martin Donath <martin.donath@squidfunk.com>
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Node, TypeScript, Python
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Dependencies
|
||||
node_modules
|
||||
__pycache__
|
||||
venv
|
||||
.venv
|
||||
|
||||
# Build files
|
||||
blog
|
||||
build
|
||||
site
|
||||
docs/blog
|
||||
# Distribution files
|
||||
dist
|
||||
mkdocs_material.egg-info
|
||||
|
||||
# Caches and logs
|
||||
*.cpuprofile
|
||||
*.log
|
||||
*.tsbuildinfo
|
||||
.cache
|
||||
.eslintcache
|
||||
__pycache__
|
||||
|
||||
# Examples
|
||||
example
|
||||
example.zip
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# General
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Never ignore .gitkeep files
|
||||
!**/.gitkeep
|
||||
|
||||
# macOS internals
|
||||
.DS_Store
|
||||
|
||||
# Temporary files
|
||||
TODO
|
||||
tmp
|
||||
|
||||
# IDEs & Editors
|
||||
.idea
|
||||
*~
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,3 +1,4 @@
|
||||
{
|
||||
"bitoAI.codeCompletion.enableAutoCompletion": false
|
||||
"bitoAI.codeCompletion.enableAutoCompletion": false,
|
||||
"Codegeex.RepoIndex": true
|
||||
}
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Wcowin
|
||||
Copyright (c) 2025 Wcowin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
85
README.md
85
README.md
@ -1,12 +1,24 @@
|
||||
# Wcowin for MkDocs主题
|
||||
|
||||
**- 基于Material for MkDocs美化**
|
||||
**- 简洁美观,功能多元化**
|
||||
**- 可自定义样式,中文教程详细**
|
||||
**- 简单易上手,小白配置**
|
||||
**- 𝕙𝕒𝕧𝕖 𝕒 𝕘𝕠𝕠𝕕 𝕥𝕚𝕞𝕖 !**
|
||||
|
||||
- **基于Material for MkDocs美化**
|
||||
- **简洁美观,功能多元化**
|
||||
- **可自定义样式,中文教程详细**
|
||||
- **简单易上手,小白配置**
|
||||
- **𝕙𝕒𝕧𝕖 𝕒 𝕘𝕠𝕠𝕕 𝕥𝕚𝕞𝕖 !**
|
||||
|
||||
## 目录
|
||||
- [Wcowin for MkDocs主题](#wcowin-for-mkdocs主题)
|
||||
- [目录](#目录)
|
||||
- [展示](#展示)
|
||||
- [来自Claude的肯定](#来自claude的肯定)
|
||||
- [如何快速使用](#如何快速使用)
|
||||
- [视频教程](#视频教程)
|
||||
- [Connect with me](#connect-with-me)
|
||||
- [案例成果](#案例成果)
|
||||
- [Star History](#star-history)
|
||||
- [贡献者](#贡献者)
|
||||
- [请作者喝杯咖啡](#请作者喝杯咖啡)
|
||||
- [License](#license)
|
||||
|
||||
## 展示
|
||||
|
||||
@ -41,20 +53,34 @@
|
||||
|
||||
</center>
|
||||
|
||||
## 来自Claude的肯定
|
||||
|
||||

|
||||
|
||||
## 如何快速使用
|
||||
|
||||
打开终端安装mkdocs: `pip install mkdocs-material`,在你本地的文件夹下(我的就是Wcowin.github.io这个文件夹)的终端执行`git clone git@github.com:Wcowin/Mkdocs-Wcowin.git`克隆本模版到本地
|
||||
首先,建议在虚拟环境下安装 mkdocs-material:
|
||||
|
||||
把Mkdocs-Wcowin文件里的东西全部复制出来到Wcowin.github.io文件里(如下图),随后在Wcowin.github.io文件目录终端里`mkdocs serve`即可
|
||||
```bash
|
||||
pip install mkdocs-material
|
||||
```
|
||||
|
||||
**方法一:**
|
||||
直接下载[releases](https://github.com/Wcowin/Mkdocs-Wcowin/releases)里的`Wcowin-for-MkDocs.zip`文件,解压到你本地的文件夹下,随后在文件目录终端里`mkdocs serve`即可
|
||||
|
||||
**方法二:**
|
||||
在你本地的文件夹下(我的就是Wcowin.github.io这个文件夹)的终端执行`git clone git@github.com:Wcowin/Mkdocs-Wcowin.git`克隆本模版到本地
|
||||
|
||||
把克隆下来文件里的东西全部复制出来到Wcowin.github.io文件里(如下图),随后在Wcowin.github.io文件目录终端里`mkdocs serve`即可运行,另一种运行方法:根目录有一个`Mkdocs-serve.bat`批处理文件,直接双击即可(仅在Windows系统下有效)
|
||||

|
||||
|
||||
> 另一种运行方法:根目录有一个`Mkdocs-serve.bat`批处理文件,直接双击即可
|
||||
!注意:如果提示未安装git-committers等插件,在终端执行`pip install git-committers`即可,缺少什么就安装什么,直接执行`pip install -r requirements.txt`也可以安装所有插件
|
||||
|
||||
详细的初步教程见:[利用Mkdocs部署静态网页至GitHubpages](TECH.md)
|
||||
初步教程见:[利用Mkdocs部署静态网页至GitHubpages](快速开始.md)
|
||||
|
||||
## 视频教程
|
||||
|
||||
[Mkdocs中文教程视频](https://space.bilibili.com/1407028951/lists/4566631?type=series)
|
||||
[Mkdocs中文教程视频](https://space.bilibili.com/1407028951/lists/4566631?type=series)(手把手教学,首次使用MKdocs建议观看)
|
||||
|
||||
# Connect with me
|
||||
|
||||
@ -77,6 +103,7 @@
|
||||
|
||||
</center>
|
||||
|
||||
|
||||
## 案例成果
|
||||
|
||||
[Lenny's Web](https://lennychen.top)
|
||||
@ -102,6 +129,38 @@
|
||||
|
||||
[](https://squidfunk.github.io/mkdocs-material/)
|
||||
|
||||
## 版权声明
|
||||
<!-- ## 版权声明
|
||||
|
||||
本作品采用[知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans)进行许可。
|
||||
本作品采用[知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans)进行许可。 -->
|
||||
## 请作者喝杯咖啡
|
||||
|
||||
<a href="https://pic2.zhimg.com/80/v2-4384c32173a239a1609309aa1b1ee9f9_1440w.webp" target="_blank">
|
||||
<center>
|
||||
<img src="https://pic2.zhimg.com/80/v2-4384c32173a239a1609309aa1b1ee9f9_1440w.webp" style="width: 450px; height: auto;">
|
||||
</center>
|
||||
</a>
|
||||
|
||||
|
||||
## License
|
||||
|
||||
**MIT License**
|
||||
|
||||
Copyright (c) 2022-2025 Wang Kewen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
1
ads.txt
Normal file
1
ads.txt
Normal file
@ -0,0 +1 @@
|
||||
google.com, pub-2327435979273742, DIRECT, f08c47fec0942fa0
|
@ -6,6 +6,7 @@ hide:
|
||||
- footer
|
||||
- feedback
|
||||
comments: false
|
||||
hide_reading_time: true
|
||||
---
|
||||
# 首頁
|
||||
|
||||
@ -158,17 +159,6 @@ comments: false
|
||||
[^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)}
|
||||
|
@ -14,10 +14,70 @@ status: new
|
||||
|
||||
## 关于我
|
||||
|
||||
<center>
|
||||
<!-- <center>
|
||||
<img src="https://picx.zhimg.com/v2-fb22186d2490043435a72876950492f5_1440w.jpg"
|
||||
style="width: 270px; border-radius: 50%; display: block; margin: 0 auto;">
|
||||
</center>
|
||||
</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">
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.flip-container {
|
||||
position: relative;
|
||||
width: 280px;
|
||||
height: 280px;
|
||||
margin: 10px auto;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
/* 对齐顶部 */
|
||||
justify-content: flex-end;
|
||||
/* 将文字放置右上角 */
|
||||
}
|
||||
.image-container {
|
||||
position: relative;
|
||||
position: relative;
|
||||
width: 280px;
|
||||
height: 280px;
|
||||
}
|
||||
.image-container img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
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过渡 */
|
||||
}
|
||||
.image-container img:first-child {
|
||||
z-index: 1;
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
.image-container img:last-child {
|
||||
z-index: 0;
|
||||
transform: rotateY(180deg);
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
.image-container:hover img:first-child {
|
||||
transform: rotateY(180deg);
|
||||
z-index: 2;
|
||||
}
|
||||
.image-container:hover img:last-child {
|
||||
transform: rotateY(0deg);
|
||||
z-index: 3;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<!-- <center>
|
||||
|
||||
@ -28,169 +88,42 @@ status: new
|
||||
</center> -->
|
||||
|
||||
<!-- <p style="text-align: center; font-size: 35px; "><strong>A college student in Chongqing</strong></p> -->
|
||||
|
||||
<center><font size=6 color= #757575>
|
||||
观史知今,当思进退,读书明志可识春秋
|
||||
<br>
|
||||
<center><font size=6rem 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="../个人简历2.pdf" target="_blank" class="md-button">下载简历</a>
|
||||
<a href="../个人简历.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>
|
||||
<!-- <p style="text-align: center; font-size: 25px; margin: 0px;"><strong>𝘿𝙤𝙣'𝙩 𝙘𝙖𝙧𝙚 𝙖𝙗𝙤𝙪𝙩 𝙬𝙤𝙧𝙡𝙙𝙡𝙮 𝙚𝙮𝙚𝙨 𝙩𝙤 𝙥𝙪𝙧𝙨𝙪𝙚 𝙮𝙤𝙪𝙧 𝙤𝙬𝙣 𝙡𝙞𝙜𝙝𝙩</strong></p> -->
|
||||
<!-- https://s1.imagehub.cc/images/2024/02/02/79cb7379982d1c7bb0ae7163985609c4.jpeg -->
|
||||
|
||||
<HR style="FILTER: progid:DXImageTransform.Microsoft.Shadow(color:#608DBD,direction:145,strength:15)" width="100%" color=#608DBD SIZE=1>
|
||||
|
||||
!!! pied-piper1 "About me"
|
||||
- [x] Hey, I'm [Wcowin](https://wcowin.work/VitePress/){target=“_blank”}!
|
||||
- [x] 清醒,知趣,明得失,知进退
|
||||
- [x] Hey, I'm [Wcowin](https://wcowin.work/VitePress/){target=“_blank”}~
|
||||
- [x] 咖啡重度爱好者
|
||||
- [x] 热爱(xiā)折腾技术/Math
|
||||
- [x] 读书明志可识春秋;诗词爱好者;喜欢村上春树;擅长羽毛球
|
||||
<img class="img1" src="https://pic2.zhimg.com/80/v2-6cf497fc08da090bd53e4a5dc962d9d9_1440w.webp">
|
||||
- [x] 热爱(xiā)折腾技术/数学,目前的研究领域是[密码学](https://wcowin.work/blog/Cryptography/)
|
||||
- [x] 读书明志;诗词爱好者;喜欢村上春树;擅长羽毛球
|
||||
- [x] 清醒知趣,明得失,知进退
|
||||
|
||||
## 联系我
|
||||
|
||||
<head>
|
||||
## 人生态度
|
||||
|
||||
<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">
|
||||
|
||||
|
||||
<!-- <head>
|
||||
<style>
|
||||
@media (min-width: 768px) {
|
||||
.mobile-only {
|
||||
@ -236,7 +169,8 @@ status: new
|
||||
|
||||
<center><font color= #757575 size=6>WeChat</font>
|
||||
<img src="https://picx.zhimg.com/80/v2-540df18f16032fbe114dd960da21b467_1440w.webp" style="width: auto; height: auto;">
|
||||
<font color= #999 >扫一扫上面的二维码图案,加我为朋友。</font></center>
|
||||
<font color= #999 >扫一扫上面的二维码图案<br>
|
||||
加我为朋友</font></center>
|
||||
|
||||
</div>
|
||||
|
||||
@ -271,15 +205,105 @@ 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: 发送电子邮件
|
||||
</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 >
|
||||
{.img1 }
|
||||
<figcaption>公众号</figcaption>
|
||||
</figure>
|
||||
|
||||
---
|
||||
|
||||
> 💬我电话号码的`MD5`码:7037F514864088F907CC921687B670EE(破解有奖)
|
||||
|
||||
|
||||
## 须知
|
||||
@ -289,10 +313,7 @@ status: new
|
||||
如果给我发[邮件](mailto:<wangkewen821@gmail.com>),或者通过右下角微信添加好友,请写上您的**真名实姓**。对于那些不知来路、上来就问问题的微信和邮件,我通常会**直接忽略**,谢谢。
|
||||
|
||||
---
|
||||
<center>[发送电子邮件 :fontawesome-solid-paper-plane:](mailto:<wangkewen821@gmail.com>){.md-button}</center>
|
||||
|
||||
|
||||
<!-- <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> -->
|
||||
<center>
|
||||
[发送电子邮件 :fontawesome-solid-paper-plane:](mailto:<wcowin@qq.com>){.md-button}
|
||||
</center>
|
||||
|
||||
|
@ -17,7 +17,9 @@ comments: false
|
||||
|
||||
</div> -->
|
||||
|
||||
请使用PC端查看,谢谢
|
||||
|
||||
<center><font color= #757575 size=6 >请使用PC端查看,谢谢</font></center>
|
||||
|
||||
---
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
@ -26,8 +28,6 @@ comments: false
|
||||
|
||||
---
|
||||
|
||||
<iframe src="../个人简历.pdf" width="100%" height="800px" style="border: 1px solid #ccc; overflow: auto;">
|
||||
</iframe>
|
||||
|
||||
<iframe src="/assets/个人简历.pdf" width="100%" height="1250px" style="border: 1.5px solid #ccc; overflow: auto; border-radius: 18px; background: #fff;"></iframe>
|
||||
|
||||
</div>
|
||||
|
@ -1,54 +1,31 @@
|
||||
: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: #080505;
|
||||
--text-color-light: #f5ab17;
|
||||
|
||||
--input-color: #F5EEE3;
|
||||
--body-color: #FDFBF8;
|
||||
--container-color: #FFFFFF;
|
||||
--text-color: #757575;
|
||||
|
||||
/*========== 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;
|
||||
|
||||
/*========== Margenes Bottom ==========*/
|
||||
|
||||
/*========== Margins ==========*/
|
||||
--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;
|
||||
@ -58,151 +35,14 @@
|
||||
}
|
||||
|
||||
/*==================== BASE ====================*/
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
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;
|
||||
@ -226,142 +66,6 @@ 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: var(--md-primary-fg-color);
|
||||
}
|
||||
|
||||
.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;
|
||||
@ -386,8 +90,16 @@ h1, h2, h3, h4 {
|
||||
|
||||
.qualification__data {
|
||||
display: grid;
|
||||
grid-template-columns: 1.5fr max-content 1.5fr;
|
||||
column-gap: 1.5rem;
|
||||
grid-template-columns: 1fr max-content 1fr;
|
||||
column-gap: 0.5rem;
|
||||
}
|
||||
|
||||
.qualification__data > div:first-child {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.qualification__data > div:last-child {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.qualification__title {
|
||||
@ -433,145 +145,3 @@ 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: 6rem 0 2rem;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
@ -1,6 +1,15 @@
|
||||
---
|
||||
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>
|
||||
|
||||
|
69
docs/about/zcw.md
Normal file
69
docs/about/zcw.md
Normal file
@ -0,0 +1,69 @@
|
||||
---
|
||||
title: 支持作者
|
||||
status: new
|
||||
---
|
||||
|
||||
# 支持作者
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-email-mark-as-unread: 给我发送[邮箱](mailto:wcowin@qq.com)
|
||||
|
||||
- :material-github: [点击此处](https://github.com/Wcowin/Wcowin.github.io){target="\_blank" rel="noopener"}访问Github仓库
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
可以的话 请我喝一杯咖啡吧☕️
|
||||
|
||||
## **:simple-alipay:** **Alipay**
|
||||
|
||||
<!-- {class="img1"} -->
|
||||
|
||||
<!-- <p align="center">
|
||||
<img src="https://pic2.zhimg.com/80/v2-42db1a89cc3e402891212189f444f081_1440w.webp" class="img1" style="max-width:500px; height:auto;" />
|
||||
</p> -->
|
||||
|
||||
<p align="center">
|
||||
<img src="https://pic2.zhimg.com/80/v2-42db1a89cc3e402891212189f444f081_1440w.webp" class="img1" style="width:120%; height:auto;" />
|
||||
</p>
|
||||
|
||||
## **:simple-wechat:** **WeChat Pay**
|
||||
|
||||
<!-- <p align="center">
|
||||
<img src="https://pic2.zhimg.com/80/v2-4384c32173a239a1609309aa1b1ee9f9_1440w.webp" class="img1" style="max-width:500px; height:auto;" />
|
||||
</p> -->
|
||||
|
||||
<p align="center">
|
||||
<img src="https://pic2.zhimg.com/80/v2-4384c32173a239a1609309aa1b1ee9f9_1440w.webp" class="img1" style="width:120%; height:auto;" />
|
||||
</p>
|
||||
|
||||
|
||||
***
|
||||
## **:simple-kofi:ko-fi**
|
||||
|
||||
[请作者喝杯咖啡](https://ko-fi.com/wcowin){ .md-button target=_blank}
|
||||
|
||||
<!-- <div class="reward-container">
|
||||
<div></div>
|
||||
<button style="border-radius: 0.5rem;" onclick="var qr = document.getElementById('qr'); qr.style.display = (qr.style.display === 'none') ? 'block' : 'none';">
|
||||
请作者喝杯咖啡
|
||||
</button>
|
||||
<div id="qr" style="display: none;">
|
||||
<div style="display: inline-block;">
|
||||
<img src="https://s2.loli.net/2024/02/01/cxrEKTLp5CiQeBw.jpg" alt="Wcowin 微信支付">
|
||||
<p>微信支付</p>
|
||||
</div>
|
||||
<div style="display: inline-block;">
|
||||
<img src="https://s2.loli.net/2024/02/01/ps8UM6xu2OL3Dyr.jpg" alt="Wcowin 支付宝">
|
||||
<p>支付宝</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
## **赞助列表**
|
||||
|
||||
赞助人|赞助金额|赞助时间
|
||||
-|-|-
|
||||
*急|15¥|25/04/18
|
||||
千平|100¥|25/04/16
|
Binary file not shown.
1
docs/ads.txt
Normal file
1
docs/ads.txt
Normal file
@ -0,0 +1 @@
|
||||
google.com, pub-2327435979273742, DIRECT, f08c47fec0942fa0
|
1
docs/assets/glightbox.min.css
vendored
1
docs/assets/glightbox.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/assets/javascripts/glightbox.min.js
vendored
1
docs/assets/javascripts/glightbox.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/assets/stylesheets/glightbox.min.css
vendored
1
docs/assets/stylesheets/glightbox.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
docs/assets/个人简历.pdf
Normal file
BIN
docs/assets/个人简历.pdf
Normal file
Binary file not shown.
@ -1,6 +1,12 @@
|
||||
---
|
||||
title: 许可声明
|
||||
status: new
|
||||
|
||||
hide:
|
||||
# - navigation
|
||||
# - toc
|
||||
# - feedback
|
||||
- footer
|
||||
---
|
||||
|
||||

|
||||
|
310
docs/blog/Mkdocs/linktech.md
Normal file
310
docs/blog/Mkdocs/linktech.md
Normal file
@ -0,0 +1,310 @@
|
||||
---
|
||||
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">Wcowin’s 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”>Wcowin’s 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">Wcowin’s 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>
|
@ -10,7 +10,7 @@ tags:
|
||||
|
||||
与所有内置插件一样,博客插件的入门非常简单。只需将以下行添加到mkdocs.yml
|
||||
|
||||
```
|
||||
``` hl_lines="2"
|
||||
plugins:
|
||||
- blog
|
||||
```
|
||||
@ -18,9 +18,10 @@ plugins:
|
||||
**然后在/docs/blog/posts下写md文件即可**(无需再mkdocs.yml配置,如没有post文件,新建一个即可)
|
||||
但是bolg文件夹下要有index.md文件(没有这个文件新建即可)!
|
||||
|
||||
在mkdocs.yml中这样写
|
||||
在mkdocs.yml的nav部分这样写
|
||||
```
|
||||
- Blogger:
|
||||
nav:
|
||||
- 博客:
|
||||
- index: blog/index.md
|
||||
```
|
||||
|
||||
@ -35,9 +36,10 @@ categories: #分类
|
||||
- Hello
|
||||
---
|
||||
```
|
||||
作者信息在docs/blog/.authors.yml里配置(没有.authors.yml新建即可)
|
||||
|
||||
|
||||
作者信息在docs/blog/.authors.yml里配置(没有.authors.yml新建即可)
|
||||
|
||||
```yml
|
||||
authors:
|
||||
Wcowin:
|
||||
@ -45,7 +47,5 @@ authors:
|
||||
description: Free and casual # Author description
|
||||
avatar: https://s1.imagehub.cc/images/2024/02/02/91a767e93d1a344e44c69936464c583e.png # Author avatar
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
结束
|
||||
|
@ -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>
|
||||
|
||||
示例:
|
||||
@ -111,4 +111,4 @@ Mkdocs Materials 教程:
|
||||
|
||||
## 加入友链
|
||||
|
||||
[友链申请 :palms_up_together_tone1:](https://wcowin.work/link){.md-button target=“_blank”}
|
||||
[友链申请 :palms_up_together_tone1:](https://wcowin.work/link/#_3){.md-button target=“_blank”}
|
@ -6,6 +6,7 @@ hide:
|
||||
# - feedback
|
||||
# comments: false
|
||||
# icon: octicons/home-fill-24
|
||||
hide_reading_time: true
|
||||
---
|
||||
|
||||
# MyBlog
|
||||
|
@ -7,6 +7,7 @@ description: >
|
||||
a blog alongside your documentation or standalone
|
||||
categories:
|
||||
- Hello World
|
||||
hide_reading_time: true
|
||||
---
|
||||
|
||||
|
||||
|
@ -5,6 +5,7 @@ date: 2022-06-06
|
||||
categories:
|
||||
- 网站更新记录
|
||||
readtime: 2
|
||||
hide_reading_time: true
|
||||
---
|
||||
|
||||
## </p><h1 id="01" name="01"><strong>2022-10-20</strong></h1><p>
|
||||
|
@ -5,6 +5,7 @@ date: 2023-12-21
|
||||
categories:
|
||||
- 网站更新记录
|
||||
readtime: 2
|
||||
hide_reading_time: true
|
||||
---
|
||||
|
||||
## </p><h1 id="01" name="01"><strong>2023-12-21</strong></h1><p>
|
||||
|
@ -5,6 +5,7 @@ date: 2024-01-01
|
||||
categories:
|
||||
- 网站更新记录
|
||||
readtime: 2
|
||||
hide_reading_time: true
|
||||
---
|
||||
## </p><h1 id="01" name="01"><strong>2024-12-24</strong></h1><p>
|
||||
* 优化网站流畅度(玄学)
|
||||
|
@ -5,7 +5,53 @@ 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)页面的显示问题
|
||||
|
||||
## </p><h1 id="01" name="01"><strong>2025-04-19</strong></h1><p>
|
||||
* 优化网站流畅度(玄学)
|
||||
* 修改教程中的各种小错误
|
||||
|
||||
## </p><h1 id="01" name="01"><strong>2025-04-13</strong></h1><p>
|
||||
* 优化网站流畅度(玄学)
|
||||
* 修复在 Markdown 文件中,HTML 和 Markdown 混用可能导致解析器无法正确渲染。
|
||||
|
||||
## </p><h1 id="01" name="01"><strong>2025-04-10</strong></h1><p>
|
||||
* 优化网站流畅度(玄学)
|
||||
* 优化[友链](../../link.md)统计方式,更加准确计数
|
||||
* 修复评论区重定位的bug,防止他人网站测试评论区导致评论区覆盖到我的网站
|
||||
|
||||
## </p><h1 id="01" name="01"><strong>2025-03-30</strong></h1><p>
|
||||
* 优化网站流畅度(玄学)
|
||||
* 我回来了
|
||||
* 开启博客半月更模式
|
||||
* 一战考研失利
|
||||
|
||||
## </p><h1 id="01" name="01"><strong>2025-03-14</strong></h1><p>
|
||||
* 优化网站流畅度(玄学)
|
||||
* π日快乐!
|
||||
|
||||
## </p><h1 id="01" name="01"><strong>2025-02-24</strong></h1><p>
|
||||
* 优化网站流畅度(玄学)
|
||||
* 一切都是最好的安排
|
||||
|
||||
## </p><h1 id="01" name="01"><strong>2025-02-19</strong></h1><p>
|
||||
* 优化网站流畅度(玄学)
|
||||
* 更新了MKdocs教程,适应官方最新版本
|
||||
|
@ -13,12 +13,16 @@ comments: false #评论,默认不开启
|
||||
|
||||

|
||||
|
||||
docs/overrides文件下新建404.html即可
|
||||
|
||||
树状结构如下
|
||||
|
||||
树状结构如下:
|
||||
首先在mkdocs.yml文件中添加custom_dir:
|
||||
``` hl_lines="3"
|
||||
theme:
|
||||
name: material
|
||||
custom_dir: docs/overrides #覆写路径
|
||||
```
|
||||
|
||||
docs/overrides文件下新建404.html
|
||||
树状结构如下:
|
||||
``` hl_lines="11"
|
||||
$ tree -a
|
||||
.
|
||||
├── .github
|
||||
@ -36,3 +40,537 @@ $ tree -a
|
||||
│
|
||||
└── mkdocs.yml
|
||||
```
|
||||
|
||||
## 404公益页面
|
||||
|
||||
??? note "点击展开"
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>404 - 页面不存在</title>```
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="description" content="公益404页面是由腾讯公司员工志愿者自主发起的互联网公益活动。" />
|
||||
<link rel="icon" href="data:image/gif;base64,R0lGODdhIAAgANUAAAAAAAgFBgYICAwMDBAPDxAQDxQTFBUYFxcaGRwcHCQkJCQoJykqKTQ0ND09PUJCQktMTFZWVltcXF1hYGNjY2doaGpqanNzc3d5eHp6eoODg4uLi5eXl5mamqOjo62tra+wr7S0tLe5uLu7u7/AwMPEw83NzdbX1tfa2dra2uTk5Ovr6+/w7/T09Pj39/f4+P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAADEALAAAAAAgACAAAAb/QJhwOGyZOpIGQ6FgNCQcU4tIrcJaIwlgy+1uJaOplZqydBOSzGaTkSS6lNN4ONoOAJRQajV0rVAgFF0jcyEAbw9hc1cjD4cAIVZ1DAAXKotEKxkAlIREJ48bYphFGwAKAHJCLWaVo6RFF1sWYnUDDpewVSoOd4QtWgCeMCmXLaqfUyopdF9GWxRiKQAVKh3CRHUdKhQEzFeCAEdbH6uOh28A3ypbCQhbEGIfWx2CCd8wJhB2CpFDIQvuAIBgYkiKN0kASOAzRJYjBwyFrGgAwNGFTFqUAMjwYogpA1woMFwRDgDIDUQ2LQGAcoiGLRr2QRgp8yUADURMMWFJRYMH3BglJOQSwq0EDA84c56ilOGVLisqKUaISKVFihIjRJRI4XTIiggAGggygKxICAoguSDI0/UEyAnXAJTLJMtLlwxUYcwD0MEEtIgtBKGi0OHDh3qn8IwKvEVKuGHXGAwI8apFCAOUOvxzBqNOAVzgthi1UgKasl7YrtS9MMXI6DEjpKieNWoaSA1dF7V4+QafkNKoLOlSIQsVCUkmATgYwWIRCxEOAByANKc0AAEAJnzYI6bFihQfBAUQjSlF3S0GIqjZgCFC2i0XfM/BUtJuSEVPjdRTwsQJYdljBAEAOw==" />
|
||||
<title>404 您访问的页面搞丢了</title>
|
||||
<script src="https://volunteer.cdn-go.cn/404/latest/404.js" rendertarget="404DlV"></script>
|
||||
<style>
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
max-width: 100vw;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
.container {
|
||||
position: relative;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 100%;
|
||||
max-width: 1600px;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.background-img {
|
||||
width: 100%;
|
||||
max-width: 1600px;
|
||||
filter: brightness(75%);
|
||||
}
|
||||
.content {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 98vw;
|
||||
max-width: 1600px;
|
||||
text-align: center;
|
||||
}
|
||||
.content h1 {
|
||||
font-size: 128px;
|
||||
font-weight: 800;
|
||||
margin: 0;
|
||||
}
|
||||
.content p {
|
||||
font-size: 28px;
|
||||
margin: 0;
|
||||
}
|
||||
.content i {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 98vw;
|
||||
max-width: 1600px;
|
||||
font-size: 0.75em;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.footer img {
|
||||
width: 160px;
|
||||
}
|
||||
.footer div {
|
||||
text-align: right;
|
||||
}
|
||||
.footer a {
|
||||
/* color: lightgray; */
|
||||
font-size: 0.8em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<img class="background-img" alt="404!您要访问的页面走丢了!" src="https://volunteer.cdn-go.cn/404/latest/img/dream4school.jpg" />
|
||||
<div class="content">
|
||||
<h1>404 NOT Found</h1>
|
||||
<p>您访问的页面走丢在寻找梦想的路上了</p>
|
||||
<p>不过您还可以和腾讯志愿者一起</p>
|
||||
<i><b>为孩子们点亮一个梦想</b></i>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<a href="https://volunteer.cdn-go.cn/404/latest/img/dream4schoolQR.png">
|
||||
<img src="https://volunteer.cdn-go.cn/404/latest/img/dream4schoolQR.png" alt="点击进入支持页面" />
|
||||
<br />扫码点亮一个梦想
|
||||
</a>
|
||||
<div>
|
||||
<p>照片拍摄于湖南省岳阳市平江县三市镇新村完小</p>
|
||||
<p>拍摄时间:二零二三年七月十一日</p>
|
||||
<p>(感恩基金会供稿)</p>
|
||||
<a href="https://support.qq.com/products/378306">我要反馈</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
## 404骰子页面
|
||||
|
||||
目前更换了新的404页面:
|
||||
|
||||
??? note "点击展开"
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>404</title>
|
||||
<style>
|
||||
body {
|
||||
background: #000;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
font-family: Anton, sans-serif;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
-webkit-perspective: 1000px;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
div {
|
||||
-webkit-transform-style: preserve-3d;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.rail {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
-webkit-transform: rotateX(-30deg) rotateY(-30deg);
|
||||
transform: rotateX(-30deg) rotateY(-30deg);
|
||||
}
|
||||
|
||||
.rail .stamp {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #141414;
|
||||
color: #fff;
|
||||
font-size: 7rem;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(1) {
|
||||
-webkit-animation: stampSlide 40s -2.3s linear infinite;
|
||||
animation: stampSlide 40s -2.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(2) {
|
||||
-webkit-animation: stampSlide 40s -4.3s linear infinite;
|
||||
animation: stampSlide 40s -4.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(3) {
|
||||
-webkit-animation: stampSlide 40s -6.3s linear infinite;
|
||||
animation: stampSlide 40s -6.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(4) {
|
||||
-webkit-animation: stampSlide 40s -8.3s linear infinite;
|
||||
animation: stampSlide 40s -8.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(5) {
|
||||
-webkit-animation: stampSlide 40s -10.3s linear infinite;
|
||||
animation: stampSlide 40s -10.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(6) {
|
||||
-webkit-animation: stampSlide 40s -12.3s linear infinite;
|
||||
animation: stampSlide 40s -12.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(7) {
|
||||
-webkit-animation: stampSlide 40s -14.3s linear infinite;
|
||||
animation: stampSlide 40s -14.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(8) {
|
||||
-webkit-animation: stampSlide 40s -16.3s linear infinite;
|
||||
animation: stampSlide 40s -16.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(9) {
|
||||
-webkit-animation: stampSlide 40s -18.3s linear infinite;
|
||||
animation: stampSlide 40s -18.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(10) {
|
||||
-webkit-animation: stampSlide 40s -20.3s linear infinite;
|
||||
animation: stampSlide 40s -20.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(11) {
|
||||
-webkit-animation: stampSlide 40s -22.3s linear infinite;
|
||||
animation: stampSlide 40s -22.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(12) {
|
||||
-webkit-animation: stampSlide 40s -24.3s linear infinite;
|
||||
animation: stampSlide 40s -24.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(13) {
|
||||
-webkit-animation: stampSlide 40s -26.3s linear infinite;
|
||||
animation: stampSlide 40s -26.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(14) {
|
||||
-webkit-animation: stampSlide 40s -28.3s linear infinite;
|
||||
animation: stampSlide 40s -28.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(15) {
|
||||
-webkit-animation: stampSlide 40s -30.3s linear infinite;
|
||||
animation: stampSlide 40s -30.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(16) {
|
||||
-webkit-animation: stampSlide 40s -32.3s linear infinite;
|
||||
animation: stampSlide 40s -32.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(17) {
|
||||
-webkit-animation: stampSlide 40s -34.3s linear infinite;
|
||||
animation: stampSlide 40s -34.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(18) {
|
||||
-webkit-animation: stampSlide 40s -36.3s linear infinite;
|
||||
animation: stampSlide 40s -36.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(19) {
|
||||
-webkit-animation: stampSlide 40s -38.3s linear infinite;
|
||||
animation: stampSlide 40s -38.3s linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(20) {
|
||||
-webkit-animation: stampSlide 40s -40.3s linear infinite;
|
||||
animation: stampSlide 40s -40.3s linear infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes stampSlide {
|
||||
0% {
|
||||
-webkit-transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px)
|
||||
translateY(130px);
|
||||
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px)
|
||||
translateY(130px);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px)
|
||||
translateY(-3870px);
|
||||
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px)
|
||||
translateY(-3870px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes stampSlide {
|
||||
0% {
|
||||
-webkit-transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px)
|
||||
translateY(130px);
|
||||
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px)
|
||||
translateY(130px);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px)
|
||||
translateY(-3870px);
|
||||
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px)
|
||||
translateY(-3870px);
|
||||
}
|
||||
}
|
||||
|
||||
.world {
|
||||
-webkit-transform: rotateX(-30deg) rotateY(-30deg);
|
||||
transform: rotateX(-30deg) rotateY(-30deg);
|
||||
}
|
||||
|
||||
.world .forward {
|
||||
position: absolute;
|
||||
-webkit-animation: slide 2s linear infinite;
|
||||
animation: slide 2s linear infinite;
|
||||
}
|
||||
|
||||
.world .box {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
-webkit-transform-origin: 100% 100%;
|
||||
transform-origin: 100% 100%;
|
||||
-webkit-animation: roll 2s cubic-bezier(1, 0.01, 1, 1) infinite;
|
||||
animation: roll 2s cubic-bezier(1, 0.01, 1, 1) infinite;
|
||||
}
|
||||
|
||||
.world .box .wall {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background: rgba(10, 10, 10, 0.8);
|
||||
border: 1px solid #fafafa;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.world .box .wall::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
font-size: 7rem;
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(1) {
|
||||
-webkit-transform: translateZ(100px);
|
||||
transform: translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(2) {
|
||||
-webkit-transform: rotateX(180deg) translateZ(100px);
|
||||
transform: rotateX(180deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(3) {
|
||||
-webkit-transform: rotateX(90deg) translateZ(100px);
|
||||
transform: rotateX(90deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(3)::before {
|
||||
-webkit-transform: rotateX(180deg) rotateZ(90deg) translateZ(-1px);
|
||||
transform: rotateX(180deg) rotateZ(90deg) translateZ(-1px);
|
||||
-webkit-animation: zeroFour 4s -2s linear infinite;
|
||||
animation: zeroFour 4s -2s linear infinite;
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(4) {
|
||||
-webkit-transform: rotateX(-90deg) translateZ(100px);
|
||||
transform: rotateX(-90deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(4)::before {
|
||||
-webkit-transform: rotateX(180deg) rotateZ(-90deg) translateZ(-1px);
|
||||
transform: rotateX(180deg) rotateZ(-90deg) translateZ(-1px);
|
||||
-webkit-animation: zeroFour 4s -2s linear infinite;
|
||||
animation: zeroFour 4s -2s linear infinite;
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(5) {
|
||||
-webkit-transform: rotateY(90deg) translateZ(100px);
|
||||
transform: rotateY(90deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(5)::before {
|
||||
-webkit-transform: rotateX(180deg) translateZ(-1px);
|
||||
transform: rotateX(180deg) translateZ(-1px);
|
||||
-webkit-animation: zeroFour 4s linear infinite;
|
||||
animation: zeroFour 4s linear infinite;
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(6) {
|
||||
-webkit-transform: rotateY(-90deg) translateZ(100px);
|
||||
transform: rotateY(-90deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(6)::before {
|
||||
-webkit-transform: rotateX(180deg) rotateZ(180deg) translateZ(-1px);
|
||||
transform: rotateX(180deg) rotateZ(180deg) translateZ(-1px);
|
||||
-webkit-animation: zeroFour 4s linear infinite;
|
||||
animation: zeroFour 4s linear infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes zeroFour {
|
||||
0% {
|
||||
content: "4";
|
||||
}
|
||||
|
||||
100% {
|
||||
content: "0";
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zeroFour {
|
||||
0% {
|
||||
content: "4";
|
||||
}
|
||||
|
||||
100% {
|
||||
content: "0";
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes roll {
|
||||
0% {
|
||||
-webkit-transform: rotateZ(0);
|
||||
transform: rotateZ(0);
|
||||
}
|
||||
|
||||
85% {
|
||||
-webkit-transform: rotateZ(90deg);
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
|
||||
87% {
|
||||
-webkit-transform: rotateZ(88deg);
|
||||
transform: rotateZ(88deg);
|
||||
}
|
||||
|
||||
90% {
|
||||
-webkit-transform: rotateZ(90deg);
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: rotateZ(90deg);
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes roll {
|
||||
0% {
|
||||
-webkit-transform: rotateZ(0);
|
||||
transform: rotateZ(0);
|
||||
}
|
||||
|
||||
85% {
|
||||
-webkit-transform: rotateZ(90deg);
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
|
||||
87% {
|
||||
-webkit-transform: rotateZ(88deg);
|
||||
transform: rotateZ(88deg);
|
||||
}
|
||||
|
||||
90% {
|
||||
-webkit-transform: rotateZ(90deg);
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: rotateZ(90deg);
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes slide {
|
||||
0% {
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: translateX(-200px);
|
||||
transform: translateX(-200px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide {
|
||||
0% {
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: translateX(-200px);
|
||||
transform: translateX(-200px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="rail">
|
||||
<div class="stamp four">4</div>
|
||||
<div class="stamp zero">0</div>
|
||||
<div class="stamp four">4</div>
|
||||
<div class="stamp zero">0</div>
|
||||
<div class="stamp four">4</div>
|
||||
<div class="stamp zero">0</div>
|
||||
<div class="stamp four">4</div>
|
||||
<div class="stamp zero">0</div>
|
||||
<div class="stamp four">4</div>
|
||||
<div class="stamp zero">0</div>
|
||||
<div class="stamp four">4</div>
|
||||
<div class="stamp zero">0</div>
|
||||
<div class="stamp four">4</div>
|
||||
<div class="stamp zero">0</div>
|
||||
<div class="stamp four">4</div>
|
||||
<div class="stamp zero">0</div>
|
||||
<div class="stamp four">4</div>
|
||||
<div class="stamp zero">0</div>
|
||||
<div class="stamp four">4</div>
|
||||
<div class="stamp zero">0</div>
|
||||
</div>
|
||||
<div class="world">
|
||||
<div class="forward">
|
||||
<div class="box">
|
||||
<div class="wall"></div>
|
||||
<div class="wall"></div>
|
||||
<div class="wall"></div>
|
||||
<div class="wall"></div>
|
||||
<div class="wall"></div>
|
||||
<div class="wall"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
589
docs/blog/websitebeauty/Mkdocs-AI-Summary.md
Normal file
589
docs/blog/websitebeauty/Mkdocs-AI-Summary.md
Normal file
@ -0,0 +1,589 @@
|
||||
---
|
||||
title: MkDocs文档AI摘要
|
||||
tags:
|
||||
- Mkdocs
|
||||
status: new
|
||||
---
|
||||
|
||||
# MkDocs AI Hooks
|
||||
|
||||
{.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驱动的摘要生成和智能阅读统计功能。
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## ✨ 功能特性
|
||||
|
||||
### 🤖 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
|
||||
```
|
||||

|
||||
---
|
||||
|
||||
然后部署到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摘要显示
|
||||
**实际效果预览**:
|
||||

|
||||
|
||||
|
||||
### 💰 成本说明
|
||||
- **单次费用**: 约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**
|
||||
<!--  -->
|
||||
<p align="center">
|
||||
<img src="https://pic3.zhimg.com/80/v2-5ef3dde831c9d0a41fe35fabb0cb8784_1440w.webp" style="border-radius: 10px;" width="50%">
|
||||
</p>
|
||||
|
||||
</center>
|
||||
|
||||
|
||||
## Star History
|
||||
|
||||
[](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文档更加智能化和用户友好。如有建议或想法,欢迎交流!
|
72
docs/blog/websitebeauty/Relativeaddress.md
Normal file
72
docs/blog/websitebeauty/Relativeaddress.md
Normal file
@ -0,0 +1,72 @@
|
||||
---
|
||||
title: 相对地址的一些问题
|
||||
tags:
|
||||
- Mkdocs
|
||||
---
|
||||
|
||||
# 针对MKdocs中相对地址引用的一些问题
|
||||
|
||||
在使用 MkDocs 构建文档网站时,常常会遇到相对地址引用的问题,尤其是在图片、PDF、其他静态资源等的引用上。合理使用相对地址可以让你的文档在本地预览和线上部署时都能正常显示。下面总结一些常见场景和注意事项:
|
||||
|
||||
## 1. 图片引用
|
||||
|
||||
**推荐写法:**
|
||||
```
|
||||

|
||||
```
|
||||
`./img/example.png` 表示当前 Markdown 文件同级目录下的 `img` 文件夹中的图片。
|
||||
如果图片在上级目录:`../assets/example.png`
|
||||
|
||||
**注意事项:**
|
||||
|
||||
- 路径区分大小写,确保文件名和路径一致。
|
||||
- MkDocs 会将 `docs` 目录下的所有文件原样复制到站点根目录,引用路径应以 `docs` 为根目录进行相对定位。
|
||||
|
||||
## 2. PDF 文件引用
|
||||
|
||||
**内嵌或下载 PDF:**
|
||||
```
|
||||
[查看PDF](./files/example.pdf)
|
||||
```
|
||||
|
||||
或使用 HTML 方式内嵌:
|
||||
|
||||
```html
|
||||
<embed src="./files/example.pdf" width="100%" height="600px" type="application/pdf">
|
||||
```
|
||||
|
||||
`./files/example.pdf` 表示当前文档同级的 `files` 文件夹下的 PDF 文件。
|
||||
`../files/example.pdf` 表示上级目录的 `files` 文件夹下的 PDF 文件。
|
||||
`../../files/example.pdf` 表示上上级目录的 `files` 文件夹下的 PDF 文件。
|
||||
|
||||
|
||||
## 3. 跨页面引用
|
||||
|
||||
**引用同一项目下的其他 Markdown 页面:**
|
||||
```
|
||||
[跳转到其他页面](../otherpage.md)
|
||||
```
|
||||
|
||||
- MkDocs 会自动将 `.md` 转换为 `.html`,所以可以直接用 Markdown 文件名。
|
||||
- ()内的路径是相对于当前 Markdown 文件的路径,可以参考[PDF文件引用](#2-pdf)的方法。
|
||||
|
||||
## 4. 静态资源引用
|
||||
|
||||
**如 CSS、JS 文件:**
|
||||
```html
|
||||
<link rel="stylesheet" href="../assets/style.css">
|
||||
<script src="../assets/script.js"></script>
|
||||
```
|
||||
|
||||
- 推荐将静态资源放在 `docs/assets` 目录下,引用时用相对路径。
|
||||
|
||||
## 5. 常见问题
|
||||
|
||||
- **路径错误导致资源无法加载**:请检查路径是否正确、文件是否存在、大小写是否一致。
|
||||
- **本地预览正常,线上不显示**:有可能是路径写死或大小写问题,建议始终用相对路径。
|
||||
- **图片/文件过大加载慢**:可适当压缩图片或 PDF 文件。
|
||||
|
||||
---
|
||||
|
||||
## **总结**
|
||||
在 MkDocs 项目中,所有资源的相对路径都应以当前 Markdown 文件为基准,确保本地和线上都能正确访问。建议统一资源管理目录结构,便于维护和引用。
|
44
docs/blog/websitebeauty/accelerate.md
Normal file
44
docs/blog/websitebeauty/accelerate.md
Normal file
@ -0,0 +1,44 @@
|
||||
---
|
||||
title: 加速网站访问的一些心得
|
||||
tags:
|
||||
- Mkdocs
|
||||
---
|
||||
|
||||
# 加速网站访问的一些心得
|
||||
|
||||
在使用 MkDocs 构建网站时,为了提高访问速度,我们可以采取以下一些措施:
|
||||
## 1. 优化图片
|
||||
使用合适的图片格式,如 WebP、JPEG2000 等,减少图片文件大小,从而加快加载速度。
|
||||
|
||||
可以使用在线工具进行图片压缩,如:
|
||||
|
||||
- [freeconvert](https://www.freeconvert.com/zh/webp-converter)
|
||||
|
||||
## 2. JS/CSS使用 CDN
|
||||
使用内容分发网络(CDN)来加速网站的访问,将静态资源(如图片、CSS、JS)缓存到全球各地的服务器上,用户就近访问,减少延迟。
|
||||
|
||||
这里推荐
|
||||
|
||||
- [jsDelivr CDN 加速 GitHub 文件](https://www.jsdelivr.com/github)
|
||||
|
||||
## 3.加速本地渲染
|
||||
|
||||
优化 git 插件的 enabled 配置
|
||||
比如我配置里有 git-revision-date-localized 和 git-committers 插件,这些插件会在每次渲染时读取所有文件的 git 历史,导致本地预览变慢。
|
||||
推荐做法是在本地开发时禁用它们,仅在 CI/CD 或线上构建时启用。
|
||||
具体写法如下:
|
||||
|
||||
```yaml hl_lines="3 5"
|
||||
plugins:
|
||||
- git-revision-date-localized:
|
||||
enabled: !ENV [CI, false] # 只有在CI环境变量为true时才启用
|
||||
- git-committers:
|
||||
enabled: !ENV [CI, false]
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
检验加速效果是否生效可以使用lighthouse进行测试,具体可以参考:
|
||||
|
||||
[使用lighthouse进行网站性能测试](../../develop/lighthouse.md)
|
134
docs/blog/websitebeauty/footer.md
Normal file
134
docs/blog/websitebeauty/footer.md
Normal file
@ -0,0 +1,134 @@
|
||||
---
|
||||
title: 页脚设置
|
||||
hide:
|
||||
# - navigation # 显示右
|
||||
# - toc #显示左
|
||||
# - footer
|
||||
# - feedback
|
||||
tags:
|
||||
- Mkdocs
|
||||
comments: false
|
||||
---
|
||||
|
||||
## 页脚设置
|
||||
|
||||
MkDocs 支持自定义页脚。
|
||||
|
||||

|
||||
|
||||
在docs/overrides/partials/footer.html中(没有该文件时,创建footer.html文件)添加以下代码:
|
||||
|
||||
```html hl_lines="71-86"
|
||||
<!-- 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 %}
|
||||
<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") %}
|
||||
<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" %}
|
||||
</div>
|
||||
<div class="md-footer__title">
|
||||
<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") %}
|
||||
<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>
|
||||
</div>
|
||||
<div class="md-footer__button md-icon">
|
||||
{% set icon = config.theme.icon.next or "material/arrow-right" %}
|
||||
{% include ".icons/" ~ icon ~ ".svg" %}
|
||||
</div>
|
||||
</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<!-- Further information -->
|
||||
<div class="md-footer-meta md-typeset">
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
{% include "partials/copyright.html" %}
|
||||
|
||||
<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> |
|
||||
<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 %}
|
||||
{% include "partials/social.html" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
```
|
||||
|
||||
高亮部分自行修改即可
|
||||
|
||||
## 页脚版权设置
|
||||
|
||||
mkdocs.yml中添加:
|
||||
```yaml
|
||||
|
||||
copyright: Copyright © 2022~2025 Wcowin # 左下角的版权声明
|
||||
|
||||
```
|
||||
|
||||
如果想删除页脚显示“Made with Material for MkDocs”(不建议)
|
||||
|
||||
```yaml
|
||||
extra:
|
||||
generator: false #删除页脚显示“使用 MkDocs 材料制造”
|
||||
```
|
@ -14,7 +14,7 @@ comments: false #评论,默认不开启
|
||||
|
||||
docs/overrides下新建main.html ,针对main.html文件
|
||||
树状结构如下:
|
||||
```
|
||||
``` hl_lines="11"
|
||||
$ tree -a
|
||||
.
|
||||
├── .github
|
||||
@ -32,7 +32,7 @@ $ tree -a
|
||||
└── mkdocs.yml
|
||||
```
|
||||
|
||||
```html
|
||||
```html hl_lines="9-22"
|
||||
{#-
|
||||
This file was automatically generated - do not edit
|
||||
-#}
|
||||
|
@ -8,7 +8,7 @@ hide:
|
||||
|
||||
复制后在需要添加友链的.md 文件页面粘贴即可
|
||||
|
||||
```html
|
||||
```html hl_lines="67-104"
|
||||
<div class="post-body">
|
||||
<div id="links">
|
||||
<style>
|
||||
@ -49,7 +49,7 @@ hide:
|
||||
}
|
||||
.card .card-header a {
|
||||
font-style: normal;
|
||||
color: #608DBD;
|
||||
color: #608dbd;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
@ -76,32 +76,44 @@ hide:
|
||||
<div class="links-content">
|
||||
<div class="link-navigation">
|
||||
<div class="card">
|
||||
<img class="ava" src="https://cn.mcecy.com/image/20231006/a05f708fb7b0426e7a5786669d5b1386.png" />
|
||||
<img
|
||||
class="ava"
|
||||
src="https://cn.mcecy.com/image/20231006/a05f708fb7b0426e7a5786669d5b1386.png"
|
||||
/>
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<a href="https://wcowin.work/ " target=“_blank”>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://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png" />
|
||||
<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>
|
||||
<a href="https://twitter.com/" target="“_blank”">Twitter</a>
|
||||
</div>
|
||||
<div class="info">社交分享平台</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<img class="ava" src="https://cn.mcecy.com/image/20231012/d96b912437fb0bec0d282dfe734b1d9b.jpeg"/>
|
||||
<img
|
||||
class="ava"
|
||||
src="https://cn.mcecy.com/image/20231012/d96b912437fb0bec0d282dfe734b1d9b.jpeg"
|
||||
/>
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<a href="https://macapp.org.cn/" target=“_blank”>Macapp</a>
|
||||
<a href="https://macapp.org.cn/" target="“_blank”">Macapp</a>
|
||||
</div>
|
||||
<div class="info">一个专注于分享Mac资源的频道</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -120,30 +132,35 @@ hide:
|
||||
<div class="info">{description}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div class="card">
|
||||
<img class="ava" src="https://cn.mcecy.com/image/20231006/a05f708fb7b0426e7a5786669d5b1386.png" />
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<a href="https://wcowin.work/ " target=“_blank”>Wcowin’s blog</a>
|
||||
</div>
|
||||
<div class="info">
|
||||
这是一个分享技术的小站。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
## 效果
|
||||
<div>
|
||||
<div class="links-content">
|
||||
<div class="link-navigation">
|
||||
<div class="card">
|
||||
<img class="ava" src="https://s2.loli.net/2024/02/01/gaE47y5fKM6kosV.png" />
|
||||
<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”>Wcowin’s blog</a>
|
||||
</div>
|
||||
<div class="info">
|
||||
这是一个分享技术的小站。
|
||||
</div>
|
||||
</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”>Wcowin’s blog</a>
|
||||
|
@ -23,15 +23,15 @@ tags:
|
||||
言归正传
|
||||
|
||||
## 第一步
|
||||
mkdocs.yml中添加
|
||||
```
|
||||
mkdocs.yml中添加custom_dir
|
||||
``` hl_lines="3"
|
||||
theme:
|
||||
name: material
|
||||
custom_dir: docs/overrides #主要是这一行
|
||||
```
|
||||
参考下图新建overrides文件,在此文件下参考下图新建覆盖html文件
|
||||
树状结构如下:
|
||||
```
|
||||
``` hl_lines="9-13"
|
||||
$ tree -a
|
||||
.
|
||||
├── .github
|
||||
@ -51,7 +51,7 @@ $ tree -a
|
||||
|
||||

|
||||
|
||||
在comments.html中
|
||||
在comments.html中先复制粘贴下面的代码,后文会说怎么修改
|
||||
|
||||
```html hl_lines="4-18"
|
||||
{% if page.meta.comments %}
|
||||
@ -133,14 +133,14 @@ $ tree -a
|
||||
</script>
|
||||
```
|
||||
|
||||
复制将此代码,替换最上面👆🏻comments.html中高亮的代码
|
||||
复制此代码,替换最上面👆🏻comments.html中高亮的代码
|
||||
|
||||
终端里`mkdocs server`一下
|
||||
|
||||
## 最后
|
||||
在你想插入评论的地方的元数据:`comments: true `
|
||||
|
||||
```
|
||||
``` hl_lines="8"
|
||||
---
|
||||
title: 留言板
|
||||
hide:
|
||||
|
@ -132,10 +132,10 @@ An open-source Chinese font derived from Fontworks' Klee One. 一款开源中文
|
||||
|
||||
## 基于Mkdocs的网站字体美化用法
|
||||
|
||||
1. 在mkdocs.yml中添加以下内容
|
||||
在mkdocs.yml中添加以下内容
|
||||
```yaml
|
||||
extra_css:
|
||||
- https://cdn.jsdelivr.net/npm/lxgw-wenkai-webfont@1.1.0/style.css
|
||||
- https://static.zeoseven.com/zsft/292/main/result.css
|
||||
# - https://cdn.jsdelivr.net/npm/lxgw-wenkai-lite-webfont@1.1.0/style.css
|
||||
# - https://cdn.jsdelivr.net/npm/lxgw-wenkai-tc-webfont@1.0.0/style.css
|
||||
# - https://cdn.jsdelivr.net/npm/lxgw-wenkai-screen-webfont@1.1.0/style.css
|
||||
@ -143,20 +143,23 @@ extra_css:
|
||||
|
||||
然后在extra.css中添加以下内容
|
||||
```css
|
||||
@import url('https://static.zeoseven.com/zsft/292/main/result.css');
|
||||
|
||||
body {
|
||||
font-family: "LXGW WenKai", sans-serif;
|
||||
/* Lite version */
|
||||
/* font-family: "LXGW WenKai Lite", sans-serif; */
|
||||
/* TC version */
|
||||
/* font-family: "LXGW WenKai TC", sans-serif; */
|
||||
/* Screen version */
|
||||
/* font-family: "LXGW WenKai Screen", sans-serif; */
|
||||
font-family: "LXGW WenKai";
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
|
||||
/* button.md-top {
|
||||
font-family: LXGW WenKai;
|
||||
font-size: 16px;
|
||||
font-weight: lighter;
|
||||
} */
|
||||
```
|
||||
|
||||
四选一,mkdocs.yml和extra.css相互对应
|
||||
|
||||
不懂extra_css的用法的可以看一下我写的[extra_css部分](../Mkdocs/mkdocs2.md)教程
|
||||
不懂extra_css的用法的可以看一下我写的[extra_css部分](../Mkdocs/mkdocs2.md/#extra_javascriptextra_css)教程
|
||||
|
||||
!!!tip
|
||||
切换字体后可能返回顶部按钮会在个别浏览器显示异常
|
||||
|
34
docs/blog/websitebeauty/mkpdf.md
Normal file
34
docs/blog/websitebeauty/mkpdf.md
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
title: 嵌入PDF
|
||||
tags:
|
||||
- Mkdocs
|
||||
---
|
||||
|
||||
[嵌入PDF](https://github.com/Wcowin/hexo-site-comments/discussions/8#discussioncomment-12101922)
|
||||
|
||||
嵌入PDF代码 ,**注意PDF的相对地址**
|
||||
```html
|
||||
<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" />
|
||||
|
||||
我的完整代码:
|
||||
|
||||
```html
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :octicons-bookmark-16:{ .lg .middle } __个人简历__
|
||||
|
||||
---
|
||||
|
||||
<iframe src="../个人简历.pdf" width="100%" height="800px" style="border: 1px solid #ccc; overflow: auto;">
|
||||
</iframe>
|
||||
|
||||
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
希望对你有帮助
|
374
docs/blog/websitebeauty/reading_time.md
Normal file
374
docs/blog/websitebeauty/reading_time.md
Normal file
@ -0,0 +1,374 @@
|
||||
---
|
||||
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. 效果展示
|
||||
|
||||

|
||||
|
||||
|
||||
## 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
|
||||
```
|
||||
|
534
docs/blog/websitebeauty/recommend.md
Normal file
534
docs/blog/websitebeauty/recommend.md
Normal file
@ -0,0 +1,534 @@
|
||||
---
|
||||
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
|
||||
```
|
||||
|
||||
|
||||
## 效果如下
|
@ -6,12 +6,8 @@ tags:
|
||||
|
||||
不建议更改,因为默认就是最简洁
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="/stylesheets/shubiao.css">
|
||||
<script src="/javascripts/shubiao.js"></script>
|
||||
</head>
|
||||
<!--  -->
|
||||
|
||||

|
||||
在`docs/javascripts/extra.js`下复制粘贴:
|
||||
```java
|
||||
var CURSOR;
|
||||
@ -100,7 +96,7 @@ class Cursor {
|
||||
```
|
||||
其中比较重要的参数就是鼠标的尺寸和颜色,已经在上图中标出,目前发现颜色只支持RGB写法和固有名称写法(例如red这种),其他参数也可以自行摸索:
|
||||
|
||||
```java
|
||||
```javascript
|
||||
* {cursor: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8' width='8px' height='8px'><circle cx='4' cy='4' r='4' opacity='1.0' fill='rgb(57, 197, 187)'/></svg>") 4 4, auto}`
|
||||
```
|
||||
|
||||
|
@ -82,3 +82,10 @@ plugins:
|
||||
## 温馨提示
|
||||
|
||||
对于大型单一的文档库,执行`mkdocs serve`后的渲染速度明显变慢,这是因为每次渲染都会检查所有文件的git历史记录。如果您不需要这个功能,可以通过将`enabled`设置为`false`来禁用它。
|
||||
|
||||
比较推荐这种方法:
|
||||
```yaml hl_lines="2"
|
||||
- git-committers:
|
||||
enabled: !ENV [CI, false]
|
||||
```
|
||||
修改enabled的策略,这样就不会每次本地渲染都检查所有文件的git历史记录,渲染速度会明显加快,发布网站时候会正常显示。
|
@ -26,5 +26,5 @@ comments: false
|
||||
```javascript
|
||||
<script src="//code.tidio.co/6jmawe9m5wy4ahvlhub2riyrnujz7xxi.js" async></script>
|
||||
```
|
||||
## 放到主页index.md即可
|
||||
## 放到你需要的页面即可
|
||||

|
@ -20,59 +20,26 @@ comments: false
|
||||
```css
|
||||
:root {
|
||||
--admonition-border-left-width: 0.2rem;
|
||||
--base-border-radius: 0.5rem;
|
||||
--base-border-radius: 1rem;
|
||||
/* --card-hover-shadow: 0 0 0.2rem #ffffff40; */
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
/* 卡片圆角与悬浮阴影 */
|
||||
.md-typeset .grid.cards > ul > li,
|
||||
.md-typeset .md-button,
|
||||
.md-typeset table:not([class]) {
|
||||
border-radius: var(--base-border-radius);
|
||||
}
|
||||
.md-typeset .grid.cards > ul > li:hover {
|
||||
box-shadow: 0 0 0.2rem #ffffff40;
|
||||
box-shadow: var(--card-hover-shadow);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
@ -87,188 +54,147 @@ comments: false
|
||||
border-bottom-left-radius: var(--base-border-radius);
|
||||
}
|
||||
|
||||
/* Blog - index.md */
|
||||
/* div.md-content header {
|
||||
display: none;
|
||||
/* 可选:如需恢复代码块、警告框等样式,取消注释即可 */
|
||||
/*
|
||||
.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,
|
||||
.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: var(--md-accent-fg-color--transparent);
|
||||
box-shadow: 0 0 0 1rem var(--md-accent-fg-color--transparent);
|
||||
/* 可选:博客相关样式,按需启用 */
|
||||
|
||||
/* .md-post--excerpt {
|
||||
background-color: rgba(68,138,255,.1);
|
||||
box-shadow: 0 0 0 1rem rgba(68,138,255,.1);
|
||||
border-radius: var(--base-border-radius);
|
||||
}
|
||||
|
||||
.md-post--excerpt .md-post__header {
|
||||
justify-content: center;
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
.md-post--excerpt .md-post__content > h2,
|
||||
.md-post__action {
|
||||
text-align: center;
|
||||
text-align: left;
|
||||
} */
|
||||
|
||||
/* Table */
|
||||
.md-typeset table:not([class]) {
|
||||
border-radius: var(--base-border-radius);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.carousel {
|
||||
width: 60%;
|
||||
height: 100%;
|
||||
|
||||
border-radius: 0.4rem;
|
||||
/* 让所有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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 图片圆角化
|
||||
|
||||
extra.css里引入:
|
||||
|
||||
```css
|
||||
img.img1 {
|
||||
border-radius: 25px;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
在md文件里使用:
|
||||
|
||||
```markdown
|
||||
{.img1}
|
||||
```
|
||||
效果:
|
||||
{.img1}
|
||||
|
||||
## 圆角边框
|
||||
|
||||
[圆角边框如何实现](https://github.com/Wcowin/hexo-site-comments/discussions/15#discussioncomment-11927654){target="_blank"}
|
||||
<div id="rcorners2" >
|
||||
<div id="rcorners1">
|
||||
<!-- <i class="fa fa-calendar" style="font-size:100"></i> -->
|
||||
<body>
|
||||
<font color="#4351AF">
|
||||
<p class="p1"></p>
|
||||
<script defer>
|
||||
//格式:2020年04月12日 10:20:00 星期二
|
||||
function format(newDate) {
|
||||
var day = newDate.getDay();
|
||||
var y = newDate.getFullYear();
|
||||
var m =
|
||||
newDate.getMonth() + 1 < 10
|
||||
? "0" + (newDate.getMonth() + 1)
|
||||
: newDate.getMonth() + 1;
|
||||
var d =
|
||||
newDate.getDate() < 10 ? "0" + newDate.getDate() : newDate.getDate();
|
||||
var h =
|
||||
newDate.getHours() < 10 ? "0" + newDate.getHours() : newDate.getHours();
|
||||
var min =
|
||||
newDate.getMinutes() < 10
|
||||
? "0" + newDate.getMinutes()
|
||||
: newDate.getMinutes();
|
||||
var s =
|
||||
newDate.getSeconds() < 10
|
||||
? "0" + newDate.getSeconds()
|
||||
: newDate.getSeconds();
|
||||
var dict = {
|
||||
1: "一",
|
||||
2: "二",
|
||||
3: "三",
|
||||
4: "四",
|
||||
5: "五",
|
||||
6: "六",
|
||||
0: "天",
|
||||
};
|
||||
//var week=["日","一","二","三","四","五","六"]
|
||||
return (
|
||||
y +
|
||||
"年" +
|
||||
m +
|
||||
"月" +
|
||||
d +
|
||||
"日" +
|
||||
" " +
|
||||
h +
|
||||
":" +
|
||||
min +
|
||||
":" +
|
||||
s +
|
||||
" 星期" +
|
||||
dict[day]
|
||||
);
|
||||
}
|
||||
var timerId = setInterval(function () {
|
||||
var newDate = new Date();
|
||||
var p1 = document.querySelector(".p1");
|
||||
if (p1) {
|
||||
p1.textContent = format(newDate);
|
||||
}
|
||||
}, 1000);
|
||||
</script>
|
||||
</font>
|
||||
</body>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## 利用内置的grid cards
|
||||
|
||||
|
@ -19,11 +19,11 @@ Markdown 编写的文档可以导出 HTML 、Word、图像、PDF、Epub 等多
|
||||
|
||||
一些Markdown文档创作工具:
|
||||
<ul>
|
||||
<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>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>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="/tools/dillinger/">Dillinger</a> 或 <a href="/tools/stackedit/">StackEdit</a></li>
|
||||
<li><strong>Web:</strong> <a href="https://dillinger.io/">Dillinger</a> 或 <a href="https://stackedit.io/">StackEdit</a></li>
|
||||
</ul>
|
||||
|
||||
## 最常用
|
||||
@ -337,7 +337,9 @@ markdown文本内的连续两个或多个回车会被替换为一个回车
|
||||
```
|
||||
下面我们来认识一下二次函数$$y=ax^2+bx+c$$
|
||||
```
|
||||
下面我们来认识一下二次函数$$y=ax^2+bx+c$$
|
||||
下面我们来认识一下二次函数
|
||||
|
||||
$$y=ax^2+bx+c$$
|
||||
|
||||
- - -
|
||||
|
||||
|
@ -25,4 +25,5 @@ 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 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>
|
||||
|
@ -157,16 +157,6 @@ 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>
|
||||
|
@ -7,6 +7,7 @@ hide:
|
||||
comments: false
|
||||
---
|
||||
|
||||
|
||||
<!--
|
||||
____ __ ____ ______ ______ ____ __ ____ __ .__ __.
|
||||
\ \ / \ / / / | / __ \ \ \ / \ / / | | | \ | |
|
||||
@ -17,7 +18,7 @@ ____ __ ____ ______ ______ ____ __ ____ __ .__ __.
|
||||
-->
|
||||
|
||||
|
||||
<center><font class="custom-font ml3">Wcowin for MkDocs博客教程</font></center>
|
||||
<center><font class="custom-font ml3">最好的MkDocs博客教程</font></center>
|
||||
<script src="https://cdn.statically.io/libs/animejs/2.0.2/anime.min.js"></script>
|
||||
<style>
|
||||
.custom-font {
|
||||
@ -32,6 +33,8 @@ ____ __ ____ ______ ______ ____ __ ____ __ .__ __.
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- <div id="rcorners2" >
|
||||
<div id="rcorners1" class="date-display">
|
||||
<p class="p1"></p>
|
||||
@ -113,16 +116,18 @@ ____ __ ____ ______ ______ ____ __ ____ __ .__ __.
|
||||
</style>
|
||||
|
||||
|
||||
>不同于市面上过时的MkDocs教程,本站提供了最详细最便捷最前沿的MkDocs中文文字/视频教程,与[官方发布](https://squidfunk.github.io/mkdocs-material/changelog/)的教程版本同步。包含了MkDocs的安装、配置、主题美化、插件使用等内容。无论你是初学者还是有经验的用户,都能在这里找到你需要的帮助。我们还提供了示例和实用的技巧,帮助你更好地使用MkDocs。𝓳𝓾𝓼𝓽 𝓮𝓷𝓳𝓸𝔂 𝓲𝓽~
|
||||
|
||||
<!-- - 基于Material for MkDocs美化
|
||||
- 简洁美观,功能多元化
|
||||
- 简单易上手,小白配置
|
||||
- 𝕙𝕒𝕧𝕖 𝕒 𝕘𝕠𝕠𝕕 𝕥𝕚𝕞𝕖 ! -->
|
||||
|
||||
视频教程(1) 联系我(2)
|
||||
<!-- 视频教程(1) 联系我(2)
|
||||
{ .annotate }
|
||||
|
||||
1. 点击右下角[:simple-bilibili:](https://space.bilibili.com/1407028951/lists/4566631?type=series)图标查看视频教程.
|
||||
2. TEL:18939533255(微信号)
|
||||
2. TEL:18939533255(微信号) -->
|
||||
|
||||
***
|
||||
|
||||
@ -138,11 +143,11 @@ ____ __ ____ ______ ______ ____ __ ____ __ .__ __.
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :simple-materialformkdocs:{ .lg .middle } __Mkdocs教程__
|
||||
- :simple-materialformkdocs:{ .lg .middle } __Mkdocs教程(必看)__
|
||||
|
||||
---
|
||||
|
||||
- [Mkdocs视频教程](https://space.bilibili.com/1407028951/lists/4566631?type=series){target=“_blank”}
|
||||
- [Mkdocs视频教程](https://space.bilibili.com/1407028951/lists/4566631?type=series){target=“_blank”}(Bilibili)
|
||||
- [部署静态网页至GitHub pages](blog/Mkdocs/mkdocs1.md)
|
||||
- [Mkdocs部署配置说明(mkdocs.yml)](blog/Mkdocs/mkdocs2.md)
|
||||
- [如何给MKdocs添加友链](blog/websitebeauty/linktech.md)
|
||||
@ -156,7 +161,8 @@ ____ __ ____ ______ ______ ____ __ ____ __ .__ __.
|
||||
- [Mkdocs-Wcowin博客主题社区](https://support.qq.com/products/646913/){target=“_blank”}
|
||||
- [留言板](liuyanban.md)[^Knowing-that-loving-you-has-no-ending]
|
||||
- [Blogger](blog/index.md)
|
||||
[:octicons-arrow-right-24: 了解我](about/geren.md)[^see-how-much-I-love-you]
|
||||
- [:octicons-arrow-right-24: 了解我](about/geren.md)[^see-how-much-I-love-you]
|
||||
- [请作者喝杯咖啡](about/zcw.md)
|
||||
|
||||
</div>
|
||||
|
||||
@ -165,7 +171,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>
|
||||
@ -192,8 +198,55 @@ ____ __ ____ ______ ______ ____ __ ____ __ .__ __.
|
||||
},1000)
|
||||
</script>
|
||||
</font>
|
||||
</body>
|
||||
</body> -->
|
||||
|
||||
|
||||
<!-- <script src="//code.tidio.co/6jmawe9m5wy4ahvlhub2riyrnujz7xxi.js" async></script> -->
|
||||
|
||||
<style>
|
||||
body {
|
||||
position: relative; /* 确保 body 元素的 position 属性为非静态值 */
|
||||
}
|
||||
|
||||
body::before {
|
||||
--size: 35px; /* 调整网格单元大小 */
|
||||
--line: color-mix(in hsl, canvasText, transparent 80%); /* 调整线条透明度 */
|
||||
content: '';
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
position: absolute; /* 修改为 absolute 以使其随页面滚动 */
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--line) 1px,
|
||||
transparent 1px var(--size)
|
||||
)
|
||||
50% 50% / var(--size) var(--size),
|
||||
linear-gradient(var(--line) 1px, transparent 1px var(--size)) 50% 50% /
|
||||
var(--size) var(--size);
|
||||
-webkit-mask: linear-gradient(-20deg, transparent 50%, white);
|
||||
mask: linear-gradient(-20deg, transparent 50%, white);
|
||||
top: 0;
|
||||
transform-style: flat;
|
||||
pointer-events: none;
|
||||
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> -->
|
@ -19,15 +19,15 @@ anime.timeline({loop: true})
|
||||
|
||||
|
||||
//全屏视频
|
||||
var video = document.getElementById("video1");
|
||||
var isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
||||
// var video = document.getElementById("video1");
|
||||
// var isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
||||
|
||||
if (isMobile) {
|
||||
video.style.display = "none";
|
||||
video.muted = true;
|
||||
} else {
|
||||
video.volume = 0.5; // 或者根据需要设置适当的音量值,例如 0.5 表示 50% 的音量
|
||||
}
|
||||
// if (isMobile) {
|
||||
// video.style.display = "none";
|
||||
// video.muted = true;
|
||||
// } else {
|
||||
// video.volume = 0.5; // 或者根据需要设置适当的音量值,例如 0.5 表示 50% 的音量
|
||||
// }
|
||||
|
||||
// 优化
|
||||
// const container = document.querySelector('.container');
|
||||
|
@ -37,7 +37,7 @@ 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/ " target="_blank">Wcowin's Blog</a>
|
||||
<a href="https://wcowin.work/ ">Wcowin's Blog</a>
|
||||
</div>
|
||||
<div class="info">
|
||||
“循此苦旅,以达星辰”
|
||||
@ -45,17 +45,27 @@ t.parentNode.insertBefore(e,t)}})();
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<img class="ava" src="https://raw.githubusercontent.com/VictorWang712/Note/refs/heads/main/docs/assets/images/avatars/Walker_V.png" />
|
||||
<img class="ava" src="https://s1.imagehub.cc/images/2025/06/03/526b59b6a2e478f2ffa1629320e3e2ce.png" />
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<a href="https://victorwang712.github.io/Note/" target="_blank">Walker_V's Notebook</a>
|
||||
<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>
|
||||
@ -69,8 +79,29 @@ t.parentNode.insertBefore(e,t)}})();
|
||||
<h2>失联人员</h2>
|
||||
</div> -->
|
||||
|
||||
<HR style="FILTER: progid:DXImageTransform.Microsoft.Shadow(color:#608DBD,direction:145,strength:15)" width="100%" color=#608DBD SIZE=1>
|
||||
<HR style="FILTER: progid:DXImageTransform.Microsoft.Shadow(color:#EEF3FE),direction:145,strength:15)" width="100%" color=#EEF3FE SIZE=1>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// 获取友链容器
|
||||
const friendLinksContainer = document.querySelector(".links-content");
|
||||
if (friendLinksContainer) {
|
||||
// 获取所有友链卡片
|
||||
const friendLinks = friendLinksContainer.querySelectorAll(".card");
|
||||
const totalLinks = friendLinks.length;
|
||||
|
||||
// 创建统计信息的元素
|
||||
const statsElement = document.createElement("div");
|
||||
statsElement.style.textAlign = "center";
|
||||
statsElement.style.margin = "20px 0";
|
||||
statsElement.style.color = "#999";
|
||||
statsElement.textContent = `当前共有友链 ${totalLinks} 个,欢迎交换友链!`;
|
||||
|
||||
// 将统计信息插入到友链容器的上方
|
||||
friendLinksContainer.parentNode.insertBefore(statsElement, friendLinksContainer);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
||||
@ -96,7 +127,7 @@ t.parentNode.insertBefore(e,t)}})();
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<HR style="FILTER: progid:DXImageTransform.Microsoft.Shadow(color:#608DBD,direction:145,strength:15)" width="100%" color=#608DBD SIZE=1>
|
||||
<HR style="FILTER: progid:DXImageTransform.Microsoft.Shadow(color:#EEF3FE,direction:145,strength:15)" width="100%" color= #EEF3FE SIZE=1>
|
||||
</div>
|
||||
|
||||
|
@ -59,7 +59,7 @@ comments: false
|
||||
width: 180px;
|
||||
color: #999;
|
||||
border-radius: 25px;
|
||||
border: 2px solid #608DBD;
|
||||
border: 2px solid #1F2635;
|
||||
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: #608DBD;
|
||||
background-color: #1F2635;
|
||||
color: white;
|
||||
border-color: #3498db;
|
||||
border-color: #1F2635;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.button-container {
|
||||
|
@ -1,29 +0,0 @@
|
||||
{#-
|
||||
This file was automatically generated - do not edit
|
||||
-#}
|
||||
{% extends "main.html" %}
|
||||
{% block tabs %}
|
||||
{{ super() }}
|
||||
<style>.md-header{position:initial}.md-main__inner{margin:0}.md-content{display:none}@media screen and (min-width:60em){.md-sidebar--secondary{display:none}}@media screen and (min-width:76.25em){.md-sidebar--primary{display:none}}</style>
|
||||
<section class="mdx-container">
|
||||
<div class="md-grid md-typeset">
|
||||
<div class="mdx-hero">
|
||||
<div class="mdx-hero__image">
|
||||
<img src="assets/images/illustration.png" alt="" width="1659" height="1200" draggable="false">
|
||||
</div>
|
||||
<div class="mdx-hero__content">
|
||||
<h1>Technical documentation that just works</h1>
|
||||
<p>{{ config.site_description }}. Set up in 5 minutes.</p>
|
||||
<a href="{{ page.next_page.url | url }}" title="{{ page.next_page.title | e }}" class="md-button md-button--primary">
|
||||
Quick start
|
||||
</a>
|
||||
<a href="{{ 'insiders/' | url }}" title="Material for MkDocs Insiders" class="md-button">
|
||||
Get Insiders
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
{% block content %}{% endblock %}
|
||||
{% block footer %}{% endblock %}
|
BIN
docs/overrides/hooks/__pycache__/ai_summary.cpython-311.pyc
Normal file
BIN
docs/overrides/hooks/__pycache__/ai_summary.cpython-311.pyc
Normal file
Binary file not shown.
BIN
docs/overrides/hooks/__pycache__/ai_summary.cpython-312.pyc
Normal file
BIN
docs/overrides/hooks/__pycache__/ai_summary.cpython-312.pyc
Normal file
Binary file not shown.
BIN
docs/overrides/hooks/__pycache__/reading_time.cpython-311.pyc
Normal file
BIN
docs/overrides/hooks/__pycache__/reading_time.cpython-311.pyc
Normal file
Binary file not shown.
BIN
docs/overrides/hooks/__pycache__/reading_time.cpython-312.pyc
Normal file
BIN
docs/overrides/hooks/__pycache__/reading_time.cpython-312.pyc
Normal file
Binary file not shown.
BIN
docs/overrides/hooks/__pycache__/related_posts.cpython-311.pyc
Normal file
BIN
docs/overrides/hooks/__pycache__/related_posts.cpython-311.pyc
Normal file
Binary file not shown.
BIN
docs/overrides/hooks/__pycache__/socialmedia.cpython-311.pyc
Normal file
BIN
docs/overrides/hooks/__pycache__/socialmedia.cpython-311.pyc
Normal file
Binary file not shown.
BIN
docs/overrides/hooks/__pycache__/socialmedia.cpython-312.pyc
Normal file
BIN
docs/overrides/hooks/__pycache__/socialmedia.cpython-312.pyc
Normal file
Binary file not shown.
1068
docs/overrides/hooks/ai_summary.py
Normal file
1068
docs/overrides/hooks/ai_summary.py
Normal file
File diff suppressed because it is too large
Load Diff
256
docs/overrides/hooks/reading_time.py
Normal file
256
docs/overrides/hooks/reading_time.py
Normal file
@ -0,0 +1,256 @@
|
||||
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
|
503
docs/overrides/hooks/related_posts.py
Normal file
503
docs/overrides/hooks/related_posts.py
Normal file
@ -0,0 +1,503 @@
|
||||
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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user