diff --git a/.devfile.yaml b/.devfile.yaml index ab2a68050..57b5f7fe7 100644 --- a/.devfile.yaml +++ b/.devfile.yaml @@ -22,7 +22,7 @@ commands: - id: serve-docs exec: component: runtime - commandLine: make serve-docs DOC_LISTEN="--host 0.0.0.0" + commandLine: make docs-serve DOC_LISTEN="--host 0.0.0.0" - id: sync exec: component: runtime diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml index 6f477c8f7..31cfe5301 100644 --- a/.github/workflows/documentation.yaml +++ b/.github/workflows/documentation.yaml @@ -66,7 +66,7 @@ jobs: - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: ./docs/build_all + path: ./docs/build # Deployment job deploy: diff --git a/.html b/.html new file mode 100644 index 000000000..e69de29bb diff --git a/CONTRIB.md b/CONTRIB.md deleted file mode 100644 index 49cc1abf5..000000000 --- a/CONTRIB.md +++ /dev/null @@ -1,36 +0,0 @@ -# contrib directory -This directory contains extensions to the core codebase, community drivers and adapter libraries and tools. - -If you are working on a driver, or adapter library of general interest, -please consider submitting it to this repository, as it will become available -to all jumpstarter users. - -If you are creating a driver or adapter library for a specific need, not of general -interest, or that needs to be private, please consider forking our [jumpstarter-driver-template](https://github.com/jumpstarter-dev/jumpstarter-driver-template) - - -## Creating a new drivers scaffold -To create a new driver scaffold, you can use the `create_driver.sh` script in this directory. This should help you star a new driver project with the right structure and dependencies quickly. - -From the root directory of the project, run the following command: -```shell -$ ./__templates__/create_driver.sh yepkit Ykush "Miguel Angel Ajo" "miguelangel@ajo.es" - -Creating: packages/jumpstarter_driver_yepkit/jumpstarter_driver_yepkit/__init__.py -Creating: packages/jumpstarter_driver_yepkit/jumpstarter_driver_yepkit/client.py -Creating: packages/jumpstarter_driver_yepkit/jumpstarter_driver_yepkit/driver_test.py -Creating: packages/jumpstarter_driver_yepkit/jumpstarter_driver_yepkit/driver.py -Creating: packages/jumpstarter_driver_yepkit/.gitignore -Creating: packages/jumpstarter_driver_yepkit/pyproject.toml -Creating: packages/jumpstarter_driver_yepkit/README.md -Creating: packages/jumpstarter_driver_yepkit/examples/exporter.yaml - -$ make sync -uv sync --all-packages --all-extras -Resolved 125 packages in 18ms - Built jumpstarter-driver-shell @ file:///Users/ajo/work/jumpstarter/contrib/drivers/shell -Prepared 1 package in 569ms -Uninstalled 1 package in 1ms -Installed 1 package in 1ms - ~ jumpstarter-driver-shell==0.1.0 (from file:///Users/ajo/work/jumpstarter/contrib/drivers/shell) -```` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..29babe125 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,256 @@ +# Contributing + +Thank you for your interest in contributing to Jumpstarter! This document outlines the process for contributing to the project and provides guidelines to make the contribution process smooth. + +## Code of Conduct + +Please be respectful and considerate of others when contributing to the project. + +## Getting Started + +1. Fork the repository +2. Clone your fork locally +3. Set up the development environment +4. Make your changes on a new branch +5. Test your changes thoroughly +6. Submit a pull request + +## Development Setup + +```bash +# Install dependencies +make sync + +# Run tests +make test +``` + +## Contribution Guidelines + +### Making Changes + +- Keep changes focused and related to a single issue +- Follow the existing code style and conventions +- Add tests for new features or bug fixes +- Update documentation as needed + +### Commit Messages + +- Use clear and descriptive commit messages +- Reference issue numbers when applicable +- Follow conventional commit format when possible + +### Pull Requests + +- Provide a clear description of the changes +- Link to any relevant issues +- Ensure all tests pass before submitting +- Be responsive to feedback and questions + +## Types of Contributions + +### Code Contributions + +We welcome contributions to the core codebase, including bug fixes, features, and improvements. + +### Contributing Drivers + +If you are working on a driver or adapter library of general interest, please consider submitting it to this repository, as it will become available to all Jumpstarter users. + +To create a new driver scaffold, you can use the `create_driver.sh` script: + +```bash +./__templates__/create_driver.sh vendor_name driver_name "Your Name" "your.email@example.com" +``` + +This will create the necessary files and structure for your driver in the `packages/` directory. For example: + +```bash +./__templates__/create_driver.sh yepkit Ykush "Your Name" "your.email@example.com" +``` + +Creates: +``` +packages/jumpstarter_driver_yepkit/jumpstarter_driver_yepkit/__init__.py +packages/jumpstarter_driver_yepkit/jumpstarter_driver_yepkit/client.py +packages/jumpstarter_driver_yepkit/jumpstarter_driver_yepkit/driver_test.py +packages/jumpstarter_driver_yepkit/jumpstarter_driver_yepkit/driver.py +packages/jumpstarter_driver_yepkit/.gitignore +packages/jumpstarter_driver_yepkit/pyproject.toml +packages/jumpstarter_driver_yepkit/README.md +packages/jumpstarter_driver_yepkit/examples/exporter.yaml +``` + +#### Driver Structure + +A Jumpstarter driver typically consists of: + +1. **Driver Implementation**: The core functionality that interfaces with the device or service +2. **Client Implementation**: Client code to interact with the driver +3. **Tests**: Tests to verify the driver's functionality +4. **Examples**: Example configurations showing how to use the driver +5. **Documentation**: Clear documentation on setup and usage + +#### Private Drivers + +If you are creating a driver or adapter library for a specific need, not of general interest, or that needs to be private, please consider forking our [jumpstarter-driver-template](https://github.com/jumpstarter-dev/jumpstarter-driver-template). + +#### Driver Development Workflow + +After creating your driver skeleton: + +1. Implement the driver functionality according to the Jumpstarter driver interface +2. Write comprehensive tests for your driver +3. Create example configurations +4. Document the setup and usage in the README.md +5. Submit a pull request to the main Jumpstarter repository + +#### Testing Your Driver + +To test your driver during development: + +```bash +# From the project root +make sync # Synchronize dependencies +cd packages/your_driver_package +pytest # Run tests for your driver +``` + +#### Driver Best Practices + +- Follow the existing code style and conventions +- Write comprehensive documentation +- Include thorough test coverage +- Create example configurations for common use cases +- Keep dependencies minimal and well-justified + +### Documentation Contributions + +We welcome improvements to our documentation. + +#### Documentation System Overview + +Jumpstarter uses [Sphinx](https://www.sphinx-doc.org/en/master/) with Markdown support for its documentation. The documentation is organized into various sections covering different aspects of the project. + +#### Setting Up Documentation Environment + +To contribute to the documentation, you'll need to set up your development environment: + +1. Clone the Jumpstarter repository +2. Navigate to the docs directory +3. Install dependencies (if not already installed with the main project) + +#### Building the Documentation Locally + +To build and preview the documentation locally: + +```bash +cd docs +make html # Build HTML documentation +make docs-serve # Serve documentation locally for preview +``` + +The documentation will be available at http://localhost:8000 in your web browser. + +#### Documentation Structure + +- `docs/source/`: Root directory for documentation source files + - `index.md`: Main landing page + - `*.md`: Various markdown files for documentation sections + - `_static/`: Static assets like images and CSS + - `_templates/`: Custom templates + +#### Writing Documentation + +Documentation is written in Markdown with some Sphinx-specific extensions. Common syntax includes: + +```markdown +# Heading 1 +## Heading 2 +### Heading 3 + +*italic text* +**bold text** + +- Bullet list item +- Another item + +1. Numbered list +2. Second item + +[Link text](URL) + + + +```{note} +This is a note admonition +``` + +```{warning} +This is a warning admonition +``` + +```python +# This is a code block +def example(): + print("Hello, world!") +``` +``` + +#### Documentation Style Guide + +Please follow these guidelines when writing documentation: + +1. Use clear, concise language +2. Write in the present tense +3. Use active voice when possible +4. Include practical examples +5. Break up text with headers, lists, and code blocks +6. Target both beginners and advanced users with appropriate sections +7. Provide cross-references to related documentation + +#### Adding New Documentation Pages + +To add a new documentation page: + +1. Create a new Markdown (`.md`) file in the appropriate directory +2. Add the page to the relevant `index.md` or `toctree` directive +3. Build the documentation to ensure it appears correctly + +#### Documentation Versioning + +Documentation is versioned alongside the main Jumpstarter releases. When making changes, consider whether they apply to the current version or future versions. + +#### Documentation Tips + +- **Screenshots**: Include screenshots for complex UI operations +- **Command Examples**: Always include example commands with expected output +- **Troubleshooting**: Add common issues and their solutions +- **Links**: Link to relevant external resources when appropriate + +#### Submitting Documentation Changes + +Once your documentation changes are complete: + +1. Build the documentation to verify there are no errors +2. Submit a pull request with your changes +3. Respond to feedback during the review process + +### Example Contributions + +To add a new example: + +1. Create a new directory in the `examples/` folder with a descriptive name +2. Include a comprehensive README.md with setup and usage instructions +3. Follow the structure of existing examples +4. Ensure the example is well-documented and easy to follow + +## Getting Help + +If you have questions or need help, please: + +1. Check the [documentation](https://docs.jumpstarter.dev/) +2. Open an issue in the repository +3. Reach out to the maintainers + +Thank you for contributing to Jumpstarter! \ No newline at end of file diff --git a/Makefile b/Makefile index a81a5aeae..f7e64c391 100644 --- a/Makefile +++ b/Makefile @@ -1,36 +1,40 @@ PKG_TARGETS = $(subst packages/,,$(wildcard packages/*)) -EXAMPLE_TARGETS = $(subst examples/,example-,$(wildcard examples/*)) -DOC_LISTEN ?= --host 127.0.0.1 default: build -docs: - uv run --isolated --all-packages --group docs $(MAKE) -C docs html - docs-all: - ./docs/make-all-versions.sh - -serve-all: - python3 -m http.server 8000 --bind 127.0.0.1 -d ./docs/build_all + uv run --isolated --all-packages --group docs $(MAKE) -C docs multiversion -serve-docs: - uv run --isolated --all-packages --group docs $(MAKE) -C docs serve HOST="$(DOC_LISTEN)" +docs-serve: clean-docs + uv run --isolated --all-packages --group docs $(MAKE) -C docs serve -clean-docs: - uv run --isolated --all-packages --group docs $(MAKE) -C docs clean +docs-serve-all: clean-docs docs-all + uv run --isolated --all-packages --group docs $(MAKE) -C docs serve-multiversion -doctest: +docs-test: uv run --isolated --all-packages --group docs $(MAKE) -C docs doctest -test-%: packages/% +docs-linkcheck: + uv run --isolated --all-packages --group docs $(MAKE) -C docs linkcheck + +pkg-test-%: packages/% uv run --isolated --directory $< pytest -mypy-%: packages/% +pkg-mypy-%: packages/% uv run --isolated --directory $< mypy . -test-packages: $(addprefix test-,$(PKG_TARGETS)) +pkg-test-all: $(addprefix pkg-test-,$(PKG_TARGETS)) + +pkg-mypy-all: $(addprefix pkg-mypy-,$(PKG_TARGETS)) -mypy-packages: $(addprefix mypy-,$(PKG_TARGETS)) +build: + uv build --all --out-dir dist + +generate: + buf generate + +sync: + uv sync --all-packages --all-extras clean-venv: -rm -rf ./.venv @@ -40,34 +44,30 @@ clean-build: -rm -rf dist clean-test: - -rm .coverage - -rm coverage.xml + -rm -f .coverage + -rm -f coverage.xml -rm -rf htmlcov -sync: - uv sync --all-packages --all-extras - -test: test-packages doctest - -mypy: mypy-packages - -generate: - buf generate - -build: - uv build --all --out-dir dist +clean-docs: + uv run --isolated --all-packages --group docs $(MAKE) -C docs clean clean: clean-docs clean-venv clean-build clean-test -.PHONY: sync docs docs-all serve-all test test-packages build clean-test clean-docs clean-venv clean-build \ - mypy-jumpstarter \ - mypy-jumpstarter-cli-admin \ - mypy-jumpstarter-driver-can \ - mypy-jumpstarter-driver-dutlink \ - mypy-jumpstarter-driver-network \ - mypy-jumpstarter-driver-raspberrypi \ - mypy-jumpstarter-driver-sdwire \ - mypy-jumpstarter-driver-tftp \ - mypy-jumpstarter-driver-yepkit \ - mypy-jumpstarter-kubernetes \ - mypy-jumpstarter-protocol +test: pkg-test-all docs-test + +mypy: pkg-mypy-all + +.PHONY: default docs docs-all docs-serve docs-serve-all docs-clean docs-test \ + docs-linkcheck pkg-test-all pkg-mypy-all build generate sync \ + clean-venv clean-build clean-test clean-all test-all mypy-all docs \ + pkg-mypy-jumpstarter \ + pkg-mypy-jumpstarter-cli-admin \ + pkg-mypy-jumpstarter-driver-can \ + pkg-mypy-jumpstarter-driver-dutlink \ + pkg-mypy-jumpstarter-driver-network \ + pkg-mypy-jumpstarter-driver-raspberrypi \ + pkg-mypy-jumpstarter-driver-sdwire \ + pkg-mypy-jumpstarter-driver-tftp \ + pkg-mypy-jumpstarter-driver-yepkit \ + pkg-mypy-jumpstarter-kubernetes \ + pkg-mypy-jumpstarter-protocol diff --git a/README.md b/README.md index d609ffd78..7736b27de 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,20 @@ -# jumpstarter-python +# jumpstarter -This project provides both a library, and a command line client to interact with the -Jumpstarter resources and services. +A Python library and CLI tool for interacting with Jumpstarter resources and services. -For more details see the following documents: +## Installation -* [Glossary](https://docs.jumpstarter.dev/glossary) -* [Usage](https://docs.jumpstarter.dev/getting-started/) -* [Architecture](https://docs.jumpstarter.dev/architecture.html) +```bash +pip install jumpstarter +``` + +## Basic Usage + +```python +from jumpstarter import Client + +client = Client() +# Use the client to interact with Jumpstarter services +``` + +For comprehensive documentation, visit [docs.jumpstarter.dev](https://docs.jumpstarter.dev) diff --git a/__templates__/create_driver.sh b/__templates__/create_driver.sh index 0f3475d66..38b437802 100755 --- a/__templates__/create_driver.sh +++ b/__templates__/create_driver.sh @@ -27,13 +27,54 @@ MODULE_DIRECTORY=${DRIVER_DIRECTORY}/jumpstarter_driver_${DRIVER_NAME} mkdir -p ${MODULE_DIRECTORY} mkdir -p ${DRIVER_DIRECTORY}/examples +# Create documentation file +DOCS_DIRECTORY=docs/source/api-reference/drivers +DOC_FILE=${DOCS_DIRECTORY}/${DRIVER_NAME}.md + +# Create initial documentation file if it doesn't exist +if [ ! -f "${DOC_FILE}" ]; then + echo "Creating initial documentation file: ${DOC_FILE}" + cat > "${DOC_FILE}" << EOF +# ${DRIVER_CLASS} Driver + +\`jumpstarter-driver-${DRIVER_NAME}\` provides functionality for interacting with ${DRIVER_NAME} devices. + +## Installation + +```bash +pip install jumpstarter-driver-${DRIVER_NAME} +``` + +## Configuration + +Example configuration: + +```yaml +interfaces: + ${DRIVER_NAME}: + driver: jumpstarter_driver_${DRIVER_NAME}.${DRIVER_CLASS}Driver + parameters: + # Add required parameters here +``` + +## API Reference + +Add API documentation here. +EOF +fi for f in __init__.py client.py driver_test.py driver.py; do echo "Creating: ${MODULE_DIRECTORY}/${f}" envsubst < __templates__/driver/jumpstarter_driver/${f}.tmpl > ${MODULE_DIRECTORY}/${f} done -for f in .gitignore pyproject.toml README.md examples/exporter.yaml; do +for f in .gitignore pyproject.toml examples/exporter.yaml; do echo "Creating: ${DRIVER_DIRECTORY}/${f}" envsubst < __templates__/driver/${f}.tmpl > ${DRIVER_DIRECTORY}/${f} done + +# Create symlink to documentation file instead of README.md +echo "Creating symlink to documentation file" +rel_path=$(realpath --relative-to="${DRIVER_DIRECTORY}" "${DOC_FILE}") +ln -sf "${rel_path}" "${DRIVER_DIRECTORY}/README.md" +echo "Created symlink: ${DRIVER_DIRECTORY}/README.md -> ${rel_path}" diff --git a/docs/Makefile b/docs/Makefile index 377d10343..a6e70b8bc 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -7,13 +7,15 @@ SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = source BUILDDIR = build -HOST ?= --host 127.0.0.1 +HOST ?= 127.0.0.1 +PORT ?= 8000 # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -.PHONY: help Makefile +# Define custom targets as PHONY to prevent them from being caught by the pattern rule +.PHONY: help Makefile serve multiversion redirect serve-multiversion # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). @@ -21,4 +23,16 @@ help: @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) serve: - sphinx-autobuild $(HOST) "$(SOURCEDIR)" "$(BUILDDIR)" + sphinx-autobuild --host $(HOST) --port $(PORT) "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +multiversion: + sphinx-multiversion "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @echo "Creating redirect index.html to main/index.html" + @echo '' > "$(BUILDDIR)/index.html" + @echo '
' >> "$(BUILDDIR)/index.html" + @echo 'Redirecting to main documentation...
' >> "$(BUILDDIR)/index.html" + +serve-multiversion: + @echo "Serving multiversion documentation using the HOST variable" + @cd "$(BUILDDIR)" && python -m http.server $(PORT) --bind $(HOST) \ No newline at end of file diff --git a/docs/make-all-versions.sh b/docs/make-all-versions.sh deleted file mode 100755 index 577d09fe6..000000000 --- a/docs/make-all-versions.sh +++ /dev/null @@ -1,106 +0,0 @@ -#!/bin/env bash -set -euox pipefail - - -# Add excluded releases as we progress, for example -# release candidates once we have provided the final release -# or versions that are not meant to be published -EXCLUDED_RELEASES=("v0\.0\.[0-9]+" \ - "v0\.5\.0rc[0-9]+") - -# find the current branch name -CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) - -# This script is used to generate all versions of the documentation. -rm -rf docs/build_all 2>/dev/null || true -mkdir -p docs/build_all -RELEASES=$(git tag) - -# remove excluded releases -for release in "${EXCLUDED_RELEASES[@]}"; do - RELEASES=$(echo "${RELEASES}" | sed -E "s/$release//g") -done - -RELEASES_WITHOUT_CURRENT="${RELEASES}" - -# if current release not in the list, add it at the start -if [[ ! $RELEASES =~ $CURRENT_BRANCH ]]; then - RELEASES="$CURRENT_BRANCH $RELEASES" -fi - -# create a temporary directory to store the necessary files -TMPDIR=$(mktemp -d) - -# copy the necessary files from the current branch, older -# branches may have different settings or nothing... -cp docs/source/_static/js/versions.js "${TMPDIR}/" -cp docs/source/_static/css/versions.css "${TMPDIR}/" -cp docs/source/_templates/sidebar/versions.html "${TMPDIR}/" -cp docs/source/conf.py.versions "${TMPDIR}/" - - -# generate a javascript array of all versions and store in docs/source/_static/js/version_array.js -echo "var version_array = [" > "${TMPDIR}/"/version_array.js -for release in $RELEASES; do - # if release in semver format, remove the leading 'v' - if [[ $release =~ ^v[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then - release="${release/#v/}" - fi - release=$(echo "${release}" | tr '[:upper:]' '[:lower:]') - echo " '$release'," >> "${TMPDIR}/"/version_array.js -done - -echo "];" >> "${TMPDIR}/"/version_array.js - -for release in $RELEASES; do - git checkout "${release}" - # copy after checkout to avoid conflict - mkdir -p docs/source/_static/js # some versions didn't have that directory - cp "${TMPDIR}/"/version_array.js docs/source/_static/js/version_array.js - if [ ! -f docs/source/_static/js/versions.js ]; then - mkdir -p docs/source/_static/js - mkdir -p docs/source/_static/css - mkdir -p docs/source/_templates/sidebar - cp "${TMPDIR}/"/versions.js docs/source/_static/js/versions.js - cp "${TMPDIR}/"/versions.css docs/source/_static/css/versions.css - cp "${TMPDIR}/"/versions.html docs/source/_templates/sidebar/versions.html - fi - # inject our configuration for version listing into the conf.py - cat "${TMPDIR}/"/conf.py.versions >> docs/source/conf.py - - make clean - make sync - make docs - - git stash # un-do uv.lock - git checkout HEAD - # remove the front v from the release tag - release="${release/#v/}" - - mv docs/build/html docs/build_all/"$(echo "${release}" | tr '[:upper:]' '[:lower:]')" -done - -git checkout "${CURRENT_BRANCH}" -f - -# use the current branch as the default redirect -REDIRECT="${CURRENT_BRANCH}" - -# if redirect has semver format, remove the leading 'v' -if [[ $REDIRECT =~ ^v[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then - REDIRECT=$(echo "${REDIRECT/#v/}" | tr '[:upper:]' '[:lower:]') -fi - -# render redirect to the right branch -cat <Welcome to the jumpstarter documentation, you will be redirected to the latest version.
-If you are not redirected automatically, follow this link.
- - -EOF diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 747ffb7b3..000000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -if "%1" == "" goto help - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/source/_static/css/custom.css b/docs/source/_static/css/custom.css new file mode 100644 index 000000000..bed65c03f --- /dev/null +++ b/docs/source/_static/css/custom.css @@ -0,0 +1,8 @@ +.admonition.warning { + margin-bottom: 2rem; +} + +/* Hide the Furo attribution text */ +.made-with-furo { + display: none !important; +} \ No newline at end of file diff --git a/docs/source/_static/css/tabs.css b/docs/source/_static/css/tabs.css new file mode 100644 index 000000000..a1cc8f7b6 --- /dev/null +++ b/docs/source/_static/css/tabs.css @@ -0,0 +1,53 @@ +.tab { + overflow: hidden; + border: 1px solid #e5e5e5; + background-color: #f8f8f8; + border-radius: 4px 4px 0 0; +} + +/* Force black text for all button states */ +.tab button, +.tab button:hover, +.tab button:active, +.tab button:focus, +.tab button.active { + color: #000000 !important; +} + +.tab button { + background-color: inherit; + float: left; + border: none; + outline: none; + cursor: pointer; + padding: 10px 16px; + transition: 0.3s; + font-size: 16px; +} + +.tab button:hover { + background-color: #c8c8c8; +} + +.tab button.active { + background-color: #e5e5e5; +} + +.tabcontent { + display: none; + padding: 6px 12px; + border: 1px solid #e5e5e5; + border-top: none; + border-radius: 0 0 4px 4px; + animation: fadeEffect 0.5s; +} + +@keyframes fadeEffect { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} \ No newline at end of file diff --git a/docs/source/_static/css/versions.css b/docs/source/_static/css/versions.css deleted file mode 100644 index fdf39c098..000000000 --- a/docs/source/_static/css/versions.css +++ /dev/null @@ -1,4 +0,0 @@ -.version { - color: #aaa; - margin-left: 10px; -} \ No newline at end of file diff --git a/docs/source/_static/img/favicon.png b/docs/source/_static/img/favicon.png new file mode 100644 index 000000000..d5fc5cd80 Binary files /dev/null and b/docs/source/_static/img/favicon.png differ diff --git a/docs/source/_static/img/logo-dark-theme.svg b/docs/source/_static/img/logo-dark-theme.svg new file mode 100644 index 000000000..bcfae8c19 --- /dev/null +++ b/docs/source/_static/img/logo-dark-theme.svg @@ -0,0 +1,14 @@ + diff --git a/docs/source/_static/img/logo-light-theme.svg b/docs/source/_static/img/logo-light-theme.svg new file mode 100644 index 000000000..799bed8d0 --- /dev/null +++ b/docs/source/_static/img/logo-light-theme.svg @@ -0,0 +1,14 @@ + diff --git a/docs/source/_static/js/tabs.js b/docs/source/_static/js/tabs.js new file mode 100644 index 000000000..0f339c9e7 --- /dev/null +++ b/docs/source/_static/js/tabs.js @@ -0,0 +1,51 @@ +// Function to handle tab switching +function openTab(evt, tabName) { + // Get the parent tab container + const tabButton = evt.currentTarget; + const tabContainer = tabButton.closest('.tab'); + + // Find all tab content related to this tab group + // We'll look for siblings of the tab container with class tabcontent + const tabGroup = tabContainer.parentNode; + const tabContents = tabGroup.querySelectorAll('.tabcontent'); + + // Hide all tab contents in this group + tabContents.forEach(content => { + content.style.display = "none"; + }); + + // Remove active class from all buttons in this tab container + const tabButtons = tabContainer.querySelectorAll('.tablinks'); + tabButtons.forEach(button => { + button.className = button.className.replace(" active", ""); + }); + + // Show the selected tab content and add active class to the button + document.getElementById(tabName).style.display = "block"; + tabButton.className += " active"; +} + +// Initialize tabs on page load +document.addEventListener('DOMContentLoaded', function () { + // Process each tab container + document.querySelectorAll('.tab').forEach(tabContainer => { + // Hide all tab content except those marked as initially visible + const tabGroup = tabContainer.parentNode; + const tabContents = tabGroup.querySelectorAll('.tabcontent'); + + // Find the active button in this tab container + const activeButton = tabContainer.querySelector('.tablinks.active'); + + if (activeButton) { + // Trigger click on the active button to set up initial state + activeButton.click(); + } else { + // If no active button is defined, hide all tab contents + tabContents.forEach(content => { + if (!content.hasAttribute("style") || content.style.display !== "block") { + content.style.display = "none"; + } + }); + } + }); +}); \ No newline at end of file diff --git a/docs/source/_static/js/theme-toggle.js b/docs/source/_static/js/theme-toggle.js new file mode 100644 index 000000000..f8912da46 --- /dev/null +++ b/docs/source/_static/js/theme-toggle.js @@ -0,0 +1,109 @@ +// Theme toggle tracker - changes logo based on current theme + +// Immediately execute theme detection and logo setting +(function () { + // Function to detect if OS prefers dark mode + function prefersDarkMode() { + return window.matchMedia('(prefers-color-scheme: dark)').matches; + } + + // Function to determine effective theme + function getEffectiveTheme(themeValue) { + // If theme is set to auto, use OS preference + if (themeValue === 'auto') { + return prefersDarkMode() ? 'dark' : 'light'; + } + // Otherwise use the explicitly set theme + return themeValue; + } + + // Set logo src attribute - called both on initial load and when theme changes + function setLogoSrc() { + // Use current theme or fallback to auto (which uses OS preference) + const currentTheme = document.body ? document.body.getAttribute('data-theme') || 'auto' : 'auto'; + const effectiveTheme = getEffectiveTheme(currentTheme); + + // Calculate base path by analyzing current URL path + // This ensures we get the correct path regardless of navigation depth + const path = window.location.pathname; + let logoPath; + + // Function to create the correct logo path based on a base path + function createLogoPath(basePath) { + return effectiveTheme === 'dark' + ? basePath + '_static/img/logo-dark-theme.svg' + : basePath + '_static/img/logo-light-theme.svg'; + } + + // Check if _static directory exists at the root level (sphinx-autobuild case) + // We dynamically create an image element to test if the root path works + const testImg = new Image(); + testImg.onerror = function () { + // Root _static doesn't exist, try to extract version from path + const segments = path.split('/').filter(Boolean); + if (segments.length > 0) { + // First segment might be a version (in multiversion) or a page (in autobuild) + // We'll try with it as a version first + logoPath = createLogoPath('/' + segments[0] + '/'); + } else { + // Fallback to just root-relative + logoPath = createLogoPath('/'); + } + applyLogoPath(logoPath); + }; + testImg.onload = function () { + // Root _static exists, use root-relative path (sphinx-autobuild case) + logoPath = createLogoPath('/'); + applyLogoPath(logoPath); + }; + // Set src to test if root _static exists + testImg.src = '/_static/img/logo-' + effectiveTheme + '-theme.svg'; + + // Function to apply the logo path to CSS + function applyLogoPath(logoPath) { + // Create or reuse a stylesheet with the logo as a CSS rule + let style = document.getElementById('logo-style'); + if (!style) { + style = document.createElement('style'); + style.id = 'logo-style'; + document.head.appendChild(style); + } + style.textContent = `.sidebar-brand img { content: url("${logoPath}"); }`; + } + + // Return an empty string initially - actual logo path will be set asynchronously + return ''; + } + + // Set initial logo - will run even before DOMContentLoaded + setLogoSrc(); + + // Wait for DOM to be loaded to set up observers and event listeners + document.addEventListener('DOMContentLoaded', function () { + // Set up a mutation observer to watch for theme changes + const observer = new MutationObserver(function (mutations) { + mutations.forEach(function (mutation) { + if (mutation.attributeName === 'data-theme') { + setLogoSrc(); + } + }); + }); + + // Start observing once the body is available + if (document.body) { + observer.observe(document.body, { attributes: true }); + } + + // Listen for OS theme preference changes + const prefersDarkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + if (prefersDarkModeMediaQuery.addEventListener) { + prefersDarkModeMediaQuery.addEventListener('change', function () { + // If current theme is auto, we need to update when OS preference changes + const currentTheme = document.body.getAttribute('data-theme'); + if (currentTheme === 'auto') { + setLogoSrc(); + } + }); + } + }); +})(); \ No newline at end of file diff --git a/docs/source/_static/js/version_array.js b/docs/source/_static/js/version_array.js deleted file mode 100644 index 30df96092..000000000 --- a/docs/source/_static/js/version_array.js +++ /dev/null @@ -1,6 +0,0 @@ -var version_array = [ - 'main', - '0.5.0', - '0.5.0rc1', - '0.5.0rc2', -]; diff --git a/docs/source/_static/js/versions.js b/docs/source/_static/js/versions.js deleted file mode 100644 index da59ad56f..000000000 --- a/docs/source/_static/js/versions.js +++ /dev/null @@ -1,43 +0,0 @@ -var url = window.location.href; - -// extract the path without the host from the url -var path = url.replace(window.location.origin, ''); - -// extract the version from the path, being our version the first part of the path i.e. /v1.0.0/... -var version = path.split('/')[1]; - -// extract the rest of the path -var rest = path.split('/').slice(2).join('/'); - - -// check if version is in version_array -if (version_array.includes(version)) { - // ok, we are running on the site compiled with all versions, we can render the version selector - versions_html = ''; - // iterate version_array and render the version selector - for (var i = 0; i < version_array.length; i++) { - v = version_array[i]; - // join v with rest - var new_url = "/" + v + "/" + rest; - if (v == version) { - // add the current version as a strong item - versions_html += 'Warning
+This documentation is actively being updated as the project evolves and may not be complete in all areas.
+