From 2821a421bc844c43371a7be0b113bcc4faeb5070 Mon Sep 17 00:00:00 2001 From: zepan Date: Wed, 7 Oct 2026 01:01:33 +0800 Subject: [PATCH] ci: check that referenced images exist; document shallow clone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 断链图片一直在悄悄累积(本次清理前有 230 处),而且线上看不出来: teedoc-upload 只上传不删除对象存储里已移除的文件,旧副本把断链盖住了, 所以页面看起来正常。CI 里也从来没有任何链接检查。 - tools/check_image_links.py:只认 markdown ![]() 与 , 先剥离代码块以免把 `convert in.jpg out.jpg` 这类示例当成引用, 并按 site_config 的 route 解析站点绝对路径。 - tools/known_broken_images.txt:当前仍存在的 121 处已知断链基线。 CI 对不在基线中的断链报错,基线只许减不许增。 - .github/workflows/check_links.yml:push 与 PR 均运行。 - README:建议 --depth=1 浅克隆并解释原因——文档站的历史里保留了 每张图的所有旧版本(约 1GB),而贡献者只需要当前版本。 --- .github/workflows/check_links.yml | 24 +++++ README.md | 19 +++- tools/check_image_links.py | 145 ++++++++++++++++++++++++++++++ tools/known_broken_images.txt | 125 ++++++++++++++++++++++++++ 4 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/check_links.yml create mode 100644 tools/check_image_links.py create mode 100644 tools/known_broken_images.txt diff --git a/.github/workflows/check_links.yml b/.github/workflows/check_links.yml new file mode 100644 index 00000000..fc085811 --- /dev/null +++ b/.github/workflows/check_links.yml @@ -0,0 +1,24 @@ +name: check_image_links + +# Broken image links are easy to introduce and hard to spot: the published site +# keeps serving stale copies uploaded by earlier builds, so a page can look fine +# long after the image was moved or deleted from the repo. Check it here instead. + +on: + push: + branches: [ main ] + pull_request: + workflow_dispatch: + +jobs: + check-image-links: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Check that referenced images exist + run: python3 tools/check_image_links.py --list diff --git a/README.md b/README.md index 754f0301..32e8a1bb 100755 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ More contribute doc : ## Preview locally ```bash -git clone https://github.com/sipeed/sipeed_wiki.git +git clone --depth=1 https://github.com/sipeed/sipeed_wiki.git cd sipeed_wiki uv sync uv run teedoc serve @@ -27,6 +27,23 @@ uv run teedoc serve `uv sync` creates/updates the `.venv` from [`pyproject.toml`](./pyproject.toml) and `uv.lock`, which already includes `teedoc` and all plugins used by this site (so `teedoc install` is not needed). Install [uv](https://docs.astral.sh/uv/) first if you don't have it. +### Why `--depth=1` + +This is a documentation site, so most of the repo is images. Git keeps every past revision +of every one of them forever, including large originals that have since been replaced by +compressed versions. A full clone therefore downloads roughly **1 GB of history** even +though you only ever need the current version of each file. + +`--depth=1` fetches just the latest commit and skips all of that. Editing docs and opening +a pull request works exactly the same from a shallow clone. Use a full clone only when you +actually need the history (`git log`, `git blame`, `git bisect`) — you can always deepen a +shallow clone later with `git fetch --unshallow`. + +When adding images, please keep them small (ideally under 300 KB, 500 KB max): anything +committed here stays in the history for good, so an oversized image costs every future +contributor bandwidth even after it is deleted. See [AGENTS.md](./AGENTS.md) for the full +image and authoring guidelines. + More build tool usage see [teedoc](http://github.com/teedoc/teedoc) ## For administrator diff --git a/tools/check_image_links.py b/tools/check_image_links.py new file mode 100644 index 00000000..7e474f8d --- /dev/null +++ b/tools/check_image_links.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +"""Check that every image referenced by the docs actually exists. + +Broken image links are easy to introduce (moving an assets/ folder, deleting a +file without touching the pages that use it) and hard to notice, because the +published site keeps serving stale copies that were uploaded by earlier builds. +This script catches them at CI time instead. + +Only real image references are checked: markdown ``![alt](path)`` and HTML +````. Fenced and inline code is stripped first, so example commands +like ``convert in.jpg out.jpg`` are not mistaken for links. + +Usage: + python3 tools/check_image_links.py # fail on anything not in the baseline + python3 tools/check_image_links.py --list # print every broken link + python3 tools/check_image_links.py --update-baseline +""" + +import argparse +import json +import os +import re +import sys +import urllib.parse + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +BASELINE = os.path.join(ROOT, "tools", "known_broken_images.txt") +SCAN_DIRS = ["docs", "news", "pages", "layout"] +SKIP_DIRS = {".git", "out", ".venv", "node_modules", "__pycache__"} +PAGE_EXTS = {".md", ".html"} +IMG_EXTS = {".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".bmp", ".ico"} + +FENCED = re.compile(r"```.*?```|~~~.*?~~~", re.S) +INLINE_CODE = re.compile(r"`[^`\n]*`") +MD_IMG = re.compile(r"!\[[^\]]*\]\(\s*]+)") +HTML_IMG = re.compile(r"]*?\bsrc\s*=\s*[\"']([^\"']+)", re.I) + + +def load_routes(): + """Map URL prefixes to on-disk directories, as teedoc does when building.""" + with open(os.path.join(ROOT, "site_config.json"), encoding="utf-8") as f: + cfg = json.load(f) + routes = {} + for group in ("docs", "pages", "assets"): + for url, path in (cfg.get("route", {}).get(group) or {}).items(): + routes[url] = path + for url, targets in (cfg.get("translate", {}).get("docs") or {}).items(): + for t in targets: + routes[t["url"]] = t["src"] + return dict(sorted(routes.items(), key=lambda kv: -len(kv[0]))) + + +def resolve(ref, page_path, routes): + """Return the on-disk path a reference points at, or None if it is external.""" + ref = ref.strip().strip("'\"") + if ref.startswith(("http://", "https://", "//", "data:", "mailto:")): + return None + ref = urllib.parse.unquote(ref.split("#")[0].split("?")[0]) + if not ref or os.path.splitext(ref)[1].lower() not in IMG_EXTS: + return None + if ref.startswith("/"): + for url, path in routes.items(): + if ref.startswith(url): + return os.path.normpath(os.path.join(ROOT, path, ref[len(url):])) + return os.path.normpath(os.path.join(ROOT, ref.lstrip("/"))) + return os.path.normpath(os.path.join(os.path.dirname(page_path), ref)) + + +def scan(): + routes = load_routes() + broken = [] + for top in SCAN_DIRS: + for dirpath, dirnames, filenames in os.walk(os.path.join(ROOT, top)): + dirnames[:] = [d for d in dirnames if d not in SKIP_DIRS] + for name in filenames: + if os.path.splitext(name)[1].lower() not in PAGE_EXTS: + continue + page = os.path.join(dirpath, name) + try: + text = open(page, encoding="utf-8").read() + except (OSError, UnicodeDecodeError): + continue + text = INLINE_CODE.sub(" ", FENCED.sub(" ", text)) + rel_page = os.path.relpath(page, ROOT) + for ref in set(MD_IMG.findall(text) + HTML_IMG.findall(text)): + if "{{" in ref or "{%" in ref: # jinja template expression + continue + target = resolve(ref, page, routes) + if target and not os.path.exists(target): + broken.append(f"{rel_page}\t{ref}") + return sorted(set(broken)) + + +def read_baseline(): + if not os.path.exists(BASELINE): + return set() + with open(BASELINE, encoding="utf-8") as f: + return {l.rstrip("\n") for l in f if l.strip() and not l.startswith("#")} + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--list", action="store_true", help="print all broken links") + ap.add_argument("--update-baseline", action="store_true", + help="record the current broken links as accepted") + args = ap.parse_args() + + broken = scan() + if args.update_baseline: + with open(BASELINE, "w", encoding="utf-8") as f: + f.write("# Image links that are already broken and not yet fixed.\n" + "# CI fails on anything NOT listed here, so this file may shrink\n" + "# but must never grow. Regenerate with:\n" + "# python3 tools/check_image_links.py --update-baseline\n") + f.write("\n".join(broken) + "\n") + print(f"baseline updated: {len(broken)} known broken links") + return 0 + + baseline = read_baseline() + new = [b for b in broken if b not in baseline] + fixed = [b for b in baseline if b not in broken] + + if args.list: + for b in broken: + page, ref = b.split("\t") + print(f"{page}: {ref}") + + print(f"{len(broken)} broken image links " + f"({len(baseline)} known, {len(new)} new, {len(fixed)} fixed since baseline)") + + if fixed: + print(f"\n{len(fixed)} link(s) in the baseline are fixed - please run " + f"`python3 tools/check_image_links.py --update-baseline` and commit.") + if new: + print(f"\nNew broken image links introduced by this change:\n") + for b in new: + page, ref = b.split("\t") + print(f" {page}\n -> {ref}") + print("\nFix the path, add the missing image, or remove the reference.") + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/known_broken_images.txt b/tools/known_broken_images.txt new file mode 100644 index 00000000..0783d3aa --- /dev/null +++ b/tools/known_broken_images.txt @@ -0,0 +1,125 @@ +# Image links that are already broken and not yet fixed. +# CI fails on anything NOT listed here, so this file may shrink +# but must never grow. Regenerate with: +# python3 tools/check_image_links.py --update-baseline +docs/hardware/en/kvm/NanoKVM/user_guide.md ./../../../assets/NanoKVM/guide/atx_2024.png +docs/hardware/en/kvm/NanoKVM_Pro/extended.md ./../../../assets/NanoKVM/pro/extended/hdmi.jpg +docs/hardware/en/kvm/NanoKVM_Pro/ledstrip.md ../../../assets/NanoKVM/pro/ledstrip/ledstrip_show.png +docs/hardware/en/lichee/K1/lc3a/lc3a.md ./assets/lc3a/lc3a_architecture.png +docs/hardware/en/lichee/K1/lc3a/lc3a.md ./assets/lc3a/lc3a_box.png +docs/hardware/en/lichee/K1/lc3a/lc3a.md ./assets/lc3a/lc3a_install_goldfinger.png +docs/hardware/en/lichee/K1/lc3a/lc3a.md ./assets/lc3a/lc3a_install_install_lm3a.png +docs/hardware/en/lichee/K1/lc3a/lc3a.md ./assets/lc3a/lc3a_install_slot.jpeg +docs/hardware/en/lichee/K1/lc3a/lc3a.md ./assets/lc3a/lc3a_power_atx20_cable.png +docs/hardware/en/lichee/K1/lc3a/lc3a.md ./assets/lc3a/lc3a_power_jumpwire.png +docs/hardware/en/lichee/K1/lc3a/lc3a.md ./assets/lc3a/lc3a_top.png +docs/hardware/en/lichee/K1/lpi3a/3_burn_image.md ./assets/images/deepin.jpg +docs/hardware/en/lichee/K1/lpi3a/3_burn_image.md ./assets/images/fedora.png +docs/hardware/en/lichee/K1/lpi3a/3_burn_image.md ./assets/images/irradium.png +docs/hardware/en/lichee/K1/lpi3a/3_burn_image.md ./assets/images/openkylin.png +docs/hardware/en/lichee/RV_Nano/2_unbox.md ./../assets/RV_Nano/unbox/1.jpg +docs/hardware/en/lichee/th1520/lbook4a/lbook4a.md ./assets/lbook/lbook.jpg +docs/hardware/en/lichee/th1520/lpi4a/8_application.md /images/kubernetes.png +docs/hardware/en/logic_analyzer/slogic16u3/Software_User_Guide.md ./assets/Screenshots/SLogic_Connect_1.png +docs/hardware/en/maix/modules_spmod/spmod_bt.md ../../assets/spmod/spmod_bt/back.jpg +docs/hardware/en/maix/modules_spmod/spmod_bt.md ../../assets/spmod/spmod_bt/connection.png +docs/hardware/en/maix/modules_spmod/spmod_eink.md ../../assets/spmod/spmod_eink/connection.png +docs/hardware/en/maix/modules_spmod/spmod_eink.md ../../assets/spmod/spmod_eink/sp_eink_c.png +docs/hardware/en/maix/modules_spmod/spmod_lcd1.14.md ../../assets/spmod/spmod_lcd1.14//connection.png +docs/hardware/en/maix/modules_spmod/spmod_lcd1.14.md ../../assets/spmod/spmod_lcd1.14/back.png +docs/hardware/en/maix/modules_spmod/spmod_lora.md ../../assets/spmod/spmod_lora//connection.png +docs/hardware/en/maix/modules_spmod/spmod_lora.md ../../assets/spmod/spmod_lora/back.png +docs/hardware/en/maix/modules_spmod/spmod_rfid.md ../../assets/spmod/spmod_rfid/back.png +docs/hardware/en/maix/modules_spmod/spmod_rfid.md ../../assets/spmod/spmod_rfid/connection.png +docs/hardware/en/maix/modules_spmod/spmod_rfid.md ../../assets/spmod/spmod_rfid/sp_rfid.png +docs/hardware/en/maix/modules_spmod/spmod_tof.md ../../assets/spmod/spmod_tof/back.png +docs/hardware/en/maix/modules_spmod/spmod_tof.md ../../assets/spmod/spmod_tof/connection.png +docs/hardware/en/maix/modules_spmod/spmod_tof.md ../../assets/spmod/spmod_tof/log_c.png +docs/hardware/en/maix/modules_spmod/spmod_weather.md ../../assets/spmod/spmod_weather/back.png +docs/hardware/en/maix/modules_spmod/spmod_weather.md ../../assets/spmod/spmod_weather/connection.png +docs/hardware/en/maix/modules_spmod/spmod_weather.md ../../assets/spmod/spmod_weather/demo.gif +docs/hardware/en/maix/modules_spmod/spmod_weather.md ../../assets/spmod/spmod_weather/log_c.png +docs/hardware/en/maixcam/README.md ../../assets/maixcam/maixcam.jpeg +docs/hardware/en/maixface/mf_ml_module/mf0_ml_module.md ../assets/mf_module/mf1_bottom_v2(nand).png +docs/hardware/en/maixface/mf_ml_module/mf1_ml_module.md ../../assests/front.png +docs/hardware/en/maixface/mf_ml_module/mf1_ml_module.md assests/bottom.jpg +docs/hardware/en/maixface/mf_ml_module/mf_update_firmwave.md ../../assets/mf_module/mf1/mf_dual_camera_2.png +docs/hardware/en/modules_spmod/spmod_template.md ../../assets/spmod/sipeed_spmod_xxx.jpg +docs/hardware/en/tang/tang-mega-138k/mega-138k-pro.md ./assets/partno_138K_Pro.png +docs/hardware/maixface/en/core_modules/k210_core_modules.md ../../assets/mf_module/m1_m1w/sipeed_m1_module.png +docs/hardware/maixface/en/core_modules/k210_core_modules.md ../../assets/mf_module/m1_m1w/sipeed_m1w_module.png +docs/hardware/maixface/en/core_modules/k210_core_modules.md ../../assets/mf_module/m1n/sipeed_m1n_module.png +docs/hardware/maixface/en/core_modules/k210_core_modules.md ../../assets/mf_module/mf0_mf0dock/sipeed_mf0_module.png +docs/hardware/maixface/en/mf_ml_module/mf0_ml_module.md ../../assets/mf_module/mf0_mf0dock/mf0_buttom.png +docs/hardware/maixface/en/mf_ml_module/mf0_ml_module.md ../../assets/mf_module/mf0_mf0dock/mf0_dock_flash.png +docs/hardware/maixface/en/mf_ml_module/mf0_ml_module.md ../../assets/mf_module/mf0_mf0dock/mf0_flash.png +docs/hardware/maixface/en/mf_ml_module/mf0_ml_module.md ../../assets/mf_module/mf0_mf0dock/mf0_pin_map.png +docs/hardware/maixface/en/mf_ml_module/mf0_ml_module.md ../../assets/mf_module/mf0_mf0dock/mf0_upgrade_firmwave.png +docs/hardware/maixface/en/mf_ml_module/mf0_ml_module.md ../assets/mf_module/mf1_bottom_v2(nand).png +docs/hardware/maixface/en/mf_ml_module/mf1_ml_module.md ../../assests/front.png +docs/hardware/maixface/en/mf_ml_module/mf1_ml_module.md ../../assets/mf_module/mf1/mf1_pin_map.png +docs/hardware/maixface/en/mf_ml_module/mf1_ml_module.md assests/bottom.jpg +docs/hardware/maixface/en/mf_ml_module/mf_update_firmwave.md ../../assets/mf_module/mf1/mf1_view.png +docs/hardware/maixface/en/mf_ml_module/mf_update_firmwave.md ../../assets/mf_module/mf1/mf_dual_camera_1.jpg +docs/hardware/maixface/en/mf_ml_module/mf_update_firmwave.md ../../assets/mf_module/mf1/mf_dual_camera_2.png +docs/hardware/maixface/zh/core_modules/k210_core_modules.md ../../assets/mf_module/m1_m1w/K210_boot_reference.png +docs/hardware/maixface/zh/core_modules/k210_core_modules.md ../../assets/mf_module/m1_m1w/sipeed_m1_module.png +docs/hardware/maixface/zh/core_modules/k210_core_modules.md ../../assets/mf_module/m1_m1w/sipeed_m1w_module.png +docs/hardware/maixface/zh/core_modules/k210_core_modules.md ../../assets/mf_module/m1n/sipeed_m1n_module.png +docs/hardware/maixface/zh/core_modules/k210_core_modules.md ../../assets/mf_module/mf0_mf0dock/sipeed_mf0_module.png +docs/hardware/maixface/zh/mf_ml_module/mf0_ml_module.md ../../assets/mf_module/mf0_mf0dock/mf0_buttom.png +docs/hardware/maixface/zh/mf_ml_module/mf0_ml_module.md ../../assets/mf_module/mf0_mf0dock/mf0_dock_flash.png +docs/hardware/maixface/zh/mf_ml_module/mf0_ml_module.md ../../assets/mf_module/mf0_mf0dock/mf0_flash.png +docs/hardware/maixface/zh/mf_ml_module/mf0_ml_module.md ../../assets/mf_module/mf0_mf0dock/mf0_pin_map.png +docs/hardware/maixface/zh/mf_ml_module/mf0_ml_module.md ../../assets/mf_module/mf0_mf0dock/mf0_upgrade_firmwave.png +docs/hardware/maixface/zh/mf_ml_module/mf0_ml_module.md ../assets/mf_module/mf1_bottom_v2(nand).png +docs/hardware/maixface/zh/mf_ml_module/mf1_ml_module.md ../../assests/front.png +docs/hardware/maixface/zh/mf_ml_module/mf1_ml_module.md ../../assets/mf_module/mf1/mf1_pin_map.png +docs/hardware/maixface/zh/mf_ml_module/mf1_ml_module.md assests/bottom.jpg +docs/hardware/maixface/zh/mf_ml_module/mf_precautions.md ../../assets/other/mf_precautions.png +docs/hardware/maixface/zh/mf_ml_module/mf_update_firmwave.md ../../assets/mf_module/mf1/mf1_view.png +docs/hardware/maixface/zh/mf_ml_module/mf_update_firmwave.md ../../assets/mf_module/mf1/mf_dual_camera_1.jpg +docs/hardware/maixface/zh/mf_ml_module/mf_update_firmwave.md ../../assets/mf_module/mf1/mf_dual_camera_2.png +docs/hardware/zh/lichee/th1520/lbook4a/lbook4a.md ./assets/lbook/lbook.jpg +docs/hardware/zh/logic_analyzer/slogic16u3/Software_User_Guide.md ../../../en/logic_analyzer/slogic16u3/assets/Screenshots/SLogic_Connect_1.png +docs/hardware/zh/maixII/M2/usage.md /news/MaixPy3/v831_usage/assest/adb.jpg +docs/hardware/zh/maixII/M2/usage.md /news/MaixPy3/v831_usage/assest/delete.jpg +docs/hardware/zh/maixcam/README.md ../../assets/maixcam/maixcam.jpeg +docs/hardware/zh/maixface/mf_ml_module/mf0_ml_module.md ../assets/mf_module/mf1_bottom_v2(nand).png +docs/hardware/zh/maixface/mf_ml_module/mf0_ml_module.md ./../zh/maixface/assets/mf_module/mf0_mf0dock/mf0_dock_flash.png +docs/hardware/zh/maixface/mf_ml_module/mf1_ml_module.md ./../assests/front.png +docs/hardware/zh/maixface/mf_ml_module/mf1_ml_module.md assests/bottom.jpg +docs/hardware/zh/modules_spmod/spmod_template.md ../../assets/spmod/sipeed_spmod_xxx.jpg +docs/hardware/zh/tang/Tang-Nano-9K/examples/LCD.md ./../../Tang-Nano-1K/Nano_1K_examples/led/assets/add_ip_file_in_project.png +docs/hardware/zh/tang/tang-PMOD/FPGA_PMOD.md ./assets/PMOD_HUb75E_bot.jpg +docs/soft/Lichee/zh/Lichee-RV/contribution/Dual_screen_display/environment.md ./assets/enviroument.png +docs/soft/Lichee/zh/Lichee-RV/get_started/user.md ./../assets/RV/wifi-1.jpg +docs/soft/Lichee/zh/Lichee-RV/get_started/user.md ./../assets/RV/wifi-2.jpg +docs/soft/Lichee/zh/MaixII/V83X/contribution/v83x_sdk_compile/V83x_sdk_kernel_compile.md ./assets/11.png +docs/soft/Lichee/zh/Zero-Doc/System_Development/SPI_falsh_doc4.md ./../static/System_Development/uboot_conf_9.gif +docs/soft/Tang/en/Gowin/Tang_nano/README.md ../assets/sipeed_longan_logo.jpg +docs/soft/Tang/zh/Tang-Common-Doc/examples/4K_led.md ./../../../Nano-4K/4K-led-2.png +docs/soft/maixpy/en/course/others/maixui.md ./image/app_main.gif +docs/soft/maixpy/en/develop_kit_board/develop_kit_board.md ../../assets/dk_board/maix_bit/BiT.png +docs/soft/maixpy/en/develop_kit_board/develop_kit_board.md ../../assets/dk_board/maix_cube/maixcube_2020-06-13_06-31-29.png +docs/soft/maixpy/en/develop_kit_board/develop_kit_board.md ../../assets/dk_board/maix_dock/Dan_Dock.png +docs/soft/maixpy/en/develop_kit_board/develop_kit_board.md ../../assets/dk_board/maix_duino/maixduino_0.png +docs/soft/maixpy/en/develop_kit_board/develop_kit_board.md ../../assets/dk_board/maix_go/Go.jpg +docs/soft/maixpy/en/get_started/knowledge_git_github.md /assets/other/github_star.jpg +docs/soft/maixpy/en/get_started/upgrade_esp8285_firmware.md ../../assets/hardware/maix_dock/sipeed_maix_dock_m1w_2.png +docs/soft/maixpy/en/get_started/upgrade_maixpy_firmware.md ../../assets/kflash_gui_screenshot_download.png +docs/soft/maixpy/zh/course/others/maixui.md ./image/app_main.gif +docs/soft/maixpy/zh/develop_kit_board/develop_kit_board.md ../../assets/dk_board/maix_bit/BiT.png +docs/soft/maixpy/zh/develop_kit_board/develop_kit_board.md ../../assets/dk_board/maix_cube/maixcube_2020-06-13_06-31-29.png +docs/soft/maixpy/zh/develop_kit_board/develop_kit_board.md ../../assets/dk_board/maix_dock/Dan_Dock.png +docs/soft/maixpy/zh/develop_kit_board/develop_kit_board.md ../../assets/dk_board/maix_duino/maixduino_0.png +docs/soft/maixpy/zh/develop_kit_board/develop_kit_board.md ../../assets/dk_board/maix_go/Go.jpg +docs/soft/maixpy3/zh/api/maix/gpio.md ./asserts/v831_gpio.png +docs/soft/maixpy3/zh/api/maix/gpio.md ./asserts/v831_pin_maps.png +docs/soft/maixpy3/zh/usage/hardware/back/GPIO_1.md ./../asserts/M2Dock_pin.png +docs/soft/maixpy3/zh/usage/hardware/back/I2C_1.md ./../asserts/M2Dock_pin.png +docs/soft/maixpy3/zh/usage/hardware/back/PWM_1.md ./../asserts/pwm.gif +docs/soft/maixpy3/zh/usage/hardware/back/PWM_1.md ./../asserts/pwm.jpg +news/MaixPy3/v831_usage/v831_usage.md /news/MaixPy3/v831_usage/assest/usb.jpg +news/others/831_ssh/831_ssh.md ./assets/succeed_login.png +pages/index/zh/threshold.html demo_color.jpg