跳转至

部署 Zensical

obsidian 笔记,通过 Zensical 私有化部署

ZensicalMaterial for MkDocs 创建者开发的现代静态站点生成器,解决了很多 MKDocs 的痛点。使用对比后最明显的感觉就是更快的生成速度、全新的搜索引擎。

1 使用 Zensical 创建笔记

pip install zensical
set PYTHONUTF8=1
python -m zensical new .

测试 && 打包

python -m zensical serve
python -m zensical build --clean

文章顺序

静态站点文章顺序需要手动配置,格式如下。

[project]
nav = [
  {"Home" = "index.md"},
  {"About" = [
     "about/index.md",
     "about/vision.md",
     "about/team.md"
  ]}
]

自定义样式

追加 css 或者 js

# zensical.toml
[project]
extra_css = ["css/extra.css"]
.md-search {
    transform: translateX(160px);
}

.md-header__option[data-md-component="palette"] {
    transform: translateX(160px);
}

自定义主题

custom_dir 目录中的任何文件都会覆盖 Zensical 提供的同名模板和局部模板。

# zensical.toml
[project.theme]
custom_dir = "overrides"

修改模板后必须清空缓存后 zensical serve 才能看到效果。 清空缓存可以用 zensical build --clean,或者手动删除 ./.cache/ 目录。

自定义模板

可以某些文章使用单独的格式,template 目录是相对于 custom_dir 的。

# zensical.toml
[project.theme]
custom_dir = "overrides"
---
template: "my_template.html"
---

# Page title

...

2 辅助脚本

完整配置

# zensical.toml
[project]
site_name = "Notes"
site_description = "Beyond欣 's Notes"
site_author = "Beyond欣"
site_url = "https://notes.beyondxin.top/"
copyright = """
<a href="https://beian.miit.gov.cn/" target="_blank">京ICP备2023033867号</a>
"""
use_directory_urls = false
extra_css = ["css/extra.css"]

nav = []


[project.theme]

custom_dir = "overrides"
favicon = "assets/images/favicon.ico"
language = "zh"

features = [
    "announce.dismiss",

    # "content.action.edit",
    # "content.action.view",
    "content.code.annotate",
    "content.code.copy",
    "content.code.select",
    "content.footnote.tooltips",
    "content.tabs.link",
    "content.tooltips",

    "navigation.tabs",
    "navigation.tabs.sticky",
    "navigation.expand",
    "navigation.footer",
    "navigation.indexes",
    "navigation.instant",
    "navigation.instant.prefetch",
    "navigation.instant.progress",
    "navigation.prune",
    "navigation.top",
    "navigation.tracking",
    # "navigation.sections",
    # "navigation.path",

    # "header.autohide",

    "search.highlight",

    "toc.follow",
    # "toc.integrate",
]


[[project.theme.palette]]
scheme = "default"
toggle.icon = "lucide/sun"
toggle.name = "Switch to dark mode"

[[project.theme.palette]]
scheme = "slate"
toggle.icon = "lucide/moon"
toggle.name = "Switch to light mode"


[project.extra]
generator = false
# homepage = "https://www.beyondxin.top"

[[project.extra.social]]
icon = "fontawesome/brands/edge"
link = "https://www.beyondxin.top/"
[[project.extra.social]]
icon = "fontawesome/brands/bilibili"
link = "https://space.bilibili.com/285016963"

手动排序

解析 obsidian 中 MAKE.md 插件排序

import os
import sqlite3
import re

db_name = '.space/context.mdb'
base_directory = './docs/'
ignore_directory = ["模板", "Blog", "读书笔记"]
toml_path = './zensical.toml'


def get_context_bt_db(path):
    db_path = path + db_name
    if not os.path.exists(db_path):
        return []
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()
    cursor.execute("SELECT File FROM files")
    file_list = [row[0] for row in cursor.fetchall()]
    conn.close()
    return file_list


def get_page_tree(relative_path=""):
    page_tree = []
    order_tree = get_context_bt_db(base_directory + relative_path)
    for key in order_tree:
        if key in ignore_directory:
            break
        if key.endswith('.md'):
            page_tree.append(key)
        else:
            subtree = get_page_tree(key+"/")
            if subtree:
                page_tree.append({key.split('/')[-1]: subtree})
    return page_tree


def format_nav_tree(tree, indent=1):
    """将导航树格式化为 TOML 字符串"""
    lines = []
    indent_str = "    " * indent

    for item in tree:
        if isinstance(item, str):
            lines.append(f'{indent_str}"{item}",')
        elif isinstance(item, dict):
            for key, value in item.items():
                lines.append(f'{indent_str}{{ "{key}" = [')
                lines.extend(format_nav_tree(value, indent + 1))
                lines.append(f'{indent_str}] }},')

    return lines


# 生成导航树
nav_tree = get_page_tree()


# 格式化为 TOML 字符串
nav_lines = ["nav = ["]
nav_lines.extend(format_nav_tree(nav_tree))
nav_lines.append("]")
new_nav_content = "\n".join(nav_lines)

# 读取原文件
with open(toml_path, 'r', encoding='utf-8') as f:
    content = f.read()

# 使用正则表达式替换 nav 部分
# 匹配从 nav = [ 到对应的 ] 结束
pattern = r'nav = \[.*?\n\]'
new_content = re.sub(pattern, new_nav_content, content, flags=re.DOTALL)

# 写回文件
with open(toml_path, 'w', encoding='utf-8') as f:
    f.write(new_content)

print(f"导航已更新到 {toml_path}")

自动提交

@echo off
python script/rebuild_nav.py
git add .
for /f "tokens=1-4 delims=/ " %%a in ('date /t') do (set mydate=%%a-%%b-%%c %%d)
for /f "tokens=1-2 delims=: " %%a in ('time /t') do (set mytime=%%a:%%b)
git commit -m "%mydate% %mytime%"
git push
pause

部署

  • Github Pages zensical new . 生成的空白项目默认带 Github Actions 配置,会自动部署到 Github Pages
  • 服务器 脚本定时更新
#!/bin/sh
cd /home/www/MyNote
git reset --hard HEAD
git pull
set PYTHONUTF8=1
/www/server/pyporject_evn/versions/3.12.11/bin/python -m zensical build --clean