跳转至

Tools

mkdocs快速搭建帮助文档(以气泡图为例)

插件安装

Bash
1
2
3
pip install mkdocs-material

mkdocs new .
Bash
1
2
3
4
.
├─ docs/
  └─ index.md
└─ mkdocs.yml
graph TD
    Root[项目根目录]
    Root --- Docs[docs/ 文件夹]
    Root --- MkdocsYml[mkdocs.yml 文件]
    Docs --- IndexMd[index.md 文件]

创建文件夹目录

例如E:\Gitee\helperdoc\BubbleDiagram

Bash
mkdocs new .

image-20250923103807473

Besides, further assets may also be put in the overrides directory:

overrides
Bash
    .
    ├─ .icons/                      # Bundled icon sets
    ├─ assets/
      ├─ images/                   # Images and icons
      ├─ javascripts/              # JavaScript files
      └─ stylesheets/              # Style sheets
    ├─ partials/
      ├─ integrations/             # Third-party integrations
        ├─ analytics/             # Analytics integrations
        └─ analytics.html         # Analytics setup
      ├─ languages/                # Translation languages
      ├─ actions.html              # Actions
      ├─ alternate.html            # Site language selector
      ├─ comments.html             # Comment system (empty by default)
      ├─ consent.html              # Consent
      ├─ content.html              # Page content
      ├─ copyright.html            # Copyright and theme information
      ├─ feedback.html             # Was this page helpful?
      ├─ footer.html               # Footer bar
      ├─ header.html               # Header bar
      ├─ icons.html                # Custom icons
      ├─ language.html             # Translation setup
      ├─ logo.html                 # Logo in header and sidebar
      ├─ nav.html                  # Main navigation
      ├─ nav-item.html             # Main navigation item
      ├─ pagination.html           # Pagination (used for blog)
      ├─ palette.html              # Color palette toggle
      ├─ post.html                 # Blog post excerpt
      ├─ progress.html             # Progress indicator
      ├─ search.html               # Search interface
      ├─ social.html               # Social links
      ├─ source.html               # Repository information
      ├─ source-file.html          # Source file information
      ├─ tabs.html                 # Tabs navigation
      ├─ tabs-item.html            # Tabs navigation item
      ├─ tags.html                 # Tags
      ├─ toc.html                  # Table of contents
      ├─ toc-item.html             # Table of contents item
      └─ top.html                  # Back-to-top button
    ├─ 404.html                     # 404 error page
    ├─ base.html                    # Base template
    ├─ blog.html                    # Blog index page
    ├─ blog-archive.html            # Blog archive index page
    ├─ blog-category.html           # Blog category index page
    ├─ blog-post.html               # Blog post page
    └─ main.html                    # Default page

修改mkdocs.yml文件

Success
YAML
  site_name: 我的帮助文档
  site_description: 关于mkdocs-material支持的markdown语法,包括传统语法和扩展语法
  site_author: JerryMa
  site_url: http://127.0.0.1:8000
  theme:
    name: material
    palette:
      # Toggle light mode
      - scheme: default
        primary: Blue Grey
        accent: Pink
        toggle:
          icon: material/toggle-switch
          name: 切换到明亮模式
      # Toggle dark mode
      - scheme: slate
        primary: blue
        accent: amber
        toggle:
          icon: material/toggle-switch-off-outline
          name: 切换到暗黑模式
    features:
      - announce.dismiss
      - content.tabs.link
      - content.tooltips
      - content.code.copy #代码复制
      - content.code.select
      - content.code.annotate   
      - content.footnote.tooltips
      - header.autohide
      - navigation.footer
      - navigation.indexes
      - navigation.instant
      - navigation.instant.prefetch
      - navigation.instant.progress
      - navigation.prune
      - navigation.sections
      - navigation.tabs
      - navigation.tabs.sticky
      - navigation.top # 返回顶部的按钮 在上滑时出现  
      - navigation.tracking
      - search.highlight # 搜索出的文章关键词加入高亮
      - search.share #搜索分享按钮   
      - search.suggest # 搜索输入一些字母时推荐补全整个单词
      - toc.follow
      - toc.integrate
    language: 'zh'
  plugins:
    - offline
    - search:
        lang: 
          - zh
          - en
        separator: '[\s\-\.]+'
    - minify:
        minify_html: true
        minify_js: true
        minify_css: true
        htmlmin_opts:
          remove_comments: true
        css_files:
          - stylesheets/extra.css
    - glightbox:
        touchNavigation: true
        loop: false
        effect: zoom
        slide_effect: slide
        width: 100%
        height: auto
        zoomable: true
        draggable: true
        skip_classes:
          - custom-skip-class-name
        auto_caption: false
        caption_position: bottom
  extra:
    social:
      - icon: fontawesome/brands/github #联系方式图标 : https://fontawesome.com/ 去这里找图标
        link: https://github.com/mazaiguo
        name: JerryMa on Github
      - icon: fontawesome/brands/gitlab
        link: https://gitlab.zwsoft.cn/mazaiguo
      - icon: fontawesome/regular/envelope
        link: mailto:mazaiguo@126.com
        name: Email
    analytics:
      feedback:
        title: 这个页面对您有帮助吗?
        ratings:
          - icon: material/emoticon-happy-outline
            name: 有帮助
            data: 1
            note: >-
              感谢您的反馈!
          - icon: material/emoticon-sad-outline
            name: 可以改进
            data: 0
            note: >-
              感谢您的反馈!请帮助我们改进这个页面,
              <a href="https://github.com/mazaiguo/mazaiguo.github.io/issues/new/?title=[Feedback]+{title}+-+{url}" target="_blank" rel="noopener">告诉我们需要改进的地方</a>。
    generator: false #是否删除页脚显示"使用 MkDocs 材料制造"
  extra_javascript:
    - javascripts/katex.js
    - https://unpkg.com/katex@0/dist/katex.min.js
    - https://unpkg.com/katex@0/dist/contrib/auto-render.min.js
    #- https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js
    #- javascripts/config.js
  extra_css:
    #- https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/default.min.css
    - stylesheets/extra.css  
    - https://unpkg.com/katex@0/dist/katex.min.css
  markdown_extensions:
    - abbr
    - admonition
    - attr_list
    - def_list
    - footnotes
    - md_in_html
    - meta
    - tables
    - toc:
        permalink: true
        title: 目录
    - pymdownx.arithmatex:
        generic: true
    - pymdownx.betterem:
        smart_enable: all
    - pymdownx.caret
    - pymdownx.details
    - pymdownx.emoji:
        emoji_generator: !!python/name:material.extensions.emoji.to_svg
        emoji_index: !!python/name:material.extensions.emoji.twemoji
    - pymdownx.highlight:
        anchor_linenums: true
        line_spans: __span
        pygments_lang_class: true
        linenums: true
        linenums_style: pymdownx.inline
        auto_title: true # 显示编程语言名称
        use_pygments: true
    - pymdownx.inlinehilite
    - pymdownx.keys
    - pymdownx.magiclink:
        normalize_issue_symbols: true
        repo_url_shorthand: true
        user: mazaiguo
        repo: helpdoc
    - pymdownx.mark
    - pymdownx.smartsymbols
    - pymdownx.snippets:
        check_paths: true
    - pymdownx.superfences:
        custom_fences:
          - name: mermaid
            class: mermaid
            format: !!python/name:pymdownx.superfences.fence_code_format
    - pymdownx.tabbed:
        alternate_style: true
        combine_header_slug: true
        slugify: !!python/object/apply:pymdownx.slugs.slugify
          kwds:
            case: lower
    - pymdownx.tasklist:
        custom_checkbox: true
    - pymdownx.tilde
    - pymdownx.critic
  copyright: Copyright &copy; 2016 - present JerryMa

