From 4206d1fe9658e4f240e426827d65611ea33ab0ed Mon Sep 17 00:00:00 2001
From: krau <71133316+krau@users.noreply.github.com>
Date: Mon, 16 Jun 2025 15:58:03 +0800
Subject: [PATCH] docs: refactor
---
.github/workflows/docs.yml | 29 +++-
docs/.gitignore | 1 +
docs/archetypes/default.md | 5 +
docs/{sabot => confs/wrt_bin} | 0
docs/{saveanybot => confs/wrt_init} | 0
docs/content/zh/_index.md | 31 ++++
docs/content/zh/contribute/_index.md | 14 ++
docs/content/zh/deployment/_index.md | 4 +
.../zh/deployment/configuration/_index.md | 141 +++++++++++++++++
.../zh/deployment/configuration/storages.md | 65 ++++++++
docs/content/zh/deployment/installation.md | 145 ++++++++++++++++++
.../faq.md => content/zh/help/_index.md} | 12 +-
.../zh/usage/_index.md} | 35 ++++-
docs/docs/contribute.md | 11 --
docs/docs/deploy.md | 94 ------------
docs/docs/help.md | 38 -----
docs/docs/index.md | 7 -
docs/go.mod | 5 +
docs/go.sum | 2 +
docs/hugo.toml | 44 ++++++
docs/layouts/shortcodes/codeblock.html | 3 +
docs/mkdocs.yml | 35 -----
...s_b807c86e8030af4cdc30edccea379f5f.content | 1 +
...scss_b807c86e8030af4cdc30edccea379f5f.json | 1 +
docs/{logo.jpg => static/logo.png} | Bin
25 files changed, 517 insertions(+), 206 deletions(-)
create mode 100644 docs/.gitignore
create mode 100644 docs/archetypes/default.md
rename docs/{sabot => confs/wrt_bin} (100%)
rename docs/{saveanybot => confs/wrt_init} (100%)
create mode 100644 docs/content/zh/_index.md
create mode 100644 docs/content/zh/contribute/_index.md
create mode 100644 docs/content/zh/deployment/_index.md
create mode 100644 docs/content/zh/deployment/configuration/_index.md
create mode 100644 docs/content/zh/deployment/configuration/storages.md
create mode 100644 docs/content/zh/deployment/installation.md
rename docs/{docs/faq.md => content/zh/help/_index.md} (62%)
rename docs/{docs/experimental.md => content/zh/usage/_index.md} (53%)
delete mode 100644 docs/docs/contribute.md
delete mode 100644 docs/docs/deploy.md
delete mode 100644 docs/docs/help.md
delete mode 100644 docs/docs/index.md
create mode 100644 docs/go.mod
create mode 100644 docs/go.sum
create mode 100644 docs/hugo.toml
create mode 100644 docs/layouts/shortcodes/codeblock.html
delete mode 100644 docs/mkdocs.yml
create mode 100644 docs/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.content
create mode 100644 docs/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.json
rename docs/{logo.jpg => static/logo.png} (100%)
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 1d072b7..dbbb3d0 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -6,17 +6,30 @@ on:
paths:
- "docs/**"
workflow_dispatch:
+
jobs:
deploy:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
+ concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-python@v5
with:
- python-version: "3.11"
- - uses: actions/cache@v4
+ submodules: true # Fetch Hugo themes (true OR recursive)
+ fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
+
+ - name: Setup Hugo
+ uses: peaceiris/actions-hugo@v3
with:
- key: ${{ github.ref }}
- path: .cache
- - run: pip install mkdocs-material
- - run: cd docs && mkdocs gh-deploy --force
+ hugo-version: '0.147.8'
+ extended: true
+
+ - name: Build
+ run: hugo --minify
+
+ - name: Deploy
+ uses: peaceiris/actions-gh-pages@v3
+ if: github.ref == 'refs/heads/main'
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ publish_dir: ./public
\ No newline at end of file
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 0000000..d298be1
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1 @@
+public/
\ No newline at end of file
diff --git a/docs/archetypes/default.md b/docs/archetypes/default.md
new file mode 100644
index 0000000..25b6752
--- /dev/null
+++ b/docs/archetypes/default.md
@@ -0,0 +1,5 @@
++++
+date = '{{ .Date }}'
+draft = true
+title = '{{ replace .File.ContentBaseName "-" " " | title }}'
++++
diff --git a/docs/sabot b/docs/confs/wrt_bin
similarity index 100%
rename from docs/sabot
rename to docs/confs/wrt_bin
diff --git a/docs/saveanybot b/docs/confs/wrt_init
similarity index 100%
rename from docs/saveanybot
rename to docs/confs/wrt_init
diff --git a/docs/content/zh/_index.md b/docs/content/zh/_index.md
new file mode 100644
index 0000000..22aceba
--- /dev/null
+++ b/docs/content/zh/_index.md
@@ -0,0 +1,31 @@
+---
+title: 介绍
+---
+
+# Save Any Bot
+
+
+
+
+
+
+把 Telegram 上的文件转存到多种存储端.
+
+## 特性
+
+- 支持文档/视频/图片/贴纸... 甚至还有 Telegraph
+- 破解禁止保存的文件
+- 批量下载
+- 流式传输
+- 多用户
+- 基于存储规则的自动整理
+- 支持多种存储端:
+ - Alist
+ - Minio (S3 兼容)
+ - WebDAV
+ - Telegram (重传回指定聊天)
+ - 本地磁盘
+
+## [贡献者](https://github.com/krau/SaveAny-Bot/graphs/contributors)
+
+
diff --git a/docs/content/zh/contribute/_index.md b/docs/content/zh/contribute/_index.md
new file mode 100644
index 0000000..a96de08
--- /dev/null
+++ b/docs/content/zh/contribute/_index.md
@@ -0,0 +1,14 @@
+---
+title: "参与开发"
+weight: 20
+---
+
+# 参与开发
+
+## 贡献新存储端
+
+1. Fork 本项目, 克隆到本地
+2. 在 `pkg/enums/storage/storages.go` 中添加新的存储端类型, 并运行代码生成
+3. 在 `config/storage` 目录下定义存储端配置, 并添加到 `config/storage/factory.go` 中
+4. 在 `storage` 目录下新建一个包, 编写存储端实现, 然后在 `storage/storage.go` 中导入并添加它
+5. 更新文档, 添加配置说明
\ No newline at end of file
diff --git a/docs/content/zh/deployment/_index.md b/docs/content/zh/deployment/_index.md
new file mode 100644
index 0000000..225ef96
--- /dev/null
+++ b/docs/content/zh/deployment/_index.md
@@ -0,0 +1,4 @@
+---
+title: "部署指南"
+weight: 5
+---
diff --git a/docs/content/zh/deployment/configuration/_index.md b/docs/content/zh/deployment/configuration/_index.md
new file mode 100644
index 0000000..aa66b8f
--- /dev/null
+++ b/docs/content/zh/deployment/configuration/_index.md
@@ -0,0 +1,141 @@
+---
+title: "配置说明"
+---
+
+# 配置说明
+
+SaveAnyBot 的配置文件使用 toml 格式, 你可以在 [TOML 官方网站](https://toml.io/) 上了解更多关于 toml 的语法.
+
+SaveAnyBot 需要读取工作目录下的 `config.toml` 文件作为配置文件, 若缺少该文件则会创建默认文件, 并尝试从环境变量中加载配置.
+
+以下是一个最简的配置文件示例:
+
+```toml
+[telegram]
+token = "1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+[[users]]
+# telegram user id
+id = 777000
+blacklist = true
+
+[[storages]]
+name = "本机存储"
+type = "local"
+enable = true
+base_path = "./downloads"
+```
+
+## 详细配置
+
+### 全局配置
+
+- `stream`: 是否启用 Stream 模式, 默认为 `false`. 启用后 Bot 将直接将文件流式传输到存储端(若存储端支持), 不需要下载到本地
+{{< hint warning >}}
+Stream 模式对于磁盘空间有限的部署环境十分有用, 但也有一些弊端:
+
+
/etc/systemd/system/saveany-bot.service 并写入以下内容:
+
+{{< codeblock >}}
+[Unit]
+Description=SaveAnyBot
+After=systemd-user-sessions.service
+
+[Service]
+Type=simple
+WorkingDirectory=/yourpath/
+ExecStart=/yourpath/saveany-bot
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
+{{< /codeblock >}}
+
+设为开机启动并启动服务:
+
+{{< codeblock >}}
+systemctl enable --now saveany-bot
+{{< /codeblock >}}
+
+{{< /tab >}}
+
+{{< tab "procd (OpenWrt)" >}}
+
+/etc/init.d/saveanybot ,参考 wrt_init 并自行修改:
+
+{{< codeblock >}}
+#!/bin/sh /etc/rc.common
+
+#This is the OpenWRT init.d script for SaveAnyBot
+
+START=99
+STOP=10
+description="SaveAnyBot"
+
+WORKING_DIR="/mnt/mmc1-1/SaveAnyBot"
+EXEC_PATH="$WORKING_DIR/saveany-bot"
+start() {
+ echo "Starting SaveAnyBot..."
+ cd $WORKING_DIR
+ $EXEC_PATH &
+}
+stop() {
+ echo "Stopping SaveAnyBot..."
+ killall saveany-bot
+}
+reload() {
+ stop
+ start
+}
+
+{{< /codeblock >}}
+
+赋予权限:
+
+{{< codeblock >}}
+chmod +x /etc/init.d/saveanybot
+{{< /codeblock >}}
+
+然后将文件复制到 /etc/rc.d 并重命名为 S99saveanybot, 同样赋予权限:
+
+{{< codeblock >}}
+chmod +x /etc/rc.d/S99saveanybot
+{{< /codeblock >}}
+
+/usr/bin/sabot ,参考 wrt_bin 并自行修改,注意此处文件编码仅支持 ANSI 936 .
+
+随后赋予权限:
+
+{{< codeblock >}}
+chmod +x /usr/bin/sabot
+{{< /codeblock >}}
+
+使用: sudo sabot start|stop|restart|status|enable|disable
+
+{{< /tab >}}
+{{< /tabs >}}
+
+
+## 使用 Docker 部署
+
+### Docker Compose
+
+下载 [docker-compose.yml](https://github.com/krau/SaveAny-Bot/blob/main/docker-compose.yml) 文件, 在同目录下新建 `config.toml` 文件, 参考 [config.example.toml](https://github.com/krau/SaveAny-Bot/blob/main/config.example.toml) 编辑配置文件.
+
+启动:
+
+```bash
+docker compose up -d
+```
+
+### Docker
+
+```shell
+docker run -d --name saveany-bot \
+ -v /path/to/config.toml:/app/config.toml \
+ -v /path/to/downloads:/app/downloads \
+ ghcr.io/krau/saveany-bot:latest
+```
+
+## 更新
+
+使用 `upgrade` 或 `up` 升级到最新版
+
+```bash
+./saveany-bot upgrade
+```
+
+如果是 Docker 部署, 使用以下命令更新:
+
+```bash
+docker pull ghcr.io/krau/saveany-bot:latest
+docker restart saveany-bot
+```
\ No newline at end of file
diff --git a/docs/docs/faq.md b/docs/content/zh/help/_index.md
similarity index 62%
rename from docs/docs/faq.md
rename to docs/content/zh/help/_index.md
index 1ef390e..c1ff08e 100644
--- a/docs/docs/faq.md
+++ b/docs/content/zh/help/_index.md
@@ -1,3 +1,8 @@
+---
+title: "常见问题"
+weight: 15
+---
+
# 常见问题
## 上传 alist 失败也会显示成功
@@ -6,11 +11,8 @@
## Bot 提示下载成功但是 alist 未显示
-alist 缓存了目录结构, 参考文档可以调整缓存时间
-
-https://alist.nn.ci/zh/guide/drivers/common.html#缓存过期
+alist 缓存了目录结构, 参考 文档 可以调整缓存时间
## docker部署配置了代理后仍无法连接 telegram (初始化客户端超时)
-docker 不能直接访问宿主机网络, 如果你不熟悉其用法, 请将容器设为 host 模式:
-
+docker 不能直接访问宿主机网络, 如果你不熟悉其用法, 请将容器设为 host 模式.
\ No newline at end of file
diff --git a/docs/docs/experimental.md b/docs/content/zh/usage/_index.md
similarity index 53%
rename from docs/docs/experimental.md
rename to docs/content/zh/usage/_index.md
index b594512..c1efc1a 100644
--- a/docs/docs/experimental.md
+++ b/docs/content/zh/usage/_index.md
@@ -1,12 +1,35 @@
-# 实验性功能
+---
+title: "使用帮助"
+weight: 10
+---
+
+# 使用帮助
+
+## 转存文件
+
+Bot 接受两种消息: 文件和链接.
+
+支持以下链接:
+
+1. Telegram 消息链接, 例如: `https://t.me/acherkrau/1097`. **即使频道禁止了转发和保存, Bot 依然可以下载其文件.**
+2. Telegra.ph 的文章链接, Bot 将下载其中的所有图片
+
+## 静默模式 (silent)
+
+使用 `/silent` 命令可以开关静默模式.
+
+默认情况下不开启静默模式, Bot 会询问你每个文件的保存位置.
+
+开启静默模式后, Bot 会直接保存文件到默认位置, 无需确认.
+
+在开启静默模式之前, 需要使用 `/storage` 命令设置默认保存位置.
-这里的功能不太稳定, 且未来可能会被删除或修改。
## 存储规则
允许你为 Bot 在上传文件到存储时设置一些重定向规则, 用于自动整理所保存的文件.
-见: https://github.com/krau/SaveAny-Bot/issues/28
+见: #28
目前支持的规则类型:
@@ -39,8 +62,4 @@ FILENAME-REGEX (?i)\.(mp4|mkv|ts|avi|flv)$ MyAlist /视频
### MESSAGE-REGEX
-同上, 根据消息文本内容正则匹配
-
-## 复制并发送媒体消息
-
-将接收到的文件(媒体)消息, 或链接对应的消息原样发送到当前聊天, 点击选择存储按钮中的 "发送到当前聊天" 即可.
+同上, 但是是根据消息本身的文本内容正则匹配
\ No newline at end of file
diff --git a/docs/docs/contribute.md b/docs/docs/contribute.md
deleted file mode 100644
index 7807410..0000000
--- a/docs/docs/contribute.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# 参与开发
-
-## 贡献新存储端
-
-1. Fork 本项目, 克隆到本地
-2. 在 `config/storage` 目录下定义存储端配置, 并添加到 `config/storage/factory.go` 中
-3. 在 `types/types.go` 中添加新的存储端类型
-4. 在 `storage` 目录下新建一个包, 编写存储端实现, 然后在 `storage/storage.go` 中导入并添加它
-5. 更新 `config.example.toml` 文件, 添加新的示例配置
-
-*可能确实有点麻烦了 = =*
\ No newline at end of file
diff --git a/docs/docs/deploy.md b/docs/docs/deploy.md
deleted file mode 100644
index 2272635..0000000
--- a/docs/docs/deploy.md
+++ /dev/null
@@ -1,94 +0,0 @@
-# 部署指南
-
-## 从二进制文件部署
-
-在 [Release](https://github.com/krau/SaveAny-Bot/releases) 页面下载对应平台的二进制文件.
-
-在解压后目录新建 `config.toml` 文件, 参考 [config.example.toml](https://github.com/krau/SaveAny-Bot/blob/main/config.example.toml) 编辑配置文件.
-
-运行:
-
-```bash
-chmod +x saveany-bot
-./saveany-bot
-```
-
-### 添加为 systemd 服务
-
-创建文件 `/etc/systemd/system/saveany-bot.service` 并写入以下内容:
-
-```
-[Unit]
-Description=SaveAnyBot
-After=systemd-user-sessions.service
-
-[Service]
-Type=simple
-WorkingDirectory=/yourpath/
-ExecStart=/yourpath/saveany-bot
-Restart=on-failure
-
-[Install]
-WantedBy=multi-user.target
-```
-
-设为开机启动并启动服务:
-
-```bash
-systemctl enable --now saveany-bot
-```
-
-### 为OpenWrt及衍生系统添加开机自启动服务
-
-创建文件 ` /etc/init.d/saveanybot` ,参考[saveanybot](https://github.com/krau/SaveAny-Bot/blob/main/docs/saveanybot)自行修改.
-
-`chmod +x /etc/init.d/saveanybot`
-
-完成后,将文件复制到 `/etc/rc.d`并重命名为`S99saveanybot`.
-
-`chmod +x /etc/rc.d/S99saveanybot`
-
-### 为OpenWrt及衍生系统添加快捷指令
-
-创建文件` /usr/bin/sabot` ,参考[sabot](https://github.com/krau/SaveAny-Bot/blob/main/docs/sabot)自行配置修改,注意此处文件编码仅支持 ANSI 936 .
-
-`chmod +x /usr/bin/sabot`
-
-之后,终端输入`sabot start|stop|restart|status|enable|disable`即可.
-
-
-## 使用 Docker 部署
-
-### Docker Compose
-
-下载 [docker-compose.yml](https://github.com/krau/SaveAny-Bot/blob/main/docker-compose.yml) 文件, 在同目录下新建 `config.toml` 文件, 参考 [config.example.toml](https://github.com/krau/SaveAny-Bot/blob/main/config.example.toml) 编辑配置文件.
-
-启动:
-
-```bash
-docker compose up -d
-```
-
-### Docker
-
-```shell
-docker run -d --name saveany-bot \
- -v /path/to/config.toml:/app/config.toml \
- -v /path/to/downloads:/app/downloads \
- ghcr.io/krau/saveany-bot:latest
-```
-
-## 更新
-
-使用 `upgrade` 或 `up` 升级到最新版
-
-```bash
-./saveany-bot upgrade
-```
-
-如果是 Docker 部署, 使用以下命令更新:
-
-```bash
-docker pull ghcr.io/krau/saveany-bot:latest
-docker restart saveany-bot
-```
\ No newline at end of file
diff --git a/docs/docs/help.md b/docs/docs/help.md
deleted file mode 100644
index 6561e28..0000000
--- a/docs/docs/help.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# 使用帮助
-
-## 保存文件
-
-Bot 接受两种消息: 文件和链接.
-
-支持以下链接:
-
-1. 公开频道 (具有用户名) 的消息链接, 例如: `https://t.me/acherkrau/1097`. **即使频道禁止了转发和保存, Bot 依然可以下载其文件.**
-2. Telegra.ph 的文章链接, Bot 将下载其中的所有图片
-
-## 静默模式 (silent)
-
-使用 `/silent` 命令可以开关静默模式.
-
-默认情况下不开启静默模式, Bot 会询问你每个文件的保存位置.
-
-开启静默模式后, Bot 会直接保存文件到默认位置, 无需确认.
-
-在开启静默模式之前, 需要使用 `/storage` 命令设置默认保存位置.
-
-## Stream 模式
-
-在配置文件中将 `stream` 设置为 `true` 可以开启 Stream 模式.
-
-未开启时, Bot 处理任务分为两步: 下载和上传. Bot 会将文件暂存到本地, 然后上传到对应存储位置, 最后删除本地文件.
-
-开启后, Bot 将直接将文件流式传输到存储端, 不需要下载到本地.
-
-该功能对于硬盘空间有限的部署环境十分有用, 然而相较于普通模式也具有一些弊端:
-
-- 无法使用多线程从 telegram 下载文件, 速度较慢.
-- 网络不稳定时, 任务失败率高.
-- 无法在中间层对文件进行处理, 例如自动文件类型识别.
-
-**不支持** Stream 模式的存储端:
-
-- alist
\ No newline at end of file
diff --git a/docs/docs/index.md b/docs/docs/index.md
deleted file mode 100644
index 32e6097..0000000
--- a/docs/docs/index.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# SaveAnyBot 文档
-
-SaveAnyBot 是一个可以保存 Telegram 上的文件到云存储的机器人, 就像 PikPak Bot 一样.
-
-不同的是, SaveAnyBot 提供更灵活的存储端选择, 并实现一些更强大的功能.
-
-本项目以 AGPL-3.0 协议开源, 请遵守协议使用.
\ No newline at end of file
diff --git a/docs/go.mod b/docs/go.mod
new file mode 100644
index 0000000..573c70d
--- /dev/null
+++ b/docs/go.mod
@@ -0,0 +1,5 @@
+module github.com/krau/SaveAny-Bot/docs
+
+go 1.24.4
+
+require github.com/alex-shpak/hugo-book v0.0.0-20250530233833-f2c703e15588 // indirect
diff --git a/docs/go.sum b/docs/go.sum
new file mode 100644
index 0000000..408f693
--- /dev/null
+++ b/docs/go.sum
@@ -0,0 +1,2 @@
+github.com/alex-shpak/hugo-book v0.0.0-20250530233833-f2c703e15588 h1:pwxkzpzw/iJSxMBgQLWjYMQubhIemLG3UrNjeWoCkSM=
+github.com/alex-shpak/hugo-book v0.0.0-20250530233833-f2c703e15588/go.mod h1:L4NMyzbn15fpLIpmmtDg9ZFFyTZzw87/lk7M2bMQ7ds=
diff --git a/docs/hugo.toml b/docs/hugo.toml
new file mode 100644
index 0000000..e1587e6
--- /dev/null
+++ b/docs/hugo.toml
@@ -0,0 +1,44 @@
+baseURL = 'https://sabot.unv.app/'
+title = 'Save Any Bot'
+disablePathToLower = true
+enableGitInfo = true
+defaultContentLanguage = 'zh'
+
+[module]
+[[module.imports]]
+path = 'github.com/alex-shpak/hugo-book'
+
+[params]
+BookTheme = 'auto'
+BookToC = true
+BookLogo = 'logo.png'
+BookSection = '*'
+BookRepo = 'https://github.com/krau/saveany-bot'
+BookCommitPath = 'commit'
+BookEditPath = 'edit/main/docs'
+
+# (Optional, default January 2, 2006) Configure the date format used on the pages
+# - In git information
+# - In blog posts
+BookDateFormat = '2006/01/02'
+
+# (Optional, default true) Enables search function with flexsearch,
+# Index is built on fly, therefore it might slowdown your website.
+# Configuration for indexing can be adjusted in i18n folder per language.
+BookSearch = false
+
+[languages]
+
+[languages.zh]
+languageName = "简体中文"
+contentDir = "content/zh"
+weight = 1
+
+[languages.en]
+languageName = "English"
+contentDir = "content/en"
+weight = 2
+
+[markup]
+[markup.goldmark.renderer]
+unsafe = true
diff --git a/docs/layouts/shortcodes/codeblock.html b/docs/layouts/shortcodes/codeblock.html
new file mode 100644
index 0000000..af55353
--- /dev/null
+++ b/docs/layouts/shortcodes/codeblock.html
@@ -0,0 +1,3 @@
+{{- $lang := .Get "lang" | default "text" -}}
+{{- $content := .Inner | strings.TrimSpace | htmlEscape -}}
+{{ $content }}
\ No newline at end of file
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
deleted file mode 100644
index ef4d829..0000000
--- a/docs/mkdocs.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-site_name: SaveAnyBot 官方文档
-site_author: Krau
-site_description: SaveAnyBot 是一个可以保存 Telegram 上的文件到多种云存储的机器人, 本文档将帮助你了解如何部署和使用它.
-repo_name: krau/saveany-bot
-repo_url: https://github.com/krau/saveany-bot
-copyright: CC BY-NC-SA 4.0
-theme:
- name: material
- language: zh
- highlightjs: true
- palette:
- - media: "(prefers-color-scheme)"
- toggle:
- icon: material/brightness-auto
- name: 切换主题
- - media: "(prefers-color-scheme: light)"
- scheme: default
- primary: indigo
- toggle:
- icon: material/brightness-7
- name: 暗色模式
- - media: "(prefers-color-scheme: dark)"
- scheme: slate
- primary: blue grey
- toggle:
- icon: material/brightness-4
- name: 亮色模式
-
-nav:
- - index.md
- - deploy.md
- - help.md
- - experimental.md
- - faq.md
- - contribute.md
diff --git a/docs/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.content b/docs/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.content
new file mode 100644
index 0000000..d0dbb4f
--- /dev/null
+++ b/docs/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.content
@@ -0,0 +1 @@
+@charset "UTF-8";:root{--gray-100:#f8f9fa;--gray-200:#e9ecef;--gray-500:#adb5bd;--color-link:#0055bb;--color-visited-link:#8440f1;--body-background:white;--body-font-color:black;--icon-filter:none;--hint-color-info:#6bf;--hint-color-warning:#fd6;--hint-color-danger:#f66}@media(prefers-color-scheme:dark){:root{--gray-100:#494e54;--gray-200:#5c6165;--gray-500:#999d9f;--color-link:#84b2ff;--color-visited-link:#b88dff;--body-background:#343a40;--body-font-color:#e9ecef;--icon-filter:brightness(0) invert(1);--hint-color-info:#6bf;--hint-color-warning:#fd6;--hint-color-danger:#f66}}/*!normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css*/html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}.flex{display:flex}.flex-auto{flex:auto}.flex-even{flex:1 1}.flex-wrap{flex-wrap:wrap}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.align-center{align-items:center}.mx-auto{margin:0 auto}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.text-small{font-size:.875em}.hidden{display:none}input.toggle{height:0;width:0;overflow:hidden;opacity:0;position:absolute}.clearfix::after{content:"";display:table;clear:both}html{font-size:16px;scroll-behavior:smooth;touch-action:manipulation;scrollbar-gutter:stable}body{min-width:20rem;color:var(--body-font-color);background:var(--body-background);font-weight:400;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box}body *{box-sizing:inherit}h1,h2,h3,h4,h5{font-weight:400}a{text-decoration:none;color:var(--color-link)}img{vertical-align:baseline}:focus{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}aside nav ul{padding:0;margin:0;list-style:none}aside nav ul li{position:relative}aside nav ul a,aside nav ul span{padding:.5em 0;display:block}aside nav ul a:hover{opacity:.5}aside nav ul ul{padding-inline-start:1rem}ul.pagination{display:flex;justify-content:center;list-style-type:none;padding-inline-start:0}ul.pagination .page-item a{padding:1rem}.container{max-width:80rem;margin:0 auto}.book-icon{filter:var(--icon-filter)}a .book-icon{height:1em;width:1em;margin-inline-end:.5em}.book-brand{margin-top:0;margin-bottom:1rem}.book-brand img{height:1.5em;width:1.5em;margin-inline-end:.5rem}.book-menu{flex:0 0 16rem;font-size:.875rem}.book-menu .book-menu-content{width:16rem;padding:1rem;background:var(--body-background);position:fixed;top:0;bottom:0;overflow-x:hidden;overflow-y:auto}.book-menu a,.book-menu label{color:inherit;cursor:pointer;word-wrap:break-word}.book-menu a.active{color:var(--color-link)}.book-menu input.toggle+label+ul{display:none}.book-menu input.toggle:checked+label+ul{display:block}.book-menu input.toggle+label::after{content:"▸";align-self:center}.book-menu input.toggle:checked+label::after{content:"▾";align-self:center}body[dir=rtl] .book-menu input.toggle+label::after{content:"◂"}body[dir=rtl] .book-menu input.toggle:checked+label::after{content:"▾"}.book-section-flat{margin:1rem 0}.book-section-flat>a,.book-section-flat>span,.book-section-flat>label{font-weight:bolder}.book-section-flat>ul{padding-inline-start:0}.book-page{min-width:20rem;flex-grow:1;padding:1rem}.book-post{margin-bottom:4rem}.book-post .book-post-date img{height:1em;width:1em;margin-inline-end:.5em}.book-post .book-post-content>:first-child{margin-top:1rem}.book-header{display:none;margin-bottom:1rem}.book-header label{line-height:0}.book-header h3{overflow:hidden;text-overflow:ellipsis;margin:0 1rem}.book-header img.book-icon{height:1.5em;width:1.5em}.book-search{position:relative;margin:.5rem 0;border-bottom:1px solid transparent}.book-search input{width:100%;padding:.5rem;border:0;border-radius:.25rem;background:var(--gray-100);color:var(--body-font-color)}.book-search input:required+.book-search-spinner{display:block}.book-search .book-search-spinner{position:absolute;top:0;margin:.5rem;margin-inline-start:calc(100% - 1.5rem);width:1rem;height:1rem;border:1px solid transparent;border-top-color:var(--body-font-color);border-radius:50%;animation:spin 1s ease infinite}@keyframes spin{100%{transform:rotate(360deg)}}.book-search ul a{padding-bottom:0}.book-search small{opacity:.5}.book-toc{flex:0 0 16rem;font-size:.75rem}.book-toc .book-toc-content{width:16rem;padding:1rem;position:fixed;top:0;bottom:0;overflow-x:hidden;overflow-y:auto}.book-toc img{height:1em;width:1em}.book-toc nav>ul>li:first-child{margin-top:0}.book-footer{padding-top:1rem;font-size:.875rem}.book-comments{margin-top:1rem}.book-languages{margin-bottom:1rem}.book-languages ul{padding-inline-start:1.5em}.book-menu-content,.book-toc-content,.book-page,.book-header aside,.markdown{transition:.2s ease-in-out;transition-property:transform,margin,opacity,visibility;will-change:transform,margin,opacity}@media screen and (max-width:56rem){#menu-control,#toc-control{display:inline}.book-menu{visibility:hidden;margin-inline-start:-16rem;z-index:1}.book-toc{display:none}.book-header{display:block}#menu-control:focus~main label[for=menu-control]{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}#menu-control:checked~main .book-menu{visibility:initial}#menu-control:checked~main .book-menu .book-menu-content{transform:translateX(16rem);box-shadow:0 0 .5rem rgba(0,0,0,.1)}#menu-control:checked~main .book-page{opacity:.25}#menu-control:checked~main .book-menu-overlay{display:block;position:absolute;top:0;bottom:0;left:0;right:0}#toc-control:focus~main label[for=toc-control]{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}#toc-control:checked~main .book-header aside{display:block}body[dir=rtl] #menu-control:checked~main .book-menu .book-menu-content{transform:translateX(-16rem)}}@media screen and (min-width:80rem){.book-page,.book-menu .book-menu-content,.book-toc .book-toc-content{padding:2rem 1rem}}@font-face{font-family:roboto;font-style:normal;font-weight:400;font-display:fallback;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:roboto;font-style:normal;font-weight:700;font-display:fallback;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:roboto mono;font-style:normal;font-weight:400;font-display:fallback;src:url(https://fonts.gstatic.com/s/robotomono/v23/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vq_ROW4AJi8SJQt.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}body{font-family:roboto,sans-serif}code{font-family:roboto mono,monospace}@media print{.book-menu,.book-footer,.book-toc{display:none}.book-header,.book-header aside{display:block}main{display:block !important}}.markdown{line-height:1.6}.markdown>:first-child{margin-top:0}.markdown h1,.markdown h2,.markdown h3,.markdown h4,.markdown h5,.markdown h6{font-weight:400;line-height:1;margin-top:1.5em;margin-bottom:1rem}.markdown h1 a.anchor,.markdown h2 a.anchor,.markdown h3 a.anchor,.markdown h4 a.anchor,.markdown h5 a.anchor,.markdown h6 a.anchor{opacity:0;font-size:.75em;vertical-align:middle;text-decoration:none}.markdown h1:hover a.anchor,.markdown h1 a.anchor:focus,.markdown h2:hover a.anchor,.markdown h2 a.anchor:focus,.markdown h3:hover a.anchor,.markdown h3 a.anchor:focus,.markdown h4:hover a.anchor,.markdown h4 a.anchor:focus,.markdown h5:hover a.anchor,.markdown h5 a.anchor:focus,.markdown h6:hover a.anchor,.markdown h6 a.anchor:focus{opacity:initial}.markdown h4,.markdown h5,.markdown h6{font-weight:bolder}.markdown h5{font-size:.875em}.markdown h6{font-size:.75em}.markdown b,.markdown optgroup,.markdown strong{font-weight:bolder}.markdown a{text-decoration:none}.markdown a:hover{text-decoration:underline}.markdown a:visited{color:var(--color-visited-link)}.markdown img{max-width:100%;height:auto}.markdown code{direction:ltr;unicode-bidi:embed;padding:0 .25rem;background:var(--gray-200);border-radius:.25rem;font-size:.875em}.markdown pre{direction:ltr;unicode-bidi:embed;padding:1rem;background:var(--gray-100);border-radius:.25rem;overflow-x:auto}.markdown pre code{padding:0;background:0 0}.markdown p{word-wrap:break-word}.markdown blockquote{margin:1rem 0;padding:.5rem 1rem .5rem .75rem;border-inline-start:.25rem solid var(--gray-200);border-radius:.25rem}.markdown blockquote :first-child{margin-top:0}.markdown blockquote :last-child{margin-bottom:0}.markdown table{overflow:auto;display:block;border-spacing:0;border-collapse:collapse;margin-top:1rem;margin-bottom:1rem}.markdown table tr th,.markdown table tr td{padding:.5rem 1rem;border:1px solid var(--gray-200)}.markdown table tr:nth-child(2n){background:var(--gray-100)}.markdown hr{height:1px;border:none;background:var(--gray-200)}.markdown ul,.markdown ol{padding-inline-start:2rem;word-wrap:break-word}.markdown dl dt{font-weight:bolder;margin-top:1rem}.markdown dl dd{margin-inline-start:0;margin-bottom:1rem}.markdown .highlight{direction:ltr;unicode-bidi:embed;border-radius:.25rem;overflow:hidden}.markdown .highlight table tr td pre code>span{display:flex}.markdown .highlight table tr td:nth-child(1) pre{margin:0;padding-inline-end:0}.markdown .highlight table tr td:nth-child(2) pre{margin:0;padding-inline-start:0}.markdown details{padding:1rem;border:1px solid var(--gray-200);border-radius:.25rem}.markdown details summary{line-height:1;padding:1rem;margin:-1rem;cursor:pointer}.markdown details[open] summary{margin-bottom:0}.markdown figure{margin:1rem 0}.markdown figure figcaption p{margin-top:0}.markdown-inner>:first-child{margin-top:0}.markdown-inner>:last-child{margin-bottom:0}.markdown .book-expand{margin-top:1rem;margin-bottom:1rem;border:1px solid var(--gray-200);border-radius:.25rem;overflow:hidden}.markdown .book-expand .book-expand-head{background:var(--gray-100);padding:.5rem 1rem;cursor:pointer}.markdown .book-expand .book-expand-content{display:none;padding:1rem}.markdown .book-expand input[type=checkbox]:checked+.book-expand-content{display:block}.markdown .book-tabs{margin-top:1rem;margin-bottom:1rem;border:1px solid var(--gray-200);border-radius:.25rem;overflow:hidden;display:flex;flex-wrap:wrap}.markdown .book-tabs label{display:inline-block;padding:.5rem 1rem;border-bottom:1px transparent;cursor:pointer}.markdown .book-tabs .book-tabs-content{order:999;width:100%;border-top:1px solid var(--gray-100);padding:1rem;display:none}.markdown .book-tabs input[type=radio]:checked+label{border-bottom:1px solid var(--color-link)}.markdown .book-tabs input[type=radio]:checked+label+.book-tabs-content{display:block}.markdown .book-tabs input[type=radio]:focus+label{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}.markdown .book-columns{margin-left:-1rem;margin-right:-1rem}.markdown .book-columns>div{margin:1rem 0;min-width:13.2rem;padding:0 1rem}.markdown a.book-btn{display:inline-block;font-size:.875rem;color:var(--color-link);line-height:2rem;padding:0 1rem;border:1px solid var(--color-link);border-radius:.25rem;cursor:pointer}.markdown a.book-btn:hover{text-decoration:none}.markdown .book-hint.info{border-color:#6bf;background-color:rgba(102,187,255,.1)}.markdown .book-hint.warning{border-color:#fd6;background-color:rgba(255,221,102,.1)}.markdown .book-hint.danger{border-color:#f66;background-color:rgba(255,102,102,.1)}
\ No newline at end of file
diff --git a/docs/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.json b/docs/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.json
new file mode 100644
index 0000000..b046947
--- /dev/null
+++ b/docs/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.json
@@ -0,0 +1 @@
+{"Target":"book.min.27c601ff251cc5c9796bb272aace960013cf351d0c28a7e1301487b32d4800ee.css","MediaType":"text/css","Data":{"Integrity":"sha256-J8YB/yUcxcl5a7Jyqs6WABPPNR0MKKfhMBSHsy1IAO4="}}
\ No newline at end of file
diff --git a/docs/logo.jpg b/docs/static/logo.png
similarity index 100%
rename from docs/logo.jpg
rename to docs/static/logo.png