diff --git a/docs/conf.py b/docs/conf.py index 8f9c7c6c3c..165593569e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -68,7 +68,7 @@ # a list of builtin themes. # html_theme = "pydata_sphinx_theme" -html_logo = "logo.svg" +html_logo = "_static/logo.svg" html_favicon = "_static/logo.svg" html_sourcelink_suffix = "" @@ -124,7 +124,7 @@ ], "logo": { "text": "PyData Theme", - "image_dark": "logo-dark.svg", + "image_dark": "_static/logo-dark.svg", "alt_text": "PyData Theme", }, "use_edit_page_button": True, diff --git a/docs/examples/pydata.md b/docs/examples/pydata.md index d55281d4c8..b4e31c176c 100644 --- a/docs/examples/pydata.md +++ b/docs/examples/pydata.md @@ -3,6 +3,8 @@ file_format: mystnb kernelspec: name: python3 display_name: Python 3 +mystnb: + execution_mode: cache --- % To test this file with nbsphinx we need to convert to ipynb. To do this: diff --git a/docs/user_guide/branding.rst b/docs/user_guide/branding.rst index 658e9a8f35..289facfdd2 100644 --- a/docs/user_guide/branding.rst +++ b/docs/user_guide/branding.rst @@ -11,14 +11,15 @@ This can be replaced by a logo image, and optionally a custom ``html_title`` as Single logo for light and dark mode ----------------------------------- -To use a local image file, put an image in a folder that is in `html_static_path`, and use the following configuration: +To use a **local image file**, use ``html_logo`` as specified in the `Sphinx documentation `__. +The file must be relative to ``conf.py``. +For example, if your documentation had a logo in ``_static/logo.png``: .. code:: python - html_static_path = ["_static"] - html_logo = "logo.png" + html_logo = "_static/logo.png" -To use an external link to an image, make sure the ``html_logo`` begins with ``http``. +To use an **external link** to an image, make sure the ``html_logo`` begins with ``http``. For example: .. code:: python @@ -31,21 +32,23 @@ Different logos for light and dark mode You may specify a different version of your logo image for "light" and "dark" modes. This is useful if your logo image is not adapted to a dark mode (light background, not enough contrast, etc...). -To do so, put the 2 image files in a folder that is in ``html_static_path`` and configure the relative path to each image with ``logo["image_light"]`` and ``logo["image_dark"]`` in ``html_theme_options``, like so: +To do so, use the ``logo["image_light"]`` and ``logo["image_dark"]`` options in ``html_theme_options``. +For each, provide a path relative to ``conf.py`` like so: .. code-block:: python - html_static_path = ["_static"] + # Assuming your `conf.py` has a sibling folder called `_static` with these files html_theme_options = { "logo": { - "image_light": "logo-light.png", - "image_dark": "logo-dark.png", + "image_light": "_static/logo-light.png", + "image_dark": "_static/logo-dark.png", } } .. note:: - ``image_light`` and ``image_dark`` will override the ``html_logo`` setting. If you only specify one of the light or dark variants, the un-specified variant will fall back to the value of ``html_logo``. + ``image_light`` and ``image_dark`` will override the ``html_logo`` setting. + If you only specify one of the light or dark variants, the un-specified variant will fall back to the value of ``html_logo``. Customize logo link ------------------- diff --git a/noxfile.py b/noxfile.py index 3bf851833e..35a1b2edcf 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,8 +1,10 @@ """Automatically build our documentation or run tests. -Environments are re-used by default. Use the following-pattern to re-install them. +Environments are re-used by default. -nox -s docs -- -r +Re-install the environment from scratch: + + nox -s docs -- -r """ import nox from pathlib import Path @@ -33,7 +35,7 @@ def _should_install(session): return should_install -@nox.session +@nox.session(name="compile") def compile(session): """Compile the theme's web assets with sphinx-theme-builder.""" if _should_install(session): @@ -42,12 +44,12 @@ def compile(session): session.run("stb", "compile") -@nox.session +@nox.session(name="docs") def docs(session): """Build the documentation and place in docs/_build/html.""" if _should_install(session): session.install("-e", ".[doc]") - session.run("sphinx-build", "-b=html", "docs/", "docs/_build/html") + session.run("sphinx-build", "-b=html", "docs/", "docs/_build/html", "-v") @nox.session(name="docs-live") @@ -61,9 +63,19 @@ def docs_live(session): @nox.session(name="test") def test(session): - """Run the test suite. Use `-- -r` to re-build the environment.""" + """Run the test suite.""" + if _should_install(session): + session.install("-e", ".[test]") + session.run("pytest", *session.posargs) + + +@nox.session(name="test-sphinx") +@nox.parametrize("sphinx", ["4", "5", "6"]) +def test_sphinx(session, sphinx): + """Run the test suite with a specific version of Sphinx.""" if _should_install(session): session.install("-e", ".[test]") + session.install(f"sphinx=={sphinx}") session.run("pytest", *session.posargs) diff --git a/src/pydata_sphinx_theme/__init__.py b/src/pydata_sphinx_theme/__init__.py index 6fd5debde2..81c4d732dd 100644 --- a/src/pydata_sphinx_theme/__init__.py +++ b/src/pydata_sphinx_theme/__init__.py @@ -12,12 +12,14 @@ from bs4 import BeautifulSoup as bs from docutils import nodes from sphinx import addnodes +from sphinx.application import Sphinx from sphinx.environment.adapters.toctree import TocTree from sphinx.addnodes import toctree as toctree_node from sphinx.transforms.post_transforms import SphinxPostTransform from sphinx.util.nodes import NodeMatcher from sphinx.errors import ExtensionError -from sphinx.util import logging +from sphinx.util import logging, isurl +from sphinx.util.fileutil import copy_asset_file from pygments.formatters import HtmlFormatter from pygments.styles import get_all_styles import requests @@ -1084,6 +1086,70 @@ def setup_translators(app): app.set_translator(name, translator, override=True) +# ------------------------------------------------------------------------------ +# customize events for logo management +# we use one event to copy over custom logo images to _static +# and another even to link them in the html context +# ------------------------------------------------------------------------------ + + +def setup_logo_path( + app: Sphinx, pagename: str, templatename: str, context: dict, doctree: nodes.Node +) -> None: + """Set up relative paths to logos in our HTML templates. + + In Sphinx, the context["logo"] is a path to the `html_logo` image now in the output + `_static` folder. + + If logo["image_light"] and logo["image_dark"] are given, we must modify them to + follow the same pattern. They have already been copied to the output folder + in the `update_config` event. + """ + + # get information from the context "logo_url" for sphinx>=6, "logo" sphinx<6 + pathto = context.get("pathto") + logo = context.get("logo_url") or context.get("logo") + theme_logo = context.get("theme_logo", {}) + + # Define the final path to logo images in the HTML context + theme_logo["image_relative"] = {} + for kind in ["light", "dark"]: + image_kind_logo = theme_logo.get(f"image_{kind}") + + # If it's a URL the "relative" path is just the URL + # else we need to calculate the relative path to a local file + if image_kind_logo: + if not isurl(image_kind_logo): + image_kind_name = Path(image_kind_logo).name + image_kind_logo = pathto(f"_static/{image_kind_name}", resource=True) + theme_logo["image_relative"][kind] = image_kind_logo + + # If there's no custom logo for this kind, just use `html_logo` + # If `logo` is also None, then do not add this key to context. + elif isinstance(logo, str) and len(logo) > 0: + theme_logo["image_relative"][kind] = logo + + # Update our context logo variables with the new image paths + context["theme_logo"] = theme_logo + + +def copy_logo_images(app: Sphinx, exception=None) -> None: + """ + If logo image paths are given, copy them to the `_static` folder + Then we can link to them directly in an html_page_context event + """ + theme_options = app.config.html_theme_options + logo = theme_options.get("logo", {}) + staticdir = Path(app.builder.outdir) / "_static" + for kind in ["light", "dark"]: + path_image = logo.get(f"image_{kind}") + if not path_image or isurl(path_image): + continue + if not (Path(app.srcdir) / path_image).exists(): + logger.warning(f"Path to {kind} image logo does not exist: {path_image}") + copy_asset_file(path_image, staticdir) + + # ----------------------------------------------------------------------------- @@ -1101,7 +1167,9 @@ def setup(app): app.connect("html-page-context", add_toctree_functions) app.connect("html-page-context", prepare_html_config) app.connect("html-page-context", update_and_remove_templates) + app.connect("html-page-context", setup_logo_path) app.connect("build-finished", _overwrite_pygments_css) + app.connect("build-finished", copy_logo_images) # Include component templates app.config.templates_path.append(str(theme_path / "components")) diff --git a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/navbar-logo.html b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/navbar-logo.html index 3cd2956c33..c963997886 100644 --- a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/navbar-logo.html +++ b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/navbar-logo.html @@ -1,3 +1,4 @@ +{# Logo link generation -#} {% if not theme_logo.get("link") %} {% set href = pathto(root_doc) %} {% elif hasdoc(theme_logo.get("link")) %} @@ -5,19 +6,15 @@ {% else %} {% set href = theme_logo.get("link") %} {# external url #} {% endif %} + +{#- Logo HTML and image #}