From 577a4bffe2709de832f5b2899a20c4825300769b Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Wed, 8 Apr 2026 12:01:29 +0300 Subject: [PATCH 01/21] initial zensical test --- .github/workflows/publish.yml | 69 +++---- .python-version | 1 + Dockerfile | 18 +- Makefile | 28 +-- docker-compose.yml | 8 + docs/index.md | 19 +- mkdocs-insiders.yml | 55 ------ mkdocs.yml | 354 ---------------------------------- pyproject.toml | 9 + uv.lock | 162 ++++++++++++++++ zensical.toml | 287 +++++++++++++++++++++++++++ 11 files changed, 525 insertions(+), 485 deletions(-) create mode 100644 .python-version create mode 100644 docker-compose.yml delete mode 100644 mkdocs-insiders.yml delete mode 100644 mkdocs.yml create mode 100644 pyproject.toml create mode 100644 uv.lock create mode 100644 zensical.toml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ca6f45c1b..5d9c7d43c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,61 +3,36 @@ on: push: branches: - master -env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - MKDOCS_GIT_COMMITTERS_APIKEY: ${{ secrets.MKDOCS_GIT_COMMITTERS_APIKEY }} +permissions: + contents: read + pages: write + id-token: write jobs: deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - - name: Checkout latest push - uses: actions/checkout@v4 - with: - fetch-depth: 0 + - uses: actions/checkout@v5 - - name: Setup Python env - uses: actions/setup-python@v5 - with: - python-version: 3.9.16 + - uses: actions/configure-pages@v5 - - name: Set up build cache - uses: actions/cache/restore@v4 + - uses: actions/setup-python@v5 with: - key: docs-${{ hashfiles('.cache/**') }} - path: .cache - restore-keys: | - docs- - - - name: Setup Github - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git pull - git checkout master + python-version: "3.11" - - name: Install Dependencies - run: | - make install-python-packages - sudo apt-get install pngquant + - name: Install Zensical + run: make install - - name: Install MkDocs Insiders - if: github.event.repository.fork == false - env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} - run: pip install --upgrade git+https://${GH_TOKEN}@github.com/squidfunk/mkdocs-material-insiders.git@9.6.14-insiders-4.53.16 + - name: Build site + run: zensical build --clean - - name: Deploy Release Version - run: | - mkdocs gh-deploy --force --config-file mkdocs-insiders.yml - - - name: Deploy to Server - run: | - git checkout gh-pages - git remote set-url --push origin https://actions:$GITHUB_TOKEN@github.com/ExpressLRS/Docs - git push -f origin gh-pages - - - name: Save build cache - uses: actions/cache/save@v4 + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v4 with: - key: docs-${{ hashfiles('.cache/**') }} - path: .cache + path: site + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.python-version b/.python-version new file mode 100644 index 000000000..2c0733315 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.11 diff --git a/Dockerfile b/Dockerfile index 7bab7e9cb..67109193d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,4 @@ -FROM squidfunk/mkdocs-material:9.6.19 -RUN apk add \ - build-base \ - libxml2-dev \ - libxslt \ - libxslt-dev \ - dumb-init +FROM zensical/zensical:0.0.32 -WORKDIR /docs -COPY ./Makefile ./Makefile -RUN make install-python-packages - -EXPOSE 8000 - -ENTRYPOINT ["dumb-init"] -CMD ["mkdocs", "serve", "--livereload", "--dev-addr=0.0.0.0:8000"] +# WORKDIR, EXPOSE 8000, ENTRYPOINT and the default +# `serve --dev-addr=0.0.0.0:8000` CMD are inherited from the base image. diff --git a/Makefile b/Makefile index aa4bc0c2e..d5ce0b201 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,21 @@ -.PHONY: install-python-packages -install-python-packages: - pip install \ - "mkdocs-minify-plugin==0.8.0" \ - "mkdocs-redirects==1.2.2" \ - "mkdocs-static-i18n==1.3.0" \ - "mkdocs-git-committers-plugin-2==2.5.0" \ - "mkdocs-git-revision-date-localized-plugin==1.4.5" \ - "cairosvg==2.8.1" \ - "jinja2==3.1.6" +# Host-only convenience wrappers around `docker compose`. +# CI invokes `docker compose` directly and never calls `make`. +.PHONY: install build run serve site shell + +install: + pip install "zensical==0.0.32" -.PHONY: build build: docker compose build -.PHONY: run -run: +run: serve + +serve: docker compose up + +site: + docker compose run --rm docs build --clean + +shell: + docker compose run --rm --entrypoint sh docs diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..5ff1ae0bc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +services: + docs: + build: . + image: expresslrs-docs + ports: + - "8000:8000" + volumes: + - .:/docs diff --git a/docs/index.md b/docs/index.md index 0d027f29a..eaf044fd3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,21 @@ --- -template: home.html title: High Performance Open Source Radio Control Link --- + +# ExpressLRS + +ExpressLRS is a fully open radio control link built for maximum range, +speed, and data throughput. Wide hardware support across both 900 MHz and +2.4 GHz frequencies. + + + +- [Getting Started](quick-start/getting-started.md) +- [Hardware Selection](hardware/hardware-selection.md) +- [FAQ](faq.md) +- [Blog](blog/index.md) diff --git a/mkdocs-insiders.yml b/mkdocs-insiders.yml deleted file mode 100644 index 633584c05..000000000 --- a/mkdocs-insiders.yml +++ /dev/null @@ -1,55 +0,0 @@ -INHERIT: mkdocs.yml - -theme: - features: - - announce.dismiss - - navigation.sections - - navigation.tabs - - navigation.footer - - navigation.tracking - - navigation.path - - search.highlight - - search.suggest - - toc.follow - - content.action.edit - - content.action.view - -plugins: - # - privacy: - # enabled: !ENV [ CI, false ] - # links_attr_map: - # target: _blank - - blog: - enabled: true - blog_toc: true - post_date_format: long - post_url_date_format: yyyy/MM/dd - - social: - cards_color: - fill: "#5f8bf3" - text: "#FFFFFF" - - search: - lang: en - - tags: { } - - minify: - minify_html: true - # - i18n: - # docs_structure: folder - # fallback_to_default: true - # reconfigure_material: true - # reconfigure_search: true - # languages: - # - locale: en - # name: English - # build: true - # default: true - # exclude: - # - blog/** - - git-revision-date-localized: - enabled: !ENV [ CI, false ] - type: timeago - enable_creation_date: true - - git-committers: - enabled: !ENV [ CI, false ] - repository: ExpressLRS/Docs - branch: master diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index de75531b7..000000000 --- a/mkdocs.yml +++ /dev/null @@ -1,354 +0,0 @@ -site_name: ExpressLRS -site_url: https://www.expresslrs.org/ -site_author: ExpressLRS -site_description: >- - ExpressLRS is a fully open radio control link built for maximum - range, speed, and data throughput. Wide hardware support across both - 900 MHz and 2.4 GHz frequencies. - -# Repository -repo_name: ExpressLRS/ExpressLRS -repo_url: https://github.com/ExpressLRS/ExpressLRS/releases/ -edit_uri: "https://github.com/ExpressLRS/Docs/tree/master/docs/" - -# Copyright -copyright: Copyright © 2018–2025 ExpressLRS LLC - -# Configuration -theme: - name: material - custom_dir: overrides - - # Static files - static_templates: - - 404.html - - include_search_page: false - search_index_only: true - - language: en - features: - - announce.dismiss - - navigation.sections - - navigation.tabs - - navigation.footer - - navigation.tracking - - navigation.indexes - - search.highlight - - search.suggest - - toc.follow - - content.action.edit - - content.action.view - - palette: - - media: "(prefers-color-scheme: dark)" - scheme: slate - toggle: - icon: material/weather-night - name: Switch to light mode - default: expresslrs-dark - primary: expresslrs-dark - accent: expresslrs-dark - - media: "(prefers-color-scheme: light)" - scheme: default - toggle: - icon: material/weather-sunny - name: Switch to dark mode - primary: expresslrs - accent: expresslrs - font: - text: Roboto - code: Roboto Mono - favicon: assets/favicon.png - logo: assets/images/logotype.svg - -# Plugins -hooks: - - overrides/hooks/blog_posts.py - -plugins: - - blog: - enabled: true - blog_toc: true - post_date_format: long - post_url_date_format: yyyy/MM/dd - - search: - lang: en - - tags: { } - - minify: - minify_html: true - # - i18n: - # docs_structure: folder - # fallback_to_default: true - # reconfigure_material: true - # reconfigure_search: true - # languages: - # - locale: en - # name: English - # build: true - # default: true - # exclude: - # - blog/** - - git-revision-date-localized: - enabled: !ENV [ CI, false ] - type: timeago - enable_creation_date: true - - git-committers: - enabled: !ENV [ CI, false ] - repository: ExpressLRS/Docs - branch: master - -extra_css: - - assets/stylesheets/main.css - - assets/stylesheets/colors.css - - assets/stylesheets/home.css - -# Analytics -extra: - analytics: - provider: google - property: G-VMF2B5BYJZ - feedback: - title: Was this page helpful? - ratings: - - icon: material/emoticon-happy-outline - name: This page was helpful - data: 1 - note: >- - Thanks for your feedback! - - icon: material/emoticon-sad-outline - name: This page could be improved - data: 0 - note: >- - Thanks for your feedback! Help us improve this page by - using our discord server! - consent: - title: Cookie consent - description: >- - We use cookies to recognize your repeated visits and preferences, as well - as to measure the effectiveness of our documentation and whether users - find what they're searching for. With your consent, you're helping us to - make our documentation better. - analytics: - name: Google Analytics - actions: - - accept - - manage - - reject - social: - - icon: fontawesome/brands/github - link: https://github.com/ExpressLRS/ - name: See the Github Repo - - icon: fontawesome/brands/discord - link: https://discord.gg/dS6ReFY - name: Check out the Discord - - icon: fontawesome/brands/facebook - link: https://www.facebook.com/groups/636441730280366 - name: Join the Facebook Group - - icon: fontawesome/brands/twitter - link: https://twitter.com/expresslrs - name: We're on Twitter too! - - icon: fontawesome/brands/instagram - link: https://instagram.com/expresslrs - name: Follow us on Instagram - - icon: fontawesome/solid/circle-dollar-to-slot - link: https://opencollective.com/expresslrs - name: Send a Donation! - generator: true - -# Markdown Extensions -markdown_extensions: - - admonition - - abbr - - attr_list - - def_list - - footnotes - - meta - - md_in_html - - tables - - toc: - permalink: true - - pymdownx.arithmatex: - generic: true - - pymdownx.betterem: - smart_enable: all - - pymdownx.caret - - pymdownx.critic - - pymdownx.details - - pymdownx.emoji: - emoji_index: !!python/name:material.extensions.emoji.twemoji - emoji_generator: !!python/name:material.extensions.emoji.to_svg - - pymdownx.highlight - - pymdownx.inlinehilite - - pymdownx.keys - - pymdownx.magiclink: - repo_url_shorthand: true - user: ExpressLRS - repo: ExpressLRS - - pymdownx.mark - - pymdownx.smartsymbols - - pymdownx.superfences: - custom_fences: - - name: mermaid - class: mermaid - format: !!python/name:pymdownx.superfences.fence_code_format - - pymdownx.tabbed: - alternate_style: true - slugify: !!python/object/apply:pymdownx.slugs.slugify - kwds: - case: lower - - pymdownx.tasklist: - custom_checkbox: true - - pymdownx.tilde - -# Navigation -nav: - - Home: index.md - - Quick Start: - - Getting Started: quick-start/getting-started.md - - Installing the Configurator: quick-start/installing-configurator.md - - Firmware Options: quick-start/firmware-options.md - - The Web UI: quick-start/webui.md - - Transmitters: - - Radio Preparation: quick-start/transmitters/tx-prep.md - - Checking FW Version: quick-start/transmitters/firmware-version.md - - Typical Updating Steps: quick-start/transmitters/updating.md - - Updating Transmitters: - - Axisflying Thor: quick-start/transmitters/axisflying-thor.md - - BetaFPV 2.4GHz: quick-start/transmitters/betafpv2400.md - - BetaFPV 900MHz: quick-start/transmitters/betafpv900.md - - BetaFPV LR3 Pro: quick-start/transmitters/betafpvlr3pro.md - - EMAX 2.4GHz: quick-start/transmitters/emax2400.md - - EMAX 900MHz: quick-start/transmitters/emax900.md - - Frsky R9 Modules: quick-start/transmitters/frsky-r9modules.md - - Happymodel ES24TX: quick-start/transmitters/es24tx.md - - Happymodel ES900TX: quick-start/transmitters/es900tx.md - - HGLRC Hermes: quick-start/transmitters/hglrc-hermes.md - - iFlight Commando: quick-start/transmitters/iflight-commando.md - - IMRC Ghost: quick-start/transmitters/ghost2400.md - - Internal RadioMaster: quick-start/transmitters/rm-internal.md - - Internal Jumper T-Pro: quick-start/transmitters/aion-internal.md - - Internal Jumper TLite V2: quick-start/transmitters/jumper-internal.md - - Internal Jumper T14/T15: quick-start/transmitters/jumper-t14-t15-internal.md - - Internal Jumper T20: quick-start/transmitters/jumper-t20-internal.md - - Jumper Aion Nano: quick-start/transmitters/jumper-aion.md - - NamimnoRC Flash: quick-start/transmitters/flash2400.md - - NamimnoRC Voyager: quick-start/transmitters/voyager900.md - - Radiomaster Bandit: quick-start/transmitters/rm-bandit.md - - Radiomaster Ranger: quick-start/transmitters/rm-ranger.md - - Siyi FM30: quick-start/transmitters/siyifm30.md - - Vantac Lite: quick-start/transmitters/vantac-lite.md - - DIY 900TX: quick-start/transmitters/diy900.md - - DIY 2400TX: quick-start/transmitters/diy2400.md - - The ExpressLRS Lua Script: quick-start/transmitters/lua-howto.md - - Receivers: - - Receiver Wiring: quick-start/receivers/wiring-up.md - - Configuring FC: quick-start/receivers/configuring-fc.md - - Checking FW Version: quick-start/receivers/firmware-version.md - - Typical Updating Steps: quick-start/receivers/updating.md - - Updating Receivers: - - Axisflying Thor: quick-start/receivers/axisflying-thor.md - - BetaFPV Lite/Nano/AIO 2.4GHz: quick-start/receivers/betafpv2400.md - - BetaFPV Nano 900MHz: quick-start/receivers/betafpv900.md - - BetaFPV SuperD 2.4GHz: quick-start/receivers/betafpv-superd.md - - BetaFPV SuperD 900MHz: quick-start/receivers/betafpv-superd900.md - - Foxeer LNA & Lite 2.4GHz: quick-start/receivers/foxeer2400.md - - Frsky R9: quick-start/receivers/r9.md - - GEPRC Nano & Dual 2.4GHz: quick-start/receivers/geprc2400.md - - GEPRC Nano & Dual 900MHz: quick-start/receivers/geprc900.md - - Happymodel EP: quick-start/receivers/hmep2400.md - - Happymodel PP: quick-start/receivers/hmpp2400.md - - Happymodel ES900RX: quick-start/receivers/hmes900.md - - HGLRC Hermes 2.4GHz: quick-start/receivers/hglrc-hermes2400.md - - HGLRC Hermes 900MHz: quick-start/receivers/hglrc-hermes900.md - - iFlight 2.4GHz: quick-start/receivers/iflight2400.md - - iFlight 900MHz: quick-start/receivers/iflight900.md - - IMRC Ghost: quick-start/receivers/ghost2400.md - - Jumper Aion Nano: quick-start/receivers/jumper-aion.md - - Jumper R9 Mini: quick-start/receivers/jumper900.md - - Matek R24: quick-start/receivers/matek2400.md - - NamimnoRC Flash: quick-start/receivers/flash2400.md - - NamimnoRC Voyager: quick-start/receivers/voyager900.md - - RadioMaster RP: quick-start/receivers/radiomaster-rp-2400.md - - Siyi FR Mini: quick-start/receivers/siyiFRmini.md - - Vantac: quick-start/receivers/vantac2400.md - - DIY 900RX: quick-start/receivers/diy900.md - - DIY 2400RX: quick-start/receivers/diy2400.md - - Post-Flash: - - LED Status: quick-start/led-status.md - - Binding ExpressLRS: quick-start/binding.md - - Before First Flight: quick-start/pre-1stflight.md - - Ardupilot Setup: quick-start/ardupilot-setup.md - - Troubleshooting: quick-start/troubleshooting.md - - Unbricking: quick-start/unbricking.md - - Software: - - Obsolete Defines: software/obsolete-defines.md - - OpenOCD for STLink: software/open-ocd.md - - PIO STLink Fix: software/stlink-fix.md - - Switch Configs: software/switch-config.md - - Toolchain Setup: software/toolchain-install.md - - User Defines: software/user-defines.md - - Updating: - - Betaflight Passthrough: software/updating/betaflight-passthrough.md - - Wifi Updating: software/updating/wifi-updating.md - - Features: - - AirPort: software/airport.md - - Dynamic Transmit Power: software/dynamic-transmit-power.md - - Gemini: software/gemini.md - - Loan Model: software/loan-model.md - - MAVLink: software/mavlink.md - - MFD Crossbow Tracker Integration: software/mfd-crossbow.md - - Model Matching: software/model-config-match.md - - Receiver Serial Protocols: software/serial-protocols.md - - Sentinel Tracker Integration: software/backpack-telemetry.md - - Serial VTX: software/serialvtx.md - - Team Racing: software/teamracing.md - - Trainer (Head Tracking) Input: software/trainer-input.md - - Testing: - - CRC Testing: software/testing/crc-testing.md - - RX Testing: software/testing/rx-scoreboard.md - - Unit Testing: software/testing/unit-testing.md - - Hardware: - - Hardware Selection: hardware/hardware-selection.md - - R9M Inverter Mod: hardware/inverter-mod.md - - R9M Fan Mod: hardware/fan-mod.md - - Troubleshooting the X9D(+): hardware/x9d-troubleshooting.md - - Crystal Oscillator (XO) Frequency Error: hardware/crystal-frequency-error.md - - SMD Antenna: hardware/smd-antenna.md - - PWM Receivers: hardware/pwm-receivers.md - - SPI Receivers: hardware/spi-receivers.md - - ESP Backpacks: - - Backpack Info: hardware/backpack/esp-backpack.md - - Tx Backpack Setup: hardware/backpack/backpack-tx-setup.md - - VRx Backpack Setup: hardware/backpack/backpack-vrx-setup.md - - HDZero Goggle VRx Backpack: hardware/backpack/hdzero-goggles.md - - Special Targets: - - DIY Receiver: hardware/special-targets/diy-rx.md - - DIY Transmitter: hardware/special-targets/diy-tx.md - - Nuclear Targets: hardware/special-targets/nuclear-hardware.md - - Info: - - Glossary: info/glossary.md - - Signal Health: info/signal-health.md - - Telemetry Bandwidth: info/telem-bandwidth.md - - Init Rate: info/init-rate.md - - Long Range Competition: info/long-range.md - - Advance Technical Info: info/advance-technical-info.md - - Privacy Policy: info/privacy-policy.md - - Licenses: info/licenses.md - - Terms & Conditions: info/terms-and-conditions.md - - FAQ: faq.md - - Blog: - - blog/index.md - - Partners Program: partners-program.md - - Contact Us: contact-us.md - -extra_javascript: - - assets/javascripts/binding-phrase.js - - https://cdnjs.cloudflare.com/ajax/libs/tablesort/5.2.1/tablesort.min.js - - https://cdnjs.cloudflare.com/ajax/libs/tablesort/5.2.1/sorts/tablesort.number.min.js - - assets/javascripts/tablesort.js - - assets/javascripts/mathjax.js - #- https://polyfill.io/v3/polyfill.min.js?features=es6 --db, removed - - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js - -INHERIT: redirects.yml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..0fd9ed52a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,9 @@ +[project] +name = "expresslrs-docs" +version = "0.1.0" +description = "ExpressLRS documentation site" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "zensical==0.0.32", +] diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000..8eb345b38 --- /dev/null +++ b/uv.lock @@ -0,0 +1,162 @@ +version = 1 +revision = 3 +requires-python = ">=3.11" + +[[package]] +name = "click" +version = "8.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/75/31212c6bf2503fdf920d87fee5d7a86a2e3bcf444984126f13d8e4016804/click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5", size = 302856, upload-time = "2026-04-03T19:14:45.118Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d", size = 108379, upload-time = "2026-04-03T19:14:43.505Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "deepmerge" +version = "2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/3a/b0ba594708f1ad0bc735884b3ad854d3ca3bdc1d741e56e40bbda6263499/deepmerge-2.0.tar.gz", hash = "sha256:5c3d86081fbebd04dd5de03626a0607b809a98fb6ccba5770b62466fe940ff20", size = 19890, upload-time = "2024-08-30T05:31:50.308Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/82/e5d2c1c67d19841e9edc74954c827444ae826978499bde3dfc1d007c8c11/deepmerge-2.0-py3-none-any.whl", hash = "sha256:6de9ce507115cff0bed95ff0ce9ecc31088ef50cbdf09bc90a09349a318b3d00", size = 13475, upload-time = "2024-08-30T05:31:48.659Z" }, +] + +[[package]] +name = "docs" +version = "0.1.0" +source = { virtual = "." } + +[package.dev-dependencies] +dev = [ + { name = "zensical" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [{ name = "zensical", specifier = ">=0.0.32" }] + +[[package]] +name = "markdown" +version = "3.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pymdown-extensions" +version = "10.21.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/08/f1c908c581fd11913da4711ea7ba32c0eee40b0190000996bb863b0c9349/pymdown_extensions-10.21.2.tar.gz", hash = "sha256:c3f55a5b8a1d0edf6699e35dcbea71d978d34ff3fa79f3d807b8a5b3fa90fbdc", size = 853922, upload-time = "2026-03-29T15:01:55.233Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/27/a2fc51a4a122dfd1015e921ae9d22fee3d20b0b8080d9a704578bf9deece/pymdown_extensions-10.21.2-py3-none-any.whl", hash = "sha256:5c0fd2a2bea14eb39af8ff284f1066d898ab2187d81b889b75d46d4348c01638", size = 268901, upload-time = "2026-03-29T15:01:53.244Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "zensical" +version = "0.0.32" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "deepmerge" }, + { name = "markdown" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/94/4a49ca9329136445f4111fda60e4bfcbe68d95e18e9aa02e4606fba5df4a/zensical-0.0.32.tar.gz", hash = "sha256:0f857b09a2b10c99202b3712e1ffc4d1d1ffa4c7c2f1aa0fafb1346b2d8df604", size = 3891955, upload-time = "2026-04-07T11:41:29.203Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/e1/dd03762447f1c2a4c8aff08e8f047ec17c73421714a0600ef71c361a5934/zensical-0.0.32-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:7ed181c76c03fec4c2dd5db207810044bf9c3fa87097fbdbabd633661e20fc70", size = 12416474, upload-time = "2026-04-07T11:40:55.888Z" }, + { url = "https://files.pythonhosted.org/packages/f5/a6/2f1babb00842c6efa5ae755b3ab414e4688ae8e47bdd2e785c0c37ef625d/zensical-0.0.32-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:8cde82bf256408f75ae2b07bffcaac7d080b6aad5f7acf210c438cb7413c3081", size = 12292801, upload-time = "2026-04-07T11:40:59.648Z" }, + { url = "https://files.pythonhosted.org/packages/2d/f1/d32706de06fd30fb07ae514222a79dd17d4578cd1634e5b692e0c790a61e/zensical-0.0.32-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60e60e2358249b2a2c5e1c5c04586d8dbba27e577441cc9dd32fe8d879c6951e", size = 12658847, upload-time = "2026-04-07T11:41:02.347Z" }, + { url = "https://files.pythonhosted.org/packages/e7/42/a3daf4047c86382749a59795c4e7acd59952b4f6f37f329cd2d41cc37a0f/zensical-0.0.32-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec79b4304009138e7a38ebe24e8a8e9dbc15d38922185f8a84470a7757d7b73f", size = 12604777, upload-time = "2026-04-07T11:41:05.227Z" }, + { url = "https://files.pythonhosted.org/packages/59/11/4af61d3fb07713cd3f77981c1b3017a60c2b210b36f1b04353f9116d03ca/zensical-0.0.32-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc92fa7d0860ec6d95426a5f545cfc5493c60f8ab44fcc11611a4251f34f1b70", size = 12956242, upload-time = "2026-04-07T11:41:07.58Z" }, + { url = "https://files.pythonhosted.org/packages/8c/34/e9b5f4376bbf460f8c07a77af59bd169c7c68ed719a074e6667ba41109f8/zensical-0.0.32-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07f69019396060e310c9c3b18747ce8982ad56d67fbab269b61e74a6a5bdcb4a", size = 12701954, upload-time = "2026-04-07T11:41:10.532Z" }, + { url = "https://files.pythonhosted.org/packages/d2/43/a52e5dcb324f38a1d22f7fafd4eec273385d04de52a7ab5ac7b444cf2bdc/zensical-0.0.32-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d096c9ed20a48e5ff095eca218eef94f67e739cdf0abf7e1f7e232e78f6d980c", size = 12835464, upload-time = "2026-04-07T11:41:13.152Z" }, + { url = "https://files.pythonhosted.org/packages/a7/95/bede89ecb4932bbd29db7b61bf530a962aed09d3a8d5aa71a64af1d4920f/zensical-0.0.32-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:bf5576b7154bde18cebd9a7b065d3ab8b334c6e73d5b2e83abe2b17f9d00a992", size = 12876574, upload-time = "2026-04-07T11:41:16.085Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e8/9b25fda22bf729ca2598cc42cefe9b20e751d12d23e35c70ea0c7939d20a/zensical-0.0.32-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:f33905a1e0b03a2ad548554a157b7f7c398e6f41012d1e755105ae2bc60eab8a", size = 13022702, upload-time = "2026-04-07T11:41:18.947Z" }, + { url = "https://files.pythonhosted.org/packages/f6/35/0c6d0b57187bd470a05e8a391c0edd1d690eb429e12b9755c99cf60a370e/zensical-0.0.32-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0a73a53b1dd41fd239875a3cb57c4284747989c45b6933f18e9b51f1b5f3d8ef", size = 12975593, upload-time = "2026-04-07T11:41:21.436Z" }, + { url = "https://files.pythonhosted.org/packages/ee/2d/4e88bcefc33b7af22f0637fd002d3cf5384e8354f0a7f8a9dbfcd40cfa24/zensical-0.0.32-cp310-abi3-win32.whl", hash = "sha256:f8cb579bdb9b56f1704b93f4e17b42895c8cb466e8eec933fbe0153b5b1e3459", size = 12012163, upload-time = "2026-04-07T11:41:23.975Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ae/a80a2f15fd10201fe3dfd6b5cdf85351165f820cf5b29e3c3b24092c158c/zensical-0.0.32-cp310-abi3-win_amd64.whl", hash = "sha256:6d662f42b5d0eadfac6d281e9d86574bc7a9f812f1ed496335d15f2d581d4b28", size = 12205948, upload-time = "2026-04-07T11:41:27.056Z" }, +] diff --git a/zensical.toml b/zensical.toml new file mode 100644 index 000000000..db9df7bf1 --- /dev/null +++ b/zensical.toml @@ -0,0 +1,287 @@ +[project] +site_name = "ExpressLRS" +site_url = "https://www.expresslrs.org/" +site_author = "ExpressLRS" +site_description = """ +ExpressLRS is a fully open radio control link built for maximum range, \ +speed, and data throughput. Wide hardware support across both 900 MHz \ +and 2.4 GHz frequencies.\ +""" +copyright = "Copyright © 2018–2026 ExpressLRS LLC" + +# Repository — used by content.action.edit / content.action.view +repo_name = "ExpressLRS/ExpressLRS" +repo_url = "https://github.com/ExpressLRS/ExpressLRS/releases/" +edit_uri = "https://github.com/ExpressLRS/Docs/tree/master/docs/" + +extra_css = [ + "assets/stylesheets/main.css", + "assets/stylesheets/colors.css", + "assets/stylesheets/home.css", +] + +extra_javascript = [ + "assets/javascripts/binding-phrase.js", + "https://cdnjs.cloudflare.com/ajax/libs/tablesort/5.2.1/tablesort.min.js", + "https://cdnjs.cloudflare.com/ajax/libs/tablesort/5.2.1/sorts/tablesort.number.min.js", + "assets/javascripts/tablesort.js", + "assets/javascripts/mathjax.js", + "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js", +] + +# --------------------------------------------------------------------------- +# Navigation — ported 1:1 from mkdocs.yml +# --------------------------------------------------------------------------- +nav = [ + { "Home" = "index.md" }, + { "Quick Start" = [ + { "Getting Started" = "quick-start/getting-started.md" }, + { "Installing the Configurator" = "quick-start/installing-configurator.md" }, + { "Firmware Options" = "quick-start/firmware-options.md" }, + { "The Web UI" = "quick-start/webui.md" }, + { "Transmitters" = [ + { "Radio Preparation" = "quick-start/transmitters/tx-prep.md" }, + { "Checking FW Version" = "quick-start/transmitters/firmware-version.md" }, + { "Typical Updating Steps" = "quick-start/transmitters/updating.md" }, + { "Updating Transmitters" = [ + { "Axisflying Thor" = "quick-start/transmitters/axisflying-thor.md" }, + { "BetaFPV 2.4GHz" = "quick-start/transmitters/betafpv2400.md" }, + { "BetaFPV 900MHz" = "quick-start/transmitters/betafpv900.md" }, + { "BetaFPV LR3 Pro" = "quick-start/transmitters/betafpvlr3pro.md" }, + { "EMAX 2.4GHz" = "quick-start/transmitters/emax2400.md" }, + { "EMAX 900MHz" = "quick-start/transmitters/emax900.md" }, + { "Frsky R9 Modules" = "quick-start/transmitters/frsky-r9modules.md" }, + { "Happymodel ES24TX" = "quick-start/transmitters/es24tx.md" }, + { "Happymodel ES900TX" = "quick-start/transmitters/es900tx.md" }, + { "HGLRC Hermes" = "quick-start/transmitters/hglrc-hermes.md" }, + { "iFlight Commando" = "quick-start/transmitters/iflight-commando.md" }, + { "IMRC Ghost" = "quick-start/transmitters/ghost2400.md" }, + { "Internal RadioMaster" = "quick-start/transmitters/rm-internal.md" }, + { "Internal Jumper T-Pro" = "quick-start/transmitters/aion-internal.md" }, + { "Internal Jumper TLite V2" = "quick-start/transmitters/jumper-internal.md" }, + { "Internal Jumper T14/T15" = "quick-start/transmitters/jumper-t14-t15-internal.md" }, + { "Internal Jumper T20" = "quick-start/transmitters/jumper-t20-internal.md" }, + { "Jumper Aion Nano" = "quick-start/transmitters/jumper-aion.md" }, + { "NamimnoRC Flash" = "quick-start/transmitters/flash2400.md" }, + { "NamimnoRC Voyager" = "quick-start/transmitters/voyager900.md" }, + { "Radiomaster Bandit" = "quick-start/transmitters/rm-bandit.md" }, + { "Radiomaster Ranger" = "quick-start/transmitters/rm-ranger.md" }, + { "Siyi FM30" = "quick-start/transmitters/siyifm30.md" }, + { "Vantac Lite" = "quick-start/transmitters/vantac-lite.md" }, + { "DIY 900TX" = "quick-start/transmitters/diy900.md" }, + { "DIY 2400TX" = "quick-start/transmitters/diy2400.md" }, + ] }, + { "The ExpressLRS Lua Script" = "quick-start/transmitters/lua-howto.md" }, + ] }, + { "Receivers" = [ + { "Receiver Wiring" = "quick-start/receivers/wiring-up.md" }, + { "Configuring FC" = "quick-start/receivers/configuring-fc.md" }, + { "Checking FW Version" = "quick-start/receivers/firmware-version.md" }, + { "Typical Updating Steps" = "quick-start/receivers/updating.md" }, + { "Updating Receivers" = [ + { "Axisflying Thor" = "quick-start/receivers/axisflying-thor.md" }, + { "BetaFPV Lite/Nano/AIO 2.4GHz" = "quick-start/receivers/betafpv2400.md" }, + { "BetaFPV Nano 900MHz" = "quick-start/receivers/betafpv900.md" }, + { "BetaFPV SuperD 2.4GHz" = "quick-start/receivers/betafpv-superd.md" }, + { "BetaFPV SuperD 900MHz" = "quick-start/receivers/betafpv-superd900.md" }, + { "Foxeer LNA & Lite 2.4GHz" = "quick-start/receivers/foxeer2400.md" }, + { "Frsky R9" = "quick-start/receivers/r9.md" }, + { "GEPRC Nano & Dual 2.4GHz" = "quick-start/receivers/geprc2400.md" }, + { "GEPRC Nano & Dual 900MHz" = "quick-start/receivers/geprc900.md" }, + { "Happymodel EP" = "quick-start/receivers/hmep2400.md" }, + { "Happymodel PP" = "quick-start/receivers/hmpp2400.md" }, + { "Happymodel ES900RX" = "quick-start/receivers/hmes900.md" }, + { "HGLRC Hermes 2.4GHz" = "quick-start/receivers/hglrc-hermes2400.md" }, + { "HGLRC Hermes 900MHz" = "quick-start/receivers/hglrc-hermes900.md" }, + { "iFlight 2.4GHz" = "quick-start/receivers/iflight2400.md" }, + { "iFlight 900MHz" = "quick-start/receivers/iflight900.md" }, + { "IMRC Ghost" = "quick-start/receivers/ghost2400.md" }, + { "Jumper Aion Nano" = "quick-start/receivers/jumper-aion.md" }, + { "Jumper R9 Mini" = "quick-start/receivers/jumper900.md" }, + { "Matek R24" = "quick-start/receivers/matek2400.md" }, + { "NamimnoRC Flash" = "quick-start/receivers/flash2400.md" }, + { "NamimnoRC Voyager" = "quick-start/receivers/voyager900.md" }, + { "RadioMaster RP" = "quick-start/receivers/radiomaster-rp-2400.md" }, + { "Siyi FR Mini" = "quick-start/receivers/siyiFRmini.md" }, + { "Vantac" = "quick-start/receivers/vantac2400.md" }, + { "DIY 900RX" = "quick-start/receivers/diy900.md" }, + { "DIY 2400RX" = "quick-start/receivers/diy2400.md" }, + ] }, + ] }, + { "Post-Flash" = [ + { "LED Status" = "quick-start/led-status.md" }, + { "Binding ExpressLRS" = "quick-start/binding.md" }, + { "Before First Flight" = "quick-start/pre-1stflight.md" }, + { "Ardupilot Setup" = "quick-start/ardupilot-setup.md" }, + ] }, + { "Troubleshooting" = "quick-start/troubleshooting.md" }, + { "Unbricking" = "quick-start/unbricking.md" }, + ] }, + { "Software" = [ + { "Obsolete Defines" = "software/obsolete-defines.md" }, + { "OpenOCD for STLink" = "software/open-ocd.md" }, + { "PIO STLink Fix" = "software/stlink-fix.md" }, + { "Switch Configs" = "software/switch-config.md" }, + { "Toolchain Setup" = "software/toolchain-install.md" }, + { "User Defines" = "software/user-defines.md" }, + { "Updating" = [ + { "Betaflight Passthrough" = "software/updating/betaflight-passthrough.md" }, + { "Wifi Updating" = "software/updating/wifi-updating.md" }, + ] }, + { "Features" = [ + { "AirPort" = "software/airport.md" }, + { "Dynamic Transmit Power" = "software/dynamic-transmit-power.md" }, + { "Gemini" = "software/gemini.md" }, + { "Loan Model" = "software/loan-model.md" }, + { "MAVLink" = "software/mavlink.md" }, + { "MFD Crossbow Tracker Integration" = "software/mfd-crossbow.md" }, + { "Model Matching" = "software/model-config-match.md" }, + { "Receiver Serial Protocols" = "software/serial-protocols.md" }, + { "Sentinel Tracker Integration" = "software/backpack-telemetry.md" }, + { "Serial VTX" = "software/serialvtx.md" }, + { "Team Racing" = "software/teamracing.md" }, + { "Trainer (Head Tracking) Input" = "software/trainer-input.md" }, + ] }, + { "Testing" = [ + { "CRC Testing" = "software/testing/crc-testing.md" }, + { "RX Testing" = "software/testing/rx-scoreboard.md" }, + { "Unit Testing" = "software/testing/unit-testing.md" }, + ] }, + ] }, + { "Hardware" = [ + { "Hardware Selection" = "hardware/hardware-selection.md" }, + { "R9M Inverter Mod" = "hardware/inverter-mod.md" }, + { "R9M Fan Mod" = "hardware/fan-mod.md" }, + { "Troubleshooting the X9D(+)" = "hardware/x9d-troubleshooting.md" }, + { "Crystal Oscillator (XO) Frequency Error" = "hardware/crystal-frequency-error.md" }, + { "SMD Antenna" = "hardware/smd-antenna.md" }, + { "PWM Receivers" = "hardware/pwm-receivers.md" }, + { "SPI Receivers" = "hardware/spi-receivers.md" }, + { "ESP Backpacks" = [ + { "Backpack Info" = "hardware/backpack/esp-backpack.md" }, + { "Tx Backpack Setup" = "hardware/backpack/backpack-tx-setup.md" }, + { "VRx Backpack Setup" = "hardware/backpack/backpack-vrx-setup.md" }, + { "HDZero Goggle VRx Backpack" = "hardware/backpack/hdzero-goggles.md" }, + ] }, + { "Special Targets" = [ + { "DIY Receiver" = "hardware/special-targets/diy-rx.md" }, + { "DIY Transmitter" = "hardware/special-targets/diy-tx.md" }, + { "Nuclear Targets" = "hardware/special-targets/nuclear-hardware.md" }, + ] }, + ] }, + { "Info" = [ + { "Glossary" = "info/glossary.md" }, + { "Signal Health" = "info/signal-health.md" }, + { "Telemetry Bandwidth" = "info/telem-bandwidth.md" }, + { "Init Rate" = "info/init-rate.md" }, + { "Long Range Competition" = "info/long-range.md" }, + { "Advance Technical Info" = "info/advance-technical-info.md" }, + { "Privacy Policy" = "info/privacy-policy.md" }, + { "Licenses" = "info/licenses.md" }, + { "Terms & Conditions" = "info/terms-and-conditions.md" }, + ] }, + { "FAQ" = "faq.md" }, + { "Blog" = [ + { "Blog" = "blog/index.md" }, + ] }, + { "Partners Program" = "partners-program.md" }, + { "Contact Us" = "contact-us.md" }, +] + +# --------------------------------------------------------------------------- +# Theme +# --------------------------------------------------------------------------- +[project.theme] +# TODO: re-enable `custom_dir = "overrides"` once the partials in +# overrides/ (header, footer, copyright, home.html, blog.html) are ported +# to Zensical's template context. The mkdocs-material variables they +# reference (e.g. config.extra.social, page.meta) don't exist in Zensical +# yet — enabling custom_dir floods the build with template errors. +language = "en" +favicon = "assets/favicon.png" +logo = "assets/images/logotype.svg" + +features = [ + "announce.dismiss", + "navigation.sections", + "navigation.tabs", + "navigation.footer", + "navigation.tracking", + "navigation.indexes", + "search.highlight", + "search.suggest", + "toc.follow", + "content.action.edit", + "content.action.view", +] + +[project.theme.font] +text = "Roboto" +code = "Roboto Mono" + +# Dark mode (default — matches the historical ExpressLRS palette) +[[project.theme.palette]] +media = "(prefers-color-scheme: dark)" +scheme = "slate" +toggle.icon = "lucide/moon" +toggle.name = "Switch to light mode" + +# Light mode +[[project.theme.palette]] +media = "(prefers-color-scheme: light)" +scheme = "default" +toggle.icon = "lucide/sun" +toggle.name = "Switch to dark mode" + +# --------------------------------------------------------------------------- +# Extra +# --------------------------------------------------------------------------- +[[project.extra.social]] +icon = "fontawesome/brands/github" +link = "https://github.com/ExpressLRS/" +name = "See the Github Repo" + +[[project.extra.social]] +icon = "fontawesome/brands/discord" +link = "https://discord.gg/dS6ReFY" +name = "Check out the Discord" + +[[project.extra.social]] +icon = "fontawesome/brands/facebook" +link = "https://www.facebook.com/groups/636441730280366" +name = "Join the Facebook Group" + +[[project.extra.social]] +icon = "fontawesome/brands/twitter" +link = "https://twitter.com/expresslrs" +name = "We're on Twitter too!" + +[[project.extra.social]] +icon = "fontawesome/brands/instagram" +link = "https://instagram.com/expresslrs" +name = "Follow us on Instagram" + +[[project.extra.social]] +icon = "fontawesome/solid/circle-dollar-to-slot" +link = "https://opencollective.com/expresslrs" +name = "Send a Donation!" + +# --------------------------------------------------------------------------- +# TODO — Zensical equivalents not yet wired up. The user accepted feature +# loss during migration; track these as follow-ups: +# +# * blog plugin (mkdocs-material insiders blog/posts/*) +# * tags plugin +# * minify plugin +# * git-revision-date-localized +# * git-committers +# * redirects.yml (mkdocs-redirects redirect_maps — see redirects.yml) +# * Google Analytics + cookie consent banner (project.extra.analytics / .consent) +# * overrides/hooks/blog_posts.py (injects recent posts into homepage) +# * pymdownx markdown extensions — Zensical's markdown_extensions surface is +# not yet documented; verify before re-enabling. Previous list: +# admonition, abbr, attr_list, def_list, footnotes, meta, md_in_html, +# tables, toc(permalink), pymdownx.{arithmatex, betterem, caret, critic, +# details, emoji, highlight, inlinehilite, keys, magiclink, mark, +# smartsymbols, superfences(mermaid), tabbed, tasklist, tilde} +# --------------------------------------------------------------------------- From 0d37f414931484cb82eea1136f87f5ab536bf83e Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Fri, 10 Jul 2026 09:05:15 +0300 Subject: [PATCH 02/21] Update Zensical to 0.0.50 --- Dockerfile | 2 +- Makefile | 2 +- pyproject.toml | 2 +- uv.lock | 186 ++++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 165 insertions(+), 27 deletions(-) diff --git a/Dockerfile b/Dockerfile index 67109193d..6eeb49855 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM zensical/zensical:0.0.32 +FROM zensical/zensical:0.0.50 # WORKDIR, EXPOSE 8000, ENTRYPOINT and the default # `serve --dev-addr=0.0.0.0:8000` CMD are inherited from the base image. diff --git a/Makefile b/Makefile index bf82fca87..664d4139b 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ .PHONY: install build run serve site shell spellcheck install: - pip install "zensical==0.0.32" + pip install "zensical==0.0.50" build: zensical build --clean diff --git a/pyproject.toml b/pyproject.toml index 0fd9ed52a..eb549e4f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,5 +5,5 @@ description = "ExpressLRS documentation site" readme = "README.md" requires-python = ">=3.11" dependencies = [ - "zensical==0.0.32", + "zensical==0.0.50", ] diff --git a/uv.lock b/uv.lock index 8eb345b38..05d9828c1 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.11" [[package]] @@ -33,19 +33,27 @@ wheels = [ ] [[package]] -name = "docs" +name = "expresslrs-docs" version = "0.1.0" source = { virtual = "." } - -[package.dev-dependencies] -dev = [ +dependencies = [ { name = "zensical" }, ] [package.metadata] +requires-dist = [{ name = "zensical", specifier = "==0.0.50" }] -[package.metadata.requires-dev] -dev = [{ name = "zensical", specifier = ">=0.0.32" }] +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] [[package]] name = "markdown" @@ -56,6 +64,80 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, ] +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + [[package]] name = "pygments" version = "2.20.0" @@ -67,15 +149,15 @@ wheels = [ [[package]] name = "pymdown-extensions" -version = "10.21.2" +version = "11.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/08/f1c908c581fd11913da4711ea7ba32c0eee40b0190000996bb863b0c9349/pymdown_extensions-10.21.2.tar.gz", hash = "sha256:c3f55a5b8a1d0edf6699e35dcbea71d978d34ff3fa79f3d807b8a5b3fa90fbdc", size = 853922, upload-time = "2026-03-29T15:01:55.233Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/a9/5f0c535ba3b08fe09270c16808e053a968868242ecbd5676d4e3a488bf28/pymdown_extensions-11.0.1.tar.gz", hash = "sha256:dd2905ae6fc5b75582fafb139a1266ffc754705efa902aa50067fa7ff4f94ec0", size = 857113, upload-time = "2026-07-02T17:59:22.955Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/27/a2fc51a4a122dfd1015e921ae9d22fee3d20b0b8080d9a704578bf9deece/pymdown_extensions-10.21.2-py3-none-any.whl", hash = "sha256:5c0fd2a2bea14eb39af8ff284f1066d898ab2187d81b889b75d46d4348c01638", size = 268901, upload-time = "2026-03-29T15:01:53.244Z" }, + { url = "https://files.pythonhosted.org/packages/d6/54/da572c98c0b77626a91b5d3b89f0231d8bff5125c225420908632f8b342d/pymdown_extensions-11.0.1-py3-none-any.whl", hash = "sha256:db3943a62bab7e03af1364f0c4083e64b91fb097675a4b6cceccfbe9a77e5eb2", size = 269455, upload-time = "2026-07-02T17:59:21.271Z" }, ] [[package]] @@ -133,30 +215,86 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + [[package]] name = "zensical" -version = "0.0.32" +version = "0.0.50" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "deepmerge" }, + { name = "jinja2" }, { name = "markdown" }, { name = "pygments" }, { name = "pymdown-extensions" }, { name = "pyyaml" }, + { name = "tomli" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/94/4a49ca9329136445f4111fda60e4bfcbe68d95e18e9aa02e4606fba5df4a/zensical-0.0.32.tar.gz", hash = "sha256:0f857b09a2b10c99202b3712e1ffc4d1d1ffa4c7c2f1aa0fafb1346b2d8df604", size = 3891955, upload-time = "2026-04-07T11:41:29.203Z" } +sdist = { url = "https://files.pythonhosted.org/packages/14/79/f7959b13c766a831f1248779bb718555f70f40c5b8e68db7de1de9936662/zensical-0.0.50.tar.gz", hash = "sha256:7040e52ebe5e6a275e4edeb351bf2bc314d007f3fb5750f178a38d840723e69c", size = 3979246, upload-time = "2026-07-09T13:52:11.908Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/e1/dd03762447f1c2a4c8aff08e8f047ec17c73421714a0600ef71c361a5934/zensical-0.0.32-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:7ed181c76c03fec4c2dd5db207810044bf9c3fa87097fbdbabd633661e20fc70", size = 12416474, upload-time = "2026-04-07T11:40:55.888Z" }, - { url = "https://files.pythonhosted.org/packages/f5/a6/2f1babb00842c6efa5ae755b3ab414e4688ae8e47bdd2e785c0c37ef625d/zensical-0.0.32-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:8cde82bf256408f75ae2b07bffcaac7d080b6aad5f7acf210c438cb7413c3081", size = 12292801, upload-time = "2026-04-07T11:40:59.648Z" }, - { url = "https://files.pythonhosted.org/packages/2d/f1/d32706de06fd30fb07ae514222a79dd17d4578cd1634e5b692e0c790a61e/zensical-0.0.32-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60e60e2358249b2a2c5e1c5c04586d8dbba27e577441cc9dd32fe8d879c6951e", size = 12658847, upload-time = "2026-04-07T11:41:02.347Z" }, - { url = "https://files.pythonhosted.org/packages/e7/42/a3daf4047c86382749a59795c4e7acd59952b4f6f37f329cd2d41cc37a0f/zensical-0.0.32-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec79b4304009138e7a38ebe24e8a8e9dbc15d38922185f8a84470a7757d7b73f", size = 12604777, upload-time = "2026-04-07T11:41:05.227Z" }, - { url = "https://files.pythonhosted.org/packages/59/11/4af61d3fb07713cd3f77981c1b3017a60c2b210b36f1b04353f9116d03ca/zensical-0.0.32-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc92fa7d0860ec6d95426a5f545cfc5493c60f8ab44fcc11611a4251f34f1b70", size = 12956242, upload-time = "2026-04-07T11:41:07.58Z" }, - { url = "https://files.pythonhosted.org/packages/8c/34/e9b5f4376bbf460f8c07a77af59bd169c7c68ed719a074e6667ba41109f8/zensical-0.0.32-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07f69019396060e310c9c3b18747ce8982ad56d67fbab269b61e74a6a5bdcb4a", size = 12701954, upload-time = "2026-04-07T11:41:10.532Z" }, - { url = "https://files.pythonhosted.org/packages/d2/43/a52e5dcb324f38a1d22f7fafd4eec273385d04de52a7ab5ac7b444cf2bdc/zensical-0.0.32-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d096c9ed20a48e5ff095eca218eef94f67e739cdf0abf7e1f7e232e78f6d980c", size = 12835464, upload-time = "2026-04-07T11:41:13.152Z" }, - { url = "https://files.pythonhosted.org/packages/a7/95/bede89ecb4932bbd29db7b61bf530a962aed09d3a8d5aa71a64af1d4920f/zensical-0.0.32-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:bf5576b7154bde18cebd9a7b065d3ab8b334c6e73d5b2e83abe2b17f9d00a992", size = 12876574, upload-time = "2026-04-07T11:41:16.085Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e8/9b25fda22bf729ca2598cc42cefe9b20e751d12d23e35c70ea0c7939d20a/zensical-0.0.32-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:f33905a1e0b03a2ad548554a157b7f7c398e6f41012d1e755105ae2bc60eab8a", size = 13022702, upload-time = "2026-04-07T11:41:18.947Z" }, - { url = "https://files.pythonhosted.org/packages/f6/35/0c6d0b57187bd470a05e8a391c0edd1d690eb429e12b9755c99cf60a370e/zensical-0.0.32-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0a73a53b1dd41fd239875a3cb57c4284747989c45b6933f18e9b51f1b5f3d8ef", size = 12975593, upload-time = "2026-04-07T11:41:21.436Z" }, - { url = "https://files.pythonhosted.org/packages/ee/2d/4e88bcefc33b7af22f0637fd002d3cf5384e8354f0a7f8a9dbfcd40cfa24/zensical-0.0.32-cp310-abi3-win32.whl", hash = "sha256:f8cb579bdb9b56f1704b93f4e17b42895c8cb466e8eec933fbe0153b5b1e3459", size = 12012163, upload-time = "2026-04-07T11:41:23.975Z" }, - { url = "https://files.pythonhosted.org/packages/8a/ae/a80a2f15fd10201fe3dfd6b5cdf85351165f820cf5b29e3c3b24092c158c/zensical-0.0.32-cp310-abi3-win_amd64.whl", hash = "sha256:6d662f42b5d0eadfac6d281e9d86574bc7a9f812f1ed496335d15f2d581d4b28", size = 12205948, upload-time = "2026-04-07T11:41:27.056Z" }, + { url = "https://files.pythonhosted.org/packages/c2/d2/e126d56642a5fd437eeb5a067b7c0d1f766c08e22d6a708bb923986f1d44/zensical-0.0.50-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:26dda9dbacab84db2743726f83202798e1893fbfddecb6a845812d7a331043ad", size = 12817706, upload-time = "2026-07-09T13:51:29.624Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9b/e1a22fb4ea8fc3e8377d7b394ee06cf0349e7f9a64ecb4c9873dee79b102/zensical-0.0.50-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:376887def6470c57509b48519870f74e2b987e30509cf5c819fcf372771f9403", size = 12691061, upload-time = "2026-07-09T13:51:33.194Z" }, + { url = "https://files.pythonhosted.org/packages/38/f5/16c087635680efb39e1d7b7aa3c2cca548305aa29ad51dee04f37b32b65f/zensical-0.0.50-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bbf3034a2cb1a9d2a72a5ee7047688717c93da0d8a90d25dd871db8d5f33a6d", size = 13129505, upload-time = "2026-07-09T13:51:36.383Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/50d02402e53aa01a5c5c116a3522c7bb18d6ae4da964d8b236173b6fa086/zensical-0.0.50-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26fe4167663d544ca385ef420a49d3b738c7606a79d7a62a6bedcbf74cd383e7", size = 13055300, upload-time = "2026-07-09T13:51:39.748Z" }, + { url = "https://files.pythonhosted.org/packages/cf/0f/45f4f3e56a6483ea9f88478b20d6b434718e7c46579239589202e5135ec1/zensical-0.0.50-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8813d1f8895b19520d4a982d6bfcfd06372d73d96e6a63f0d2cf8f3b6036d5a1", size = 13445447, upload-time = "2026-07-09T13:51:43.106Z" }, + { url = "https://files.pythonhosted.org/packages/33/9b/1f5065c664afde6a63e0ada8c14eae75f9e6960df597a5ca28c07340f938/zensical-0.0.50-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a64f957f94f7d8e8847a7f1e8205509ba4b188c93c157c5e7be49bcb8556a127", size = 13098775, upload-time = "2026-07-09T13:51:46.437Z" }, + { url = "https://files.pythonhosted.org/packages/46/65/1e42e18d8f9a0cb9fcc8b871852810c7fe196237b79aaa831e863325091e/zensical-0.0.50-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:781d333bcb42c6a6b92360e05eadc944e2674794e1a95537971de1bf64cb9f89", size = 13305922, upload-time = "2026-07-09T13:51:51.107Z" }, + { url = "https://files.pythonhosted.org/packages/50/36/9cace194ba13aef3bdde8ca2d327f3a05cdf5c456e94f6d896852e2cbd38/zensical-0.0.50-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:5d0f86eec76c23efb04566cd444bf025845ac8a02e75c8a6b4af13a39a2a9955", size = 13325488, upload-time = "2026-07-09T13:51:54.883Z" }, + { url = "https://files.pythonhosted.org/packages/b1/80/2891a9316e486794dae43151561b49f3313499cfb2c95c7886abad7bcf2d/zensical-0.0.50-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:392a7299a3c4d1ac7ff288bbed181d77a9fd23e6614180476e01630aaee0d67e", size = 13496866, upload-time = "2026-07-09T13:51:58.257Z" }, + { url = "https://files.pythonhosted.org/packages/7e/09/fbd3af58081a0d399fa913ef1f2fd6a007f2f06f7026c8d6a6096e34b996/zensical-0.0.50-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:cac3448d8c8a76dfb43e55908b4ca9ad17596aee89a9287a184c1a0ba7dce2a2", size = 13437565, upload-time = "2026-07-09T13:52:01.766Z" }, + { url = "https://files.pythonhosted.org/packages/d3/79/709311ff202326260ff223fb6e9ddefa3100474c428e3c70a04036cefe0e/zensical-0.0.50-cp310-abi3-win32.whl", hash = "sha256:32b244f96a930f68e049b2e03d50790c120ef701b6277ddd854905a33040c75f", size = 12371966, upload-time = "2026-07-09T13:52:05.207Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e1/4babb30544d7465c95a6a93abc0680b1c51752411a225c2b7f2cb44e80c7/zensical-0.0.50-cp310-abi3-win_amd64.whl", hash = "sha256:2f054769bf96ae46353de3eca2463ad4db6df1da9fde515c6d7fc54c3608e615", size = 12604832, upload-time = "2026-07-09T13:52:09.013Z" }, ] From f277ecd502a8a467f42a460631512dbdcf80daf3 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Fri, 10 Jul 2026 09:05:15 +0300 Subject: [PATCH 03/21] Port master changes to Zensical config Bring the post-merge state in line with what changed on master: - edit_uri now points at edit/master, navigation.path replaces navigation.indexes - Stub out the product finder page: it is rendered by an mkdocs hook and a custom override template that Zensical does not run yet, so the page links to Hardware Selection until it is ported - Drop the duplicate docker-compose.yml (master renamed it to compose.yml; the branch had recreated the old name) - Publish workflow installs Zensical via setup-uv and the pinned uv.lock instead of make/pip --- .github/workflows/publish.yml | 10 +++------- docker-compose.yml | 8 -------- docs/product-finder.md | 15 ++++++++++++++- zensical.toml | 9 ++++----- 4 files changed, 21 insertions(+), 21 deletions(-) delete mode 100644 docker-compose.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5d9c7d43c..3b5989979 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,15 +18,11 @@ jobs: - uses: actions/configure-pages@v5 - - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install Zensical - run: make install + - name: Install uv + uses: astral-sh/setup-uv@v8 - name: Build site - run: zensical build --clean + run: uv run zensical build --clean - name: Upload Pages artifact uses: actions/upload-pages-artifact@v4 diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 5ff1ae0bc..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -services: - docs: - build: . - image: expresslrs-docs - ports: - - "8000:8000" - volumes: - - .:/docs diff --git a/docs/product-finder.md b/docs/product-finder.md index c7371e858..2525c6494 100644 --- a/docs/product-finder.md +++ b/docs/product-finder.md @@ -1,8 +1,21 @@ --- -template: product-finder.html title: Product Finder description: Find ExpressLRS-compatible hardware by manufacturer, type, band, power, and more. hide: - navigation - toc --- + + + +# Product Finder + +The interactive product finder is temporarily unavailable while we migrate +the documentation site. In the meantime, check out the +[Hardware Selection](hardware/hardware-selection.md) guide. diff --git a/zensical.toml b/zensical.toml index ee62d4805..76faea0e0 100644 --- a/zensical.toml +++ b/zensical.toml @@ -18,7 +18,6 @@ extra_css = [ "assets/stylesheets/main.css", "assets/stylesheets/colors.css", "assets/stylesheets/home.css", - "assets/stylesheets/product-finder.css", ] extra_javascript = [ @@ -35,7 +34,6 @@ extra_javascript = [ # --------------------------------------------------------------------------- nav = [ { "Home" = "index.md" }, - { "Product Finder" = "product-finder.md" }, { "Quick Start" = [ { "Getting Started" = "quick-start/getting-started.md" }, { "Installing the Configurator" = "quick-start/installing-configurator.md" }, @@ -282,9 +280,10 @@ name = "Send a Donation!" # * Google Analytics + cookie consent banner (project.extra.analytics / .consent) # * overrides/hooks/blog_posts.py (injects recent posts into homepage) # * overrides/hooks/llms_txt.py (generates llms.txt / llms-full.txt) -# * overrides/hooks/product_catalog.py + overrides/product-finder.html -# (product finder page — nav entry + CSS are wired up, but the page is -# rendered by an mkdocs hook that Zensical doesn't run yet) +# * product finder page (docs/product-finder.md, excluded below) — rendered +# by overrides/hooks/product_catalog.py + overrides/product-finder.html + +# product-finder.css, none of which Zensical runs yet. Re-add the nav +# entry and extra_css when ported. # * pymdownx markdown extensions — Zensical's markdown_extensions surface is # not yet documented; verify before re-enabling. Previous list: # admonition, abbr, attr_list, def_list, footnotes, meta, md_in_html, From 7ae3ef8b1bfae0f4a3a91544b339efaa982b8061 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Fri, 10 Jul 2026 09:13:24 +0300 Subject: [PATCH 04/21] Implement product finder on Zensical The mkdocs implementation needed no build-time templating: the override template was a static HTML skeleton and the product_catalog.py hook just pre-generates assets/data/products.json, which product-finder.js loads client-side. So instead of waiting for Zensical's module system: - Inline the template's HTML skeleton into docs/product-finder.md as raw HTML (Zensical has no per-page template frontmatter yet); sidebars are hidden via the existing hide frontmatter - Make product_catalog.py runnable as a standalone script while keeping the on_pre_build hook entry point for mkdocs on master - Generate the catalog in CI before the build and add a make catalog wrapper for local refreshes - Restore the Product Finder nav entry and stylesheet in zensical.toml --- .github/workflows/publish.yml | 3 + Makefile | 5 +- docs/product-finder.md | 98 +++++++++++++++++++++++++++--- overrides/hooks/product_catalog.py | 10 +++ zensical.toml | 10 +-- 5 files changed, 112 insertions(+), 14 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3b5989979..b17b18206 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -21,6 +21,9 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v8 + - name: Generate product catalog + run: python3 overrides/hooks/product_catalog.py + - name: Build site run: uv run zensical build --clean diff --git a/Makefile b/Makefile index 664d4139b..4aa205908 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # Host-only convenience wrappers around `docker compose`. # CI invokes `docker compose` directly and never calls `make`. -.PHONY: install build run serve site shell spellcheck +.PHONY: install build run serve site shell spellcheck catalog install: pip install "zensical==0.0.50" @@ -20,5 +20,8 @@ site: shell: docker compose run --rm --entrypoint sh docs +catalog: + docker compose run --rm --entrypoint python3 docs overrides/hooks/product_catalog.py + spellcheck: pyspelling --config .spellcheck.yml --spellchecker aspell --name Markdown diff --git a/docs/product-finder.md b/docs/product-finder.md index 2525c6494..5f0dbb7f6 100644 --- a/docs/product-finder.md +++ b/docs/product-finder.md @@ -7,15 +7,95 @@ hide: --- -# Product Finder +
+
+ +
+

