diff --git a/.github/workflows/publish-documentation.yaml b/.github/workflows/publish-documentation.yaml
index e4eb297e0..b8d20f053 100644
--- a/.github/workflows/publish-documentation.yaml
+++ b/.github/workflows/publish-documentation.yaml
@@ -1,31 +1,306 @@
name: Publish Documentation
on:
push:
- branches:
- - main
+ workflow_dispatch:
+ inputs:
+ deploy_as_release:
+ description: 'Deploy as release version (e.g., v1.2.3) - will deploy the docs as if this is a release with this version'
+ required: false
+ type: string
+ default: ''
+ force_deploy:
+ description: 'Force deployment even on non-main branches (for testing)'
+ required: false
+ type: boolean
+ default: false
+ skip_build:
+ description: 'Skip build-and-test job (use existing artifacts)'
+ required: false
+ type: boolean
+ default: false
+ workflow_call:
+ inputs:
+ deploy_as_release:
+ description: 'Deploy as release version (e.g., v1.2.3) - will deploy the docs as if this is a release with this version'
+ required: false
+ type: string
+ default: ''
+ skip_build:
+ description: 'Skip build-and-test job (assumes artifacts already exist)'
+ required: false
+ type: boolean
+ default: false
+
+env:
+ PYTHON_VERSION: '3.11'
+ NODE_VERSION: '20'
+ VERSIONED_BUCKET_NAME: simpl-element-release
+ CLOUDFRONT_DOMAIN: d2uqfzn4lxgtwv.cloudfront.net
jobs:
build-and-test:
+ if: ${{ !inputs.skip_build && github.event.inputs.skip_build != 'true' }}
uses: ./.github/workflows/build-and-test.yaml
secrets:
SIEMENS_NPM_TOKEN: ${{ secrets.SIEMENS_NPM_TOKEN }}
SIEMENS_NPM_USER: ${{ secrets.SIEMENS_NPM_USER }}
MAPTILER_KEY: ${{ secrets.MAPTILER_KEY }}
- publish-documentation:
+ # Simple deployment for main branch (no versioning)
+ publish-documentation-main:
runs-on: ubuntu-24.04
+ needs:
+ - build-and-test
+ if: >-
+ ${{
+ needs.build-and-test.result == 'success' &&
+ !inputs.deploy_as_release
+ }}
permissions:
+ contents: write
pages: write
id-token: write
- needs:
- - build-and-test
steps:
- - uses: actions/configure-pages@v5
- - uses: actions/download-artifact@v7
+ - name: Download documentation artifact
+ uses: actions/download-artifact@v7
with:
name: pages
path: dist/design
- - uses: actions/upload-pages-artifact@v4
+
+ - name: Setup Pages
+ uses: actions/configure-pages@v5
+
+ - name: Upload Pages artifact
+ uses: actions/upload-pages-artifact@v4
with:
path: 'dist/design'
- - uses: actions/deploy-pages@v4
+
+ - name: Deploy to GitHub Pages
+ uses: actions/deploy-pages@v4
+
+ determine-documentation-versions:
+ needs:
+ - build-and-test
+ if: ${{ always() && (needs.build-and-test.result == 'success' || inputs.skip_build) && inputs.deploy_as_release }}
+ permissions:
+ contents: read
+ runs-on: ubuntu-24.04
+ outputs:
+ deploy_major: ${{ steps.deploy.outputs.deploy_major }}
+ major_version: ${{ steps.deploy.outputs.major_version }}
+ version: ${{ steps.deploy.outputs.version }}
+ should_deploy: ${{ steps.deploy.outputs.should_deploy }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Determine deployment targets
+ id: deploy
+ run: |
+ # Initialize all outputs
+ DEPLOY_MAJOR="false"
+ MAJOR_VERSION=""
+ VERSION=""
+ SHOULD_DEPLOY="false"
+
+ DEPLOY_AS_RELEASE="${{ github.event.inputs.deploy_as_release || inputs.deploy_as_release }}"
+
+ # Validate and extract version info
+ if [[ -n "$DEPLOY_AS_RELEASE" ]]; then
+ # Ensure version starts with 'v'
+ if [[ ! "$DEPLOY_AS_RELEASE" =~ ^v[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then
+ echo "Error: Deploy as release version must be in format 'v1.2.3' or 'v1.2.3-suffix'"
+ echo "Provided: '$DEPLOY_AS_RELEASE'"
+ exit 1
+ fi
+
+ VERSION="${DEPLOY_AS_RELEASE#v}"
+ MAJOR_VERSION="v$(echo "$VERSION" | cut -d. -f1)"
+
+ # Check if it's a pre-release (contains -, like v1.0.0-rc1)
+ IS_PRERELEASE="false"
+ if [[ "$VERSION" =~ -.*$ ]]; then
+ IS_PRERELEASE="true"
+ fi
+
+ # Deploy to major version except for pre/next/rc releases
+ if [[ "$IS_PRERELEASE" == "false" ]]; then
+ DEPLOY_MAJOR="true"
+ SHOULD_DEPLOY="true"
+ fi
+ fi
+
+ echo "deploy_major=$DEPLOY_MAJOR" >> $GITHUB_OUTPUT
+ echo "major_version=$MAJOR_VERSION" >> $GITHUB_OUTPUT
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+ echo "should_deploy=$SHOULD_DEPLOY" >> $GITHUB_OUTPUT
+
+ echo "DEPLOYMENT PLAN"
+ echo "==============="
+ echo "Branch: ${{ github.ref }}"
+ echo "Trigger: ${{ github.event_name }}"
+ echo "Major ($MAJOR_VERSION): $DEPLOY_MAJOR"
+
+ # Versioned deployment for releases (S3 only, not GitHub Pages)
+ publish-documentation-release:
+ runs-on: ubuntu-24.04
+ needs:
+ - build-and-test
+ - determine-documentation-versions
+ # always() is required because build-and-test may be skipped (when skip_build=true)
+ # We still want to run if determine-documentation-versions succeeded
+ if: >-
+ ${{
+ always() &&
+ needs.determine-documentation-versions.result == 'success' &&
+ needs.determine-documentation-versions.outputs.should_deploy == 'true'
+ }}
+ permissions:
+ contents: write
+ id-token: write
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Download documentation artifact
+ uses: actions/download-artifact@v7
+ with:
+ name: pages
+ path: new-docs
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v5.1.1
+ with:
+ role-to-assume: arn:aws:iam::974483672234:role/simpl-element-release
+ role-session-name: element-release-docs
+ aws-region: eu-west-1
+
+ - name: Download existing versions from S3
+ run: |
+ mkdir -p deploy-site
+
+ # Download all existing documentation from S3 (as backup/source of truth)
+ echo "Downloading existing versions from S3..."
+ aws s3 cp s3://${{ env.VERSIONED_BUCKET_NAME }}/ deploy-site/ --recursive --quiet || echo "No existing versions found"
+
+ - name: Update with new version(s)
+ run: |
+ # Deploy to major version directory
+ MAJOR_VERSION="${{ needs.determine-documentation-versions.outputs.major_version }}"
+
+ echo "Updating /$MAJOR_VERSION/..."
+ # Remove old version to ensure clean deployment
+ rm -rf "deploy-site/$MAJOR_VERSION"
+ mkdir -p "deploy-site/$MAJOR_VERSION"
+ cp -r new-docs/* "deploy-site/$MAJOR_VERSION/"
+
+ # Add base tag to docs HTML files only (exclude element-examples, dashboards-demo, coverage)
+ find "deploy-site/$MAJOR_VERSION" -name "*.html" -type f \
+ ! -path "*/element-examples/*" \
+ ! -path "*/dashboards-demo/*" \
+ ! -path "*/coverage/*" \
+ -exec sed -i.bak "s|
|\n |" {} \;
+
+ find "deploy-site/$MAJOR_VERSION" -name "*.bak" -type f -delete
+
+ - name: Generate versions.json
+ run: |
+ # Generate versions.json from actual folder structure in deploy-site
+ echo "Generating versions.json from deploy-site folder structure..."
+
+ VERSIONS='[]'
+
+ # Add preview as a redirect to element.siemens.io
+ echo "Adding: preview (redirect to https://element.siemens.io)"
+ VERSIONS=$(echo "$VERSIONS" | jq '. += [{"version": "https://element.siemens.io", "title": "Preview", "aliases": []}]')
+
+ # Find all version-specific folders (v1, v2, v48, etc.)
+ for version_dir in deploy-site/v*/; do
+ if [[ -d "$version_dir" ]]; then
+ version_name=$(basename "$version_dir")
+ version_num=$(echo "$version_name" | sed 's/^v//')
+ echo "Found: $version_name"
+ VERSIONS=$(echo "$VERSIONS" | jq --arg version "$version_name" --arg title "v$version_num" '. += [{"version": $version, "title": $title, "aliases": []}]')
+ fi
+ done
+
+ # Sort versions using the merge-versions.mjs script's sort logic
+ # Create a temporary script to sort the versions
+ cat > sort-versions.mjs << 'EOFSCRIPT'
+ import { readFileSync, writeFileSync } from 'fs';
+
+ const sortKey = (version) => {
+ // Preview (URL) comes first
+ if (version.startsWith('http')) return [0, 0];
+
+ const match = version.match(/^v(\d+)(?:\D|$)/);
+ if (match) {
+ return [1, -parseInt(match[1], 10)];
+ }
+
+ return [2, version];
+ };
+
+ const sortVersions = (versions) => {
+ return versions.sort((a, b) => {
+ const [groupA, valueA] = sortKey(a.version);
+ const [groupB, valueB] = sortKey(b.version);
+
+ if (groupA !== groupB) return groupA - groupB;
+ if (valueA === valueA) return 0;
+ return valueA < valueB ? -1 : 1;
+ });
+ };
+
+ const versions = JSON.parse(readFileSync(process.argv[2], 'utf-8'));
+ const sorted = sortVersions(versions);
+ writeFileSync(process.argv[3], JSON.stringify(sorted, null, 2));
+ console.log('Sorted versions:', sorted.map(v => v.version).join(', '));
+ EOFSCRIPT
+
+ echo "$VERSIONS" > unsorted-versions.json
+ node sort-versions.mjs unsorted-versions.json deploy-site/versions.json
+
+ echo "Generated versions.json:"
+ cat deploy-site/versions.json
+
+ - name: Update canonical URLs
+ run: |
+ SITE_URL="https://element.siemens.io/"
+
+ # Update canonical URLs for version directories
+ MAJOR_VERSION="${{ needs.determine-documentation-versions.outputs.major_version }}"
+ VERSION_URL="${SITE_URL}${MAJOR_VERSION}/"
+
+ find "deploy-site/$MAJOR_VERSION" -name "*.html" -type f -exec sed -i.bak \
+ -e "s|/dev/null || true
+
+ - name: Upload to S3
+ run: |
+ echo "Uploading versioned documentation to S3..."
+
+ MAJOR_VERSION="${{ needs.determine-documentation-versions.outputs.major_version }}"
+ echo "Uploading /$MAJOR_VERSION/..."
+ if [[ ! -d "deploy-site/$MAJOR_VERSION" ]]; then
+ echo "Error: deploy-site/$MAJOR_VERSION directory does not exist"
+ exit 1
+ fi
+ aws s3 sync --quiet --no-progress --delete "deploy-site/$MAJOR_VERSION/" "s3://${{ env.VERSIONED_BUCKET_NAME }}/$MAJOR_VERSION/"
+
+ # Upload versions.json with short cache-control for quick updates
+ if [[ ! -f "deploy-site/versions.json" ]]; then
+ echo "Error: deploy-site/versions.json file does not exist"
+ exit 1
+ fi
+ aws s3 cp deploy-site/versions.json s3://${{ env.VERSIONED_BUCKET_NAME }}/versions.json \
+ --cache-control "max-age=60,public"
+
+ echo "Uploaded versioned documentation to S3 at s3://${{ env.VERSIONED_BUCKET_NAME }}/"
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 8a431c3e9..48700bac6 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -16,6 +16,9 @@ jobs:
- build-and-test
permissions:
id-token: write
+ outputs:
+ new_release: ${{ steps.check_release.outputs.new_release }}
+ release_tag: ${{ steps.check_release.outputs.release_tag }}
steps:
- uses: actions/checkout@v6
with:
@@ -30,6 +33,13 @@ jobs:
name: dist
path: dist
- run: npm ci --prefer-offline --no-audit
+
+ - name: Get tag before semantic-release
+ id: before_release
+ run: |
+ BEFORE_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
+ echo "before_tag=$BEFORE_TAG" >> $GITHUB_OUTPUT
+
- run: npx semantic-release
env:
SKIP_COMMIT: ${{ github.ref_name == 'next' && 'true' || '' }}
@@ -38,3 +48,28 @@ jobs:
GIT_COMMITTER_NAME: 'Siemens Element Bot'
GIT_COMMITTER_EMAIL: 'simpl.si@siemens.com'
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_GITHUB_TOKEN }}
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+
+ - name: Check if new release was created
+ id: check_release
+ run: |
+ AFTER_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
+ BEFORE_TAG="${{ steps.before_release.outputs.before_tag }}"
+
+ if [[ -n "$AFTER_TAG" && "$AFTER_TAG" != "$BEFORE_TAG" ]]; then
+ echo "release_tag=$AFTER_TAG" >> $GITHUB_OUTPUT
+ echo "new_release=true" >> $GITHUB_OUTPUT
+ else
+ echo "new_release=false" >> $GITHUB_OUTPUT
+ echo "release_tag=" >> $GITHUB_OUTPUT
+ fi
+
+ trigger-documentation:
+ uses: ./.github/workflows/publish-documentation.yaml
+ needs:
+ - publish
+ - build-and-test
+ if: success() && needs.publish.outputs.new_release == 'true'
+ with:
+ deploy_as_release: ${{ needs.publish.outputs.release_tag }}
+ skip_build: true
diff --git a/docs/_src/version-selector.js b/docs/_src/version-selector.js
new file mode 100644
index 000000000..8fdc603a3
--- /dev/null
+++ b/docs/_src/version-selector.js
@@ -0,0 +1,167 @@
+/**
+ * Version Selector for Siemens Element Documentation
+ *
+ * This script implements a version switcher that:
+ * - Fetches versions.json from the root of the domain
+ * - Supports absolute version URLs
+ * - Preserves current page path when switching versions
+ * - Gracefully degrades if versions.json is not found (no errors, just no selector)
+ *
+ * If versions.json is not available (404), the page loads normally without the version selector.
+ * No errors are thrown to ensure documentation remains accessible.
+ *
+ * Based on MkDocs Material's version selector implementation
+ * https://github.com/squidfunk/mkdocs-material
+ */
+
+(function () {
+ 'use strict';
+
+ /**
+ * Get the base URL of the site (root domain)
+ */
+ function getBaseURL() {
+ const location = window.location;
+ return `${location.protocol}//${location.host}`;
+ }
+
+ /**
+ * Get current version from URL path
+ * Only recognizes versions that exist in versions.json
+ * Returns empty string if at root (no version in path)
+ */
+ function getCurrentVersion(versions) {
+ const path = window.location.pathname;
+
+ // If we don't have versions yet, try to extract from path
+ if (!versions) {
+ const match = path.match(/\/([^/]+)\//);
+ return match ? match[1] : '';
+ }
+
+ // Check if any known version is in the path
+ for (const version of versions) {
+ if (version.version && path.includes(`/${version.version}/`)) {
+ return version.version;
+ }
+ }
+
+ // No version found in path, assume root-level version
+ return '';
+ }
+
+ /**
+ * Get current page path relative to version
+ */
+ function getCurrentPagePath(currentVersion) {
+ const path = window.location.pathname;
+
+ // If no version (root-level), return the full path
+ if (!currentVersion || currentVersion === '') {
+ return path.substring(1); // Remove leading slash
+ }
+
+ // Find version in path and get everything after it
+ const versionIndex = path.indexOf(`/${currentVersion}/`);
+ if (versionIndex !== -1) {
+ return path.substring(versionIndex + currentVersion.length + 2);
+ }
+
+ return '';
+ }
+
+ /**
+ * Build version URL with current page path
+ * Supports versions at root (empty string), in subdirectories, or absolute URLs
+ */
+ function buildVersionURL(version, currentVersion, preservePath = true) {
+ // If version is an absolute URL, return as-is
+ if (
+ version.startsWith('http://') ||
+ version.startsWith('https://') ||
+ version.startsWith('//')
+ ) {
+ return version;
+ }
+
+ const baseURL = getBaseURL();
+ const pagePath = preservePath ? getCurrentPagePath(currentVersion) : '';
+
+ // If version is empty string or "/", host at root
+ if (!version || version === '/' || version === '') {
+ return `${baseURL}/${pagePath}`;
+ }
+
+ return `${baseURL}/${version}/${pagePath}`;
+ }
+
+ /**
+ * Render version selector HTML
+ */
+ function renderVersionSelector(versions, currentVersion) {
+ const current = versions.find(v => v.version === currentVersion) || versions[0];
+ const visibleVersions = versions.filter(v => !v.hidden);
+
+ const html = ``;
+
+ return html;
+ }
+
+ /**
+ * Initialize version selector
+ */
+ function initVersionSelector() {
+ const baseURL = getBaseURL();
+ const versionsURL = `${baseURL}/versions.json`;
+
+ fetch(versionsURL)
+ .then(response => {
+ if (!response.ok) {
+ if (response.status === 404) {
+ return null;
+ }
+ throw new Error(`Failed to fetch versions.json: ${response.status}`);
+ }
+ return response.json();
+ })
+ .then(versions => {
+ if (!versions) {
+ return;
+ }
+
+ if (!Array.isArray(versions) || versions.length === 0) {
+ console.warn('[Version Selector] versions.json is empty or invalid');
+ return;
+ }
+
+ // Get current version after we have the versions list
+ const currentVersion = getCurrentVersion(versions);
+
+ // Find the .md-header element
+ const header = document.querySelector('.md-header');
+ if (!header) {
+ console.warn('[Version Selector] .md-header element not found');
+ return;
+ }
+
+ // Create .md-header__topic wrapper with version selector inside
+ const html = renderVersionSelector(versions, currentVersion);
+ const topicWrapper = document.createElement('div');
+ topicWrapper.className = 'md-header__topic';
+ topicWrapper.innerHTML = html;
+
+ // Append to .md-header
+ header.appendChild(topicWrapper);
+ })
+ .catch(error => {
+ console.error('[Version Selector] Failed to load:', error.message);
+ });
+ }
+
+ // Initialize when DOM is ready
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', initVersionSelector);
+ } else {
+ initVersionSelector();
+ }
+})();
diff --git a/docs/_src/versions.example.json b/docs/_src/versions.example.json
new file mode 100644
index 000000000..a05b58ee5
--- /dev/null
+++ b/docs/_src/versions.example.json
@@ -0,0 +1,18 @@
+[
+ {
+ "version": "",
+ "title": "Latest"
+ },
+ {
+ "version": "v18",
+ "title": "18.x"
+ },
+ {
+ "version": "v17",
+ "title": "17.x"
+ },
+ {
+ "version": "https://element.siemens.io",
+ "title": "Preview"
+ }
+]
diff --git a/mkdocs.yml b/mkdocs.yml
index dacb61308..1c54350a9 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -225,6 +225,7 @@ extra_javascript:
- '//w3.siemens.com/ote/ote_config.js'
- '//w3.siemens.com/ote/sinet/ote.js'
- 'https://assets.adobedtm.com/5dfc7d97c6fb/f16b45bec907/launch-af252bb19983.min.js'
+ - '_src/version-selector.js'
extra:
links:
- name: 'GitHub'
diff --git a/pyproject.toml b/pyproject.toml
index 6af26dd81..95395dca0 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,7 +7,7 @@ license = "MIT"
requires-python = ">=3.11, <4"
readme = "README.md"
dependencies = [
- "mkdocs-code-siemens-code-docs-theme>=7.7.0,<8",
+ "mkdocs-code-siemens-code-docs-theme>=7.8.0,<8",
"mkdocs-minify-html-plugin==0.3.9",
"mkdocs-element-docs-builder",
]
diff --git a/uv.lock b/uv.lock
index 7241d79fc..7a81a8e5b 100644
--- a/uv.lock
+++ b/uv.lock
@@ -120,7 +120,7 @@ dependencies = [
[package.metadata]
requires-dist = [
- { name = "mkdocs-code-siemens-code-docs-theme", specifier = ">=7.7.0,<8", index = "https://code.siemens.com/api/v4/groups/3259/-/packages/pypi/simple" },
+ { name = "mkdocs-code-siemens-code-docs-theme", specifier = ">=7.8.0,<8", index = "https://code.siemens.com/api/v4/groups/3259/-/packages/pypi/simple" },
{ name = "mkdocs-element-docs-builder", editable = "projects/element-docs-builder" },
{ name = "mkdocs-minify-html-plugin", specifier = "==0.3.9" },
]
@@ -259,15 +259,15 @@ wheels = [
[[package]]
name = "mkdocs-code-siemens-code-docs-theme"
-version = "7.7.0"
+version = "7.8.0"
source = { registry = "https://code.siemens.com/api/v4/groups/3259/-/packages/pypi/simple" }
dependencies = [
{ name = "mkdocs" },
{ name = "mkdocs-material" },
]
-sdist = { url = "https://code.siemens.com/api/v4/groups/3259/-/packages/pypi/files/5466e241cd053e818300f964f74e70764f988156ba6d0eb042364be23828728b/mkdocs_code_siemens_code_docs_theme-7.7.0.tar.gz", hash = "sha256:5466e241cd053e818300f964f74e70764f988156ba6d0eb042364be23828728b" }
+sdist = { url = "https://code.siemens.com/api/v4/groups/3259/-/packages/pypi/files/e3b7946b001b72cb17cc129c7af2ad748812d34a200ef51265a6253fcbc30648/mkdocs_code_siemens_code_docs_theme-7.8.0.tar.gz", hash = "sha256:e3b7946b001b72cb17cc129c7af2ad748812d34a200ef51265a6253fcbc30648" }
wheels = [
- { url = "https://code.siemens.com/api/v4/groups/3259/-/packages/pypi/files/68573928ab9d3c5bf14ca954481629c9512c9c837d4f3cdc15fa72eb54f47bb7/mkdocs_code_siemens_code_docs_theme-7.7.0-py3-none-any.whl", hash = "sha256:68573928ab9d3c5bf14ca954481629c9512c9c837d4f3cdc15fa72eb54f47bb7" },
+ { url = "https://code.siemens.com/api/v4/groups/3259/-/packages/pypi/files/035a290de19b47fb31e9a91cc9c88083ce7baadde41f1a57b2237b8f59f080e3/mkdocs_code_siemens_code_docs_theme-7.8.0-py3-none-any.whl", hash = "sha256:035a290de19b47fb31e9a91cc9c88083ce7baadde41f1a57b2237b8f59f080e3" },
]
[[package]]
@@ -297,7 +297,7 @@ wheels = [
[[package]]
name = "mkdocs-material"
-version = "9.6.16"
+version = "9.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "babel" },
@@ -312,9 +312,9 @@ dependencies = [
{ name = "pymdown-extensions" },
{ name = "requests" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/dd/84/aec27a468c5e8c27689c71b516fb5a0d10b8fca45b9ad2dd9d6e43bc4296/mkdocs_material-9.6.16.tar.gz", hash = "sha256:d07011df4a5c02ee0877496d9f1bfc986cfb93d964799b032dd99fe34c0e9d19", size = 4028828, upload-time = "2025-07-26T15:53:47.542Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/9c/3b/111b84cd6ff28d9e955b5f799ef217a17bc1684ac346af333e6100e413cb/mkdocs_material-9.7.0.tar.gz", hash = "sha256:602b359844e906ee402b7ed9640340cf8a474420d02d8891451733b6b02314ec", size = 4094546, upload-time = "2025-11-11T08:49:09.73Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/65/f4/90ad67125b4dd66e7884e4dbdfab82e3679eb92b751116f8bb25ccfe2f0c/mkdocs_material-9.6.16-py3-none-any.whl", hash = "sha256:8d1a1282b892fe1fdf77bfeb08c485ba3909dd743c9ba69a19a40f637c6ec18c", size = 9223743, upload-time = "2025-07-26T15:53:44.236Z" },
+ { url = "https://files.pythonhosted.org/packages/04/87/eefe8d5e764f4cf50ed91b943f8e8f96b5efd65489d8303b7a36e2e79834/mkdocs_material-9.7.0-py3-none-any.whl", hash = "sha256:da2866ea53601125ff5baa8aa06404c6e07af3c5ce3d5de95e3b52b80b442887", size = 9283770, upload-time = "2025-11-11T08:49:06.26Z" },
]
[[package]]