image-20250923154538869

这几个是MKDOCS内置使用的,我们建立文件夹时不要与这些名字冲突了

增加一些配置文件

katex-docsjavascriptskatexjs

Note
JavaScript
   document$.subscribe(({ body }) => { 
     renderMathInElement(body, {
       delimiters: [
         { left: "$$",  right: "$$",  display: true },
         { left: "$",   right: "$",   display: false },
         { left: "\\(", right: "\\)", display: false },
         { left: "\\[", right: "\\]", display: true }
       ],
     })
   })

docs/javascripts/tablesort.js

Note
JavaScript
1
2
3
4
5
6
document$.subscribe(function() {
  var tables = document.querySelectorAll("article table:not([class])")
  tables.forEach(function(table) {
    new Tablesort(table)
  })
})

[docs\stylesheets\extra.css]

Warning
CSS
    :root > * {
      --md-code-hl-string-color: #0ff1ce;
      --md-code-hl-number-color: #ae81ff;
      --md-code-hl-special-color: #a846b9;
      --md-code-hl-function-color: #66d9ef;
      --md-code-hl-constant-color: #f92672;
      --md-code-hl-keyword-color: #f92672;
      --md-code-hl-string-color: #e6db74;
      --md-code-hl-name-color: #feffff;
      --md-code-hl-operator-color: #f92672;
      --md-code-hl-punctuation-color: #ffffff;
      --md-code-hl-comment-color: #757575;
      --md-code-hl-generic-color: #af82fc;
      --md-code-hl-variable-color: #f92672;
      --md-code-fg-color: #ffffff;
      --md-code-bg-color: #282c34;
      --md-code-hl-color: #ffff7f;
      --md-default-fg-color--light: #75715f;
    }
    .md-typeset p > code {
      background-color: #ffffff;
      color: #eb245c;
    }
    .md-typeset li code {
      background-color: #ffffff;
      color: #eb245c;
    }
    /*代码块头部图标 start*/
    .highlight span.filename pre:before {
      content: "";
      display: block;
      background: url(../assets/images/codeHeader.png);
      height: 30px;
      background-size: 40px;
      background-repeat: no-repeat;
      background-color: #212121;
      background-position: 10px 10px;
    }
    /*代码块头部图标 end*/

    .highlighttable .code pre > code {
      color: #c0c3c1;
      font-family: "Inconsolata", consolas, "PingFang SC", "Microsoft YaHei",
        monospace;
      background-color: #212121;
      font-size: 15px;
      white-space: pre;
      line-height: 1.5;
      -moz-tab-size: 4;
      -o-tab-size: 4;
      tab-size: 4;
    }
    .highlight span.filename {
      color: white;
    }

    /* 基础容器样式:确保导航项布局正常 */
    .md-nav__list {
      list-style: none;
      padding: 0;
      margin: 0;
      width: 280px; /* 适配侧边导航宽度,可按需调整 */
    }
    .md-nav__item {
      margin: 4px 0;
    }
    .md-nav__link {
      display: block;
      padding: 8px 12px;
      border-radius: 6px;
      text-decoration: none;
      color: #333; /* 默认文本色 */
      transition: all 0.3s ease; /* 统一过渡动画,确保流畅性 */
    }

    /* 核心:.md-ellipsis 交互特效 */
    .md-ellipsis {
      position: relative;
      z-index: 1;
      transition: color 0.3s ease;
    }
    /* 鼠标悬浮(hover)效果:文本变色 + 底部渐变下划线 */
    .md-nav__link:hover .md-ellipsis {
      color: #165dff; /* 悬浮文本主色(可替换为品牌色) */
    }
    .md-nav__link:hover .md-ellipsis::after {
      content: "";
      position: absolute;
      left: 0;
      bottom: -2px;
      width: 100%;
      height: 2px;
      background: linear-gradient(90deg, #165dff, #4080ff); /* 渐变下划线 */
      border-radius: 1px;
      transform: scaleX(1);
      transform-origin: left center;
      transition: transform 0.3s ease;
    }
    /* 初始状态:下划线收缩至0,hover时展开 */
    .md-ellipsis::after {
      content: "";
      position: absolute;
      left: 0;
      bottom: -2px;
      width: 100%;
      height: 2px;
      background: linear-gradient(90deg, #165dff, #4080ff);
      border-radius: 1px;
      transform: scaleX(0);
      transform-origin: left center;
      transition: transform 0.3s ease;
    }

    /* 鼠标点击(active)效果:文本加深 + 背景压暗 */
    .md-nav__link:active .md-ellipsis {
      color: #0e42d2; /* 点击文本加深色 */
      font-weight: 500; /* 点击时文本轻微加粗 */
    }
    .md-nav__link:active {
      background-color: rgba(22, 93, 255, 0.1); /* 点击背景色(淡蓝压暗) */
      transform: translateY(1px); /* 轻微下沉,模拟物理按压感 */
      transition: transform 0.1s ease, background-color 0.1s ease;
    }

    /* 激活状态(.md-nav__link--active):区分当前选中项 */
    .md-nav__link--active .md-ellipsis {
      color: #165dff;
      font-weight: 500;
    }
    .md-nav__link--active .md-ellipsis::after {
      transform: scaleX(1); /* 激活项默认显示下划线 */
    }
    .md-nav__link--active {
      background-color: rgba(22, 93, 255, 0.05); /* 激活项背景色 */
    }

    /* 标签云样式 */
    .tag-cloud {
      margin: 1rem 0;
      line-height: 2;
    }

    .tag-cloud .tag {
      display: inline-block;
      padding: 0.25rem 0.5rem;
      margin: 0.125rem;
      background-color: var(--md-primary-fg-color--light);
      color: var(--md-primary-bg-color);
      border-radius: 0.25rem;
      text-decoration: none;
      font-size: 0.875rem;
      font-weight: 500;
      transition: all 0.2s ease;
    }

    .tag-cloud .tag:hover {
      background-color: var(--md-primary-fg-color);
      transform: translateY(-1px);
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }

    /* 暗色主题下的标签样式 */
    [data-md-color-scheme="slate"] .tag-cloud .tag {
      background-color: var(--md-accent-fg-color);
      color: var(--md-default-bg-color);
    }

    [data-md-color-scheme="slate"] .tag-cloud .tag:hover {
      background-color: var(--md-accent-fg-color--transparent);
    }

    /* 博客卡片样式 */
    .blog-card {
      background: var(--md-default-bg-color);
      border: 1px solid var(--md-default-fg-color--lightest);
      border-radius: 0.5rem;
      padding: 1.5rem;
      margin: 1rem 0;
      transition: all 0.2s ease;
    }

    .blog-card:hover {
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
      transform: translateY(-2px);
    }

    /* 统计数字样式 */
    .stats-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: 1rem;
      margin: 2rem 0;
    }

    .stat-item {
      text-align: center;
      padding: 1rem;
      background: var(--md-default-bg-color);
      border: 1px solid var(--md-default-fg-color--lightest);
      border-radius: 0.5rem;
    }

    .stat-number {
      font-size: 2rem;
      font-weight: bold;
      color: var(--md-primary-fg-color);
      display: block;
    }

    .stat-label {
      font-size: 0.875rem;
      color: var(--md-default-fg-color--light);
      margin-top: 0.5rem;
    }

    /* 分类页面样式 */
    .category-section {
      margin: 2rem 0;
    }

    .category-title {
      color: var(--md-primary-fg-color);
      border-bottom: 2px solid var(--md-primary-fg-color--light);
      padding-bottom: 0.5rem;
      margin-bottom: 1rem;
    }

    .category-list {
      list-style: none;
      padding: 0;
    }

    .category-list li {
      margin: 0.5rem 0;
      padding-left: 1rem;
      border-left: 3px solid var(--md-accent-fg-color);
    }

    .category-list a {
      text-decoration: none;
      color: var(--md-default-fg-color);
      font-weight: 500;
    }

    .category-list a:hover {
      color: var(--md-primary-fg-color);
    }

    /* 响应式设计 */
    @media screen and (max-width: 768px) {
      .tag-cloud .tag {
        font-size: 0.75rem;
        padding: 0.2rem 0.4rem;
      }

      .stats-grid {
        grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
      }

      .stat-number {
        font-size: 1.5rem;
      }
    }

    /* 代码块优化 */
    .highlight pre {
      border-radius: 0.5rem;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }

    /* 文章元信息样式 */
    .article-meta {
      display: flex;
      flex-wrap: wrap;
      gap: 1rem;
      margin: 1rem 0;
      padding: 1rem;
      background: var(--md-code-bg-color);
      border-radius: 0.5rem;
      font-size: 0.875rem;
      color: var(--md-default-fg-color--light);
    }

    .article-meta .meta-item {
      display: flex;
      align-items: center;
      gap: 0.25rem;
    }

    .article-meta .meta-icon {
      width: 1rem;
      height: 1rem;
      opacity: 0.7;
    }

    /* 博客文章列表样式优化 */
    .md-content .md-typeset .md-post {
      margin-bottom: 2rem;
      padding-bottom: 2rem;
      border-bottom: 1px solid var(--md-default-fg-color--lightest);
    }

    .md-content .md-typeset .md-post:last-child {
      border-bottom: none;
    }

    /* 提高阅读体验 */
    .md-typeset h1,
    .md-typeset h2,
    .md-typeset h3 {
      margin-top: 2rem;
      margin-bottom: 1rem;
    }

    .md-typeset h1:first-child,
    .md-typeset h2:first-child,
    .md-typeset h3:first-child {
      margin-top: 0;
    }

[docs\assets\images\codeHeader.png]

codeHeader

帮助文档生成样式

image-20250923160659893

image-20250923160714067

mkdocs_help_doc

MkDocs博客文件命名规则

MkDocs博客文件命名规则

❌ 避免的命名方式

  1. 以点号开头的文件名

  2. ❌ .Net封装ObjectARX自定义实体类型.md

  3. ❌ .gitignore.md

  4. 包含特殊字符的文件名

  5. file@name.md

  6. ❌ file#name.md

  7. ❌ file<>name.md

  8. 过长的文件名

  9. ❌ 超过255字符的文件名

✅ 推荐的命名方式

  1. 使用标准字符

  2. ✅ Net封装ObjectARX自定义实体类型.md

  3. ✅ dotnet-objectarx-custom-entity.md

  4. 使用连字符分隔

  5. ✅ c-sharp-json-processing.md

  6. ✅ wpf-custom-drawer-menu.md

  7. 使用下划线分隔

  8. ✅ objectarx_net_learning_part2.md

mkdocs快速搭建博客

安装依赖

requirement.txt

Text Only
# MkDocs 核心
mkdocs>=1.5.0
mkdocs-material>=9.4.0

# 博客功能(包含在 mkdocs-material 中)
# mkdocs-blog-plugin  # 不需要单独安装

# 功能增强插件
mkdocs-minify-plugin>=0.7.0
mkdocs-glightbox>=0.3.4

# Git 相关插件(可选,需要系统安装 Git)
# mkdocs-git-revision-date-localized-plugin>=1.2.0

# 其他可选插件
mkdocs-awesome-pages-plugin
# mkdocs-redirects
# mkdocs-rss-plugin

yaml文件配置

mkdoc.yml

mkdoc.yml
YAML
site_name: 我的帮助文档
site_description: 关于mkdocs-material支持的markdown语法,包括传统语法和扩展语法
site_author: JerryMa
site_url: http://127.0.0.1:8000

repo_name: 'mkdocsblog'
repo_url: 'https://github.com/mazaiguo/mkdocsblog'
theme:
  name: material
  palette:
    # Toggle light mode
    - scheme: default
      primary: Blue Grey
      accent: Pink
      toggle:
        icon: material/toggle-switch
        name: 切换到明亮模式
    # Toggle dark mode
    - scheme: slate
      primary: blue
      accent: amber
      toggle:
        icon: material/toggle-switch-off-outline
        name: 切换到暗黑模式
  features:
    - announce.dismiss
    - content.tabs.link
    - content.tooltips
    - content.code.copy #代码复制
    - content.code.select
    - content.code.annotate   
    - content.footnote.tooltips
    - header.autohide
    - navigation.footer
    - navigation.indexes
    - navigation.instant
    - navigation.instant.prefetch
    - navigation.instant.progress
    - navigation.prune
    - navigation.sections
    - navigation.tabs
    - navigation.tabs.sticky
    - navigation.top # 返回顶部的按钮 在上滑时出现  
    - navigation.tracking
    - search.highlight # 搜索出的文章关键词加入高亮
    - search.share #搜索分享按钮   
    - search.suggest # 搜索输入一些字母时推荐补全整个单词
    - toc.follow
    - toc.integrate
  language: 'zh'
plugins:
  - macros
  - blog:
      blog_dir: blog
      post_dir: "{blog}/posts"
      post_date_format: full
      post_url_format: "{date}/{slug}"
      pagination_per_page: 10
      pagination_url_format: "page/{page}"
      authors_file: "{blog}/.authors.yml"
      blog_toc: true
      categories_toc: true
      archive: true
      archive_name: 归档
      archive_date_format: "YYYY年MM月"
      archive_url_format: "archive/{date}"
      archive_toc: true
      archive_file: "archive/index.md"
      categories: true
      categories_name: 分类
      categories_url_format: "category/{slug}"
      categories_slugify: !!python/object/apply:pymdownx.slugs.slugify
        kwds:
          case: lower
  - offline
  - tags:
      tags_hierarchy: true
      tags_slugify_format: "tag:{slug}"
      tags_slugify: !!python/object/apply:pymdownx.slugs.slugify
        kwds:
          case: lower
  - search:
      lang: 
        - zh
        - en
      separator: '[\s\-\.]+'
  - minify:
      minify_html: true
      minify_js: true
      minify_css: true
      htmlmin_opts:
        remove_comments: true
      css_files:
        - stylesheets/extra.css
  - glightbox:
      touchNavigation: true
      loop: false
      effect: zoom
      slide_effect: slide
      width: 100%
      height: auto
      zoomable: true
      draggable: true
      skip_classes:
        - custom-skip-class-name
      auto_caption: false
      caption_position: bottom
  # 注释掉 git 插件,因为需要系统安装 Git
  # - git-revision-date-localized:
  #     enable_creation_date: true
  #     type: timeago
  #     locale: zh
  #     fallback_to_build_date: false
  #     exclude:
  #       - index.md
  #       - tags.md
  #       - blog/index.md
extra:
  social:
    - icon: fontawesome/brands/github #联系方式图标 : https://fontawesome.com/ 去这里找图标
      link: https://github.com/mazaiguo
      name: JerryMa on Github
    - icon: fontawesome/brands/gitlab
      link: https://gitlab.zwsoft.cn/mazaiguo
    - icon: fontawesome/regular/envelope
      link: mailto:mazaiguo@126.com
      name: Email
  analytics:
    feedback:
      title: 这个页面对您有帮助吗?
      ratings:
        - icon: material/emoticon-happy-outline
          name: 有帮助
          data: 1
          note: >-
            感谢您的反馈!
        - icon: material/emoticon-sad-outline
          name: 可以改进
          data: 0
          note: >-
            感谢您的反馈!请帮助我们改进这个页面,
            <a href="https://github.com/mazaiguo/mazaiguo.github.io/issues/new/?title=[Feedback]+{title}+-+{url}" target="_blank" rel="noopener">告诉我们需要改进的地方</a>。
  tags:
    HTML5: html
    JavaScript: js
    CSS: css
    Python: python
    AutoCAD: autocad
    C++: cpp
    "Csharp": csharp
    ".NET": dotnet
  generator: false #是否删除页脚显示"使用 MkDocs 材料制造"
#extra_javascript:
  #- https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js
  #- javascripts/config.js
extra_css:
  #- https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/default.min.css
  - stylesheets/extra.css  
markdown_extensions:
  - abbr
  - admonition
  - attr_list
  - def_list
  - footnotes
  - md_in_html
  - meta
  - toc:
      permalink: true
      title: 目录
  - pymdownx.arithmatex:
      generic: true
  - pymdownx.betterem:
      smart_enable: all
  - pymdownx.caret
  - pymdownx.details
  - pymdownx.emoji:
      emoji_generator: !!python/name:material.extensions.emoji.to_svg
      emoji_index: !!python/name:material.extensions.emoji.twemoji
  - pymdownx.highlight:
      anchor_linenums: true
      line_spans: __span
      pygments_lang_class: true
      linenums: true
      linenums_style: pymdownx.inline
      auto_title: true # 显示编程语言名称
      use_pygments: true
  - pymdownx.inlinehilite
  - pymdownx.keys
  - pymdownx.magiclink:
      normalize_issue_symbols: true
      repo_url_shorthand: true
      user: mazaiguo
      repo: helpdoc
  - pymdownx.mark
  - pymdownx.smartsymbols
  - pymdownx.snippets:
      check_paths: true
  - pymdownx.superfences:
      custom_fences:
        - name: mermaid
          class: mermaid
          format: !!python/name:pymdownx.superfences.fence_code_format
  - pymdownx.tabbed:
      alternate_style: true
      combine_header_slug: true
      slugify: !!python/object/apply:pymdownx.slugs.slugify
        kwds:
          case: lower
  - pymdownx.tasklist:
      custom_checkbox: true
  - pymdownx.tilde
  - pymdownx.critic
copyright: Copyright &copy; 2016 - present [JerryMa](https://github.com/mazaiguo)
nav:
  - 首页: index.md
  - 博客:
     - blog/index.md
  - 归档: archive/index.md
  - 分类: blog/category.md
  - 标签: tags.md
  - 关于: 
     - 关于本站: about.md

增加latex

Bash
1
2
3
$$
\cos x=\sum_{k=0}^{\infty}\frac{(-1)^k}{(2k)!}x^{2k}
$$
\[ \cos x=\sum_{k=0}^{\infty}\frac{(-1)^k}{(2k)!}x^{2k} \]
Bash
1
2
3
The homomorphism $f$ is injective if and only if its kernel is only the
singleton set $e_G$, because otherwise $\exists a,b\in G$ with $a\neq b$ such
that $f(a)=f(b)$.

The homomorphism \(f\) is injective if and only if its kernel is only the singleton set \(e_G\), because otherwise \(\exists a,b\in G\) with \(a\neq b\) such that \(f(a)=f(b)\).

contents tab

  • Sed sagittis eleifend rutrum
  • Donec vitae suscipit est
  • Nulla tempor lobortis orci
  1. Sed sagittis eleifend rutrum
  2. Donec vitae suscipit est
  3. Nulla tempor lobortis orci
C
1
2
3
4
5
6
#include <stdio.h>

int main(void) {
  printf("Hello world!\n");
  return 0;
}
C++
1
2
3
4
5
6
#include <iostream>

int main(void) {
  std::cout << "Hello world!" << std::endl;
  return 0;
}

Example

Example:

Markdown
1
2
3
* Sed sagittis eleifend rutrum
* Donec vitae suscipit est
* Nulla tempor lobortis orci

Result:

  • Sed sagittis eleifend rutrum
  • Donec vitae suscipit est
  • Nulla tempor lobortis orci

Example:

Markdown
1
2
3
1. Sed sagittis eleifend rutrum
2. Donec vitae suscipit est
3. Nulla tempor lobortis orci

Result:

  1. Sed sagittis eleifend rutrum
  2. Donec vitae suscipit est
  3. Nulla tempor lobortis orci

Admonition

Outer Note

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor massa, nec semper lorem quam in massa.

Inner Note

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor massa, nec semper lorem quam in massa.

Note

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor massa, nec semper lorem quam in massa.

Info

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.

Abstract

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.

Info

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.

Tip

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.

Success

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.

Question

这是个问题

Warning

这是个警告

Failure

这是失败的提示

Danger

危险错误的提示

Error

错误的提示

Bug

bug的提示

Quote

quote的提示

特殊数据处理

archive/index.md

tags.md

不识别[TAGS]、[ARCHIVE],用main.py中定义的自定义宏来处理

main.py

main.py
Python
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
"""
MkDocs macros for auto-generating archive content
"""
import os
import re
import yaml
from pathlib import Path
from datetime import datetime, date  # 顶部导入
from collections import defaultdict
import urllib.parse

def generate_url_slug(title):
    """根据实际URL格式生成URL片段"""
    # 1. 转为小写
    slug = title.lower()

    # 2. 特殊处理:如果以点号开头,删除点号并在后面添加分隔符
    if slug.startswith('.'):
        slug = slug[1:]  # 删除开头的点号
        # 在英文和中文之间添加分隔符
        slug = re.sub(r'^([a-z]+)(?=[\u4e00-\u9fff])', r'\1-', slug)

    # 3. 处理其他点号 - 直接删除(保持原有行为)
    slug = slug.replace('.', '')

    # 4. 处理括号(删除括号,保留内容)
    slug = re.sub(r'[()()]', '', slug)

    # 5. 处理连续的+号(C++等)
    slug = re.sub(r'\+{2,}', '', slug)

    # 6. 只对没有自然分隔符的长字符串添加中英文分隔符
    # 检查是否需要添加分隔符(没有空格或连字符的长字符串)
    # if ' ' not in slug and '-' not in slug and len(slug) > 10:
    #     # 在中英文交界处添加分隔符
    #     slug = re.sub(r'(?<=[a-z0-9])(?=[\u4e00-\u9fff])', '-', slug)
    #     slug = re.sub(r'(?<=[\u4e00-\u9fff])(?=[a-z0-9])', '-', slug)

    # 7. 将多个空格合并为一个,然后转为连字符
    slug = re.sub(r'\s+', '-', slug)

    # 8. 清理多余的连字符
    slug = re.sub(r'-+', '-', slug)
    slug = slug.strip('-')

    # 9. URL编码
    return urllib.parse.quote(slug)

def define_env(env):
    """
    Define macros for MkDocs  
    """

    @env.macro  
    def auto_archive():
        """
        Automatically generate archive content from blog posts
        从md文件中完全自动获取所有信息,无硬编码
        """
        try:
            blog_posts_dir = Path("docs/blog/posts")
            if not blog_posts_dir.exists():
                return "## ❌ 错误\n\n无法找到博客文章目录\n\n"

            posts = []
            all_categories = set()  # 收集所有出现的分类
            debug_info = []  # 添加调试信息

            # 遍历所有文章文件
            for md_file in blog_posts_dir.rglob("*.md"):
                debug_info.append(f"处理文件: {md_file}")
                try:
                    with open(md_file, 'r', encoding='utf-8-sig') as f:
                        content = f.read()
                    debug_info.append(f"成功读取: {md_file.name}")

                    # 提取front matter
                    if content.startswith('---'):
                        parts = content.split('---', 2)
                        if len(parts) >= 3:
                            front_matter_text = parts[1].strip()
                            try:
                                # 解析YAML front matter
                                front_matter = yaml.safe_load(front_matter_text)
                                if not front_matter:
                                    continue

                                title = front_matter.get('title', md_file.stem)
                                date_val = front_matter.get('date', '2025-09-18')
                                categories = front_matter.get('categories', [])

                                # 确定主分类 - 完全从front matter获取
                                if isinstance(categories, list) and categories:
                                    main_category = categories[0]
                                    # 收集所有分类
                                    for cat in categories:
                                        all_categories.add(str(cat))
                                else:
                                    # 如果没有分类,跳过这篇文章或使用未分类
                                    main_category = "未分类"
                                    all_categories.add("未分类")

                                # 生成相对路径
                                relative_path = os.path.relpath(md_file, Path("docs/archive")).replace('\\', '/')

                                posts.append({
                                    'title': title,
                                    'date': str(date_val),
                                    'category': str(main_category),
                                    'path': relative_path,
                                    'all_categories': categories if isinstance(categories, list) else [main_category]
                                })

                            except yaml.YAMLError as e:
                                # YAML解析失败,尝试提取基本信息
                                posts.append({
                                    'title': md_file.stem,
                                    'date': '2025-09-18', 
                                    'category': "解析失败",
                                    'path': os.path.relpath(md_file, Path("docs/archive")).replace('\\', '/'),
                                    'all_categories': ["解析失败"]
                                })
                                all_categories.add("解析失败")

                except Exception as e:
                    # 文件读取失败
                    continue

            # 添加调试输出
            debug_text = "\n".join(debug_info[:10])  # 显示前10行调试信息

            if not posts:
                return f"## 📝 调试信息\n\n找到 {len(posts)} 篇文章\n\n调试:\n```\n{debug_text}\n```\n\n分类: {list(all_categories)}\n\n"

            # 生成干净的结果,不包含调试信息

            # 动态生成分类名称映射
            category_display_names = {}
            for category in all_categories:
                cat_lower = category.lower()
                if cat_lower == 'cpp' or 'c++' in cat_lower:
                    category_display_names[category] = 'CPP开发'
                elif cat_lower == 'python' or 'python' in cat_lower:
                    category_display_names[category] = 'Python开发'
                elif 'autocad' in cat_lower or 'cad' in cat_lower:
                    category_display_names[category] = 'AutoCAD开发'
                elif cat_lower == 'csharp' or 'c#' in cat_lower or '.net' in cat_lower:
                    category_display_names[category] = 'C#/.NET开发'
                elif '开发工具' in category or '工具' in category:
                    category_display_names[category] = '开发工具'
                elif '未分类' in category:
                    category_display_names[category] = '未分类'
                elif '解析失败' in category:
                    category_display_names[category] = '解析失败'
                else:
                    # 默认添加"开发"后缀,除非已经包含
                    if '开发' not in category:
                        category_display_names[category] = f'{category}开发'
                    else:
                        category_display_names[category] = category

            # 按日期分组
            posts.sort(key=lambda x: x['date'], reverse=True)
            date_groups = defaultdict(list)

            for post in posts:
                try:
                    if isinstance(post['date'], str):
                        date_obj = datetime.strptime(post['date'], '%Y-%m-%d')
                    else:
                        date_obj = post['date']
                    month_key = date_obj.strftime('%Y年%m月')
                    date_groups[month_key].append(post)
                except:
                    # 日期解析失败,使用默认
                    date_groups['2025年09月'].append(post)

            # 生成归档内容
            result = []

            for month in sorted(date_groups.keys(), reverse=True):
                month_posts = date_groups[month]
                result.append(f"## 🗓️ {month}")
                result.append("")

                # 按分类分组
                category_groups = defaultdict(list)
                for post in month_posts:
                    category_groups[post['category']].append(post)

                # 按分类显示
                for category in sorted(category_groups.keys()):
                    display_name = category_display_names.get(category, category)
                    result.append(f"### {display_name}")

                    for post in category_groups[category]:
                        result.append(f"- [{post['title']}]({post['path']}) - {post['date']}")

                    result.append("")

            # 在最后添加统计信息
            result.append("---")
            result.append("")
            result.append("## 📊 统计信息")
            result.append("")
            result.append(f"- **总文章数**: {len(posts)}篇")
            result.append(f"- **分类数量**: {len(all_categories)}个")
            result.append("- **分类列表**: " + "、".join(sorted(all_categories)))

            return '\n'.join(result)

        except Exception as e:
            return f"## ❌ 生成错误\n\n生成归档时出错: {str(e)}\n\n请检查md文件格式或front matter语法。"

    @env.macro
    def auto_category():
        """
        Automatically generate category content from blog posts
        从md文件中完全自动获取分类信息,无硬编码
        """
        try:
            blog_posts_dir = Path("docs/blog/posts")
            if not blog_posts_dir.exists():
                return "## ❌ 错误\n\n无法找到博客文章目录\n\n"

            # 收集分类信息
            category_info = defaultdict(list)
            all_categories = set()
            nBlogCount = 0
            # 遍历所有文章文件
            for md_file in blog_posts_dir.rglob("*.md"):
                try:
                    with open(md_file, 'r', encoding='utf-8-sig') as f:
                        content = f.read()

                    nBlogCount += 1
                    # 提取front matter
                    if content.startswith('---'):
                        parts = content.split('---', 2)
                        if len(parts) >= 3:
                            front_matter_text = parts[1].strip()
                            try:
                                front_matter = yaml.safe_load(front_matter_text)
                                if not front_matter:
                                    continue

                                title = front_matter.get('title', md_file.stem)
                                categories = front_matter.get('categories', [])
                                date_val = front_matter.get('date', '2025-09-18')

                                # 处理分类
                                if isinstance(categories, list) and categories:
                                    for category in categories:
                                        cat_str = str(category).strip().lower()  # 归一化
                                        all_categories.add(cat_str)
                                        category_info[cat_str].append((title, date_val, md_file.stem))

                            except yaml.YAMLError:
                                continue

                except Exception:
                    continue

            # 生成分类页面内容  
            result = []
            result.append(f"## 🔍 找到{len(all_categories)}个分类")
            result.append("")

            # 编程语言部分
            result.append("## 🖥️ 编程语言")
            result.append("")

            for category in sorted(all_categories):
                cat_lower = category.lower()
                if cat_lower in ['cpp', 'python', 'csharp'] or 'c++' in cat_lower:
                    count = len(category_info[category])

                    if cat_lower == 'cpp' or 'c++' in cat_lower:
                        display_name = 'CPP'
                        icon = '🖥️'
                    elif cat_lower == 'python':
                        display_name = 'Python'
                        icon = '🐍'
                    else:
                        display_name = category
                        icon = '💻'

                    # 添加分类标题
                    result.append(f"### {icon} [{display_name}](category/{cat_lower}.html)")
                    result.append(f"- **文章数量**: {count}篇")

                    latest = []
                    print(f"category={category}, items={category_info[category]}")
                    for t, d, stem in sorted(category_info[category], key=lambda x: x[1], reverse=True)[:3]:
                        url = f"{generate_url_slug(t)}.html"  # 使用title转小写再编码
                        dt = None
                        try:
                            if isinstance(d, datetime):
                                dt = d
                            elif isinstance(d, date):
                                dt = datetime.combine(d, datetime.min.time())
                            elif isinstance(d, str) and len(d) == 10:
                                dt = datetime.strptime(d, "%Y-%m-%d")
                        except Exception as ex:
                            pass
                        if dt:
                            url = f"{dt.year}/{dt.month:02d}/{dt.day:02d}/{generate_url_slug(t)}.html"
                        latest.append(f"[{t}]({url})")
                    result.append(f"- **最新文章**: {', '.join(latest)}")
                    result.append("")
                else:
                    icon = '💻'
                    result.append(f"### {icon} [{cat_lower}](category/{cat_lower}.html)")
                    result.append(f"- **文章数量**: {len(category_info[category])}篇")
                    # 修正这里,生成带链接的最新文章
                    latest = []
                    print(f"category={category}, items={category_info[category]}")
                    for t, d, stem in sorted(category_info[category], key=lambda x: x[1], reverse=True)[:3]:
                        url = f"{generate_url_slug(t)}.html"  # 使用title转小写再编码
                        dt = None
                        try:
                            if isinstance(d, datetime):
                                dt = d
                            elif isinstance(d, date):
                                dt = datetime.combine(d, datetime.min.time())
                            elif isinstance(d, str) and len(d) == 10:
                                dt = datetime.strptime(d, "%Y-%m-%d")
                        except Exception as ex:
                            pass
                        if dt:
                            url = f"{dt.year}/{dt.month:02d}/{dt.day:02d}/{generate_url_slug(t)}.html"
                        latest.append(f"[{t}]({url})")
                    result.append(f"- **最新文章**: {', '.join(latest)}")
                    result.append("")

            # 开发框架和工具部分
            result.append("## 🔧 开发框架与工具")
            result.append("")

            for category in sorted(all_categories):
                cat_lower = category.lower()
                if 'autocad' in cat_lower or 'cad' in cat_lower or '工具' in cat_lower:
                    count = len(category_info[category])

                    # 添加分类标题
                    if 'autocad' in cat_lower or 'cad' in cat_lower:
                        icon = '🏗️'
                        display_name = 'AutoCAD/CAD开发'
                    else:
                        icon = '🔧'
                        display_name = category

                    result.append(f"### {icon} [{display_name}](category/{cat_lower}.html)")
                    result.append(f"- **文章数量**: {count}篇")

                    latest = []
                    print(f"category={category}, items={category_info[category]}")
                    for t, d, stem in sorted(category_info[category], key=lambda x: x[1], reverse=True)[:3]:
                        url = f"{generate_url_slug(t)}.html"  # 使用title转小写再编码
                        dt = None
                        try:
                            if isinstance(d, datetime):
                                dt = d
                            elif isinstance(d, date):
                                dt = datetime.combine(d, datetime.min.time())
                            elif isinstance(d, str) and len(d) == 10:
                                dt = datetime.strptime(d, "%Y-%m-%d")
                        except Exception as ex:
                            pass
                        if dt:
                            url = f"{dt.year}/{dt.month:02d}/{dt.day:02d}/{generate_url_slug(t)}.html"
                        latest.append(f"[{t}]({url})")
                    result.append(f"- **最新文章**: {', '.join(latest)}")
                    result.append("")

            # 统计信息
            result.append("---")
            result.append("")
            result.append("## 📊 分类统计")
            result.append("")
            total_articles = sum(len(articles) for articles in category_info.values())
            result.append(f"- **总分类数**: {len(all_categories)}个")
            result.append(f"- **总文章数**:  {nBlogCount}篇")

            return '\n'.join(result)

        except Exception as e:
            return f"## ❌ 生成错误\n\n{str(e)}\n\n"

    @env.macro
    def auto_tag():
        """
        Automatically generate tag content from blog posts
        从md文件中完全自动获取标签信息,无硬编码
        """

        try:
            all_tags = set()
            tag_info = defaultdict(list)
            blog_posts_dir = Path("docs/blog/posts")
            if not blog_posts_dir.exists():
                return "## ❌ 错误\n\n无法找到博客文章目录\n\n"
            # 遍历所有文章文件
            for md_file in blog_posts_dir.rglob("*.md"):
                try:
                    with open(md_file, 'r', encoding='utf-8-sig') as f:
                        content = f.read()

                    # 提取front matter
                    if content.startswith('---'):
                        parts = content.split('---', 2)
                        if len(parts) >= 3:
                            front_matter_text = parts[1].strip()
                            try:        
                                front_matter = yaml.safe_load(front_matter_text)
                                if not front_matter:
                                    continue

                                tags = front_matter.get('tags', [])

                                # 处理标签
                                if isinstance(tags, list) and tags:
                                    for tag in tags:
                                        tag_str = str(tag)
                                        all_tags.add(tag_str)
                                        tag_info[tag_str].append(md_file.stem)

                            except yaml.YAMLError:
                                continue

                except Exception:
                    continue

            if not tag_info:
                return f"## 📝 调试信息\n\n找到 {len(all_tags)} 个标签,{len(tag_info)} 个有文章的标签\n\n所有标签: {list(all_tags)}\n\n"

            # 生成标签页面内容
            result = []
            result.append(f"## 🔍 找到{len(all_tags)}个标签")
            result.append("")

            for tag in sorted(all_tags):
                count = len(tag_info[tag])
                result.append(f"### [{tag}](tag/{tag}.html)") # 修改为tag/{tag}.html
                result.append(f"- **文章数量**: {count}篇")
                result.append(f"- **最新文章**: {', '.join(tag_info[tag][:3])}")
                result.append("")   
            return '\n'.join(result)

        except Exception as e:
            return f"## ❌ 生成错误\n\n{str(e)}\n\n"

    @env.macro
    def auto_home_category():
        """
        Automatically generate category content from blog posts
        从md文件中完全自动获取分类信息,无硬编码
        """
        try:
            blog_posts_dir = Path("docs/blog/posts")
            if not blog_posts_dir.exists():
                return "## ❌ 错误\n\n无法找到博客文章目录\n\n"

            # 收集分类信息
            category_info = defaultdict(list)
            all_categories = set()
            nBlogCount = 0
            # 遍历所有文章文件
            for md_file in blog_posts_dir.rglob("*.md"):
                try:
                    with open(md_file, 'r', encoding='utf-8-sig') as f:
                        content = f.read()

                    nBlogCount += 1
                    # 提取front matter
                    if content.startswith('---'):
                        parts = content.split('---', 2)
                        if len(parts) >= 3:
                            front_matter_text = parts[1].strip()
                            try:
                                front_matter = yaml.safe_load(front_matter_text)
                                if not front_matter:
                                    continue

                                title = front_matter.get('title', md_file.stem)
                                categories = front_matter.get('categories', [])
                                date_val = front_matter.get('date', '2025-09-18')

                                # 处理分类
                                if isinstance(categories, list) and categories:
                                    for category in categories:
                                        cat_str = str(category).strip().lower()  # 归一化
                                        all_categories.add(cat_str)
                                        category_info[cat_str].append((title, date_val, md_file.stem))

                            except yaml.YAMLError:
                                continue

                except Exception:
                    continue

            # if not category_info:
            #     return f"## 📝 调试信息\n\n找到 {len(all_categories)} 个分类,{len(category_info)} 个有文章的分类\n\n所有分类: {list(all_categories)}\n\n"

            # 生成分类页面内容  
            result = []
            result.append(f"## 🔍 找到{len(all_categories)}个分类")
            result.append("")

            # 编程语言部分
            result.append("## 🖥️ 编程语言")
            result.append("")

            for category in sorted(all_categories):
                cat_lower = category.lower()
                if 'windows' in cat_lower or 'window' in cat_lower:
                    count = len(category_info[category])

                    if cat_lower == 'window' or 'windows' in cat_lower:
                        display_name = 'windows程序'
                        icon = '🔨'
                    else:
                        display_name = category
                        icon = '💻'

                    # 添加分类标题
                    result.append(f"### {icon} [{display_name}](blog/category/{cat_lower}.html)")
                    result.append(f"- **文章数量**: {count}篇")

                    latest = []
                    print(f"category={category}, items={category_info[category]}")
                    for t, d, stem in sorted(category_info[category], key=lambda x: x[1], reverse=True)[:]:
                        url = f"blog/{generate_url_slug(t)}.html"  # 使用title转小写再编码
                        dt = None
                        try:
                            if isinstance(d, datetime):
                                dt = d
                            elif isinstance(d, date):
                                dt = datetime.combine(d, datetime.min.time())
                            elif isinstance(d, str) and len(d) == 10:
                                dt = datetime.strptime(d, "%Y-%m-%d")
                        except Exception as ex:
                            pass
                        if dt:
                            url = f"blog/{dt.year}/{dt.month:02d}/{dt.day:02d}/{generate_url_slug(t)}.html"
                        latest.append(f"<li>[{t}]({url})</li>")
                    result.append(f"- **最新文章**: {' '.join(latest)}")
                    result.append("")
                else:
                    icon = '🛠️'
                    result.append(f"### {icon} [{cat_lower}](blog/category/{cat_lower}.html)")
                    result.append(f"- **文章数量**: {len(category_info[category])}篇")
                    # 修正这里,生成带链接的最新文章
                    latest = []
                    print(f"category={category}, items={category_info[category]}")
                    for t, d, stem in sorted(category_info[category], key=lambda x: x[1], reverse=True)[:]:
                        url = f"blog/{generate_url_slug(t)}.html"  # 使用title转小写再编码
                        dt = None
                        try:
                            if isinstance(d, datetime):
                                dt = d
                            elif isinstance(d, date):
                                dt = datetime.combine(d, datetime.min.time())
                            elif isinstance(d, str) and len(d) == 10:
                                dt = datetime.strptime(d, "%Y-%m-%d")
                        except Exception as ex:
                            pass
                        if dt:
                            url = f"blog/{dt.year}/{dt.month:02d}/{dt.day:02d}/{generate_url_slug(t)}.html"
                        latest.append(f"<li>[{t}]({url})</li>")
                    result.append(f"- **最新文章**: {' '.join(latest)}")
                    result.append("")

            # 开发框架和工具部分
            result.append("## 🏗️ 开发框架与工具")
            result.append("")

            for category in sorted(all_categories):
                cat_lower = category.lower()
                if 'autocad' in cat_lower or 'cad' in cat_lower or '工具' in cat_lower:
                    count = len(category_info[category])

                    # 添加分类标题
                    if 'autocad' in cat_lower or 'cad' in cat_lower:
                        icon = '🏗️'
                        display_name = 'AutoCAD/CAD开发'
                    else:
                        icon = '✏️'
                        display_name = category

                    result.append(f"### {icon} [{display_name}](blog/category/{cat_lower}.html)")
                    result.append(f"- **文章数量**: {count}篇")

                    latest = []
                    print(f"category={category}, items={category_info[category]}")
                    for t, d, stem in sorted(category_info[category], key=lambda x: x[1], reverse=True)[:]:
                        url = f"blog/{generate_url_slug(t)}.html"  # 使用title转小写再编码
                        dt = None
                        try:
                            if isinstance(d, datetime):
                                dt = d
                            elif isinstance(d, date):
                                dt = datetime.combine(d, datetime.min.time())
                            elif isinstance(d, str) and len(d) == 10:
                                dt = datetime.strptime(d, "%Y-%m-%d")
                        except Exception as ex:
                            pass
                        if dt:
                            url = f"blog/{dt.year}/{dt.month:02d}/{dt.day:02d}/{generate_url_slug(t)}.html"
                        latest.append(f"<li>[{t}]({url})</li>")
                    result.append(f"- **最新文章**: {''.join(latest)}")
                    result.append("")

            # 统计信息
            result.append("---")
            result.append("")
            result.append("## 📊 分类统计")
            result.append("")
            total_articles = sum(len(articles) for articles in category_info.values())
            result.append(f"- **总分类数**: {len(all_categories)}个")
            result.append(f"- **总文章数**:  {nBlogCount}篇")

            return '\n'.join(result)

        except Exception as e:
            return f"## ❌ 生成错误\n\n{str(e)}\n\n" 

发布到github中

使用GitHub Actions

使用GitHub Actions可以自动部署网站。在库的根目录下新建一个GitHub Actions workflow,比如:.github/workflows/ci.yml,并粘贴入以下内容:

Material for MkDocs

Text Only
name: ci
on:
  push:
    branches:
      - master
      - main
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
        with:
          python-version: 3.x
      - run: pip install mkdocs-material
      - run: mkdocs gh-deploy --force

此时,当一个新的提交推送到mastermain时,我们的静态网站的内容将自动生成并完成部署。可以尝试推送一个提交来查看GitHub Actions的工作状况。

添加相关权限:

image-20250919104024286

image-20250919104720037

安装docsify

  • 先安装nodejs

  • 安装docsify工具

Bash
npm i docsify-cli -g
  • 初始化目录
Text Only
1
2
3
docsify init <path> [--local false] [--theme vue] [--plugins false]

# docsify i <path> [-l false] [-t vue] [--plugins false]

<path>默认为当前目录。使用./docs(或docs)之类的相对路径。

  • local选项:

    • 速记:-l
    • 类型:布尔值
    • 默认:false
    • 说明:将文件复制docsify到文档路径,默认值是false用来使用cdn.jsdelivr.net<内容分发网络(CDN)>。要显式设置此选项以使用--no-local
  • theme选项:

    • 速记:-t
    • 类型:字符串
    • 默认:vue
    • 说明:选择一个主题,默认为vue,其他选项为bubledarkpure
  • plugins选项:

    • 速记:-p
    • 类型:布尔值
    • 默认:false
    • 描述:提供插件列表作为<script>标签插入到index.html.
Bash
docsify init ./docs
  • serve命令

localhost使用 livereload运行服务器。

Bash
1
2
3
docsify serve <path> [--open false] [--port 3000]

# docsify s <path> [-o false] [-p 3000]
  • open选项:

    • 速记:-o
    • 类型:布尔值
    • 默认:false
    • 说明:在默认浏览器中打开文档,默认为false. 要显式设置此选项以false使用--no-open.
  • port选项:

    • 速记:-p
    • 类型:数字
    • 默认:3000
    • 说明:选择监听端口,默认为3000.
  • Docsify 的生成器。

Bash
1
2
3
docsify generate <path> [--sidebar _sidebar.md]

# docsify g <path> [-s _sidebar.md]
  • sidebar选项:
    • 速记:-s
    • 类型:字符串
    • 默认:_sidebar.md
    • 说明:生成侧边栏文件,默认为_sidebar.md.

插件设置

设置配色

HTML
<script>
    .markdown-section strong {
          color: rgb(239, 112, 96);
        }

    .markdown-section code {
      border-radius: 2px;
      font-family: "Helvetica Neue",Helvetica,"Hiragino Sans GB","Microsoft YaHei",Arial,sans-serif;
      font-size: 16px !important;
      margin: 0 2px;
      padding: 3px 5px;
      white-space: nowrap;
      /*border: 1px solid #282c34;*/
      /*color: rgb(184, 101, 208);*/
    }
    .markdown-section > div > img, .markdown-section pre {
      box-shadow: 2px 2px 20px 6px rgb(255, 255, 255) !important;
    }

    .markdown-section a:not(:hover) {
      text-decoration: none;
    }
    #main h2 span{ color:#18b566 !important; }
    #main h3 span{ color:#089acc !important; }
    #main h4 span{ color:#FF9700 !important; }
    p code{
      background-color: rgb(255, 255, 255) !important;
    }

    /*添加代码块复制按钮样式*/
    .docsify-copy-code-button {
      background: #00a1d6 !important;
      color: #FFFFFF !important;
      font-size: 13px !important;
    }

    ::after{
      color: #9da2fd !important;
      font-size: 13px !important;
    }
    .markdown-section>p {
      font-size: 16px !important;
    }


    /*代码块头部图标 start*/
    .markdown-section pre:before {
      content: '';
      display: block;
      background: url(_media/codeHeader.png);
      height: 30px;
      background-size: 40px;
      background-repeat: no-repeat;
      background-color: #1C1C1C;
      background-position: 10px 10px;
    }
    /*代码块头部图标 end*/

    .markdown-section pre>code {
      color: #c0c3c1;
      font-family: 'Inconsolata', consolas,"PingFang SC", "Microsoft YaHei", monospace;
      background-color: #212121;
      font-size: 15px;
      white-space: pre;
      line-height: 1.5;
      -moz-tab-size: 4;
      -o-tab-size: 4;
      tab-size: 4;
    }

    @media (max-width:600px) {
      pre {
        padding-left: 3px !important;
        padding-right: 3px !important;
        margin-left: -20px !important;
        margin-right: -20px !important;
        box-shadow: 0px 0px 20px 0px #f7f7f7 !important;
      }

      /*代码块复制按钮默认隐藏*/
      .docsify-copy-code-button {
        display: none;
      }

      .advertisement{
        display: none;
      }

    }

    .token.keyword{
      color: #f92672 !important;
    }

    .token.comment{
      color: #75715e !important;
    }

    .token.tag{
      color: #a589ad !important;
    }

    .token.attr-name{
      color: #de916c !important;
    }

    .token.attr-value{
      color: #4faee2 !important;
    }

    .token.macro.property{
      color: #4faee2 !important;
    }

    .token.function{
      color: #66D9EF !important;
    }
    .token.string{
      color: #e6db74 !important;
    }
    .token.punctuation{
      color: #c0c3c1 !important;
    }

    .token.number{
      color: #ae81ff  !important;
    }
    .token.operator{
      color: #f92672 !important;
    }
    .token.builtin{
      color: #66D9EF !important;
    }
    .token.decorator.annotation.punctuation
    {
      color: #a6e22e !important;
    }

    .token.class-name{
      color: #a6e22e !important;
    }

    .token.namespace{
      color: #f92672 !important;
    }

    .token.property{
      color: #f92672 !important;
    }

    .token.parameter{
      color: #f92672 !important;
    }

    .token.variable{
      color: #f92672 !important;
    }

    .token.namespace{
      color: #ededed !important;
    }
</script>
HTML
<script src="assets/js/docsify-copy-code.min.js"></script>
<script src="assets/js/docsify-tabs.min.js"></script>
<script src="assets/js/docsify-themeable.min.js"></script>
<script src="assets/js/prism-line-numbers.min.js"></script>
<script src="assets/js/docsify-sidebar-collapse.min.js"></script>
<script src="assets/js/search.js"></script>
<script src="assets/js/docsify.min.js"></script>
<script src="assets/js/emoji.min.js"></script>
<script src="assets/js/zoom-image.min.js"></script>
<script src="assets/js/prism-autoloader.min.js"></script>
<script src="assets/js/prism-autoloader.js"></script>
<script src="assets/js/prism-javascript.js"></script>
<script src="assets/js/prism-php.js"></script>
<script src="assets/js/prism-bash.js"></script>
<script src="assets/js/prism-c.js"></script>
<script src="assets/js/prism-cpp.js"></script>
<script src="assets/js/prism-python.js"></script>
<script src="assets/js/prism-go.js"></script>
<script src="assets/js/prism-java.js"></script>
<script src="assets/js/prism-sql.js"></script>
<script src="assets/js/prism-markup.js"></script>
<script src="assets/js/prism-yaml.js"></script>
<script src="assets/js/prism-json.js"></script>
<script src="assets/js/prism-docker.js"></script>
<script src="assets/js/prism-git.js"></script>
<script src="assets/js/prism-dart.js"></script>
<script src="assets/js/prism-ini.js"></script>
<script src="assets/js/prism-nginx.js"></script>
<script src="assets/js/prism-css.js"></script>
<script src="assets/js/prism-http.js"></script>
<script src="assets/js/prism-latex.js"></script>
<script src="assets/js/prism-markdown.js"></script>
<script src="assets/js/prism-matlab.js"></script>
<script src="assets/js/prism-powershell.js"></script>
<script src="assets/js/prism-c++.js"></script>
<script src="assets/js/prism-csharp.js"></script>

出现两个搜索框

  • 隐藏第一个input框就好
XML
1
2
3
4
5
<style>
.sidebar .search:nth-child(1){
  display: none;
}
</style>

插件的问题直接去

https://docsify.js.org/#/awesome?id=plugins 查询

美化提示样式

Docsify-alerts

[!NOTE] An alert of type 'note' using global style 'callout'.

[!NOTE|style:flat] An alert of type 'note' using alert specific style 'flat' which overrides global style 'callout'.

As you can see in the second snippet, output can be configured on alert level also. Supported options are listed in following table:

Key Allowed value
style One of follwowing values: callout, flat
label Any text
icon A valid Font Awesome icon, e.g. 'fas fa-comment'
className A name of a CSS class which specifies the look and feel
labelVisibility One of follwowing values: visible (default), hidden
iconVisibility One of follwowing values: visible (default), hidden

[!TIP|style:flat|label:My own heading|iconVisibility:hidden] An alert of type 'tip' using alert specific style 'flat' which overrides global style 'callout'. In addition, this alert uses an own heading and hides specific icon.

As mentioned above you can provide your own alert types. Therefore, you have to provide the type configuration via index.html. Following example shows an additional type COMMENT.

HTML
<script>
  window.$docsify = {
    'flexible-alerts': {
      comment: {
        label: 'Comment',

        // localization
        label: {
          '/en-GB/': 'Comment',
          '/': 'Kommentar'
        },

        // Assuming that we use Font Awesome
        icon: 'fas fa-comment',
        className: 'note'
      }
    }
  };
</script>

[!COMMENT] An alert of type 'comment' using style 'callout' with default settings.

avatar

avatar

docsify-plantuml

docsify-plantuml

HTML
1
2
3
4
5
6
7
8
9
<script>
window.$docsify = {
  plantuml: {
    skin: 'default',
  },
}
</script>

<script src="//unpkg.com/docsify-plantuml/dist/docsify-plantuml.min.js"></script>
  • 怎么用
Text Only
1
2
3
4
5
6
7
8
9
@startuml
autonumber

Alice -> Bob: Authentication Request
Bob --> Alice: Authentication Response

Alice -> Bob: Another authentication Request
Alice <-- Bob: another authentication Response
@enduml
Text Only
1
2
3
4
@startuml
Alice -> Bob: Authentication Request [[$./other-file docs]]
Bob --> Alice: Authentication Response [[$../other-file docs]]
@enduml

徽章查询服务

Bash
1
2
3
[徽章](badgen.net)

[自定义徽章](img.shields.io)

激活IDM

第一步:https://www.internetdownloadmanager.com/官网下载IDM

第二步:安装idm并运行idm,在Windows搜索栏中输入:“powershell”右键以管理员运行。

第三步:在power shell窗口中,右键粘贴代码:

irm https://massgrave.dev/ias | iex

回车键运行,按数字键2,再按数字键9 等待片刻,关闭所有窗口再次启动你的IDM下载器,就完成激活了。

image-20240923114031157 Github