Product Finder

+

Every device listed here is officially supported - tested and approved by the ExpressLRS development team. + + + + +

+
+
+ +
+
+

Filters

+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+

Loading catalog...

+
+
+
+
+
+
-The interactive product finder is temporarily unavailable while we migrate -the documentation site. In the meantime, check out the -[Hardware Selection](hardware/hardware-selection.md) guide. + diff --git a/overrides/hooks/product_catalog.py b/overrides/hooks/product_catalog.py index 923466f32..738c6e389 100644 --- a/overrides/hooks/product_catalog.py +++ b/overrides/hooks/product_catalog.py @@ -6,6 +6,11 @@ Skips the build if products.json already exists. Delete the file to force a refresh. + +Runs as an mkdocs hook (on_pre_build) or as a standalone script for +builders like Zensical that have no hook support: + + python3 overrides/hooks/product_catalog.py """ import json @@ -251,3 +256,8 @@ def _read_layout(hardware_dir, layout_file, category): def _compact(record): return {k: record[k] for k in COMPACT_FIELDS if record.get(k) is not None and record.get(k) != ""} + + +if __name__ == "__main__": + _repo_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + on_pre_build({"docs_dir": os.path.join(_repo_root, "docs")}) diff --git a/zensical.toml b/zensical.toml index 76faea0e0..b57b30200 100644 --- a/zensical.toml +++ b/zensical.toml @@ -18,6 +18,7 @@ extra_css = [ "assets/stylesheets/main.css", "assets/stylesheets/colors.css", "assets/stylesheets/home.css", + "assets/stylesheets/product-finder.css", ] extra_javascript = [ @@ -34,6 +35,7 @@ extra_javascript = [ # --------------------------------------------------------------------------- nav = [ { "Home" = "index.md" }, + { "Product Finder" = "product-finder.md" }, { "Quick Start" = [ { "Getting Started" = "quick-start/getting-started.md" }, { "Installing the Configurator" = "quick-start/installing-configurator.md" }, @@ -280,10 +282,10 @@ name = "Send a Donation!" # * Google Analytics + cookie consent banner (project.extra.analytics / .consent) # * overrides/hooks/blog_posts.py (injects recent posts into homepage) # * overrides/hooks/llms_txt.py (generates llms.txt / llms-full.txt) -# * product finder page (docs/product-finder.md, excluded below) — rendered -# by overrides/hooks/product_catalog.py + overrides/product-finder.html + -# product-finder.css, none of which Zensical runs yet. Re-add the nav -# entry and extra_css when ported. +# * product finder — works, but the mkdocs override template is inlined +# as raw HTML in docs/product-finder.md (Zensical has no per-page +# `template:` frontmatter yet), and assets/data/products.json must be +# generated before the build: python3 overrides/hooks/product_catalog.py # * pymdownx markdown extensions — Zensical's markdown_extensions surface is # not yet documented; verify before re-enabling. Previous list: # admonition, abbr, attr_list, def_list, footnotes, meta, md_in_html, From f8b96ddcaf574ccb50e1f10a2fc4a09881e1e9e0 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Fri, 10 Jul 2026 09:20:51 +0300 Subject: [PATCH 05/21] Restore the real homepage via template overrides Zensical 0.0.50 renders our Material template overrides, so enable custom_dir and drop the placeholder homepage: - docs/index.md and docs/product-finder.md are restored to master's versions; home.html, main.html (announcement banner) and product-finder.html all render via per-page template frontmatter - Guard page.parent lookups in the footer partial: Zensical's template engine errors on attribute access of undefined values where Jinja2 tolerated it (the guard behaves identically under mkdocs) - Set the expresslrs/expresslrs-dark palette names so colors.css header and accent colors apply again The homepage Latest News section stays hidden until blog_posts.py has a Zensical equivalent - the template skips it when recent_posts is absent. --- docs/index.md | 19 +------ docs/product-finder.md | 95 +--------------------------------- overrides/partials/footer.html | 8 +-- zensical.toml | 32 +++++++----- 4 files changed, 24 insertions(+), 130 deletions(-) diff --git a/docs/index.md b/docs/index.md index eaf044fd3..0d027f29a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,21 +1,4 @@ --- +template: home.html title: High Performance Open Source Radio Control Link --- - -# ExpressLRS - -ExpressLRS is a fully open radio control link built for maximum range, -speed, and data throughput. Wide hardware support across both 900 MHz and -2.4 GHz frequencies. - - - -- [Getting Started](quick-start/getting-started.md) -- [Hardware Selection](hardware/hardware-selection.md) -- [FAQ](faq.md) -- [Blog](blog/index.md) diff --git a/docs/product-finder.md b/docs/product-finder.md index 5f0dbb7f6..c7371e858 100644 --- a/docs/product-finder.md +++ b/docs/product-finder.md @@ -1,101 +1,8 @@ --- +template: product-finder.html title: Product Finder description: Find ExpressLRS-compatible hardware by manufacturer, type, band, power, and more. hide: - navigation - toc --- - - - -
-
- -
-

Product Finder

-

Every device listed here is officially supported - tested and approved by the ExpressLRS development team. - - - - -

-
-
- -
-
-

Filters

- -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- -
-

Loading catalog...

-
-
-
-
-
-
- - diff --git a/overrides/partials/footer.html b/overrides/partials/footer.html index ecbc18311..2dd62a024 100644 --- a/overrides/partials/footer.html +++ b/overrides/partials/footer.html @@ -29,11 +29,11 @@ {% if page.previous_page %} {% set direction = lang.t("footer.previous") %} - {% if page.parent.title == "Flashing Transmitters" %} + {% if page.parent and page.parent.title == "Flashing Transmitters" %} {% set prev_page_url = "quick-start/transmitters/tx-prep/" %} {% set prev_page_title = "Preparing your Radio" %}
', + '
', + ] + return "\n".join(lines) + "\n" + + +if __name__ == "__main__": + _repo_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + _posts = _collect_posts(os.path.join(_repo_root, "docs", "blog", "posts")) + _output_path = os.path.join(_repo_root, "overrides", "partials", "recent-posts.html") + if _posts: + with open(_output_path, "w", encoding="utf-8") as f: + f.write(_render_partial(_posts)) + print(f"blog_posts: wrote {len(_posts)} posts to {_output_path}") + else: + print("blog_posts: no posts found, skipping") diff --git a/overrides/hooks/llms_txt.py b/overrides/hooks/llms_txt.py index 287048190..94058b916 100644 --- a/overrides/hooks/llms_txt.py +++ b/overrides/hooks/llms_txt.py @@ -1,4 +1,11 @@ -"""Hook to generate llms.txt at build time from the nav configuration.""" +"""Hook to generate llms.txt at build time from the nav configuration. + +Runs as an mkdocs hook (on_post_build) or as a standalone script for +builders like Zensical that have no hook support — run it after the +site has been built: + + python3 overrides/hooks/llms_txt.py +""" import os import yaml @@ -116,3 +123,20 @@ def _format_link(title, url, description=None): if description: return f"- [{title}]({url}): {description}" return f"- [{title}]({url})" + + +if __name__ == "__main__": + import tomllib + + _repo_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + with open(os.path.join(_repo_root, "zensical.toml"), "rb") as f: + _project = tomllib.load(f)["project"] + on_post_build({ + "site_name": _project["site_name"], + "site_url": _project["site_url"], + "site_description": _project.get("site_description", ""), + "docs_dir": os.path.join(_repo_root, "docs"), + "site_dir": os.path.join(_repo_root, "site"), + "nav": _project["nav"], + }) + print("llms_txt: wrote site/llms.txt") diff --git a/overrides/hooks/redirects.py b/overrides/hooks/redirects.py new file mode 100644 index 000000000..0f94cb246 --- /dev/null +++ b/overrides/hooks/redirects.py @@ -0,0 +1,81 @@ +"""Generate static redirect pages from redirects.yml. + +Under mkdocs the mkdocs-redirects plugin consumes redirects.yml (via +INHERIT) and writes meta-refresh stub pages. Zensical has no redirects +support yet, so this standalone script does the same thing — run it +after the site has been built: + + python3 overrides/hooks/redirects.py +""" + +import html +import os +import yaml + +STUB = """\ + + + + + + + + Redirecting… + + + Redirecting to {url}… + + + +""" + + +def _src_to_dir(src_path): + """Map an old source path to its pretty-URL output directory.""" + path = src_path.replace("\\", "/") + if path.endswith("/index.md"): + return path[: -len("index.md")] + if path == "index.md": + return "" + return path[: -len(".md")] + "/" if path.endswith(".md") else path + + +def _target_to_url(target, site_url): + """Map a redirect target (external URL or source path) to a URL.""" + if target.startswith(("http://", "https://")): + return target + path, anchor = (target.split("#", 1) + [""])[:2] + url = site_url + _src_to_dir(path) + return f"{url}#{anchor}" if anchor else url + + +def main(repo_root): + with open(os.path.join(repo_root, "redirects.yml"), encoding="utf-8") as f: + redirect_maps = yaml.safe_load(f)["plugins"]["redirects"]["redirect_maps"] + + import tomllib + with open(os.path.join(repo_root, "zensical.toml"), "rb") as f: + site_url = tomllib.load(f)["project"]["site_url"].rstrip("/") + "/" + + site_dir = os.path.join(repo_root, "site") + if not os.path.isdir(site_dir): + raise SystemExit("redirects: site/ not found — build the site first") + + count = 0 + for src, target in redirect_maps.items(): + out_dir = os.path.join(site_dir, _src_to_dir(src)) + out_path = os.path.join(out_dir, "index.html") + if os.path.exists(out_path): + print(f"redirects: skipping {src} — a real page exists at {out_path}") + continue + url = html.escape(_target_to_url(target, site_url), quote=True) + os.makedirs(out_dir, exist_ok=True) + with open(out_path, "w", encoding="utf-8") as f: + f.write(STUB.format(url=url)) + count += 1 + + print(f"redirects: wrote {count} redirect stubs to {site_dir}") + + +if __name__ == "__main__": + main(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) diff --git a/zensical.toml b/zensical.toml index d74e0e3be..690dfa2f2 100644 --- a/zensical.toml +++ b/zensical.toml @@ -241,6 +241,41 @@ toggle.name = "Switch to dark mode" # --------------------------------------------------------------------------- # Extra # --------------------------------------------------------------------------- +[project.extra.analytics] +provider = "google" +property = "G-VMF2B5BYJZ" + +[project.extra.analytics.feedback] +title = "Was this page helpful?" + +[[project.extra.analytics.feedback.ratings]] +icon = "material/emoticon-happy-outline" +name = "This page was helpful" +data = 1 +note = "Thanks for your feedback!" + +[[project.extra.analytics.feedback.ratings]] +icon = "material/emoticon-sad-outline" +name = "This page could be improved" +data = 0 +note = """ +Thanks for your feedback! Help us improve this page by using our \ +discord server!\ +""" + +[project.extra.consent] +title = "Cookie consent" +description = """ +We use cookies to recognize your repeated visits and preferences, as well \ +as to measure the effectiveness of our documentation and whether users \ +find what they're searching for. With your consent, you're helping us to \ +make our documentation better.\ +""" +actions = ["accept", "manage", "reject"] + +[project.extra.consent.analytics] +name = "Google Analytics" + [[project.extra.social]] icon = "fontawesome/brands/github" link = "https://github.com/ExpressLRS/" @@ -272,31 +307,32 @@ link = "https://opencollective.com/expresslrs" name = "Send a Donation!" # --------------------------------------------------------------------------- -# TODO — Zensical equivalents not yet wired up. The user accepted feature -# loss during migration; track these as follow-ups: +# TODO — blocked on upstream Zensical support (all "planned" in the +# Zensical backlog, see https://zensical.org/compatibility/plugins/): # -# * blog plugin (mkdocs-material insiders blog/posts/*) +# * blog plugin — posts render as plain pages, but blog/index.md has no +# post listing, archive, or category pages yet # * tags plugin -# * minify plugin -# * git-revision-date-localized -# * git-committers -# * redirects.yml (mkdocs-redirects redirect_maps — see redirects.yml) # * social cards plugin (cards_layout_options background #5f8bf3) -# * Google Analytics + cookie consent banner (project.extra.analytics / .consent) -# * overrides/hooks/blog_posts.py (injects recent posts into homepage — -# without it the "Latest News" section on the homepage is skipped) -# * overrides/hooks/llms_txt.py (generates llms.txt / llms-full.txt) +# * minify plugin (HTML output is currently unminified) +# * git-revision-date-localized + git-committers (page footer dates and +# contributor avatars) +# +# Handled outside Zensical until its module system ships — the mkdocs +# hooks in overrides/hooks/ also run as standalone scripts, wired into +# .github/workflows/publish.yml and `make site`: +# * product_catalog.py (before build) → docs/assets/data/products.json +# * blog_posts.py (before build) → overrides/partials/ +# recent-posts.html, included by home.html for the Latest News section +# * llms_txt.py (after build) → site/llms.txt +# * redirects.py (after build) → meta-refresh stubs from +# redirects.yml (mkdocs-redirects equivalent) # -# Working via custom_dir/overrides (no follow-up needed): -# * homepage (overrides/home.html via `template:` frontmatter) and the -# announcement banner (overrides/main.html) -# * product finder (overrides/product-finder.html); assets/data/ -# products.json must be generated before the build: -# python3 overrides/hooks/product_catalog.py (or `make catalog`) -# * pymdownx markdown extensions — Zensical's markdown_extensions surface is -# not yet documented; verify before re-enabling. Previous list: -# admonition, abbr, attr_list, def_list, footnotes, meta, md_in_html, -# tables, toc(permalink), pymdownx.{arithmatex, betterem, caret, critic, -# details, emoji, highlight, inlinehilite, keys, magiclink, mark, -# smartsymbols, superfences(mermaid), tabbed, tasklist, tilde} +# Working natively (verified): +# * template overrides via custom_dir (home.html, main.html announcement +# banner, product-finder.html, partials/) +# * Google Analytics, cookie consent, and the feedback widget via +# project.extra above +# * pymdownx markdown extensions (admonitions, tabs, keys, emoji, +# arithmatex, critic/mark, superfences+mermaid) render out of the box # --------------------------------------------------------------------------- From 48afc4924996f5b910c0acfba6d0251d88f52387 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Fri, 10 Jul 2026 09:35:04 +0300 Subject: [PATCH 08/21] Trim resolved items from the TODO list --- zensical.toml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/zensical.toml b/zensical.toml index 690dfa2f2..3974c74d1 100644 --- a/zensical.toml +++ b/zensical.toml @@ -327,12 +327,4 @@ name = "Send a Donation!" # * llms_txt.py (after build) → site/llms.txt # * redirects.py (after build) → meta-refresh stubs from # redirects.yml (mkdocs-redirects equivalent) -# -# Working natively (verified): -# * template overrides via custom_dir (home.html, main.html announcement -# banner, product-finder.html, partials/) -# * Google Analytics, cookie consent, and the feedback widget via -# project.extra above -# * pymdownx markdown extensions (admonitions, tabs, keys, emoji, -# arithmatex, critic/mark, superfences+mermaid) render out of the box # --------------------------------------------------------------------------- From e8b222af6611171bfb3486f51da32656fc6bd840 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Fri, 10 Jul 2026 09:39:57 +0300 Subject: [PATCH 09/21] Fix spellcheck workflow after Makefile changes The workflow still called make install-python-packages, which the Zensical Makefile no longer has. Pin pyspelling in a uv dependency group instead and invoke it directly, matching the publish workflow's uv setup. The make spellcheck convenience target now uses the same command. --- .github/workflows/spellcheck.yml | 6 +- Makefile | 3 +- pyproject.toml | 5 + uv.lock | 211 +++++++++++++++++++++++++++++++ 4 files changed, 221 insertions(+), 4 deletions(-) diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index 396edcdd8..5a63d1135 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -14,7 +14,7 @@ jobs: uses: actions/checkout@v5 - name: Install aspell run: sudo apt-get install -y aspell aspell-en - - name: Install Python packages - run: make install-python-packages + - name: Install uv + uses: astral-sh/setup-uv@v8 - name: Check Spelling - run: make spellcheck + run: uv run --group spellcheck pyspelling --config .spellcheck.yml --spellchecker aspell --name Markdown diff --git a/Makefile b/Makefile index afc77f858..0e9a07ac7 100644 --- a/Makefile +++ b/Makefile @@ -29,5 +29,6 @@ shell: catalog: docker compose run --rm --entrypoint python3 docs overrides/hooks/product_catalog.py +# Needs aspell + aspell-en installed on the host spellcheck: - pyspelling --config .spellcheck.yml --spellchecker aspell --name Markdown + uv run --group spellcheck pyspelling --config .spellcheck.yml --spellchecker aspell --name Markdown diff --git a/pyproject.toml b/pyproject.toml index eb549e4f9..786432800 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,3 +7,8 @@ requires-python = ">=3.11" dependencies = [ "zensical==0.0.50", ] + +[dependency-groups] +spellcheck = [ + "pyspelling==2.12.1", +] diff --git a/uv.lock b/uv.lock index 05d9828c1..30d5e5ff4 100644 --- a/uv.lock +++ b/uv.lock @@ -2,6 +2,28 @@ version = 1 revision = 2 requires-python = ">=3.11" +[[package]] +name = "beautifulsoup4" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/65/318323f98dbee45d42dff61d8f047181bc6f2268a9068cfad035a46be5af/beautifulsoup4-4.15.0.tar.gz", hash = "sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7", size = 632571, upload-time = "2026-06-07T16:44:20.453Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl", hash = "sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9", size = 109924, upload-time = "2026-06-07T16:44:21.566Z" }, +] + +[[package]] +name = "bracex" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/f5/4473ad9b48cd0420a2d762a3750fa0e078e23e060b1af72662e5987e5530/bracex-3.0.tar.gz", hash = "sha256:b73f718d6bd98d8419e45df02426c86e9967c179949f779340d6c3a8c83b9111", size = 43162, upload-time = "2026-06-30T00:43:35.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/2e/68781b78e764e5ccc4af1e3d27e060069c73af90234853fa80000e7ee79d/bracex-3.0-py3-none-any.whl", hash = "sha256:3833e61c2f092d5aa0468fa2e6c6e990a306185abf763b6d122f0158e59c58a5", size = 11738, upload-time = "2026-06-30T00:43:34.196Z" }, +] + [[package]] name = "click" version = "8.3.2" @@ -40,9 +62,30 @@ dependencies = [ { name = "zensical" }, ] +[package.dev-dependencies] +spellcheck = [ + { name = "pyspelling" }, +] + [package.metadata] requires-dist = [{ name = "zensical", specifier = "==0.0.50" }] +[package.metadata.requires-dev] +spellcheck = [{ name = "pyspelling", specifier = "==2.12.1" }] + +[[package]] +name = "html5lib" +version = "1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, + { name = "webencodings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/b6/b55c3f49042f1df3dcd422b7f224f939892ee94f22abcf503a9b7339eaf2/html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f", size = 272215, upload-time = "2020-06-22T23:32:38.834Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/dd/a834df6482147d48e225a49515aabc28974ad5a4ca3215c18a882565b028/html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d", size = 112173, upload-time = "2020-06-22T23:32:36.781Z" }, +] + [[package]] name = "jinja2" version = "3.1.6" @@ -55,6 +98,108 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] +[[package]] +name = "lxml" +version = "6.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/3b/aab6728cae887456f409b4d75e8a01856e4f04bd510de38052a47768b680/lxml-6.1.1.tar.gz", hash = "sha256:ba96ae44888e0185281e937633a743ea90d5a196c6000f82565ebb0580012d40", size = 4197430, upload-time = "2026-05-18T19:19:06.424Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/b0/83f481780d1548750b8ce2ec824073deef2f452d9cd1a6faff8507e3d16d/lxml-6.1.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:53b7d2b7a10b1c35c0a5e21e9224accf60c1bbfba523990732e521b2b73adef2", size = 8526461, upload-time = "2026-05-18T19:17:25.862Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/30fa0f808002c7329397bfbb24e306789c0b29f04aa5842c07b174b4216f/lxml-6.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3f333630ab480244a1bff72043e511a91eb22e7595dead8653ee5612dd8f3d", size = 4595375, upload-time = "2026-05-18T19:17:34.555Z" }, + { url = "https://files.pythonhosted.org/packages/4f/d2/edb71cf0e561581a7c5eb2626244320eb04e9f8ce6d563184fd668b45073/lxml-6.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a4bbea04c97f6d78a48e3fbc1cb9116d2780b1b39e03a23f6eb9b603fd61f510", size = 4923654, upload-time = "2026-05-18T19:17:42.917Z" }, + { url = "https://files.pythonhosted.org/packages/4c/77/1bc7eeb0de4577d783fb625aa092cc9357883bba35845a3666bf1259f3dc/lxml-6.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db1d75f6617a49c1c01bc7023713e0ff59ab32c9579ae62a7674c0e34f3b0b0a", size = 5067921, upload-time = "2026-05-18T19:17:49.175Z" }, + { url = "https://files.pythonhosted.org/packages/1b/3c/c0690d74bd2bc17bc03b5b0d093569ead597dd0bfa088bf99eef8c24e19c/lxml-6.1.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a12689be69a28ddaa0ab99a5a1137da2afd5f8f16df7b5680b66f616d3eda1d", size = 5002456, upload-time = "2026-05-18T19:17:59.715Z" }, + { url = "https://files.pythonhosted.org/packages/66/8d/d1b3271af0c0f1e27e8472a849e4d2c65bc7766884b9ad2da9e76e145c88/lxml-6.1.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b73c339ae29b90fd2d06e58ebd555a751bde9cd6bbd36cc0281b9a2c94e9d8", size = 5202776, upload-time = "2026-05-18T19:18:08.924Z" }, + { url = "https://files.pythonhosted.org/packages/7a/45/689824ffb237fd10125ad273f32b28ff04dc6203c2822c85ff65a93df65e/lxml-6.1.1-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:752d3bbfe874715ccd0aec7f88d7fc623c0f1fd7aa7b3238a084e017bad2a009", size = 5329945, upload-time = "2026-05-18T19:18:13.673Z" }, + { url = "https://files.pythonhosted.org/packages/5d/c0/ef73af53767e958fd87d437c170f272e2f6e6c0f854939f133a895f1e711/lxml-6.1.1-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:6b1761fbf9ec984e2e9d9c589ef5f5fd684b7c19f92aadd567a26c5224958db6", size = 4659237, upload-time = "2026-05-18T19:18:18.657Z" }, + { url = "https://files.pythonhosted.org/packages/a0/5e/e1158e40397585e91cb0472374a1f63d0926a1ddeaa92f13d1a1ffe306d5/lxml-6.1.1-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d680fbcb768404c601ecb43519ecd8461f6954cb11c06a78962f666832ccfca8", size = 5265904, upload-time = "2026-05-18T19:18:24.883Z" }, + { url = "https://files.pythonhosted.org/packages/a0/16/8687e5d1400ed1c0bc41dace232ebb7553952b618ea1f2e5fb6e2cfbbe23/lxml-6.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:162af1091cd785f2f27e62d3547ae9bc58ec5c86dd314d67021fd02463708d83", size = 5045225, upload-time = "2026-05-18T19:17:20.073Z" }, + { url = "https://files.pythonhosted.org/packages/ca/18/d877bd1ae2e5ffdfd4836565aba350db31feb2f2656d6ce70316ed66a05e/lxml-6.1.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e9308ff8241c532df3f3e570f9a5aeed6c853f888512ba4b75638d7c11c95ef6", size = 4712721, upload-time = "2026-05-18T19:17:40.512Z" }, + { url = "https://files.pythonhosted.org/packages/44/4d/1f44fd1d770b10dacbf6b5c6e520f4d6e0708744930f719dc04e67cab981/lxml-6.1.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5f6994074ebae6ffb04447268e37dc16edc304f9859cf91acb86e0af6c1b395c", size = 5252549, upload-time = "2026-05-18T19:17:51.236Z" }, + { url = "https://files.pythonhosted.org/packages/64/5d/1d66b84f850089254c230ef6ea6b267a5a54e2e179a5d960036a05d501d7/lxml-6.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:80c2dfadb855da477cf73373ad29a333535dedb9b12bad02c9814c8e2b43bf08", size = 5226877, upload-time = "2026-05-18T19:18:00.875Z" }, + { url = "https://files.pythonhosted.org/packages/ad/00/84c4b5302d42a2d0184f38d538c8a197f33b52a50bd4f7bcfe990bce3036/lxml-6.1.1-cp311-cp311-win32.whl", hash = "sha256:30a89d3ac8faec007453fb541f3f46807eeec88edd5826f6e3fe001752a2c621", size = 3594072, upload-time = "2026-05-18T19:17:12.714Z" }, + { url = "https://files.pythonhosted.org/packages/61/9d/2e2f7d876349f45e0f3e29f72da311668853d59b58d473a2dea4f0160135/lxml-6.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:abbefa31eee84842140f67acef1c828e28bba8bbf0c3bc6e5492a9af88152c28", size = 4025469, upload-time = "2026-05-18T19:17:50.566Z" }, + { url = "https://files.pythonhosted.org/packages/b0/d5/570e6390e4110331e6208b2ba83d1482cc9146808ee118b22824a34c1070/lxml-6.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:dcb292aa7fe485ceff7af4f92e46c5af397daec5dff64871a528f0fc47a3cc5b", size = 3667640, upload-time = "2026-05-19T19:22:48.293Z" }, + { url = "https://files.pythonhosted.org/packages/6a/6e/c4add832b6fc1e887125b96f880d7b9b70aae5248718e046b1704bcac4b9/lxml-6.1.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:104c09bda8d2a562824c0e319d0768ce26a779b7601e0931d33b09b53c392ef7", size = 8570821, upload-time = "2026-05-18T19:17:42.068Z" }, + { url = "https://files.pythonhosted.org/packages/22/00/ff3009c88e65de8011630acf8ab5a09cb2becd2aaf47fba2f3449f6224e9/lxml-6.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:25c6997a9a534e016695a0ba06b2f07945de682731ff01065b6d5a4474179da1", size = 4624252, upload-time = "2026-05-18T19:17:47.897Z" }, + { url = "https://files.pythonhosted.org/packages/42/95/bb63f0fd62e554fe078e1fb3c8fe9083c14ddc7ad7fa178d10e57e071ac7/lxml-6.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c921ba5c51e4e9f63b8b00267d06566e1f63407408a0496da2d1d0bfc819c7fc", size = 4930746, upload-time = "2026-05-18T19:18:29.637Z" }, + { url = "https://files.pythonhosted.org/packages/eb/99/0013e8d9b5960f4f041cf0b73e2f80c23eb5205b1f7bfb20203243651359/lxml-6.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:54a7f95e4de5fb94e2f9f4b9055c6ba33bf3d628fd77a1d647c5923caa2cdcdc", size = 5093723, upload-time = "2026-05-18T19:18:34.168Z" }, + { url = "https://files.pythonhosted.org/packages/29/91/317b332636bfc7bddcff828d41b3307f50043f4b237e40849c333d80fa1a/lxml-6.1.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f2ec43df44b1f76249ee0a615334f9b5b060e1c8bd90e706dad2d14d02f383", size = 5005557, upload-time = "2026-05-18T19:18:39.798Z" }, + { url = "https://files.pythonhosted.org/packages/42/2f/cc9bf06afe70f9c9093ae60855d9759da9db601ec4080f7473319666ffd7/lxml-6.1.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:70ef8a7e102a1508f8121aae5b0867abd663f72c14f0a9c937e6554cb4587b7b", size = 5631036, upload-time = "2026-05-18T19:18:44.858Z" }, + { url = "https://files.pythonhosted.org/packages/08/f6/af32e23e563971ffb0fb86be52bc5be5c2c118858ffc119bf6a9039b173d/lxml-6.1.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ebe6af670449830d6d9b752c256a983291c766a1365ba5d5460048f9e33a7818", size = 5240367, upload-time = "2026-05-18T19:18:49.217Z" }, + { url = "https://files.pythonhosted.org/packages/78/83/8555d40948b09ce86f1bd0c68a7ac31d07b1929f92cc1b074006c97ef2d2/lxml-6.1.1-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:27acc820660aaffa4f7c087f29120e12980f7779d56d8492d263170111284740", size = 5350171, upload-time = "2026-05-18T19:18:52.779Z" }, + { url = "https://files.pythonhosted.org/packages/63/75/5d92da93729b7bad783689e6496049fa40927b45bec7bf183c981de3ca70/lxml-6.1.1-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:1db753c9115ec7100d073b744d17e25e88a8f90f5c39b2f5dd878149af59671f", size = 4694874, upload-time = "2026-05-18T19:18:55.139Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b5/3aad415a9a25b822e783f15deeb4dffccf5113030f1afa2222dd929313d9/lxml-6.1.1-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4f469aebd783bb741c2ecb2a681008fd26bfe5c16a9a72ed5467f834e810df2", size = 5244492, upload-time = "2026-05-18T19:19:01.28Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a1/5fcf7eb9904b80086aa47dcf0027de07b1bb990afad2e6823144c368ae04/lxml-6.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:766b010012d59470072c1816b5b6c69f1d243e5db36ea5968e94accf430a4635", size = 5048232, upload-time = "2026-05-18T19:18:12.67Z" }, + { url = "https://files.pythonhosted.org/packages/77/74/1f601b63c7a69fcdf10fa9b148c81da8442204194f6c55509cc485c786b9/lxml-6.1.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b8d812c6011c08b8111a15e54dd990b8923692d80adf35488bee34026c35accf", size = 4777023, upload-time = "2026-05-18T19:18:15.928Z" }, + { url = "https://files.pythonhosted.org/packages/a2/b9/7a78f51aec95b1bf780d78e12705a9f6533284f8693dc5c0e6724fa53d3f/lxml-6.1.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:fe0306bd29505a9177aac19f1877174b0e7422c222a59f70b2cd41633448c3dc", size = 5645773, upload-time = "2026-05-18T19:18:23.223Z" }, + { url = "https://files.pythonhosted.org/packages/a5/6e/98a7b7ad54e4e74fa1f20fff776913980619d0ebe5558232d7da6580bdd8/lxml-6.1.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5ba186ad207446c65d3bb3d3e0412b032b1d9f595e59861e2354798c5703d955", size = 5233088, upload-time = "2026-05-18T19:18:31.433Z" }, + { url = "https://files.pythonhosted.org/packages/65/d1/bc0ed2427bf609f2ee10da303a6a226f9c8bce94f945dc29a32ce55de6e4/lxml-6.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aa366a1e55b8ebfe8ca8ddc3cfe75c8ebade181aeb0f661d0cb05986b647f72a", size = 5260995, upload-time = "2026-05-18T19:18:37.091Z" }, + { url = "https://files.pythonhosted.org/packages/69/8b/6772e1a4b513fc50a8d931f19edde0e13ae6918510a1e13ff67864f3e5ed/lxml-6.1.1-cp312-cp312-win32.whl", hash = "sha256:126c93f7f56f0eda92f6d8c619edc463a4f23d9252f1c9d0405a76f25fa9f11a", size = 3596382, upload-time = "2026-05-18T19:17:18.37Z" }, + { url = "https://files.pythonhosted.org/packages/1b/89/45198e9624762af2dfd2cb8782598477ceb29f6e59caab560388ae1f4ec1/lxml-6.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:26e6eda8d38c1fcab1090dd196ee87cbd13788e531937610e2589085de074e77", size = 3997255, upload-time = "2026-05-18T19:17:56.781Z" }, + { url = "https://files.pythonhosted.org/packages/90/a9/7a54b6834088d9ae528a7b780584ba6a39a9457b0ac330479f20ffbc9449/lxml-6.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:6540377fbd53fe1b629172288c464fb18db11ce1fa7dc15891da10aa9dcc3e7f", size = 3659610, upload-time = "2026-05-19T19:22:50.843Z" }, + { url = "https://files.pythonhosted.org/packages/a5/eb/7e6f37c5584ccbb2ff267f56fd0339016938c1c8684cfefab9b33ffc2f36/lxml-6.1.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:68a9198d0fc122d14bb76837de9aa80cf84caed990b5b237f532ed87d3706736", size = 8559780, upload-time = "2026-05-18T19:17:57.661Z" }, + { url = "https://files.pythonhosted.org/packages/a1/36/587c2521cf23a2cd6c9c22108aa7528f683a1f195ed7ccd23a4b1786ad36/lxml-6.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7d47866cb32fb503450b6edc9df355d10dc49836af2e89901bd6ac6b0896d9d9", size = 4618006, upload-time = "2026-05-18T19:18:04.452Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ca/ab7bfe2bf4c972af5e7878262845ead3a24a929a9b04bc11c7c1ece6c82a/lxml-6.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb7c9811bfaa8b1ed5ed319f5d370dfbcaa59d52ea64be2a5a85e18195930354", size = 4924139, upload-time = "2026-05-18T19:19:04.873Z" }, + { url = "https://files.pythonhosted.org/packages/6b/55/a0c72851dfee5ecc689f949723a73dea457758912542cb955b108eaf0d8f/lxml-6.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:762ff394d5bd56da0cf034a23dcce4e13923f15321a2adfa2ac00201dc6d3fca", size = 5082329, upload-time = "2026-05-18T19:19:09.728Z" }, + { url = "https://files.pythonhosted.org/packages/f0/b6/0608f7d61a3b96cc67e5648a3d906e31a5082093e10e7be65b3886289938/lxml-6.1.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a088f287f7d8275a33c07f2cac6c50b9319309a0200a39e7e75d80c707723099", size = 4993564, upload-time = "2026-05-18T19:19:13.608Z" }, + { url = "https://files.pythonhosted.org/packages/4c/66/ae227524b066d29d55bf0b453d93d2d793c40218657d643dcbbca13b8faf/lxml-6.1.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e902da4b04e6b52e5893900d4b8ab46068f75f3561f01bf1080957f9fd932ed6", size = 5613467, upload-time = "2026-05-18T19:19:16.228Z" }, + { url = "https://files.pythonhosted.org/packages/a6/76/dbe4a00b50385e40194231dcfe5a12c059de7cf90e89c83407d2b085b719/lxml-6.1.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d4962d4c66bf830a7e59ed6cfc17d148149898a3aefa8ec6e59763e6e3ed085", size = 5228304, upload-time = "2026-05-18T19:19:19.354Z" }, + { url = "https://files.pythonhosted.org/packages/1c/01/00b1b8442ed2041793336868ba0b9ea4b13d7da7c085c6404c207a63bf79/lxml-6.1.1-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:581d4c8ae690a6609e64862dd6b7c2489635c2d13907fc2b20f2bc200ff1d21e", size = 5341607, upload-time = "2026-05-18T19:19:22.297Z" }, + { url = "https://files.pythonhosted.org/packages/63/36/1ad29931e9a4638bb707869f01d423a6c815f82152138d1a40dfcfde2b95/lxml-6.1.1-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:876e1ff5930ed8bf295ec5ef9a8155e9b6b1876bbf1deed8b3a8069311875a8f", size = 4700168, upload-time = "2026-05-18T19:19:25.133Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d1/a9536cecf9be18a0dc72d32bead283a2332d1ffebd2dd3ac70ce444686e5/lxml-6.1.1-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9eb9b5a968f6e0f6d640092a567e14529ff8cea2e29d00da6f78a79fa49f013c", size = 5232487, upload-time = "2026-05-18T19:19:28.603Z" }, + { url = "https://files.pythonhosted.org/packages/0e/77/b4fb1e03bf5d130e879214d3100092e386418807fb74dd0adc4b0a48f351/lxml-6.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:aa49e06d94aba782c6a02eecb7e507969e7e7a41b267f1b359bb35585f295d5b", size = 5044231, upload-time = "2026-05-18T19:18:42.246Z" }, + { url = "https://files.pythonhosted.org/packages/26/4c/d00daeeb0a5530c4028a9232aa1b93db3ef4ed2158c116ea73c79a9765b3/lxml-6.1.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:70cdfd80589d59e43e18005dd7244e8895e93db8ab6a620b7e23df5445a4e3d2", size = 4769450, upload-time = "2026-05-18T19:18:48.013Z" }, + { url = "https://files.pythonhosted.org/packages/ed/6a/715a3a8d156ce42f29cf014706f5410c2ff3b02267774110fc23266409fe/lxml-6.1.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:aad9aa39483ed8ec44d6d2e59e5b98a0d80676ef0d92f44bfc374836111f62f5", size = 5635874, upload-time = "2026-05-18T19:18:51.914Z" }, + { url = "https://files.pythonhosted.org/packages/45/37/0544bc21dde2a88f3a17b504e6fc79c0e01d25a33c2f6079724e9e72b9c7/lxml-6.1.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:d49514be2f28d895c38cf9d2b72d7b9a07d00314519f456c0b50b53cfcf4c785", size = 5223987, upload-time = "2026-05-18T19:18:59.715Z" }, + { url = "https://files.pythonhosted.org/packages/4d/f8/f6a5e8185bcb28c2befae3d31f8e3df3b811cb0f47746517a81279fcafe1/lxml-6.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:47402e62c52ff5988c1e8c6c63177f5708bccf48e366dea4e3dcf1e645e04947", size = 5250276, upload-time = "2026-05-18T19:19:03.834Z" }, + { url = "https://files.pythonhosted.org/packages/c7/f2/1a2b9f1b7a49d45495369be7ef9ad05b262930f2eab3e3145706fca8083f/lxml-6.1.1-cp313-cp313-win32.whl", hash = "sha256:3483644525531e1d5762b0c44a8e18b6efba321b6dcf8a8952de10b037618bca", size = 3596903, upload-time = "2026-05-18T19:17:29.863Z" }, + { url = "https://files.pythonhosted.org/packages/e6/99/f4ffb024f238eec2131aaa09f3278fb6129cf892741bf68e1fc1afb8c100/lxml-6.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:a10bd2fd62e8ce916ececb342f348f190724a098c1faa056fdfb2a22ad5e8660", size = 3995869, upload-time = "2026-05-18T19:18:02.596Z" }, + { url = "https://files.pythonhosted.org/packages/d1/53/70eb8c5c6037f27448f1e3c54ebede9545a801ae63f0a7254afca4fe8e45/lxml-6.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:424aa57aca0897eb922aef34395bd1289b3b6f04e6bae20ea123c0c7e333cffc", size = 3658490, upload-time = "2026-05-19T19:22:53.846Z" }, + { url = "https://files.pythonhosted.org/packages/13/e2/2e325795566de01d0d7c3bb57d3c370616b2d07b01214e84eec5d3b10963/lxml-6.1.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:19b7ab10b210b0b3ad7985d9ac4eb66ab09a90b20fe6e2f7ba55d01a234345d0", size = 8577146, upload-time = "2026-05-18T19:18:17.765Z" }, + { url = "https://files.pythonhosted.org/packages/93/cf/5630b5e4be7d2e6bee8efe83865c925221103cf0221303b104ce134b01e2/lxml-6.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c08e5c694306507275f2290073350c4f32e383db15213b2c69e7ff39c1193840", size = 4623866, upload-time = "2026-05-18T19:18:30.669Z" }, + { url = "https://files.pythonhosted.org/packages/d2/51/3904907c063451cf8d4a5c9fe0cad95fa1f4ec57f4e3884fa0731bd7a305/lxml-6.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:74a9717fd0d82effef5c2854f0d917231d5324b5a3eb7275c43ac9fa32f97a14", size = 4950022, upload-time = "2026-05-18T19:19:31.958Z" }, + { url = "https://files.pythonhosted.org/packages/94/cd/9c7611a51c37a2830928405817cc5d56a97f64fab83cc3f628748b135749/lxml-6.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efe0374196335f93b53269acd811b944f2e6bdc88e8894f214bd636455484909", size = 5086695, upload-time = "2026-05-18T19:19:34.764Z" }, + { url = "https://files.pythonhosted.org/packages/da/d6/24e3b5906abb0b674ff2ae195bc3ce59708df2bcd17cf17703b2d7dd643a/lxml-6.1.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac931cdc9442c1763b8a8f6cd62c0c938737eafc5be75eff88df55fc73bc0d00", size = 5031642, upload-time = "2026-05-18T19:19:37.771Z" }, + { url = "https://files.pythonhosted.org/packages/2d/db/6ec54f99019838bff54785c51da07f189eb4676861c5f2730962b0d8d665/lxml-6.1.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:aee395f5d0927f947758b4ec119fd5fc8ec71f07a1c5c52077b30b04c0fa6955", size = 5647338, upload-time = "2026-05-18T19:19:40.553Z" }, + { url = "https://files.pythonhosted.org/packages/42/3d/ef4dcfffd22d27a61805d8ed9f7fb888495bc6aa88648fa07c1eaa5586b6/lxml-6.1.1-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9395002973c827b3ed67db77e6ec09f092919a587022174554096a269378fb13", size = 5239528, upload-time = "2026-05-18T19:19:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/62/bb/37fb3f0dff146bdcfa78eec47879273820b2a0bf350ec236ce14bd0b1c26/lxml-6.1.1-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:73bc2086f141224ebddb7fc5c6a36ca58b31b94b561e1dfe8e073e3270fad1e7", size = 5350730, upload-time = "2026-05-18T19:19:46.307Z" }, + { url = "https://files.pythonhosted.org/packages/90/42/43253f168388df4fae1f38c01df36ddb9bee39e2048167b54cdcbae85ea3/lxml-6.1.1-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:3779def59032b81e44a5f70096ef6bf2082f8d901937dca354474ba09782e245", size = 4697530, upload-time = "2026-05-18T19:19:49.889Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a8/c5a8504f81bbdfc8e7094c2c850cdb4ed6777fc4d5ddd9e5ab819f3b0d54/lxml-6.1.1-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:86c89b9d55ebf820ad7c90bc533410f0d098054f293351f10603c0c46ff598f5", size = 5250670, upload-time = "2026-05-18T19:19:53.199Z" }, + { url = "https://files.pythonhosted.org/packages/77/b7/c7e76ab18744d75e21f320ebf9ff9d1ceae2b54dd431ea5a64caf26c9672/lxml-6.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19607c6bbff2a44cf3fe8250abccd20942d3462473e0a721d01d379ed017e462", size = 5084485, upload-time = "2026-05-18T19:19:08.422Z" }, + { url = "https://files.pythonhosted.org/packages/31/31/b35c53f8ef7b7c31cacd23d3638652fff7bcd1deb6eedb709ab43b685908/lxml-6.1.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c6ed5141a5c7507cf3ee76bd363b0d6f801e3321adc35b5d825a23115faa5465", size = 4737635, upload-time = "2026-05-18T19:19:12.321Z" }, + { url = "https://files.pythonhosted.org/packages/d9/06/31f23c813a7fe8e0cb1b175e915b08c9bf4e86d225b210feadbdbe519667/lxml-6.1.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:62aeb7e85b5d60320b9d77eef2e773994e2c0ce10121b277e0a19804e1654a5a", size = 5670681, upload-time = "2026-05-18T19:19:15.001Z" }, + { url = "https://files.pythonhosted.org/packages/1a/bc/ce619bccc89b1fd9ad8a8e1330ee3f3beff9f2ff95b712d7bbcdd6e22fc3/lxml-6.1.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b1b963fd8f5caa68e99dfae060d54de1fe9cba899b8718b44a00cdca53c3e590", size = 5238229, upload-time = "2026-05-18T19:19:18.131Z" }, + { url = "https://files.pythonhosted.org/packages/2f/5d/b329acbbedc0b619ebc2be6cf7ee9ed07e80892c88d4dfd612c33805789a/lxml-6.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63876be28efefa04a1df615b46770e82042cce445cfdce55160522f57b231ccb", size = 5264191, upload-time = "2026-05-18T19:19:21.118Z" }, + { url = "https://files.pythonhosted.org/packages/d6/85/be36fb1425b30db3c3f9df75fe86343ebffb79e6320bd7f588e25bfeac39/lxml-6.1.1-cp314-cp314-win32.whl", hash = "sha256:7f7a92e8583f06b1fd49d01158143b8461cfcd135dcb10ec807270a3051bd603", size = 3657202, upload-time = "2026-05-18T19:17:39.509Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ce/3cf9a827342269f54d405a6202397de63f07c69cbd6ce7d183a3f0cba1e9/lxml-6.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:b2d444f2e66624d68e9c6b211e28a76e22fff5fcabcfff4deac18b529b7d4137", size = 4064497, upload-time = "2026-05-18T19:18:14.662Z" }, + { url = "https://files.pythonhosted.org/packages/d9/3e/1a957bde8f0760039e627f94699f82caa782c9d838d86c3d28245ee67212/lxml-6.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:3fd9728a2735fda14f4e8235830c86b539e9661e849665bf926d3f867943b4bf", size = 3741991, upload-time = "2026-05-19T19:22:59.111Z" }, + { url = "https://files.pythonhosted.org/packages/78/b2/00ed55b3a2efa4658fb795c38d1090ec9b3e8a6c3683d4441fa517f09c3b/lxml-6.1.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:787b2496d0dbe8cd180984e8d29e3a6f76e7ea34db781cb3bd55e4ba1ef8b4ee", size = 8827545, upload-time = "2026-05-18T19:18:41.193Z" }, + { url = "https://files.pythonhosted.org/packages/c0/73/74573db19baa618d5f266f2407898b087ff6927115b00b71e5fc1b700847/lxml-6.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2c8daa471358dc2d6fcf02165e80ec68f77871a286df95bc5cc3816153b0fd2c", size = 4735736, upload-time = "2026-05-18T19:18:46.761Z" }, + { url = "https://files.pythonhosted.org/packages/16/02/6f7061f4f95f51e545d48e87647c54791d204a4e881be4156e7a26ba5338/lxml-6.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:acd7d70b64c0aae0c7922cca83d288a16f5f6da523637697872253415269baef", size = 4970291, upload-time = "2026-05-18T19:19:56.215Z" }, + { url = "https://files.pythonhosted.org/packages/b0/02/55fc057d8283427dea7d6edb102e7a840239c77a64a983d92f62a304c0e9/lxml-6.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4f0dd2f01f9f8a89f565d000e03abcf0a13d692a346c8d22f628d49af098777a", size = 5102822, upload-time = "2026-05-18T19:19:59.223Z" }, + { url = "https://files.pythonhosted.org/packages/e4/48/8e1cf78d89d66850121d9255a2a24414c98f775da93b90cf976956c24b14/lxml-6.1.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b7e8a14c8634bf6f7a568634cb395305a6d964aeb5b7ee32248094bed3a7e2c", size = 5027923, upload-time = "2026-05-18T19:20:01.549Z" }, + { url = "https://files.pythonhosted.org/packages/ed/00/0632a0647612c8af24d26997b3b961397daa9d5b2581444805933629a4cb/lxml-6.1.1-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:86281fbdd6a8162756f8d603f37e3435bfa38043adb79c6dc6a2dfee065e7525", size = 5595843, upload-time = "2026-05-18T19:20:03.93Z" }, + { url = "https://files.pythonhosted.org/packages/bc/86/ab008a7dc360711b66858d61c80a5979a70a09f2aa2b05d9698df80b803d/lxml-6.1.1-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5d7152ec39ca7c402d8fb9bad86140a15b9503bd0c54484e3f1bbe3dd37ceca", size = 5224515, upload-time = "2026-05-18T19:20:06.381Z" }, + { url = "https://files.pythonhosted.org/packages/75/c6/2702ff375e728e34f56d9a45339a9cf7e4427e917f542225242d63a05afa/lxml-6.1.1-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:88d8cb75b9d82858497a5393e3c63cfbf03035225e4b35a49ed7ccb151e4dc0e", size = 5312511, upload-time = "2026-05-18T19:20:09.308Z" }, + { url = "https://files.pythonhosted.org/packages/b7/57/a5807c98f87a86f10ef9ffab35516df7c0f0c4b6d5d33e9f608ab9c04a31/lxml-6.1.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f64ec5397ea6a41fc1b4af0380d79b44a755b5531dcaccd9940fb260dca93038", size = 4639206, upload-time = "2026-05-18T19:20:11.704Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e1/8a0a2c35734812395f4da4eaf33748a7e5705bfb2a58b128da764339d5ec/lxml-6.1.1-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d34bbf07dbc7ca5970671b1512e928991fb5e9d95365636c9b2d8b4f53af405e", size = 5232404, upload-time = "2026-05-18T19:20:14.064Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e2/0e6a4dd5ad84d01d99aa7bae7cfefd4a760a0e0f8176818241de17d9b6c0/lxml-6.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:17e0e18d4ad8adbd0399291bc44845b69d9dd68439a3cdebdf35ff902ec05072", size = 5083769, upload-time = "2026-05-18T19:19:23.758Z" }, + { url = "https://files.pythonhosted.org/packages/a0/7e/161f33d463f6ffc1c7679104b65086dea120080d49dde4d238f015aaee2f/lxml-6.1.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:3ab541146f1f6968c462d6c2ac495148e8cdba2f8347700b2141b6ec5a75bf52", size = 4758936, upload-time = "2026-05-18T19:19:27.256Z" }, + { url = "https://files.pythonhosted.org/packages/f1/fb/2369825e3f6ca99305bf9f7b7085fda91c8b0922a89e54d900974aa3ef85/lxml-6.1.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2a0217714657e023ef4293500f65aa20fce6164c8fd6b08fa5bd4a859fb14b9b", size = 5620296, upload-time = "2026-05-18T19:19:29.993Z" }, + { url = "https://files.pythonhosted.org/packages/30/90/d61e383146f74c5ab683947ea14dc7b82778838ab9b95ea73a23b60d0191/lxml-6.1.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:05a82eb6e1530a64f26225b55cbd178113bd0b5af1c2b625f25e5296742c26d2", size = 5228598, upload-time = "2026-05-18T19:19:33.523Z" }, + { url = "https://files.pythonhosted.org/packages/76/2d/2dafd8149e94b05bb070690efd5bb2680720681e03ff03fc57d2b70a1105/lxml-6.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9e36f163528fc50cbef305f02a5fd66d404edf7049cdaff211dbc2cba5a7013e", size = 5247845, upload-time = "2026-05-18T19:19:36.649Z" }, + { url = "https://files.pythonhosted.org/packages/ce/68/b30e913340c380ddac9580c6e6230991fc37240ec4f64704833e4f3e2769/lxml-6.1.1-cp314-cp314t-win32.whl", hash = "sha256:649dda677cf3bd6ac9ae14007ba0c824ded8ce5808b53fc7431d9140399118c1", size = 3897345, upload-time = "2026-05-18T19:17:33.562Z" }, + { url = "https://files.pythonhosted.org/packages/3c/4e/9eb2af5335545f9fbcd7af57bcf87c6025d31eaa31b14ec184a6c8675328/lxml-6.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:793033d6c5cdf33a573f910d9bea14ef8f5771820411d118da8e1182edb53d5e", size = 4393350, upload-time = "2026-05-18T19:18:10.076Z" }, + { url = "https://files.pythonhosted.org/packages/7f/2c/0f1e93c636720e8a3eb59af2bfda99d98b55891e1c53bc30c2e0e865f01b/lxml-6.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:58bb955caba94e467d2a96da17660d2d704e0675894cba21ab8a775b8621fd1c", size = 3817223, upload-time = "2026-05-19T19:22:56.823Z" }, + { url = "https://files.pythonhosted.org/packages/b5/32/86a3f0f724a3a402d4627937a7fc27b160e45e7012b4adf47f6e1e844511/lxml-6.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:31033dc34636ea6b7d5cc11b1ddbda78a14de858ba9d3e1ed4b69a3085bc521e", size = 3930127, upload-time = "2026-05-18T19:19:02.27Z" }, + { url = "https://files.pythonhosted.org/packages/40/44/d832e82af08723761556d004b1d04d281c09f9a8cecd7d3148548c9941a3/lxml-6.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3893c14c4b6ac5b2d54ba8cf03e99fe5104e592de491f19bd6b82756c09f8004", size = 4210769, upload-time = "2026-05-18T19:20:41.427Z" }, + { url = "https://files.pythonhosted.org/packages/6d/39/0dc5949f759ed7d951e0bb8c2f2d9d7aca1908d22352fa84a8afd2ea54af/lxml-6.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c07da4cebf6889f03ebac8d238f62318e29f495de0aa18a51ea14e61ae907e2e", size = 4318163, upload-time = "2026-05-18T19:20:44.702Z" }, + { url = "https://files.pythonhosted.org/packages/e6/fb/8ab3845fe046ba4cbf74536bcf6801a774b7caf4350de1c5d37f1f0a9e90/lxml-6.1.1-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6f0ce10945fab9c4c06ce14e22af9059d1a87493a9af4501a5b0b9187e21cf2", size = 4250945, upload-time = "2026-05-18T19:20:47.385Z" }, + { url = "https://files.pythonhosted.org/packages/68/1b/7553ab136894374ffae8851ec06f98f511cd8e66246e41b6be059d0a7289/lxml-6.1.1-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f8844cd288697c6425c9beba919302241e3278871dc6519515e72b04e987abcf", size = 4401664, upload-time = "2026-05-18T19:20:50.489Z" }, + { url = "https://files.pythonhosted.org/packages/db/a4/441aee36c6f6b249823d20fd91f9be9ab89d7c5a8ae542a4a4ca6d342d56/lxml-6.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:ed21202aec73cda4d55d1ce57b389aadb90ffb044e6cd1080b8347efe1b1ec84", size = 3508989, upload-time = "2026-05-18T19:18:38.158Z" }, +] + [[package]] name = "markdown" version = "3.10.2" @@ -160,6 +305,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d6/54/da572c98c0b77626a91b5d3b89f0231d8bff5125c225420908632f8b342d/pymdown_extensions-11.0.1-py3-none-any.whl", hash = "sha256:db3943a62bab7e03af1364f0c4083e64b91fb097675a4b6cceccfbe9a77e5eb2", size = 269455, upload-time = "2026-07-02T17:59:21.271Z" }, ] +[[package]] +name = "pyspelling" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "html5lib" }, + { name = "lxml" }, + { name = "markdown" }, + { name = "pyyaml" }, + { name = "soupsieve" }, + { name = "wcmatch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fe/ed/3fe00b8a3f8a74a993e1e08931d674cde7bd718c1af44ef415ff1f6b000b/pyspelling-2.12.1.tar.gz", hash = "sha256:9108981dc174d6400a7ed4917cf4af91daf3eb83578d4570c0726b92c2a32e7a", size = 163855, upload-time = "2025-11-26T21:46:59.282Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/af/2526f6300004267ee82d3f3ca2eaa4830232852edb99deb2db751427234f/pyspelling-2.12.1-py3-none-any.whl", hash = "sha256:0213266bea0acb4877541499087dc89d69e04808867a49270094773047a66f25", size = 45388, upload-time = "2025-11-26T21:46:58.112Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -215,6 +378,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "soupsieve" +version = "2.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/2c/0a5f6f8ee0d5589e48c7640213ed5175d52cf540a06725b628cc1a45d6ce/soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e", size = 121110, upload-time = "2026-05-24T13:55:57.154Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304, upload-time = "2026-05-24T13:55:55.406Z" }, +] + [[package]] name = "tomli" version = "2.4.1" @@ -269,6 +450,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, ] +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "wcmatch" +version = "11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bracex" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/25/1da725838132221e33568973da484ff43813662ccc06ebf7f6e3abddfcd5/wcmatch-11.0.tar.gz", hash = "sha256:55d95c2447789712774b198ceec72939e88b5618f1f8f0a9b605bf7740b63b96", size = 141360, upload-time = "2026-07-10T05:50:24.183Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/12/f38b6fee116274d7221743caab07d765032e1370bb54cad8714f87aeb0e8/wcmatch-11.0-py3-none-any.whl", hash = "sha256:3a5977ace27e075eef67eb03d539563f1a19018b62881949a42932cf66926934", size = 42914, upload-time = "2026-07-10T05:50:22.995Z" }, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, +] + [[package]] name = "zensical" version = "0.0.50" From 5afe7adc8698d4556fe37cfad3751f7362abab21 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Fri, 10 Jul 2026 09:41:06 +0300 Subject: [PATCH 10/21] Pin setup-uv to v8.3.2 astral-sh/setup-uv publishes no floating v8 major tag, so @v8 fails to resolve on the runner. --- .github/workflows/publish.yml | 2 +- .github/workflows/spellcheck.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 37832f00d..e14a5b6dc 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/configure-pages@v5 - name: Install uv - uses: astral-sh/setup-uv@v8 + uses: astral-sh/setup-uv@v8.3.2 - name: Generate product catalog and homepage news run: | diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index 5a63d1135..5c3f9d16d 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -15,6 +15,6 @@ jobs: - name: Install aspell run: sudo apt-get install -y aspell aspell-en - name: Install uv - uses: astral-sh/setup-uv@v8 + uses: astral-sh/setup-uv@v8.3.2 - name: Check Spelling run: uv run --group spellcheck pyspelling --config .spellcheck.yml --spellchecker aspell --name Markdown From 8a5c4caa28a6783fafb0f67c80a1af380908f952 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Fri, 10 Jul 2026 10:00:19 +0300 Subject: [PATCH 11/21] Ignore local TODO.md scratch notes --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 15d568e08..ade9a89ef 100644 --- a/.gitignore +++ b/.gitignore @@ -150,3 +150,6 @@ docs/assets/data/products.json # Generated homepage Latest News partial (overrides/hooks/blog_posts.py) overrides/partials/recent-posts.html + +# Local scratch notes +TODO.md From 107a6177ed154bdd8b48d583d87f562adcd53fac Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 3 Oct 2026 22:44:32 +0300 Subject: [PATCH 12/21] Use native Zensical blog, tags, social, minify and redirects Bump Zensical 0.0.50 -> 0.0.67, which ships replacements for the Material plugins we were missing. The blog now gets its index, archive, category and author views with the same dated post URLs as master. The redirect map moves from redirects.yml into zensical.toml (INHERIT is YAML-only), replacing the standalone redirects.py. The native stubs land at the same 257 paths and keep #anchor fragments. llms_txt.py stays: the native llmstxt plugin drops front-matter descriptions and nav order. --- .github/workflows/publish.yml | 6 +- Dockerfile | 2 +- Makefile | 5 +- overrides/hooks/redirects.py | 81 ---------- pyproject.toml | 2 +- redirects.yml | 260 ------------------------------ uv.lock | 37 +++-- zensical.toml | 289 ++++++++++++++++++++++++++++++++-- 8 files changed, 307 insertions(+), 375 deletions(-) delete mode 100644 overrides/hooks/redirects.py delete mode 100644 redirects.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e14a5b6dc..b52dd58c3 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -29,10 +29,8 @@ jobs: - name: Build site run: uv run zensical build --clean - - name: Generate llms.txt and redirect stubs - run: | - uv run python overrides/hooks/llms_txt.py - uv run python overrides/hooks/redirects.py + - name: Generate llms.txt + run: uv run python overrides/hooks/llms_txt.py - name: Upload Pages artifact uses: actions/upload-pages-artifact@v4 diff --git a/Dockerfile b/Dockerfile index 6eeb49855..5a37aca5a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM zensical/zensical:0.0.50 +FROM zensical/zensical:0.0.67 # WORKDIR, EXPOSE 8000, ENTRYPOINT and the default # `serve --dev-addr=0.0.0.0:8000` CMD are inherited from the base image. diff --git a/Makefile b/Makefile index 0e9a07ac7..3d04be348 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ .PHONY: install build run serve site shell spellcheck catalog install: - pip install "zensical==0.0.50" + pip install "zensical==0.0.67" build: zensical build --clean @@ -20,8 +20,7 @@ site: python3 overrides/hooks/product_catalog.py && \ python3 overrides/hooks/blog_posts.py && \ zensical build --clean && \ - python3 overrides/hooks/llms_txt.py && \ - python3 overrides/hooks/redirects.py" + python3 overrides/hooks/llms_txt.py" shell: docker compose run --rm --entrypoint sh docs diff --git a/overrides/hooks/redirects.py b/overrides/hooks/redirects.py deleted file mode 100644 index 0f94cb246..000000000 --- a/overrides/hooks/redirects.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Generate static redirect pages from redirects.yml. - -Under mkdocs the mkdocs-redirects plugin consumes redirects.yml (via -INHERIT) and writes meta-refresh stub pages. Zensical has no redirects -support yet, so this standalone script does the same thing — run it -after the site has been built: - - python3 overrides/hooks/redirects.py -""" - -import html -import os -import yaml - -STUB = """\ - - - - - - - - Redirecting… - - - Redirecting to {url}… - - - -""" - - -def _src_to_dir(src_path): - """Map an old source path to its pretty-URL output directory.""" - path = src_path.replace("\\", "/") - if path.endswith("/index.md"): - return path[: -len("index.md")] - if path == "index.md": - return "" - return path[: -len(".md")] + "/" if path.endswith(".md") else path - - -def _target_to_url(target, site_url): - """Map a redirect target (external URL or source path) to a URL.""" - if target.startswith(("http://", "https://")): - return target - path, anchor = (target.split("#", 1) + [""])[:2] - url = site_url + _src_to_dir(path) - return f"{url}#{anchor}" if anchor else url - - -def main(repo_root): - with open(os.path.join(repo_root, "redirects.yml"), encoding="utf-8") as f: - redirect_maps = yaml.safe_load(f)["plugins"]["redirects"]["redirect_maps"] - - import tomllib - with open(os.path.join(repo_root, "zensical.toml"), "rb") as f: - site_url = tomllib.load(f)["project"]["site_url"].rstrip("/") + "/" - - site_dir = os.path.join(repo_root, "site") - if not os.path.isdir(site_dir): - raise SystemExit("redirects: site/ not found — build the site first") - - count = 0 - for src, target in redirect_maps.items(): - out_dir = os.path.join(site_dir, _src_to_dir(src)) - out_path = os.path.join(out_dir, "index.html") - if os.path.exists(out_path): - print(f"redirects: skipping {src} — a real page exists at {out_path}") - continue - url = html.escape(_target_to_url(target, site_url), quote=True) - os.makedirs(out_dir, exist_ok=True) - with open(out_path, "w", encoding="utf-8") as f: - f.write(STUB.format(url=url)) - count += 1 - - print(f"redirects: wrote {count} redirect stubs to {site_dir}") - - -if __name__ == "__main__": - main(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) diff --git a/pyproject.toml b/pyproject.toml index 786432800..61fe2d7df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "ExpressLRS documentation site" readme = "README.md" requires-python = ">=3.11" dependencies = [ - "zensical==0.0.50", + "zensical==0.0.67", ] [dependency-groups] diff --git a/redirects.yml b/redirects.yml deleted file mode 100644 index b5a95bed2..000000000 --- a/redirects.yml +++ /dev/null @@ -1,260 +0,0 @@ -plugins: - redirects: - redirect_maps: - 'github.md': 'https://github.com/ExpressLRS/ExpressLRS' - '3.0/index.md' : 'index.md' - 'quick-start/transmitters/betfpvlr3pro.md' : 'quick-start/transmitters/betafpvlr3pro.md' - '3.0/quick-start/getting-started.md' : 'quick-start/getting-started.md' - '3.0/quick-start/installing-configurator.md' : 'quick-start/installing-configurator.md' - '3.0/quick-start/firmware-options.md' : 'quick-start/firmware-options.md' - '3.0/quick-start/transmitters/tx-prep.md' : 'quick-start/transmitters/tx-prep.md' - '3.0/quick-start/transmitters/axisflying-thor.md' : 'quick-start/transmitters/axisflying-thor.md' - '3.0/quick-start/transmitters/betafpv2400.md' : 'quick-start/transmitters/betafpv2400.md' - '3.0/quick-start/transmitters/betafpv900.md' : 'quick-start/transmitters/betafpv900.md' - '3.0/quick-start/transmitters/betfpvlr3pro.md' : 'quick-start/transmitters/betafpvlr3pro.md' - '3.0/quick-start/transmitters/emax2400.md' : 'quick-start/transmitters/emax2400.md' - '3.0/quick-start/transmitters/emax900.md' : 'quick-start/transmitters/emax900.md' - '3.0/quick-start/transmitters/frsky-r9modules.md' : 'quick-start/transmitters/frsky-r9modules.md' - '3.0/quick-start/transmitters/es24tx.md' : 'quick-start/transmitters/es24tx.md' - '3.0/quick-start/transmitters/es900tx.md' : 'quick-start/transmitters/es900tx.md' - '3.0/quick-start/transmitters/hglrc-hermes.md' : 'quick-start/transmitters/hglrc-hermes.md' - '3.0/quick-start/transmitters/iflight-commando.md' : 'quick-start/transmitters/iflight-commando.md' - '3.0/quick-start/transmitters/ghost2400.md' : 'quick-start/transmitters/ghost2400.md' - '3.0/quick-start/transmitters/rm-internal.md' : 'quick-start/transmitters/rm-internal.md' - '3.0/quick-start/transmitters/aion-internal.md' : 'quick-start/transmitters/aion-internal.md' - '3.0/quick-start/transmitters/jumper-internal.md' : 'quick-start/transmitters/jumper-internal.md' - '3.0/quick-start/transmitters/jumper-aion.md' : 'quick-start/transmitters/jumper-aion.md' - '3.0/quick-start/transmitters/flash2400.md' : 'quick-start/transmitters/flash2400.md' - '3.0/quick-start/transmitters/voyager900.md' : 'quick-start/transmitters/voyager900.md' - '3.0/quick-start/transmitters/rm-ranger.md' : 'quick-start/transmitters/rm-ranger.md' - '3.0/quick-start/transmitters/siyifm30.md' : 'quick-start/transmitters/siyifm30.md' - '3.0/quick-start/transmitters/vantac-lite.md' : 'quick-start/transmitters/vantac-lite.md' - '3.0/quick-start/transmitters/diy900.md' : 'quick-start/transmitters/diy900.md' - '3.0/quick-start/transmitters/diy2400.md' : 'quick-start/transmitters/diy2400.md' - '3.0/quick-start/transmitters/lua-howto.md' : 'quick-start/transmitters/lua-howto.md' - '3.0/quick-start/receivers/wiring-up.md' : 'quick-start/receivers/wiring-up.md' - '3.0/quick-start/receivers/configuring-fc.md' : 'quick-start/receivers/configuring-fc.md' - '3.0/quick-start/receivers/axisflying-thor.md' : 'quick-start/receivers/axisflying-thor.md' - '3.0/quick-start/receivers/betafpv2400.md' : 'quick-start/receivers/betafpv2400.md' - '3.0/quick-start/receivers/betafpv900.md' : 'quick-start/receivers/betafpv900.md' - '3.0/quick-start/receivers/betafpv-superd.md' : 'quick-start/receivers/betafpv-superd.md' - '3.0/quick-start/receivers/foxeer2400.md' : 'quick-start/receivers/foxeer2400.md' - '3.0/quick-start/receivers/r9.md' : 'quick-start/receivers/r9.md' - '3.0/quick-start/receivers/geprc2400.md' : 'quick-start/receivers/geprc2400.md' - '3.0/quick-start/receivers/geprc900.md' : 'quick-start/receivers/geprc900.md' - '3.0/quick-start/receivers/hmep2400.md' : 'quick-start/receivers/hmep2400.md' - '3.0/quick-start/receivers/hmpp2400.md' : 'quick-start/receivers/hmpp2400.md' - '3.0/quick-start/receivers/hmes900.md' : 'quick-start/receivers/hmes900.md' - '3.0/quick-start/receivers/hglrc-hermes2400.md' : 'quick-start/receivers/hglrc-hermes2400.md' - '3.0/quick-start/receivers/hglrc-hermes900.md' : 'quick-start/receivers/hglrc-hermes900.md' - '3.0/quick-start/receivers/iflight2400.md' : 'quick-start/receivers/iflight2400.md' - '3.0/quick-start/receivers/iflight900.md' : 'quick-start/receivers/iflight900.md' - '3.0/quick-start/receivers/ghost2400.md' : 'quick-start/receivers/ghost2400.md' - '3.0/quick-start/receivers/jumper-aion.md' : 'quick-start/receivers/jumper-aion.md' - '3.0/quick-start/receivers/jumper900.md' : 'quick-start/receivers/jumper900.md' - '3.0/quick-start/receivers/matek2400.md' : 'quick-start/receivers/matek2400.md' - '3.0/quick-start/receivers/flash2400.md' : 'quick-start/receivers/flash2400.md' - '3.0/quick-start/receivers/voyager900.md' : 'quick-start/receivers/voyager900.md' - '3.0/quick-start/receivers/radiomaster-rp-2400.md' : 'quick-start/receivers/radiomaster-rp-2400.md' - '3.0/quick-start/receivers/siyiFRmini.md' : 'quick-start/receivers/siyiFRmini.md' - '3.0/quick-start/receivers/vantac2400.md' : 'quick-start/receivers/vantac2400.md' - '3.0/quick-start/receivers/diy900.md' : 'quick-start/receivers/diy900.md' - '3.0/quick-start/receivers/diy2400.md' : 'quick-start/receivers/diy2400.md' - '3.0/quick-start/led-status.md' : 'quick-start/led-status.md' - '3.0/quick-start/binding.md' : 'quick-start/binding.md' - '3.0/quick-start/pre-1stflight.md' : 'quick-start/pre-1stflight.md' - '3.0/quick-start/ardupilot-setup.md' : 'quick-start/ardupilot-setup.md' - '3.0/quick-start/troubleshooting.md' : 'quick-start/troubleshooting.md' - '3.0/quick-start/unbricking.md' : 'quick-start/unbricking.md' - '3.0/software/switch-config.md' : 'software/switch-config.md' - '3.0/software/user-defines.md' : 'software/user-defines.md' - '3.0/software/obsolete-defines.md' : 'software/obsolete-defines.md' - '3.0/software/open-ocd.md' : 'software/open-ocd.md' - '3.0/software/toolchain-install.md' : 'software/toolchain-install.md' - '3.0/software/stlink-fix.md' : 'software/stlink-fix.md' - '3.0/software/updating/betaflight-passthrough.md' : 'software/updating/betaflight-passthrough.md' - '3.0/software/updating/wifi-updating.md' : 'software/updating/wifi-updating.md' - '3.0/software/model-config-match.md' : 'software/model-config-match.md' - '3.0/software/dynamic-transmit-power.md' : 'software/dynamic-transmit-power.md' - '3.0/software/loan-model.md' : 'software/loan-model.md' - '3.0/software/testing/crc-testing.md' : 'software/testing/crc-testing.md' - '3.0/software/testing/unit-testing.md' : 'software/testing/unit-testing.md' - '3.0/software/testing/rx-scoreboard.md' : 'software/testing/rx-scoreboard.md' - '3.0/hardware/supported-hardware.md' : 'hardware/hardware-selection.md' - '3.0/hardware/receiver-selection.md' : 'hardware/hardware-selection.md' - '3.0/hardware/inverter-mod.md' : 'hardware/inverter-mod.md' - '3.0/hardware/fan-mod.md' : 'hardware/fan-mod.md' - '3.0/hardware/x9d-troubleshooting.md' : 'hardware/x9d-troubleshooting.md' - '3.0/hardware/crystal-frequency-error.md' : 'hardware/crystal-frequency-error.md' - '3.0/hardware/smd-antenna.md' : 'hardware/smd-antenna.md' - '3.0/hardware/pwm-receivers.md' : 'hardware/pwm-receivers.md' - '3.0/hardware/spi-receivers.md' : 'hardware/spi-receivers.md' - '3.0/hardware/backpack/esp-backpack.md' : 'hardware/backpack/esp-backpack.md' - '3.0/hardware/backpack/backpack-tx-setup.md' : 'hardware/backpack/backpack-tx-setup.md' - '3.0/hardware/backpack/backpack-vrx-setup.md' : 'hardware/backpack/backpack-vrx-setup.md' - '3.0/hardware/special-targets/diy-rx.md' : 'hardware/special-targets/diy-rx.md' - '3.0/hardware/special-targets/diy-tx.md' : 'hardware/special-targets/diy-tx.md' - '3.0/hardware/special-targets/nuclear-hardware.md' : 'hardware/special-targets/nuclear-hardware.md' - '3.0/info/glossary.md' : 'info/glossary.md' - '3.0/info/signal-health.md' : 'info/signal-health.md' - '3.0/info/telem-bandwidth.md' : 'info/telem-bandwidth.md' - '3.0/info/long-range.md' : 'info/long-range.md' - '3.0/info/advance-technical-info.md' : 'info/advance-technical-info.md' - '3.0/info/privacy-policy.md' : 'info/privacy-policy.md' - '3.0/info/licenses.md' : 'info/licenses.md' - '3.0/faq.md' : 'faq.md' - '3.0/gsod.md' : 'gsod.md' - '2.0/index.md' : 'index.md' - '2.0/quick-start/getting-started.md' : 'quick-start/getting-started.md' - '2.0/quick-start/installing-configurator.md' : 'quick-start/installing-configurator.md' - '2.0/quick-start/firmware-options.md' : 'quick-start/firmware-options.md' - '2.0/quick-start/transmitters/tx-prep.md' : 'quick-start/transmitters/tx-prep.md' - '2.0/quick-start/transmitters/axis-thor.md' : 'quick-start/transmitters/axisflying-thor.md' - '2.0/quick-start/transmitters/betafpv2400.md' : 'quick-start/transmitters/betafpv2400.md' - '2.0/quick-start/transmitters/betafpv900.md' : 'quick-start/transmitters/betafpv900.md' - '2.0/quick-start/transmitters/betfpvlr3pro.md' : 'quick-start/transmitters/betafpvlr3pro.md' - '2.0/quick-start/transmitters/frsky-r9modules.md' : 'quick-start/transmitters/frsky-r9modules.md' - '2.0/quick-start/transmitters/es24tx.md' : 'quick-start/transmitters/es24tx.md' - '2.0/quick-start/transmitters/es900tx.md' : 'quick-start/transmitters/es900tx.md' - '2.0/quick-start/transmitters/hglrc-hermes.md' : 'quick-start/transmitters/hglrc-hermes.md' - '2.0/quick-start/transmitters/iflight-commando.md' : 'quick-start/transmitters/iflight-commando.md' - '2.0/quick-start/transmitters/ghost2400.md' : 'quick-start/transmitters/ghost2400.md' - '2.0/quick-start/transmitters/rm-internal.md' : 'quick-start/transmitters/rm-internal.md' - '2.0/ quick-start/transmitters/aion-internal.md' : 'quick-start/transmitters/aion-internal.md' - '2.0/quick-start/transmitters/jumper-aion.md' : 'quick-start/transmitters/jumper-aion.md' - '2.0/quick-start/transmitters/flash2400.md' : 'quick-start/transmitters/flash2400.md' - '2.0/quick-start/transmitters/voyager900.md' : 'quick-start/transmitters/voyager900.md' - '2.0/quick-start/transmitters/siyifm30.md' : 'quick-start/transmitters/siyifm30.md' - '2.0/quick-start/transmitters/vantac-lite.md' : 'quick-start/transmitters/vantac-lite.md' - '2.0/quick-start/transmitters/diy900.md' : 'quick-start/transmitters/diy900.md' - '2.0/quick-start/transmitters/diy2400.md' : 'quick-start/transmitters/diy2400.md' - '2.0/quick-start/transmitters/lua-howto.md' : 'quick-start/transmitters/lua-howto.md' - '2.0/quick-start/receivers/wiring-up.md' : 'quick-start/receivers/wiring-up.md' - '2.0/quick-start/receivers/configuring-fc.md' : 'quick-start/receivers/configuring-fc.md' - '2.0/quick-start/receivers/axis-thor.md' : 'quick-start/receivers/axisflying-thor.md' - '2.0/quick-start/receivers/betafpv2400.md' : 'quick-start/receivers/betafpv2400.md' - '2.0/quick-start/receivers/betafpv900.md' : 'quick-start/receivers/betafpv900.md' - '2.0/quick-start/receivers/r9.md' : 'quick-start/receivers/r9.md' - '2.0/quick-start/receivers/geprc2400.md' : 'quick-start/receivers/geprc2400.md' - '2.0/quick-start/receivers/geprc900.md' : 'quick-start/receivers/geprc900.md' - '2.0/quick-start/receivers/hmep2400.md' : 'quick-start/receivers/hmep2400.md' - '2.0/quick-start/receivers/hmpp2400.md' : 'quick-start/receivers/hmpp2400.md' - '2.0/quick-start/receivers/hmes900.md' : 'quick-start/receivers/hmes900.md' - '2.0/quick-start/receivers/hglrc-hermes2400.md' : 'quick-start/receivers/hglrc-hermes2400.md' - '2.0/quick-start/receivers/hglrc-hermes900.md' : 'quick-start/receivers/hglrc-hermes900.md' - '2.0/quick-start/receivers/iflight2400.md' : 'quick-start/receivers/iflight2400.md' - '2.0/quick-start/receivers/iflight900.md' : 'quick-start/receivers/iflight900.md' - '2.0/quick-start/receivers/ghost2400.md' : 'quick-start/receivers/ghost2400.md' - '2.0/quick-start/receivers/jumper-aion.md' : 'quick-start/receivers/jumper-aion.md' - '2.0/quick-start/receivers/jumper900.md' : 'quick-start/receivers/jumper900.md' - '2.0/quick-start/receivers/matek2400.md' : 'quick-start/receivers/matek2400.md' - '2.0/quick-start/receivers/flash2400.md' : 'quick-start/receivers/flash2400.md' - '2.0/quick-start/receivers/voyager900.md' : 'quick-start/receivers/voyager900.md' - '2.0/quick-start/receivers/radiomaster-rp-2400.md' : 'quick-start/receivers/radiomaster-rp-2400.md' - '2.0/quick-start/receivers/siyiFRmini.md' : 'quick-start/receivers/siyiFRmini.md' - '2.0/quick-start/receivers/vantac2400.md' : 'quick-start/receivers/vantac2400.md' - '2.0/quick-start/receivers/diy900.md' : 'quick-start/receivers/diy900.md' - '2.0/quick-start/receivers/diy2400.md' : 'quick-start/receivers/diy2400.md' - '2.0/quick-start/led-status.md' : 'quick-start/led-status.md' - '2.0/quick-start/binding.md' : 'quick-start/binding.md' - '2.0/quick-start/pre-1stflight.md' : 'quick-start/pre-1stflight.md' - '2.0/quick-start/ardupilot-setup.md' : 'quick-start/ardupilot-setup.md' - '2.0/quick-start/troubleshooting.md' : 'quick-start/troubleshooting.md' - '2.0/quick-start/unbricking.md' : 'quick-start/unbricking.md' - '2.0/software/switch-config.md' : 'software/switch-config.md' - '2.0/software/user-defines.md' : 'software/user-defines.md' - '2.0/software/obsolete-defines.md' : 'software/obsolete-defines.md' - '2.0/software/open-ocd.md' : 'software/open-ocd.md' - '2.0/software/toolchain-install.md' : 'software/toolchain-install.md' - '2.0/software/stlink-fix.md' : 'software/stlink-fix.md' - '2.0/software/updating/betaflight-passthrough.md' : 'software/updating/betaflight-passthrough.md' - '2.0/software/updating/wifi-updating.md' : 'software/updating/wifi-updating.md' - '2.0/software/model-config-match.md' : 'software/model-config-match.md' - '2.0/software/dynamic-transmit-power.md' : 'software/dynamic-transmit-power.md' - '2.0/software/testing/crc-testing.md' : 'software/testing/crc-testing.md' - '2.0/software/testing/unit-testing.md' : 'software/testing/unit-testing.md' - '2.0/software/testing/rx-scoreboard.md' : 'software/testing/rx-scoreboard.md' - '2.0/hardware/supported-hardware.md' : 'hardware/hardware-selection.md' - '2.0/hardware/receiver-selection.md' : 'hardware/hardware-selection.md' - '2.0/hardware/inverter-mod.md' : 'hardware/inverter-mod.md' - '2.0/hardware/fan-mod.md' : 'hardware/fan-mod.md' - '2.0/hardware/x9d-troubleshooting.md' : 'hardware/x9d-troubleshooting.md' - '2.0/hardware/smd-antenna.md' : 'hardware/smd-antenna.md' - '2.0/hardware/pwm-receivers.md' : 'hardware/pwm-receivers.md' - '2.0/hardware/spi-receivers.md' : 'hardware/spi-receivers.md' - '2.0/hardware/backpack/esp-backpack.md' : 'hardware/backpack/esp-backpack.md' - '2.0/hardware/backpack/backpack-tx-setup.md' : 'hardware/backpack/backpack-tx-setup.md' - '2.0/hardware/backpack/backpack-vrx-setup.md' : 'hardware/backpack/backpack-vrx-setup.md' - '2.0/hardware/special-targets/diy-rx.md' : 'hardware/special-targets/diy-rx.md' - '2.0/hardware/special-targets/diy-tx.md' : 'hardware/special-targets/diy-tx.md' - '2.0/hardware/special-targets/nuclear-hardware.md' : 'hardware/special-targets/nuclear-hardware.md' - '2.0/info/glossary.md' : 'info/glossary.md' - '2.0/info/signal-health.md' : 'info/signal-health.md' - '2.0/info/telem-bandwidth.md' : 'info/telem-bandwidth.md' - '2.0/info/long-range.md' : 'info/long-range.md' - '2.0/info/advance-technical-info.md' : 'info/advance-technical-info.md' - '2.0/faq.md' : 'faq.md' - '2.0/gsod.md' : 'gsod.md' - '1.0/index.md' : 'index.md' - '1.0/quick-start/getting-started.md' : 'quick-start/getting-started.md' - '1.0/quick-start/installing-configurator.md' : 'quick-start/installing-configurator.md' - '1.0/quick-start/firmware-options.md' : 'quick-start/firmware-options.md' - '1.0/quick-start/tx-prep.md' : 'quick-start/transmitters/tx-prep.md' - '1.0/quick-start/tx-r9m.md' : 'quick-start/transmitters/frsky-r9modules.md' - '1.0/quick-start/tx-es900tx.md' : 'quick-start/transmitters/es900tx.md' - '1.0/quick-start/tx-voyager900.md' : 'quick-start/transmitters/voyager900.md' - '1.0/quick-start/tx-betafpv900.md' : 'quick-start/transmitters/betafpv900.md' - '1.0/quick-start/tx-es24tx.md' : 'quick-start/transmitters/es24tx.md' - '1.0/quick-start/tx-flash2400.md' : 'quick-start/transmitters/flash2400.md' - '1.0/quick-start/tx-betafpv2400.md' : 'quick-start/transmitters/betafpv2400.md' - '1.0/quick-start/tx-ghost2400.md' : 'quick-start/transmitters/ghost2400.md' - '1.0/quick-start/tx-siyifm30.md' : 'quick-start/transmitters/siyifm30.md' - '1.0/quick-start/tx-diy900.md' : 'quick-start/transmitters/diy900.md' - '1.0/quick-start/tx-diy.md' : 'quick-start/transmitters/diy2400.md' - '1.0/quick-start/rx-bootloader.md' : 'quick-start/receivers/r9.md' - '1.0/quick-start/rx-fcprep.md' : 'quick-start/receivers/configuring-fc.md' - '1.0/quick-start/rx-stlink.md' : 'quick-start/receivers/r9.md' - '1.0/quick-start/rx-r9receivers.md' : 'quick-start/receivers/r9.md' - '1.0/quick-start/rx-jumper900.md' : 'quick-start/receivers/jumper900.md' - '1.0/quick-start/rx-hmes900.md' : 'quick-start/receivers/hmes900.md' - '1.0/quick-start/rx-voyager900.md' : 'quick-start/receivers/voyager900.md' - '1.0/quick-start/rx-betafpv900.md' : 'quick-start/receivers/betafpv900.md' - '1.0/quick-start/rx-hmep2400.md' : 'quick-start/receivers/hmep2400.md' - '1.0/quick-start/rx-hmpp2400.md' : 'quick-start/receivers/hmpp2400.md' - '1.0/quick-start/rx-flash2400.md' : 'quick-start/receivers/flash2400.md' - '1.0/quick-start/rx-betafpv2400.md' : 'quick-start/receivers/betafpv2400.md' - '1.0/quick-start/rx-matek2400.md' : 'quick-start/receivers/matek2400.md' - '1.0/quick-start/rx-ghost2400.md' : 'quick-start/receivers/ghost2400.md' - '1.0/quick-start/rx-siyiFRmini.md' : 'quick-start/receivers/siyiFRmini.md' - '1.0/quick-start/rx-diyreceivers900.md' : 'quick-start/receivers/diy900.md' - '1.0/quick-start/rx-diyreceivers.md' : 'quick-start/receivers/diy2400.md' - '1.0/quick-start/binding.md' : 'quick-start/binding.md' - '1.0/quick-start/pre-1stflight.md' : 'quick-start/pre-1stflight.md' - '1.0/quick-start/ardupilot-setup.md' : 'quick-start/ardupilot-setup.md' - '1.0/quick-start/troubleshooting.md' : 'quick-start/troubleshooting.md' - '1.0/quick-start/unbricking.md' : 'quick-start/unbricking.md' - '1.0/software/switch-config.md' : 'software/switch-config.md' - '1.0/software/user-defines.md' : 'software/user-defines.md' - '1.0/software/obsolete-defines.md' : 'software/obsolete-defines.md' - '1.0/software/open-ocd.md' : 'software/open-ocd.md' - '1.0/software/toolchain-install.md' : 'software/toolchain-install.md' - '1.0/software/stlink-fix.md' : 'software/stlink-fix.md' - '1.0/software/updating/betaflight-passthrough.md' : 'software/updating/betaflight-passthrough.md' - '1.0/software/updating/wifi-updating.md' : 'software/updating/wifi-updating.md' - '1.0/hardware/supported-hardware.md' : 'hardware/hardware-selection.md' - '1.0/hardware/inverter-mod.md' : 'hardware/inverter-mod.md' - '1.0/hardware/fan-mod.md' : 'hardware/fan-mod.md' - '1.0/hardware/x9d-troubleshooting.md' : 'hardware/x9d-troubleshooting.md' - '1.0/hardware/esp-backpack.md' : 'hardware/backpack/esp-backpack.md' - '1.0/hardware/smd-antenna.md' : 'hardware/smd-antenna.md' - '1.0/hardware/spi-receivers.md' : 'hardware/spi-receivers.md' - '1.0/hardware/diy-rx.md' : 'hardware/special-targets/diy-rx.md' - '1.0/hardware/diy-tx.md' : 'hardware/special-targets/diy-tx.md' - '1.0/hardware/nuclear-hardware.md' : 'hardware/special-targets/nuclear-hardware.md' - '1.0/info/glossary.md' : 'info/glossary.md' - '1.0/info/signal-health.md' : 'info/signal-health.md' - '1.0/info/telem-bandwidth.md' : 'info/telem-bandwidth.md' - '1.0/info/unit-testing.md' : 'software/testing/unit-testing.md' - '1.0/info/crc-testing.md' : 'software/testing/crc-testing.md' - '1.0/info/rx-scoreboard.md' : 'software/testing/rx-scoreboard.md' - '1.0/faq.md' : 'faq.md' diff --git a/uv.lock b/uv.lock index 30d5e5ff4..804671015 100644 --- a/uv.lock +++ b/uv.lock @@ -68,7 +68,7 @@ spellcheck = [ ] [package.metadata] -requires-dist = [{ name = "zensical", specifier = "==0.0.50" }] +requires-dist = [{ name = "zensical", specifier = "==0.0.67" }] [package.metadata.requires-dev] spellcheck = [{ name = "pyspelling", specifier = "==2.12.1" }] @@ -283,6 +283,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, ] +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + [[package]] name = "pygments" version = "2.20.0" @@ -482,30 +491,28 @@ wheels = [ [[package]] name = "zensical" -version = "0.0.50" +version = "0.0.67" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "deepmerge" }, { name = "jinja2" }, { name = "markdown" }, + { name = "pathspec" }, { name = "pygments" }, { name = "pymdown-extensions" }, { name = "pyyaml" }, { name = "tomli" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/79/f7959b13c766a831f1248779bb718555f70f40c5b8e68db7de1de9936662/zensical-0.0.50.tar.gz", hash = "sha256:7040e52ebe5e6a275e4edeb351bf2bc314d007f3fb5750f178a38d840723e69c", size = 3979246, upload-time = "2026-07-09T13:52:11.908Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/11/d655563a922eb96bc930543132a6cb404971d980b36dcf7aa90751f4af8f/zensical-0.0.67.tar.gz", hash = "sha256:6691e79d1fec9e6235ded01a7d98c19342bfff144fceae7d043c4d3830890038", size = 4300450, upload-time = "2026-09-30T09:26:24.153Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/d2/e126d56642a5fd437eeb5a067b7c0d1f766c08e22d6a708bb923986f1d44/zensical-0.0.50-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:26dda9dbacab84db2743726f83202798e1893fbfddecb6a845812d7a331043ad", size = 12817706, upload-time = "2026-07-09T13:51:29.624Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9b/e1a22fb4ea8fc3e8377d7b394ee06cf0349e7f9a64ecb4c9873dee79b102/zensical-0.0.50-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:376887def6470c57509b48519870f74e2b987e30509cf5c819fcf372771f9403", size = 12691061, upload-time = "2026-07-09T13:51:33.194Z" }, - { url = "https://files.pythonhosted.org/packages/38/f5/16c087635680efb39e1d7b7aa3c2cca548305aa29ad51dee04f37b32b65f/zensical-0.0.50-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bbf3034a2cb1a9d2a72a5ee7047688717c93da0d8a90d25dd871db8d5f33a6d", size = 13129505, upload-time = "2026-07-09T13:51:36.383Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/50d02402e53aa01a5c5c116a3522c7bb18d6ae4da964d8b236173b6fa086/zensical-0.0.50-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26fe4167663d544ca385ef420a49d3b738c7606a79d7a62a6bedcbf74cd383e7", size = 13055300, upload-time = "2026-07-09T13:51:39.748Z" }, - { url = "https://files.pythonhosted.org/packages/cf/0f/45f4f3e56a6483ea9f88478b20d6b434718e7c46579239589202e5135ec1/zensical-0.0.50-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8813d1f8895b19520d4a982d6bfcfd06372d73d96e6a63f0d2cf8f3b6036d5a1", size = 13445447, upload-time = "2026-07-09T13:51:43.106Z" }, - { url = "https://files.pythonhosted.org/packages/33/9b/1f5065c664afde6a63e0ada8c14eae75f9e6960df597a5ca28c07340f938/zensical-0.0.50-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a64f957f94f7d8e8847a7f1e8205509ba4b188c93c157c5e7be49bcb8556a127", size = 13098775, upload-time = "2026-07-09T13:51:46.437Z" }, - { url = "https://files.pythonhosted.org/packages/46/65/1e42e18d8f9a0cb9fcc8b871852810c7fe196237b79aaa831e863325091e/zensical-0.0.50-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:781d333bcb42c6a6b92360e05eadc944e2674794e1a95537971de1bf64cb9f89", size = 13305922, upload-time = "2026-07-09T13:51:51.107Z" }, - { url = "https://files.pythonhosted.org/packages/50/36/9cace194ba13aef3bdde8ca2d327f3a05cdf5c456e94f6d896852e2cbd38/zensical-0.0.50-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:5d0f86eec76c23efb04566cd444bf025845ac8a02e75c8a6b4af13a39a2a9955", size = 13325488, upload-time = "2026-07-09T13:51:54.883Z" }, - { url = "https://files.pythonhosted.org/packages/b1/80/2891a9316e486794dae43151561b49f3313499cfb2c95c7886abad7bcf2d/zensical-0.0.50-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:392a7299a3c4d1ac7ff288bbed181d77a9fd23e6614180476e01630aaee0d67e", size = 13496866, upload-time = "2026-07-09T13:51:58.257Z" }, - { url = "https://files.pythonhosted.org/packages/7e/09/fbd3af58081a0d399fa913ef1f2fd6a007f2f06f7026c8d6a6096e34b996/zensical-0.0.50-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:cac3448d8c8a76dfb43e55908b4ca9ad17596aee89a9287a184c1a0ba7dce2a2", size = 13437565, upload-time = "2026-07-09T13:52:01.766Z" }, - { url = "https://files.pythonhosted.org/packages/d3/79/709311ff202326260ff223fb6e9ddefa3100474c428e3c70a04036cefe0e/zensical-0.0.50-cp310-abi3-win32.whl", hash = "sha256:32b244f96a930f68e049b2e03d50790c120ef701b6277ddd854905a33040c75f", size = 12371966, upload-time = "2026-07-09T13:52:05.207Z" }, - { url = "https://files.pythonhosted.org/packages/e3/e1/4babb30544d7465c95a6a93abc0680b1c51752411a225c2b7f2cb44e80c7/zensical-0.0.50-cp310-abi3-win_amd64.whl", hash = "sha256:2f054769bf96ae46353de3eca2463ad4db6df1da9fde515c6d7fc54c3608e615", size = 12604832, upload-time = "2026-07-09T13:52:09.013Z" }, + { url = "https://files.pythonhosted.org/packages/75/88/15bf6b4eef8895c37e96d6beb1c57fedcdcaa51527ba728869b81db0341d/zensical-0.0.67-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:2fbb86fb0773d32aab2ce5bb40be64a6962871ef049c925d633996a6f54d7130", size = 18057715, upload-time = "2026-09-30T09:26:01.536Z" }, + { url = "https://files.pythonhosted.org/packages/00/c0/bb8dd996633ba4ad2a0d73bd0b09381fff309cdccec965b0978746e22189/zensical-0.0.67-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:679a513ffb0013b6db3bd5aacbc73484f6a734df702e5ba41baf049753d38995", size = 17588275, upload-time = "2026-09-30T09:26:04.495Z" }, + { url = "https://files.pythonhosted.org/packages/c6/6b/f3aa259b9ee24a26de148d0d49a239683a43ecd985dd35b30cf0c3f5dead/zensical-0.0.67-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:100b21a42eb6f48c5f3fb89c7a4b3e3b12dcf6f7e2bd7da65b344842b1da30ad", size = 18039684, upload-time = "2026-09-30T09:26:06.963Z" }, + { url = "https://files.pythonhosted.org/packages/94/9c/f850af0905eb7319201f823a9fdedf32d0a479845dd3186e4a80f845f181/zensical-0.0.67-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:610c577a3d1200e21c443590e12334b46cb38ce0ef2dad12110afe19925160e2", size = 17988405, upload-time = "2026-09-30T09:26:09.578Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ac/9a9210bb96cbeb848df301462ac2d557e67548366014f4fa33dac10385d8/zensical-0.0.67-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9ee6369a253c9ff18f3c221bb48ac8c82eda22cb46f03650b937be8786b00b9", size = 18508864, upload-time = "2026-09-30T09:26:11.917Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5d/7180698732ca5cf566f88300fa9ec487d81e852392be98d84d7cbc7cca80/zensical-0.0.67-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7f9baff27363551dcdb4970936a126a714f7ac84fd646bb673e2676ff2ab526d", size = 18204439, upload-time = "2026-09-30T09:26:14.152Z" }, + { url = "https://files.pythonhosted.org/packages/9b/89/04d8768895a39bb0ea65c1275fca0af0b1e1e5f3722a075fbd77476c199a/zensical-0.0.67-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3e1a7b7d5edf58394df235ade589b187da8f97107b11056ca989dbd6d7e646cc", size = 18769301, upload-time = "2026-09-30T09:26:16.585Z" }, + { url = "https://files.pythonhosted.org/packages/79/63/45108c9b220d73af5dc3577edf48c63f4336d7d80295fa2abc8c6ee3a4fe/zensical-0.0.67-cp310-abi3-win_amd64.whl", hash = "sha256:df5cc21899b4a5f3956d959e403dc754d7abcdcc2704c40dc43cff84e257b037", size = 18700647, upload-time = "2026-09-30T09:26:19.028Z" }, + { url = "https://files.pythonhosted.org/packages/93/43/09836dcb02085b7da0339d753b2b153352205209bfe14a3e16ec56740a0d/zensical-0.0.67-cp310-abi3-win_arm64.whl", hash = "sha256:20fb40bd0b8d05be92a793887e312de5b522fbda8fbdf80267395fd97baefc7b", size = 18120901, upload-time = "2026-09-30T09:26:21.683Z" }, ] diff --git a/zensical.toml b/zensical.toml index 3974c74d1..fe5dc530e 100644 --- a/zensical.toml +++ b/zensical.toml @@ -190,6 +190,282 @@ nav = [ { "Contact Us" = "contact-us.md" }, ] +# --------------------------------------------------------------------------- +# Plugins +# --------------------------------------------------------------------------- +[project.plugins.blog] +blog_toc = true +post_date_format = "long" +post_url_date_format = "yyyy/MM/dd" + +[project.plugins.tags] + +[project.plugins.social.cards_layout_options] +background_color = "#5f8bf3" +color = "#FFFFFF" + +[project.plugins.minify] +minify_html = true + +[project.plugins.redirects.redirect_maps] +"github.md" = "https://github.com/ExpressLRS/ExpressLRS" +"3.0/index.md" = "index.md" +"quick-start/transmitters/betfpvlr3pro.md" = "quick-start/transmitters/betafpvlr3pro.md" +"3.0/quick-start/getting-started.md" = "quick-start/getting-started.md" +"3.0/quick-start/installing-configurator.md" = "quick-start/installing-configurator.md" +"3.0/quick-start/firmware-options.md" = "quick-start/firmware-options.md" +"3.0/quick-start/transmitters/tx-prep.md" = "quick-start/transmitters/tx-prep.md" +"3.0/quick-start/transmitters/axisflying-thor.md" = "quick-start/transmitters/axisflying-thor.md" +"3.0/quick-start/transmitters/betafpv2400.md" = "quick-start/transmitters/betafpv2400.md" +"3.0/quick-start/transmitters/betafpv900.md" = "quick-start/transmitters/betafpv900.md" +"3.0/quick-start/transmitters/betfpvlr3pro.md" = "quick-start/transmitters/betafpvlr3pro.md" +"3.0/quick-start/transmitters/emax2400.md" = "quick-start/transmitters/emax2400.md" +"3.0/quick-start/transmitters/emax900.md" = "quick-start/transmitters/emax900.md" +"3.0/quick-start/transmitters/frsky-r9modules.md" = "quick-start/transmitters/frsky-r9modules.md" +"3.0/quick-start/transmitters/es24tx.md" = "quick-start/transmitters/es24tx.md" +"3.0/quick-start/transmitters/es900tx.md" = "quick-start/transmitters/es900tx.md" +"3.0/quick-start/transmitters/hglrc-hermes.md" = "quick-start/transmitters/hglrc-hermes.md" +"3.0/quick-start/transmitters/iflight-commando.md" = "quick-start/transmitters/iflight-commando.md" +"3.0/quick-start/transmitters/ghost2400.md" = "quick-start/transmitters/ghost2400.md" +"3.0/quick-start/transmitters/rm-internal.md" = "quick-start/transmitters/rm-internal.md" +"3.0/quick-start/transmitters/aion-internal.md" = "quick-start/transmitters/aion-internal.md" +"3.0/quick-start/transmitters/jumper-internal.md" = "quick-start/transmitters/jumper-internal.md" +"3.0/quick-start/transmitters/jumper-aion.md" = "quick-start/transmitters/jumper-aion.md" +"3.0/quick-start/transmitters/flash2400.md" = "quick-start/transmitters/flash2400.md" +"3.0/quick-start/transmitters/voyager900.md" = "quick-start/transmitters/voyager900.md" +"3.0/quick-start/transmitters/rm-ranger.md" = "quick-start/transmitters/rm-ranger.md" +"3.0/quick-start/transmitters/siyifm30.md" = "quick-start/transmitters/siyifm30.md" +"3.0/quick-start/transmitters/vantac-lite.md" = "quick-start/transmitters/vantac-lite.md" +"3.0/quick-start/transmitters/diy900.md" = "quick-start/transmitters/diy900.md" +"3.0/quick-start/transmitters/diy2400.md" = "quick-start/transmitters/diy2400.md" +"3.0/quick-start/transmitters/lua-howto.md" = "quick-start/transmitters/lua-howto.md" +"3.0/quick-start/receivers/wiring-up.md" = "quick-start/receivers/wiring-up.md" +"3.0/quick-start/receivers/configuring-fc.md" = "quick-start/receivers/configuring-fc.md" +"3.0/quick-start/receivers/axisflying-thor.md" = "quick-start/receivers/axisflying-thor.md" +"3.0/quick-start/receivers/betafpv2400.md" = "quick-start/receivers/betafpv2400.md" +"3.0/quick-start/receivers/betafpv900.md" = "quick-start/receivers/betafpv900.md" +"3.0/quick-start/receivers/betafpv-superd.md" = "quick-start/receivers/betafpv-superd.md" +"3.0/quick-start/receivers/foxeer2400.md" = "quick-start/receivers/foxeer2400.md" +"3.0/quick-start/receivers/r9.md" = "quick-start/receivers/r9.md" +"3.0/quick-start/receivers/geprc2400.md" = "quick-start/receivers/geprc2400.md" +"3.0/quick-start/receivers/geprc900.md" = "quick-start/receivers/geprc900.md" +"3.0/quick-start/receivers/hmep2400.md" = "quick-start/receivers/hmep2400.md" +"3.0/quick-start/receivers/hmpp2400.md" = "quick-start/receivers/hmpp2400.md" +"3.0/quick-start/receivers/hmes900.md" = "quick-start/receivers/hmes900.md" +"3.0/quick-start/receivers/hglrc-hermes2400.md" = "quick-start/receivers/hglrc-hermes2400.md" +"3.0/quick-start/receivers/hglrc-hermes900.md" = "quick-start/receivers/hglrc-hermes900.md" +"3.0/quick-start/receivers/iflight2400.md" = "quick-start/receivers/iflight2400.md" +"3.0/quick-start/receivers/iflight900.md" = "quick-start/receivers/iflight900.md" +"3.0/quick-start/receivers/ghost2400.md" = "quick-start/receivers/ghost2400.md" +"3.0/quick-start/receivers/jumper-aion.md" = "quick-start/receivers/jumper-aion.md" +"3.0/quick-start/receivers/jumper900.md" = "quick-start/receivers/jumper900.md" +"3.0/quick-start/receivers/matek2400.md" = "quick-start/receivers/matek2400.md" +"3.0/quick-start/receivers/flash2400.md" = "quick-start/receivers/flash2400.md" +"3.0/quick-start/receivers/voyager900.md" = "quick-start/receivers/voyager900.md" +"3.0/quick-start/receivers/radiomaster-rp-2400.md" = "quick-start/receivers/radiomaster-rp-2400.md" +"3.0/quick-start/receivers/siyiFRmini.md" = "quick-start/receivers/siyiFRmini.md" +"3.0/quick-start/receivers/vantac2400.md" = "quick-start/receivers/vantac2400.md" +"3.0/quick-start/receivers/diy900.md" = "quick-start/receivers/diy900.md" +"3.0/quick-start/receivers/diy2400.md" = "quick-start/receivers/diy2400.md" +"3.0/quick-start/led-status.md" = "quick-start/led-status.md" +"3.0/quick-start/binding.md" = "quick-start/binding.md" +"3.0/quick-start/pre-1stflight.md" = "quick-start/pre-1stflight.md" +"3.0/quick-start/ardupilot-setup.md" = "quick-start/ardupilot-setup.md" +"3.0/quick-start/troubleshooting.md" = "quick-start/troubleshooting.md" +"3.0/quick-start/unbricking.md" = "quick-start/unbricking.md" +"3.0/software/switch-config.md" = "software/switch-config.md" +"3.0/software/user-defines.md" = "software/user-defines.md" +"3.0/software/obsolete-defines.md" = "software/obsolete-defines.md" +"3.0/software/open-ocd.md" = "software/open-ocd.md" +"3.0/software/toolchain-install.md" = "software/toolchain-install.md" +"3.0/software/stlink-fix.md" = "software/stlink-fix.md" +"3.0/software/updating/betaflight-passthrough.md" = "software/updating/betaflight-passthrough.md" +"3.0/software/updating/wifi-updating.md" = "software/updating/wifi-updating.md" +"3.0/software/model-config-match.md" = "software/model-config-match.md" +"3.0/software/dynamic-transmit-power.md" = "software/dynamic-transmit-power.md" +"3.0/software/loan-model.md" = "software/loan-model.md" +"3.0/software/testing/crc-testing.md" = "software/testing/crc-testing.md" +"3.0/software/testing/unit-testing.md" = "software/testing/unit-testing.md" +"3.0/software/testing/rx-scoreboard.md" = "software/testing/rx-scoreboard.md" +"3.0/hardware/supported-hardware.md" = "hardware/hardware-selection.md" +"3.0/hardware/receiver-selection.md" = "hardware/hardware-selection.md" +"3.0/hardware/inverter-mod.md" = "hardware/inverter-mod.md" +"3.0/hardware/fan-mod.md" = "hardware/fan-mod.md" +"3.0/hardware/x9d-troubleshooting.md" = "hardware/x9d-troubleshooting.md" +"3.0/hardware/crystal-frequency-error.md" = "hardware/crystal-frequency-error.md" +"3.0/hardware/smd-antenna.md" = "hardware/smd-antenna.md" +"3.0/hardware/pwm-receivers.md" = "hardware/pwm-receivers.md" +"3.0/hardware/spi-receivers.md" = "hardware/spi-receivers.md" +"3.0/hardware/backpack/esp-backpack.md" = "hardware/backpack/esp-backpack.md" +"3.0/hardware/backpack/backpack-tx-setup.md" = "hardware/backpack/backpack-tx-setup.md" +"3.0/hardware/backpack/backpack-vrx-setup.md" = "hardware/backpack/backpack-vrx-setup.md" +"3.0/hardware/special-targets/diy-rx.md" = "hardware/special-targets/diy-rx.md" +"3.0/hardware/special-targets/diy-tx.md" = "hardware/special-targets/diy-tx.md" +"3.0/hardware/special-targets/nuclear-hardware.md" = "hardware/special-targets/nuclear-hardware.md" +"3.0/info/glossary.md" = "info/glossary.md" +"3.0/info/signal-health.md" = "info/signal-health.md" +"3.0/info/telem-bandwidth.md" = "info/telem-bandwidth.md" +"3.0/info/long-range.md" = "info/long-range.md" +"3.0/info/advance-technical-info.md" = "info/advance-technical-info.md" +"3.0/info/privacy-policy.md" = "info/privacy-policy.md" +"3.0/info/licenses.md" = "info/licenses.md" +"3.0/faq.md" = "faq.md" +"3.0/gsod.md" = "gsod.md" +"2.0/index.md" = "index.md" +"2.0/quick-start/getting-started.md" = "quick-start/getting-started.md" +"2.0/quick-start/installing-configurator.md" = "quick-start/installing-configurator.md" +"2.0/quick-start/firmware-options.md" = "quick-start/firmware-options.md" +"2.0/quick-start/transmitters/tx-prep.md" = "quick-start/transmitters/tx-prep.md" +"2.0/quick-start/transmitters/axis-thor.md" = "quick-start/transmitters/axisflying-thor.md" +"2.0/quick-start/transmitters/betafpv2400.md" = "quick-start/transmitters/betafpv2400.md" +"2.0/quick-start/transmitters/betafpv900.md" = "quick-start/transmitters/betafpv900.md" +"2.0/quick-start/transmitters/betfpvlr3pro.md" = "quick-start/transmitters/betafpvlr3pro.md" +"2.0/quick-start/transmitters/frsky-r9modules.md" = "quick-start/transmitters/frsky-r9modules.md" +"2.0/quick-start/transmitters/es24tx.md" = "quick-start/transmitters/es24tx.md" +"2.0/quick-start/transmitters/es900tx.md" = "quick-start/transmitters/es900tx.md" +"2.0/quick-start/transmitters/hglrc-hermes.md" = "quick-start/transmitters/hglrc-hermes.md" +"2.0/quick-start/transmitters/iflight-commando.md" = "quick-start/transmitters/iflight-commando.md" +"2.0/quick-start/transmitters/ghost2400.md" = "quick-start/transmitters/ghost2400.md" +"2.0/quick-start/transmitters/rm-internal.md" = "quick-start/transmitters/rm-internal.md" +"2.0/ quick-start/transmitters/aion-internal.md" = "quick-start/transmitters/aion-internal.md" +"2.0/quick-start/transmitters/jumper-aion.md" = "quick-start/transmitters/jumper-aion.md" +"2.0/quick-start/transmitters/flash2400.md" = "quick-start/transmitters/flash2400.md" +"2.0/quick-start/transmitters/voyager900.md" = "quick-start/transmitters/voyager900.md" +"2.0/quick-start/transmitters/siyifm30.md" = "quick-start/transmitters/siyifm30.md" +"2.0/quick-start/transmitters/vantac-lite.md" = "quick-start/transmitters/vantac-lite.md" +"2.0/quick-start/transmitters/diy900.md" = "quick-start/transmitters/diy900.md" +"2.0/quick-start/transmitters/diy2400.md" = "quick-start/transmitters/diy2400.md" +"2.0/quick-start/transmitters/lua-howto.md" = "quick-start/transmitters/lua-howto.md" +"2.0/quick-start/receivers/wiring-up.md" = "quick-start/receivers/wiring-up.md" +"2.0/quick-start/receivers/configuring-fc.md" = "quick-start/receivers/configuring-fc.md" +"2.0/quick-start/receivers/axis-thor.md" = "quick-start/receivers/axisflying-thor.md" +"2.0/quick-start/receivers/betafpv2400.md" = "quick-start/receivers/betafpv2400.md" +"2.0/quick-start/receivers/betafpv900.md" = "quick-start/receivers/betafpv900.md" +"2.0/quick-start/receivers/r9.md" = "quick-start/receivers/r9.md" +"2.0/quick-start/receivers/geprc2400.md" = "quick-start/receivers/geprc2400.md" +"2.0/quick-start/receivers/geprc900.md" = "quick-start/receivers/geprc900.md" +"2.0/quick-start/receivers/hmep2400.md" = "quick-start/receivers/hmep2400.md" +"2.0/quick-start/receivers/hmpp2400.md" = "quick-start/receivers/hmpp2400.md" +"2.0/quick-start/receivers/hmes900.md" = "quick-start/receivers/hmes900.md" +"2.0/quick-start/receivers/hglrc-hermes2400.md" = "quick-start/receivers/hglrc-hermes2400.md" +"2.0/quick-start/receivers/hglrc-hermes900.md" = "quick-start/receivers/hglrc-hermes900.md" +"2.0/quick-start/receivers/iflight2400.md" = "quick-start/receivers/iflight2400.md" +"2.0/quick-start/receivers/iflight900.md" = "quick-start/receivers/iflight900.md" +"2.0/quick-start/receivers/ghost2400.md" = "quick-start/receivers/ghost2400.md" +"2.0/quick-start/receivers/jumper-aion.md" = "quick-start/receivers/jumper-aion.md" +"2.0/quick-start/receivers/jumper900.md" = "quick-start/receivers/jumper900.md" +"2.0/quick-start/receivers/matek2400.md" = "quick-start/receivers/matek2400.md" +"2.0/quick-start/receivers/flash2400.md" = "quick-start/receivers/flash2400.md" +"2.0/quick-start/receivers/voyager900.md" = "quick-start/receivers/voyager900.md" +"2.0/quick-start/receivers/radiomaster-rp-2400.md" = "quick-start/receivers/radiomaster-rp-2400.md" +"2.0/quick-start/receivers/siyiFRmini.md" = "quick-start/receivers/siyiFRmini.md" +"2.0/quick-start/receivers/vantac2400.md" = "quick-start/receivers/vantac2400.md" +"2.0/quick-start/receivers/diy900.md" = "quick-start/receivers/diy900.md" +"2.0/quick-start/receivers/diy2400.md" = "quick-start/receivers/diy2400.md" +"2.0/quick-start/led-status.md" = "quick-start/led-status.md" +"2.0/quick-start/binding.md" = "quick-start/binding.md" +"2.0/quick-start/pre-1stflight.md" = "quick-start/pre-1stflight.md" +"2.0/quick-start/ardupilot-setup.md" = "quick-start/ardupilot-setup.md" +"2.0/quick-start/troubleshooting.md" = "quick-start/troubleshooting.md" +"2.0/quick-start/unbricking.md" = "quick-start/unbricking.md" +"2.0/software/switch-config.md" = "software/switch-config.md" +"2.0/software/user-defines.md" = "software/user-defines.md" +"2.0/software/obsolete-defines.md" = "software/obsolete-defines.md" +"2.0/software/open-ocd.md" = "software/open-ocd.md" +"2.0/software/toolchain-install.md" = "software/toolchain-install.md" +"2.0/software/stlink-fix.md" = "software/stlink-fix.md" +"2.0/software/updating/betaflight-passthrough.md" = "software/updating/betaflight-passthrough.md" +"2.0/software/updating/wifi-updating.md" = "software/updating/wifi-updating.md" +"2.0/software/model-config-match.md" = "software/model-config-match.md" +"2.0/software/dynamic-transmit-power.md" = "software/dynamic-transmit-power.md" +"2.0/software/testing/crc-testing.md" = "software/testing/crc-testing.md" +"2.0/software/testing/unit-testing.md" = "software/testing/unit-testing.md" +"2.0/software/testing/rx-scoreboard.md" = "software/testing/rx-scoreboard.md" +"2.0/hardware/supported-hardware.md" = "hardware/hardware-selection.md" +"2.0/hardware/receiver-selection.md" = "hardware/hardware-selection.md" +"2.0/hardware/inverter-mod.md" = "hardware/inverter-mod.md" +"2.0/hardware/fan-mod.md" = "hardware/fan-mod.md" +"2.0/hardware/x9d-troubleshooting.md" = "hardware/x9d-troubleshooting.md" +"2.0/hardware/smd-antenna.md" = "hardware/smd-antenna.md" +"2.0/hardware/pwm-receivers.md" = "hardware/pwm-receivers.md" +"2.0/hardware/spi-receivers.md" = "hardware/spi-receivers.md" +"2.0/hardware/backpack/esp-backpack.md" = "hardware/backpack/esp-backpack.md" +"2.0/hardware/backpack/backpack-tx-setup.md" = "hardware/backpack/backpack-tx-setup.md" +"2.0/hardware/backpack/backpack-vrx-setup.md" = "hardware/backpack/backpack-vrx-setup.md" +"2.0/hardware/special-targets/diy-rx.md" = "hardware/special-targets/diy-rx.md" +"2.0/hardware/special-targets/diy-tx.md" = "hardware/special-targets/diy-tx.md" +"2.0/hardware/special-targets/nuclear-hardware.md" = "hardware/special-targets/nuclear-hardware.md" +"2.0/info/glossary.md" = "info/glossary.md" +"2.0/info/signal-health.md" = "info/signal-health.md" +"2.0/info/telem-bandwidth.md" = "info/telem-bandwidth.md" +"2.0/info/long-range.md" = "info/long-range.md" +"2.0/info/advance-technical-info.md" = "info/advance-technical-info.md" +"2.0/faq.md" = "faq.md" +"2.0/gsod.md" = "gsod.md" +"1.0/index.md" = "index.md" +"1.0/quick-start/getting-started.md" = "quick-start/getting-started.md" +"1.0/quick-start/installing-configurator.md" = "quick-start/installing-configurator.md" +"1.0/quick-start/firmware-options.md" = "quick-start/firmware-options.md" +"1.0/quick-start/tx-prep.md" = "quick-start/transmitters/tx-prep.md" +"1.0/quick-start/tx-r9m.md" = "quick-start/transmitters/frsky-r9modules.md" +"1.0/quick-start/tx-es900tx.md" = "quick-start/transmitters/es900tx.md" +"1.0/quick-start/tx-voyager900.md" = "quick-start/transmitters/voyager900.md" +"1.0/quick-start/tx-betafpv900.md" = "quick-start/transmitters/betafpv900.md" +"1.0/quick-start/tx-es24tx.md" = "quick-start/transmitters/es24tx.md" +"1.0/quick-start/tx-flash2400.md" = "quick-start/transmitters/flash2400.md" +"1.0/quick-start/tx-betafpv2400.md" = "quick-start/transmitters/betafpv2400.md" +"1.0/quick-start/tx-ghost2400.md" = "quick-start/transmitters/ghost2400.md" +"1.0/quick-start/tx-siyifm30.md" = "quick-start/transmitters/siyifm30.md" +"1.0/quick-start/tx-diy900.md" = "quick-start/transmitters/diy900.md" +"1.0/quick-start/tx-diy.md" = "quick-start/transmitters/diy2400.md" +"1.0/quick-start/rx-bootloader.md" = "quick-start/receivers/r9.md" +"1.0/quick-start/rx-fcprep.md" = "quick-start/receivers/configuring-fc.md" +"1.0/quick-start/rx-stlink.md" = "quick-start/receivers/r9.md" +"1.0/quick-start/rx-r9receivers.md" = "quick-start/receivers/r9.md" +"1.0/quick-start/rx-jumper900.md" = "quick-start/receivers/jumper900.md" +"1.0/quick-start/rx-hmes900.md" = "quick-start/receivers/hmes900.md" +"1.0/quick-start/rx-voyager900.md" = "quick-start/receivers/voyager900.md" +"1.0/quick-start/rx-betafpv900.md" = "quick-start/receivers/betafpv900.md" +"1.0/quick-start/rx-hmep2400.md" = "quick-start/receivers/hmep2400.md" +"1.0/quick-start/rx-hmpp2400.md" = "quick-start/receivers/hmpp2400.md" +"1.0/quick-start/rx-flash2400.md" = "quick-start/receivers/flash2400.md" +"1.0/quick-start/rx-betafpv2400.md" = "quick-start/receivers/betafpv2400.md" +"1.0/quick-start/rx-matek2400.md" = "quick-start/receivers/matek2400.md" +"1.0/quick-start/rx-ghost2400.md" = "quick-start/receivers/ghost2400.md" +"1.0/quick-start/rx-siyiFRmini.md" = "quick-start/receivers/siyiFRmini.md" +"1.0/quick-start/rx-diyreceivers900.md" = "quick-start/receivers/diy900.md" +"1.0/quick-start/rx-diyreceivers.md" = "quick-start/receivers/diy2400.md" +"1.0/quick-start/binding.md" = "quick-start/binding.md" +"1.0/quick-start/pre-1stflight.md" = "quick-start/pre-1stflight.md" +"1.0/quick-start/ardupilot-setup.md" = "quick-start/ardupilot-setup.md" +"1.0/quick-start/troubleshooting.md" = "quick-start/troubleshooting.md" +"1.0/quick-start/unbricking.md" = "quick-start/unbricking.md" +"1.0/software/switch-config.md" = "software/switch-config.md" +"1.0/software/user-defines.md" = "software/user-defines.md" +"1.0/software/obsolete-defines.md" = "software/obsolete-defines.md" +"1.0/software/open-ocd.md" = "software/open-ocd.md" +"1.0/software/toolchain-install.md" = "software/toolchain-install.md" +"1.0/software/stlink-fix.md" = "software/stlink-fix.md" +"1.0/software/updating/betaflight-passthrough.md" = "software/updating/betaflight-passthrough.md" +"1.0/software/updating/wifi-updating.md" = "software/updating/wifi-updating.md" +"1.0/hardware/supported-hardware.md" = "hardware/hardware-selection.md" +"1.0/hardware/inverter-mod.md" = "hardware/inverter-mod.md" +"1.0/hardware/fan-mod.md" = "hardware/fan-mod.md" +"1.0/hardware/x9d-troubleshooting.md" = "hardware/x9d-troubleshooting.md" +"1.0/hardware/esp-backpack.md" = "hardware/backpack/esp-backpack.md" +"1.0/hardware/smd-antenna.md" = "hardware/smd-antenna.md" +"1.0/hardware/spi-receivers.md" = "hardware/spi-receivers.md" +"1.0/hardware/diy-rx.md" = "hardware/special-targets/diy-rx.md" +"1.0/hardware/diy-tx.md" = "hardware/special-targets/diy-tx.md" +"1.0/hardware/nuclear-hardware.md" = "hardware/special-targets/nuclear-hardware.md" +"1.0/info/glossary.md" = "info/glossary.md" +"1.0/info/signal-health.md" = "info/signal-health.md" +"1.0/info/telem-bandwidth.md" = "info/telem-bandwidth.md" +"1.0/info/unit-testing.md" = "software/testing/unit-testing.md" +"1.0/info/crc-testing.md" = "software/testing/crc-testing.md" +"1.0/info/rx-scoreboard.md" = "software/testing/rx-scoreboard.md" +"1.0/faq.md" = "faq.md" + # --------------------------------------------------------------------------- # Theme # --------------------------------------------------------------------------- @@ -307,14 +583,8 @@ link = "https://opencollective.com/expresslrs" name = "Send a Donation!" # --------------------------------------------------------------------------- -# TODO — blocked on upstream Zensical support (all "planned" in the -# Zensical backlog, see https://zensical.org/compatibility/plugins/): +# TODO — blocked on upstream Zensical support: # -# * blog plugin — posts render as plain pages, but blog/index.md has no -# post listing, archive, or category pages yet -# * tags plugin -# * social cards plugin (cards_layout_options background #5f8bf3) -# * minify plugin (HTML output is currently unminified) # * git-revision-date-localized + git-committers (page footer dates and # contributor avatars) # @@ -324,7 +594,6 @@ name = "Send a Donation!" # * product_catalog.py (before build) → docs/assets/data/products.json # * blog_posts.py (before build) → overrides/partials/ # recent-posts.html, included by home.html for the Latest News section -# * llms_txt.py (after build) → site/llms.txt -# * redirects.py (after build) → meta-refresh stubs from -# redirects.yml (mkdocs-redirects equivalent) +# * llms_txt.py (after build) → site/llms.txt, built from the nav +# and page descriptions (the native llmstxt plugin can't read either) # --------------------------------------------------------------------------- From 9328ff4da460ec3a2ffbe311da93f6b5fecf41ad Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 3 Oct 2026 22:44:32 +0300 Subject: [PATCH 13/21] Fix ES915RX link in SPI receivers page The stray slash in hmes900.md/# made the link point at a page that doesn't exist. --- docs/hardware/spi-receivers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hardware/spi-receivers.md b/docs/hardware/spi-receivers.md index 91447985a..a7e65f4d8 100644 --- a/docs/hardware/spi-receivers.md +++ b/docs/hardware/spi-receivers.md @@ -32,7 +32,7 @@ Using the latest [Betaflight Configurator](https://github.com/betaflight/betafli If your Flight Controller model is not in the list above, consult your Flight Controller manufacturer for details. !!! info "NOTE" - The Happymodel Mobula6 900MHz AIO with the CrazyF4 ELRS FC (Target: CRAZYBEEF4DX) doesn't use an SPI ExpressLRS receiver. Check the page for [ES915RX](../quick-start/receivers/hmes900.md/#updating-your-receiver-firmware-es915rxes868rx) instead. + The Happymodel Mobula6 900MHz AIO with the CrazyF4 ELRS FC (Target: CRAZYBEEF4DX) doesn't use an SPI ExpressLRS receiver. Check the page for [ES915RX](../quick-start/receivers/hmes900.md#updating-your-receiver-firmware-es915rxes868rx) instead. Likewise, the [v2.0 BetaFPV F4 1S 5A](https://betafpv.com/collections/brushless-flight-controller/products/f4-1s-5a-aio-brushless-flight-controller-elrs-2-4g) comes with an on-board UART-based ExpressLRS Receiver and doesn't use the SPI ExpressLRS implementation. Use the `BETAFPV AIO 2400 RX` Device target. From 8f53c2c2cc6ee6b957539d427d624d834ab2ae13 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 3 Oct 2026 22:54:56 +0300 Subject: [PATCH 14/21] Add RSS and JSON feeds for the blog Publish /feed.xml and /feed.json, covering blog posts only, with pages advertising them via and a footer RSS icon. Dates come from post front matter because CI's shallow checkout would give every post the latest commit's date. The feeds sit at the site root because the generated rss.xsl only resolves from there. --- overrides/main.html | 2 ++ zensical.toml | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/overrides/main.html b/overrides/main.html index 97f3a1102..f8eec1564 100644 --- a/overrides/main.html +++ b/overrides/main.html @@ -7,6 +7,8 @@ name="google-site-verification" content="pg26rs-EELwcFEnEgD5uQqtX1ZTdRSD7Mp3O-4CthDs" /> + + {% endblock %} {% block announce %} ExpressLRS v4.0.1 is out now! Check it out on GitHub. diff --git a/zensical.toml b/zensical.toml index fe5dc530e..ccd8732c2 100644 --- a/zensical.toml +++ b/zensical.toml @@ -200,6 +200,21 @@ post_url_date_format = "yyyy/MM/dd" [project.plugins.tags] +[project.plugins.rss] +match_path = "blog/posts/.*" +categories = ["categories"] + +# CI checks out shallow, so git dates would all be the latest commit +[project.plugins.rss.date_from_meta] +as_creation = "date" +as_update = "date" + +[project.plugins.rss.feeds_filenames] +rss_created = "feed.xml" +json_created = "feed.json" +rss_updated = "feed-updated.xml" +json_updated = "feed-updated.json" + [project.plugins.social.cards_layout_options] background_color = "#5f8bf3" color = "#FFFFFF" @@ -577,6 +592,11 @@ icon = "fontawesome/brands/instagram" link = "https://instagram.com/expresslrs" name = "Follow us on Instagram" +[[project.extra.social]] +icon = "fontawesome/solid/rss" +link = "/feed.xml" +name = "Subscribe to the blog" + [[project.extra.social]] icon = "fontawesome/solid/circle-dollar-to-slot" link = "https://opencollective.com/expresslrs" From 7904b248f4e82f267a144aaf6306a57f4868657c Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 3 Oct 2026 23:03:37 +0300 Subject: [PATCH 15/21] Drop unused Bootloaders link reference Nothing on the page uses it and the anchor doesn't exist there. --- docs/quick-start/receivers/hmpp2400.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/quick-start/receivers/hmpp2400.md b/docs/quick-start/receivers/hmpp2400.md index 379460e62..f20fb0d72 100644 --- a/docs/quick-start/receivers/hmpp2400.md +++ b/docs/quick-start/receivers/hmpp2400.md @@ -154,4 +154,3 @@ The next steps will not be able to proceed smoothly and you'll have issues later [Configured]: ../receivers/configuring-fc.md [firmware options]: ../firmware-options.md [Receiver Wiring]: wiring-up.md#connecting-a-receiver -[Bootloaders]: #bootloaders From 282d8f6b1d125d708ee3f72f6e29e89d6cd774cd Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 3 Oct 2026 23:03:37 +0300 Subject: [PATCH 16/21] Port Markdown extensions from mkdocs.yml and enable strict mode Without a markdown_extensions table Zensical used its own defaults, which dropped master's tab slugs, critic and magiclink shorthand. Tabs went back to __tabbed_N ids, breaking links like firmware-version.md#via-lua-script, and critic marks and #1234 / @user shorthand rendered as raw text. With those restored the build is warning-free, so strict mode now keeps it that way. --- zensical.toml | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/zensical.toml b/zensical.toml index ccd8732c2..3dbc18d64 100644 --- a/zensical.toml +++ b/zensical.toml @@ -14,6 +14,8 @@ repo_name = "ExpressLRS/ExpressLRS" repo_url = "https://github.com/ExpressLRS/ExpressLRS/releases/" edit_uri = "https://github.com/ExpressLRS/Docs/edit/master/docs/" +strict = true + extra_css = [ "assets/stylesheets/main.css", "assets/stylesheets/colors.css", @@ -190,6 +192,43 @@ nav = [ { "Contact Us" = "contact-us.md" }, ] +# --------------------------------------------------------------------------- +# Markdown extensions — ported from mkdocs.yml; replaces Zensical's defaults +# --------------------------------------------------------------------------- +[project.markdown_extensions] +abbr = {} +admonition = {} +attr_list = {} +def_list = {} +footnotes = {} +md_in_html = {} +toc.permalink = true +pymdownx.arithmatex.generic = true +pymdownx.betterem.smart_enable = "all" +pymdownx.caret = {} +pymdownx.critic = {} +pymdownx.details = {} +pymdownx.emoji.emoji_generator = "zensical.extensions.emoji.to_svg" +pymdownx.emoji.emoji_index = "zensical.extensions.emoji.twemoji" +pymdownx.highlight.anchor_linenums = true +pymdownx.highlight.line_spans = "__span" +pymdownx.highlight.pygments_lang_class = true +pymdownx.inlinehilite = {} +pymdownx.keys = {} +pymdownx.magiclink.repo_url_shorthand = true +pymdownx.magiclink.user = "ExpressLRS" +pymdownx.magiclink.repo = "ExpressLRS" +pymdownx.mark = {} +pymdownx.smartsymbols = {} +pymdownx.superfences.custom_fences = [ + { name = "mermaid", class = "mermaid", format = "pymdownx.superfences.fence_code_format" }, +] +# Slugged tab ids keep links like firmware-version.md#via-lua-script working +pymdownx.tabbed.alternate_style = true +pymdownx.tabbed.slugify = { object = "pymdownx.slugs.slugify", kwds = { case = "lower" } } +pymdownx.tasklist.custom_checkbox = true +pymdownx.tilde = {} + # --------------------------------------------------------------------------- # Plugins # --------------------------------------------------------------------------- From 558087e0a7ff7e000bc5ef6a7dae4e1ab7166229 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 3 Oct 2026 23:06:51 +0300 Subject: [PATCH 17/21] Restore the header search button The header override copied from Material looked for the material/search plugin, which Zensical registers as plain search, so the button never rendered even though the index was built. --- overrides/partials/header.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/overrides/partials/header.html b/overrides/partials/header.html index 54c2fd169..0ff2489e9 100644 --- a/overrides/partials/header.html +++ b/overrides/partials/header.html @@ -90,8 +90,8 @@ {% endif %} - {% if "material/search" in config.plugins %} - {% set search = config.plugins["material/search"] | attr("config") %} + {% if "search" in config.plugins %} + {% set search = config.plugins["search"] | attr("config") %} {% if search.enabled %} From 9a04e8ddb122fb0f97edad79c88daa50f8ef5fbb Mon Sep 17 00:00:00 2001 From: MUSTARDTIGER Date: Sat, 3 Oct 2026 16:35:25 -0700 Subject: [PATCH 18/21] Restore git revision dates and committers on Zensical Zensical has no plugin API yet, so a Markdown extension fills in the page meta fields the MkDocs git-revision-date-localized and git-committers plugins used to set. --- .github/workflows/publish.yml | 4 + .gitignore | 1 + Dockerfile | 3 + docs/assets/javascripts/timeago.js | 14 ++ overrides/hooks/git_info.py | 345 ++++++++++++++++++++++++++++ overrides/partials/source-file.html | 114 +++++++++ pyproject.toml | 15 ++ uv.lock | 4 +- zensical.toml | 7 + 9 files changed, 505 insertions(+), 2 deletions(-) create mode 100644 docs/assets/javascripts/timeago.js create mode 100644 overrides/hooks/git_info.py create mode 100644 overrides/partials/source-file.html diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f7a587c3f..d6b6882b5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -22,6 +22,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 + with: + fetch-depth: 0 # git_info needs full history for page dates - uses: actions/configure-pages@v5 @@ -35,6 +37,8 @@ jobs: - name: Build site run: uv run zensical build --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # git_info committer lookups - name: Generate llms.txt run: uv run python overrides/hooks/llms_txt.py diff --git a/.gitignore b/.gitignore index ade9a89ef..7003fcb5d 100644 --- a/.gitignore +++ b/.gitignore @@ -153,3 +153,4 @@ overrides/partials/recent-posts.html # Local scratch notes TODO.md +.git-committers.json diff --git a/Dockerfile b/Dockerfile index 5a37aca5a..eef27b12f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,3 +2,6 @@ FROM zensical/zensical:0.0.67 # WORKDIR, EXPOSE 8000, ENTRYPOINT and the default # `serve --dev-addr=0.0.0.0:8000` CMD are inherited from the base image. + +# Lets zensical.toml load the git_info Markdown extension +ENV PYTHONPATH=/docs/overrides/hooks diff --git a/docs/assets/javascripts/timeago.js b/docs/assets/javascripts/timeago.js new file mode 100644 index 000000000..be7ab288f --- /dev/null +++ b/docs/assets/javascripts/timeago.js @@ -0,0 +1,14 @@ +// Turns the git_info dates () into "3 months ago" +document$.subscribe(function() { + var rtf = new Intl.RelativeTimeFormat(document.documentElement.lang || "en", { numeric: "auto" }) + var units = [["year", 31536000], ["month", 2592000], ["week", 604800], ["day", 86400], ["hour", 3600], ["minute", 60]] + document.querySelectorAll(".md-source-file .timeago[datetime]").forEach(function(el) { + var secs = (Date.parse(el.getAttribute("datetime")) - Date.now()) / 1000 + for (var i = 0; i < units.length; i++) { + if (Math.abs(secs) >= units[i][1] || i === units.length - 1) { + el.textContent = rtf.format(Math.round(secs / units[i][1]), units[i][0]) + break + } + } + }) +}) diff --git a/overrides/hooks/git_info.py b/overrides/hooks/git_info.py new file mode 100644 index 000000000..12585b7aa --- /dev/null +++ b/overrides/hooks/git_info.py @@ -0,0 +1,345 @@ +"""Git revision dates and GitHub committers for Zensical. + +Zensical has no plugin API yet, but every page still goes through Python +Markdown, and the page metadata dict handed to extensions is what Rust reads +back for the templates. So this is a Markdown extension that does the work of +mkdocs-git-revision-date-localized-plugin and mkdocs-git-committers-plugin-2: + + page.meta.git_revision_date_localized last commit touching the file + page.meta.git_creation_date_localized first commit (follows renames) + page.meta.git_committers [{login, name, url, avatar}, ...] + +The stock partials/source-file.html already renders the two dates. The +committers need a one-line override, since the stock template reads a +top-level `committers` variable that only the MkDocs plugin could set. + +zensical.toml: + + [project.markdown_extensions.git_info] + repository = "ExpressLRS/Docs" + branch = "master" + docs_dir = "docs" + type = "date" # date | datetime | iso_date | iso_datetime | timeago + locale = "en" + committers = true + cache_file = ".git-committers.json" + +Committer logins come from the GitHub API (one call per unknown author, cached +in cache_file). Set GITHUB_TOKEN, or MKDOCS_GIT_COMMITTERS_APIKEY, in CI. +Without a token it uses whatever is cached plus noreply addresses, and skips +the rest rather than failing the build. The checkout needs full history +(actions/checkout with fetch-depth: 0), or every page looks a day old. +""" + +from __future__ import annotations + +import atexit +import http.client +import json +import logging +import os +import re +import subprocess +import threading +from concurrent.futures import ThreadPoolExecutor +import urllib.error +import urllib.request +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + +from markdown import Extension, Markdown +from markdown.preprocessors import Preprocessor + +try: + from babel.dates import format_date, format_datetime +except ImportError: # babel is optional, dates fall back to strftime + format_date = format_datetime = None + + +NOREPLY = re.compile(r"^(?:\d+\+)?([^@]+)@users\.noreply\.github\.com$") + + +log = logging.getLogger(__name__) + + +def git(*args: str, cwd: Path | None = None) -> str: + # safe.directory: the repo is often a bind mount owned by someone else + return subprocess.run( + ["git", "-c", "safe.directory=*", *args], + cwd=cwd, + capture_output=True, + text=True, + check=True, + ).stdout + + +# ---------------------------------------------------------------------------- +# Git history and the committer cache, shared across every page render +# ---------------------------------------------------------------------------- + + +class History: + def __init__(self, cfg: dict[str, Any]): + self.cfg = cfg + self.lock = threading.Lock() + self.commits: dict[str, list[tuple[int, str, str, str]]] = {} + try: + self.root = Path(git("rev-parse", "--show-toplevel").strip()) + except (OSError, subprocess.CalledProcessError) as e: + # No git, not a checkout, or a bind mount git refuses to trust: + # build without dates rather than not at all + log.warning("git_info: no git history, skipping (%s)", e) + self.root = None + self.cache_path = Path(cfg["cache_file"]) + self.users: dict[str, dict | None] = {} + self.dirty = False + self.token = os.environ.get("MKDOCS_GIT_COMMITTERS_APIKEY") or os.environ.get( + "GITHUB_TOKEN" + ) + self.api_dead = False + if self.cache_path.exists(): + try: + self.users = json.loads(self.cache_path.read_text())["users"] + except (ValueError, KeyError): + pass + atexit.register(self.save) + if self.root is None: + return + self.index() + if cfg["committers"]: + self.prefetch() + + def index(self) -> None: + """Read the whole docs history in one git call, following renames. + + One subprocess per page is ~100ms each under build load; this is one + call for the lot. Walking newest to oldest, a rename maps the old + name onto whatever the file is called at HEAD. + """ + self.docs = ( + (Path.cwd() / self.cfg["docs_dir"]) + .resolve() + .relative_to(self.root.resolve()) + .as_posix() + ) + out = git( + "log", + "--no-merges", + "-M", + "--name-status", + "--format=%x00%at%x1f%an%x1f%ae%x1f%H", + "--", + self.docs, + cwd=self.root, + ) + alias: dict[str, str] = {} + for chunk in out.split("\0")[1:]: + head, *files = chunk.strip("\n").split("\n") + t, name, email, sha = head.split("\x1f") + commit = (int(t), name, email.lower(), sha) + for line in files: + if not line: + continue + status, *paths = line.split("\t") + current = alias.get(paths[-1], paths[-1]) + self.commits.setdefault(current, []).append(commit) + if status.startswith("R"): + alias[paths[0]] = current + + def log(self, path: str) -> list[tuple[int, str, str, str]]: + """Commits touching a docs-relative path, newest first.""" + if self.root is None: + return [] + return self.commits.get(f"{self.docs}/{path}", []) + + def prefetch(self) -> None: + """Resolve every author up front, in parallel. + + Lookups are a few hundred ms each and page renders are serial, so + doing them lazily turns a cold cache into minutes of build time. + """ + todo: dict[str, tuple[str, str]] = {} + for commits in self.commits.values(): + for _, name, email, sha in commits: + if email not in self.users and email not in todo: + todo[email] = (name, sha) + if todo: + with ThreadPoolExecutor(8) as pool: + for email, (name, sha) in todo.items(): + pool.submit(self.user, email, name, sha) + + def user(self, email: str, name: str, sha: str) -> dict | None: + with self.lock: + if email in self.users: + return self.users[email] + user = None + if m := NOREPLY.match(email): + user = self._profile(m.group(1), name) + elif not self.api_dead: + data = self._api(f"repos/{self.cfg['repository']}/commits/{sha}") + if data and data.get("author"): + a = data["author"] + user = { + "login": a["login"], + "name": name, + "url": a["html_url"], + "avatar": a["avatar_url"], + } + elif data is None: + return None # API failure, don't cache a negative + with self.lock: + self.users[email] = user + self.dirty = True + return user + + def _profile(self, login: str, name: str) -> dict: + return { + "login": login, + "name": name, + "url": f"https://github.com/{login}", + "avatar": f"https://avatars.githubusercontent.com/{login}", + } + + def _api(self, path: str) -> dict | None: + req = urllib.request.Request( + f"https://api.github.com/{path}", + headers={"Accept": "application/vnd.github+json"}, + ) + if self.token: + req.add_header("Authorization", f"Bearer {self.token}") + try: + with urllib.request.urlopen(req, timeout=10) as r: + return json.load(r) + except urllib.error.HTTPError as e: + if e.code in (401, 403, 429): # bad token or rate limited + self.api_dead = True + return {} if e.code == 422 else None # 422: sha not on GitHub + except (OSError, http.client.HTTPException, ValueError): + return None # flaky network shouldn't fail the build + + def save(self) -> None: + if not self.dirty: + return + self.cache_path.parent.mkdir(parents=True, exist_ok=True) + self.cache_path.write_text( + json.dumps({"users": self.users}, indent=1, sort_keys=True) + ) + + +_history: History | None = None +_history_lock = threading.Lock() + + +def history(cfg: dict[str, Any]) -> History: + global _history + with _history_lock: + if _history is None: + _history = History(cfg) + return _history + + +# ---------------------------------------------------------------------------- +# The Markdown side +# ---------------------------------------------------------------------------- + + +def render_date(ts: int, cfg: dict[str, Any]) -> str: + dt = datetime.fromtimestamp(ts, tz=timezone.utc) + kind, locale = cfg["type"], cfg["locale"] + if kind == "timeago": + # Readable date as the no-JS fallback; assets/javascripts/timeago.js + # swaps it for "3 months ago" + return ( + f'' + f"{render_date(ts, {**cfg, 'type': 'date'})}" + ) + if kind == "iso_date": + return dt.strftime("%Y-%m-%d") + if kind == "iso_datetime": + return dt.strftime("%Y-%m-%d %H:%M:%S") + if kind == "datetime": + if format_datetime: + return format_datetime(dt, format="medium", locale=locale) + return dt.strftime("%B %d, %Y %H:%M:%S") + if format_date: + return format_date(dt, format="long", locale=locale) + return dt.strftime("%B %d, %Y") + + +class GitInfoPreprocessor(Preprocessor): + def __init__(self, md: Markdown, cfg: dict[str, Any]): + super().__init__(md) + self.cfg = cfg + + def run(self, lines: list[str]) -> list[str]: + # Imported late so this module can be loaded outside Zensical + from zensical.extensions.context import ContextPreprocessor + + ctx = ContextPreprocessor.from_markdown(self.md) + if ctx is None: + return lines + page = ctx.page + hist = history(self.cfg) + commits = hist.log(page.path) + if hist.root is None: + return lines + if not commits: # untracked file: say "now", like the MkDocs plugin + now = int(datetime.now(tz=timezone.utc).timestamp()) + commits = [(now, "", "", "")] + + meta = page.meta + meta.setdefault( + "git_revision_date_localized", render_date(commits[0][0], self.cfg) + ) + meta.setdefault( + "git_creation_date_localized", render_date(commits[-1][0], self.cfg) + ) + + if self.cfg["committers"] and commits[0][3]: + # Most commits first, matching mkdocs-git-committers-plugin-2 + counts: dict[str, list] = {} + for _, name, email, sha in commits: + entry = counts.setdefault(email, [0, name, sha]) + entry[0] += 1 + seen, people = set(), [] + for email, (_, name, sha) in sorted( + counts.items(), key=lambda kv: -kv[1][0] + ): + user = hist.user(email, name, sha) + if ( + user + and user["login"] not in seen + and user["login"] not in self.cfg["exclude"] + ): + seen.add(user["login"]) + people.append(user) + meta["git_committers"] = people + return lines + + +class GitInfoExtension(Extension): + def __init__(self, **kwargs: Any): + self.config = { + "repository": ["", "owner/name on GitHub, for committer lookup"], + "branch": ["master", "unused for now, kept for parity"], + "docs_dir": ["docs", "docs dir relative to the repo root"], + "type": ["date", "date | datetime | iso_date | iso_datetime | timeago"], + "locale": ["en", "babel locale for date formatting"], + "committers": [True, "resolve GitHub committers"], + "exclude": [[], "logins to leave out, e.g. bots"], + "cache_file": [".git-committers.json", "login cache"], + } + super().__init__(**kwargs) + + def extendMarkdown(self, md: Markdown) -> None: + # Below Zensical's context preprocessor (priority 0) is irrelevant, + # it's looked up by type, but stay ahead of front-matter stripping. + md.preprocessors.register( + GitInfoPreprocessor(md, self.getConfigs()), "git_info", 5 + ) + + +def makeExtension(**kwargs: Any) -> GitInfoExtension: + return GitInfoExtension(**kwargs) diff --git a/overrides/partials/source-file.html b/overrides/partials/source-file.html new file mode 100644 index 000000000..a63621f80 --- /dev/null +++ b/overrides/partials/source-file.html @@ -0,0 +1,114 @@ +{#- + Stock Zensical 0.0.67 partial, plus page.meta.git_committers from + overrides/hooks/git_info.py. Drop once Zensical sets `committers` itself. +-#} +{% macro render_updated(date) %} + + + {% include ".icons/material/clock-edit-outline.svg" %} + + {{ date }} + +{% endmacro %} +{% macro render_created(date) %} + + + {% include ".icons/material/clock-plus-outline.svg" %} + + {{ date }} + +{% endmacro %} +{% macro render_authors(authors) %} + {% set git_authors = config.plugins["git-authors"] %} + + + {% if authors | length == 1 %} + {% include ".icons/material/account.svg" %} + {% else %} + {% include ".icons/material/account-group.svg" %} + {% endif %} + + + +{% endmacro %} +{% macro render_committers_github(title) %} + + {% include ".icons/material/github.svg" %} + + {{ title }} +{% endmacro %} +{% macro render_committers_gitlab(title) %} + + {% include ".icons/material/gitlab.svg" %} + + {{ title }} +{% endmacro %} +{% macro render_committers(authors) %} + + {% if committers_source == "gitlab" %} + {{ render_committers_gitlab("GitLab") }} + {% else %} + {{ render_committers_github("GitHub") }} + {% endif %} + + +{% endmacro %} +{% if page.meta %} + {% if page.meta.git_committers %} + {% set committers = page.meta.git_committers %} + {% endif %} + {% if page.meta.git_revision_date_localized %} + {% set updated = page.meta.git_revision_date_localized %} + {% elif page.meta.revision_date %} + {% set updated = page.meta.revision_date %} + {% endif %} + {% if page.meta.git_creation_date_localized %} + {% set created = page.meta.git_creation_date_localized %} + {% endif %} +{% endif %} +{% if updated or created or git_info or committers %} + +{% endif %} diff --git a/pyproject.toml b/pyproject.toml index 61fe2d7df..8c468eeb7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,3 +12,18 @@ dependencies = [ spellcheck = [ "pyspelling==2.12.1", ] + +# Installs the project editable so overrides/hooks is on sys.path, which is +# how zensical.toml finds the git_info Markdown extension +[tool.uv] +package = true + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +bypass-selection = true + +[tool.hatch.build] +dev-mode-dirs = ["overrides/hooks"] diff --git a/uv.lock b/uv.lock index 804671015..9d4e7e2e6 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.11" [[package]] @@ -57,7 +57,7 @@ wheels = [ [[package]] name = "expresslrs-docs" version = "0.1.0" -source = { virtual = "." } +source = { editable = "." } dependencies = [ { name = "zensical" }, ] diff --git a/zensical.toml b/zensical.toml index 3dbc18d64..6ac3b6179 100644 --- a/zensical.toml +++ b/zensical.toml @@ -28,6 +28,7 @@ extra_javascript = [ "https://cdnjs.cloudflare.com/ajax/libs/tablesort/5.2.1/tablesort.min.js", "https://cdnjs.cloudflare.com/ajax/libs/tablesort/5.2.1/sorts/tablesort.number.min.js", "assets/javascripts/tablesort.js", + "assets/javascripts/timeago.js", "assets/javascripts/mathjax.js", "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js", ] @@ -656,3 +657,9 @@ name = "Send a Donation!" # * llms_txt.py (after build) → site/llms.txt, built from the nav # and page descriptions (the native llmstxt plugin can't read either) # --------------------------------------------------------------------------- + +# Replaces git-revision-date-localized + git-committers (overrides/hooks/git_info.py) +[project.markdown_extensions.git_info] +repository = "ExpressLRS/Docs" +type = "timeago" +committers = true From 44668b8e831bb7e8941ea5f61363a3a33bd22041 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Mon, 5 Oct 2026 08:47:12 +0300 Subject: [PATCH 19/21] Load git_info via PYTHONPATH instead of an editable install Making the docs a hatchling package only served to put overrides/hooks on sys.path. PYTHONPATH does that with no build backend, matching the Dockerfile, so the Makefile and publish workflow now set it too. The Makefile also switches from pip to uv, as CI already uses. --- .github/workflows/publish.yml | 1 + Makefile | 11 +++++++---- pyproject.toml | 15 --------------- uv.lock | 2 +- 4 files changed, 9 insertions(+), 20 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d6b6882b5..4068c1590 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -38,6 +38,7 @@ jobs: - name: Build site run: uv run zensical build --clean env: + PYTHONPATH: overrides/hooks # zensical.toml loads git_info from here GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # git_info committer lookups - name: Generate llms.txt diff --git a/Makefile b/Makefile index 3d04be348..1cf328028 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,16 @@ -# Host-only convenience wrappers around `docker compose`. -# CI invokes `docker compose` directly and never calls `make`. +# Host-only convenience wrappers around `uv` and `docker compose`. +# CI invokes `uv` directly and never calls `make`. .PHONY: install build run serve site shell spellcheck catalog +# zensical.toml loads git_info from here +export PYTHONPATH := overrides/hooks + install: - pip install "zensical==0.0.67" + uv sync build: - zensical build --clean + uv run zensical build --clean run: serve diff --git a/pyproject.toml b/pyproject.toml index 8c468eeb7..61fe2d7df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,18 +12,3 @@ dependencies = [ spellcheck = [ "pyspelling==2.12.1", ] - -# Installs the project editable so overrides/hooks is on sys.path, which is -# how zensical.toml finds the git_info Markdown extension -[tool.uv] -package = true - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -bypass-selection = true - -[tool.hatch.build] -dev-mode-dirs = ["overrides/hooks"] diff --git a/uv.lock b/uv.lock index 9d4e7e2e6..44d07d49d 100644 --- a/uv.lock +++ b/uv.lock @@ -57,7 +57,7 @@ wheels = [ [[package]] name = "expresslrs-docs" version = "0.1.0" -source = { editable = "." } +source = { virtual = "." } dependencies = [ { name = "zensical" }, ] From 8c3b5a0fefb1dd93a413cf5a16f2d1cdeea5abab Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Mon, 5 Oct 2026 08:57:50 +0300 Subject: [PATCH 20/21] Give the docs container git and a GitHub token The zensical image ships without git, so git_info skipped every page and the dev server showed no dates or committers. The token comes from an optional .env and lifts committer lookups off the 60 per hour unauthenticated limit. --- Dockerfile | 3 +++ compose.yml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Dockerfile b/Dockerfile index eef27b12f..6ab207d2e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,5 +3,8 @@ FROM zensical/zensical:0.0.67 # WORKDIR, EXPOSE 8000, ENTRYPOINT and the default # `serve --dev-addr=0.0.0.0:8000` CMD are inherited from the base image. +# git_info reads page dates and committers from git +RUN apk add --no-cache git + # Lets zensical.toml load the git_info Markdown extension ENV PYTHONPATH=/docs/overrides/hooks diff --git a/compose.yml b/compose.yml index 71551a86b..3b6271bad 100644 --- a/compose.yml +++ b/compose.yml @@ -4,6 +4,8 @@ services: context: . dockerfile: Dockerfile dns_search: '' + environment: + GITHUB_TOKEN: ${GITHUB_TOKEN:-} # git_info committer lookups, set in .env networks: - net ports: From 48f3b74f6bd11bc20aa95731cdf792f3713c1d48 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 10 Oct 2026 15:48:52 +0300 Subject: [PATCH 21/21] Keep sidebar entries for collapsible FAQ headings Zensical copies heading markup into the table of contents, so the custom-heading spans also appear in the sidebar. The script removed the parent of every such span and blanked those entries. It now only matches spans directly inside a heading. --- docs/assets/javascripts/admonition-enhancement.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/assets/javascripts/admonition-enhancement.js b/docs/assets/javascripts/admonition-enhancement.js index e8e449c79..13ab4ea8d 100644 --- a/docs/assets/javascripts/admonition-enhancement.js +++ b/docs/assets/javascripts/admonition-enhancement.js @@ -1,6 +1,8 @@ // This code helps in linking Table of content with Admonition title -var elements = document.getElementsByClassName('custom-heading'); +// Zensical copies heading markup into the sidebar TOC, so match only the +// headings in the page body or the TOC entries get removed along with them +var elements = document.querySelectorAll(':is(h1, h2, h3, h4, h5, h6) > .custom-heading'); for (var i = 0; i < elements.length; i++) { var ele = elements[i]; @@ -18,6 +20,6 @@ for (var i = 0; i < elements.length; i++) { } // Removing all custom headings -while (elements.length) { - elements[0].parentElement.remove(); +for (var i = 0; i < elements.length; i++) { + elements[i].parentElement.remove(); } \ No newline at end of file