From 0596d44c4f21da567452b45a3b350db6851a299a Mon Sep 17 00:00:00 2001 From: Chris Holdgraf Date: Sun, 20 Feb 2022 08:02:06 -0800 Subject: [PATCH] ENH: Standardize header buttons --- .github/workflows/tests.yml | 1 + ARCHITECTURE.md | 8 +- CHANGELOG.md | 2 +- docs/customize/index.md | 8 +- docs/customize/sidebar-secondary.md | 2 +- docs/tutorials/get-started.md | 2 +- src/sphinx_book_theme/__init__.py | 65 ++----- .../_compile_translations.py | 13 +- src/sphinx_book_theme/assets/scripts/index.js | 5 +- .../assets/styles/abstracts/_variables.scss | 4 +- .../assets/styles/components/_buttons.scss | 131 ++++++++----- .../styles/sections/_header-article.scss | 16 +- .../styles/sections/_sidebars-toggle.scss | 4 +- .../header_buttons/__init__.py | 177 ++++++++++++++++++ .../{ => header_buttons}/launch.py | 74 +++++++- .../components/download.html | 21 --- .../components/fullscreen.html | 7 - .../components/launchbuttons.html | 40 ---- .../components/repobuttons.html | 20 -- .../components/toc-button.html | 2 +- .../theme/sphinx_book_theme/layout.html | 2 + .../sphinx_book_theme/macros/buttons.html | 78 ++++++++ .../sections/header-article.html | 24 +-- .../theme/sphinx_book_theme/theme.conf | 8 +- tests/test_build.py | 113 +++++------ tests/test_build/build__header-article.html | 159 +++++++++------- .../header__repo-buttons--all-off.html | 40 ++++ .../header__repo-buttons--all-on.html | 83 ++++++++ .../header__repo-buttons--custom-branch.html | 57 ++++++ .../header__repo-buttons--one-on.html | 49 +++++ tests/test_build/test_header_launchbtns.html | 61 ++++++ tests/test_build/test_repo_custombranch.html | 33 ---- .../test_topbar_edit_buttons_off.html | 9 - .../test_topbar_edit_buttons_on.html | 23 --- tests/test_build/test_topbar_launchbtns.html | 88 ++++++--- tests/test_locale_convert.py | 19 +- 36 files changed, 991 insertions(+), 457 deletions(-) create mode 100644 src/sphinx_book_theme/header_buttons/__init__.py rename src/sphinx_book_theme/{ => header_buttons}/launch.py (71%) delete mode 100644 src/sphinx_book_theme/theme/sphinx_book_theme/components/download.html delete mode 100644 src/sphinx_book_theme/theme/sphinx_book_theme/components/fullscreen.html delete mode 100644 src/sphinx_book_theme/theme/sphinx_book_theme/components/launchbuttons.html delete mode 100644 src/sphinx_book_theme/theme/sphinx_book_theme/components/repobuttons.html create mode 100644 src/sphinx_book_theme/theme/sphinx_book_theme/macros/buttons.html create mode 100644 tests/test_build/header__repo-buttons--all-off.html create mode 100644 tests/test_build/header__repo-buttons--all-on.html create mode 100644 tests/test_build/header__repo-buttons--custom-branch.html create mode 100644 tests/test_build/header__repo-buttons--one-on.html create mode 100644 tests/test_build/test_header_launchbtns.html delete mode 100644 tests/test_build/test_repo_custombranch.html delete mode 100644 tests/test_build/test_topbar_edit_buttons_off.html delete mode 100644 tests/test_build/test_topbar_edit_buttons_on.html diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index db9f59bc2..ff3b65cbb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -111,6 +111,7 @@ jobs: --exclude 'someurl' --exclude 'mailto:docutils-develop' --exclude 'pathto\(' + --exclude 'deepnote.com' - name: Audit with Lighthouse uses: treosh/lighthouse-ci-action@v8 diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 3c742644c..37ad6e13b 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -17,7 +17,10 @@ This folder contains all of the source files for this theme, and most changes to This module does things like load in Sphinx's default HTML for the sidebar, and modify it in order to have dropdown nested lists. It also inserts several variables into the Jinja template context that are then used in our HTML templates. -`launch.py` +`header_buttons/` +: Scripts to generate metadata for buttons in the header. We use [Jinja Macros](https://jinja.palletsprojects.com/en/3.0.x/templates/) (in the `macros/` folder) to generate the HTML for header buttons. The scripts in `header_buttons/` generate the data structure that is used to generate buttons with these macros (in the `header-article.html` template). + +`header_buttons/launch.py` : Logic to create the correct URLs for our launch buttons. This basically means building the URL for a given launch service in a proper fashion. The other folders in this section are described in the next few sections. @@ -31,6 +34,9 @@ These follow the [`sphinx-basic-ng` template structure](https://sphinx-basic-ng. - `layout.html` inherits from the [pydata sphinx theme](https://pydata-sphinx-theme.readthedocs.io/) and modifies several sections. - `theme.conf` contains the Sphinx configuration file for this theme. +- `macros/` contains HTML templates that define Jinja macros +- `sections/` contains HTML templates for major sections of the page. +- `components/` contains HTML templates for smaller, self-contained parts of the page. ### `/assets/scripts` - JavaScript assets diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0e76023..4b127ecba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -160,7 +160,7 @@ This updates to the latest PyData Sphinx Theme, which re-works some of the HTML ### New features added - Added tag for cell-input [#259](https://github.com/executablebooks/sphinx-book-theme/pull/259) ([@AakashGfude](https://github.com/AakashGfude)) -- Add a shadow to topbar, on scroll [#255](https://github.com/executablebooks/sphinx-book-theme/pull/255) ([@pradyunsg](https://github.com/pradyunsg)) +- Add a shadow to header, on scroll [#255](https://github.com/executablebooks/sphinx-book-theme/pull/255) ([@pradyunsg](https://github.com/pradyunsg)) - Add CSS to center align images with class [#292](https://github.com/executablebooks/sphinx-book-theme/pull/292) ([@DrDrij](https://github.com/DrDrij)) ### Enhancements made diff --git a/docs/customize/index.md b/docs/customize/index.md index c6444abfc..a4bf19c06 100644 --- a/docs/customize/index.md +++ b/docs/customize/index.md @@ -26,16 +26,16 @@ The following options are available via `html_theme_options` - Branch of the repository for the documentation (e.g., `master`, `main`, `docs`). See [](source-files:repository). * - `use_issues_button` - bool - - Add an button in the topbar with a link to issues for the repository (used in conjunction with `repository_url` and `repository_branch`). See [](source-files:repository). + - Add an button in the header with a link to issues for the repository (used in conjunction with `repository_url` and `repository_branch`). See [](source-files:repository). * - `use_download_button` - bool - - Add a button in the topbar to download the source file of the page. See [](customize:source-files). + - Add a button in the header to download the source file of the page. See [](customize:source-files). * - `use_fullscreen_button` - bool - - Add a button in the topbar to trigger full-screen mode. + - Add a button in the header to trigger full-screen mode. * - `use_repository_button` - bool - - Add a button in the topbar that links to the repository of the documentation.See [](source-files:repository). + - Add a button in the header that links to the repository of the documentation.See [](source-files:repository). * - `launch_buttons` - bool - Include Binder launch buttons for pages that were built from Jupyter Notebooks. See [](customize:launch). diff --git a/docs/customize/sidebar-secondary.md b/docs/customize/sidebar-secondary.md index 293fdc837..d2157f6d9 100644 --- a/docs/customize/sidebar-secondary.md +++ b/docs/customize/sidebar-secondary.md @@ -1,7 +1,7 @@ # Customize the secondary sidebar The secondary sidebar contains information about the current page. -It begins at the top of the page (in the topbar), and extends downwards (by default, from the right side of the page). +It begins at the top of the page (in the header), and extends downwards (by default, from the right side of the page). This page describes ways to control and customize the secondary sidebar. ## Rename the in-page Table of Contents diff --git a/docs/tutorials/get-started.md b/docs/tutorials/get-started.md index c4470ca16..f6ecf7ddd 100644 --- a/docs/tutorials/get-started.md +++ b/docs/tutorials/get-started.md @@ -59,7 +59,7 @@ html_theme_options = { } ``` -When you build your documentation, your topbar should now include a small GitHub logo that has a link to the repository. +When you build your documentation, your header should now include a small GitHub logo that has a link to the repository. :::{seealso} diff --git a/src/sphinx_book_theme/__init__.py b/src/sphinx_book_theme/__init__.py index 187c4a319..e4ab6732e 100644 --- a/src/sphinx_book_theme/__init__.py +++ b/src/sphinx_book_theme/__init__.py @@ -8,7 +8,8 @@ from sphinx.locale import get_translation from sphinx.util import logging -from .launch import add_hub_urls +from .header_buttons import prep_header_buttons, add_header_buttons +from .header_buttons.launch import add_launch_buttons __version__ = "0.2.0" """sphinx-book-theme version""" @@ -24,8 +25,8 @@ def get_html_theme_path(): return theme_path -def add_to_context(app, pagename, templatename, context, doctree): - +def add_metadata_to_page(app, pagename, templatename, context, doctree): + """Adds some metadata about the page that we re-use later.""" # Add the site title to our context so it can be inserted into the navbar if not context.get("root_doc"): # TODO: Sphinx renamed master to root in 4.x, deprecate when we drop 3.x @@ -49,43 +50,7 @@ def add_to_context(app, pagename, templatename, context, doctree): if app.config.author != "unknown": context["author"] = app.config.author - # Add HTML context variables that the pydata theme uses that we configure elsewhere - # For some reason the source_suffix sometimes isn't there even when doctree is - if doctree and context.get("page_source_suffix"): - config_theme = app.config.html_theme_options - repo_url = config_theme.get("repository_url", "") - # Only add the edit button if `repository_url` is given - if repo_url: - branch = config_theme.get("repository_branch") - if not branch: - # Explicitly check in cae branch is "" - branch = "master" - relpath = config_theme.get("path_to_docs", "") - org, repo = repo_url.strip("/").split("/")[-2:] - context.update( - { - "github_user": org, - "github_repo": repo, - "github_version": branch, - "doc_path": relpath, - } - ) - else: - # Disable using the button so we don't get errors - context["theme_use_edit_page_button"] = False - - # Make sure the context values are bool - btns = [ - "theme_use_edit_page_button", - "theme_use_repository_button", - "theme_use_issues_button", - "theme_use_download_button", - "theme_use_fullscreen_button", - ] - for key in btns: - if key in context: - context[key] = _string_or_bool(context[key]) - + # Translations translation = get_translation(MESSAGE_CATALOG_NAME) context["translate"] = translation # this is set in the html_theme @@ -128,15 +93,6 @@ def update_thebe_config(app): app.env.config.thebe_config = thebe_config -def _string_or_bool(var): - if isinstance(var, str): - return var.lower() == "true" - elif isinstance(var, bool): - return var - else: - return var is None - - class Margin(Sidebar): """Goes in the margin to the right of the page.""" @@ -159,16 +115,19 @@ def run(self): def setup(app: Sphinx): app.connect("builder-inited", update_thebe_config) - # Configuration for Juypter Book - app.connect("html-page-context", add_hub_urls) - # add translations theme_dir = get_html_theme_path() locale_dir = os.path.join(theme_dir, "static", "locales") app.add_message_catalog(MESSAGE_CATALOG_NAME, locale_dir) app.add_html_theme("sphinx_book_theme", theme_dir) - app.connect("html-page-context", add_to_context) + app.connect("html-page-context", add_metadata_to_page) + + # Header buttons + app.connect("html-page-context", prep_header_buttons) + app.connect("html-page-context", add_launch_buttons) + # Bump priority by 1 so that it runs after the pydata theme sets up the edit URL. + app.connect("html-page-context", add_header_buttons, priority=501) app.add_directive("margin", Margin) diff --git a/src/sphinx_book_theme/_compile_translations.py b/src/sphinx_book_theme/_compile_translations.py index 612732f39..318f97a20 100644 --- a/src/sphinx_book_theme/_compile_translations.py +++ b/src/sphinx_book_theme/_compile_translations.py @@ -11,11 +11,18 @@ } -def convert_json(): +def convert_json(folder=None): + """Convert JSON translations into .mo/.po files for Sphinx. + + folder: + the source folder of the JSON translations. This function will put the + compiled .mo/.po files in a specific folder relative to this source + folder. This parameter is just provided to make testing easier. + """ # Raw translation JSONs that are hand-edited - folder = Path(__file__).parent / "assets" / "translations" + folder = folder or Path(__file__).parent / "assets" / "translations" # Location of compiled static translation assets - out_folder = Path(__file__).parent / "theme" / "sphinx_book_theme" / "static" + out_folder = folder / ".." / ".." / "theme" / "sphinx_book_theme" / "static" # compile po for path in (folder / "jsons").glob("*.json"): diff --git a/src/sphinx_book_theme/assets/scripts/index.js b/src/sphinx_book_theme/assets/scripts/index.js index 4326cfee2..9115cb054 100644 --- a/src/sphinx_book_theme/assets/scripts/index.js +++ b/src/sphinx_book_theme/assets/scripts/index.js @@ -176,7 +176,10 @@ var initThebeSBT = () => { */ var initTooltips = () => { $(document).ready(function () { - $('[data-toggle="tooltip"]').tooltip({ trigger: "hover" }); + $('[data-toggle="tooltip"]').tooltip({ + trigger: "hover", + delay: { show: 500, hide: 100 }, + }); }); }; diff --git a/src/sphinx_book_theme/assets/styles/abstracts/_variables.scss b/src/sphinx_book_theme/assets/styles/abstracts/_variables.scss index 0d338892b..eeae31059 100644 --- a/src/sphinx_book_theme/assets/styles/abstracts/_variables.scss +++ b/src/sphinx_book_theme/assets/styles/abstracts/_variables.scss @@ -13,7 +13,7 @@ $grey--medium: #aaa; $grey--light: #ccc; $non-content-grey: $grey--dark; -$topbar-height: 3em; +$header-article-height: 3em; $leftbar-width-mobile: 75%; $leftbar-width-wide: 275px; $toc-width-mobile: 75%; @@ -58,7 +58,7 @@ $border-thin: 1px solid rgba(0, 0, 0, 0.1); // Variables for this theme --sbt-sidebar-font-size: var(--sbt-font-size-small-1); - --sbt-topbar-font-size: var(--sbt-font-size-small-1); + --sbt-header-article-font-size: var(--sbt-font-size-small-1); --sbt-prevnext-font-size: var(--sbt-font-size-small-1); --sbt-footer-font-size: var(--sbt-font-size-small-1); } diff --git a/src/sphinx_book_theme/assets/styles/components/_buttons.scss b/src/sphinx_book_theme/assets/styles/components/_buttons.scss index 65ef5063a..648f9ef8e 100644 --- a/src/sphinx_book_theme/assets/styles/components/_buttons.scss +++ b/src/sphinx_book_theme/assets/styles/components/_buttons.scss @@ -1,26 +1,14 @@ /********************************************* -* Buttons, mostly for the topbar * +* Buttons, mostly for the headers * *********************************************/ -.topbarbtn, -.topbarbtn--wide { +/** + * Basic button style + */ +.headerbtn { display: flex; align-items: center; justify-content: center; - img { - height: 1.15em; - padding-right: 6px; - margin-left: -5px; - } - - // Icon inside the button - i { - vertical-align: baseline; - line-height: 1; - } -} - -.topbarbtn { background-color: white; color: $non-content-grey; cursor: pointer; @@ -35,47 +23,94 @@ color: black; background-color: white; box-shadow: none; + text-decoration: none; + } + + span { + display: flex; + align-items: center; + } + + img, + i { + margin: auto; } } -// Buttons with a dropdown list underneath -div.dropdown-buttons-trigger { - div.dropdown-buttons { - display: none; - position: absolute; - max-width: 130px; - margin-top: 0; - z-index: 1000; - transform: translate(-70%); - - // Create extra spacing for these icons since they have - i { - margin-right: 0.5em; - } +/** + * Dropdown groups of buttons + */ +.menu-dropdown__trigger:hover + .menu-dropdown__content, +.menu-dropdown__content:hover { + visibility: visible; + opacity: 1; + transform: translate(-70%); // To achieve a slight shift effect +} + +.menu-dropdown__content { + // Hide by default, we'll show on hover + position: absolute; + visibility: hidden; + opacity: 0; + transform: translate(-65%); + transition: opacity 0.2s ease-out, transform 0.2s ease-out; - // Links shouldn't have any effect when hovered - a:hover { - text-decoration: none; + // Spacing and position + width: 13em; + z-index: 1000; + border-radius: $box-border-radius; + box-shadow: 0px 3px 10px 0px rgba(0, 0, 0, 0.25); + padding: 0.5em; + + // Style + font-size: 0.9em; + background-color: white; + + .headerbtn { + justify-content: left; + + // Center the icon in the available white space + i, + img { + margin: auto; } - // Wide topbar buttons have dark backgrounds since they hover over text - .topbarbtn--wide { - font-size: 1em; - padding-top: 0.35rem; - padding-bottom: 0.35rem; - min-width: 125px !important; - border: 1px white solid !important; - background-color: $non-content-grey; - color: white; - border-radius: 0.5em; + img { + width: 1em; } } - &:hover div.dropdown-buttons { - display: block; + ul { + list-style: none; + padding-left: 0; + margin-bottom: 0; + } + + a, + button { + padding: 0; + } + + span { + display: flex; + } + + span.headerbtn__icon-container { + width: 2em; + } + + span.headerbtn__text-container { + flex-grow: 1; } } -a.dropdown-buttons i { - margin-right: 0.5em; +/** + * In-page table of contents + */ +.headerbtn-page-toc { + // Hide the button on wide screens since we display the TOC. + display: block; + @media (min-width: $breakpoint-md) { + display: none; + } } diff --git a/src/sphinx_book_theme/assets/styles/sections/_header-article.scss b/src/sphinx_book_theme/assets/styles/sections/_header-article.scss index ea02acfc9..eec579764 100644 --- a/src/sphinx_book_theme/assets/styles/sections/_header-article.scss +++ b/src/sphinx_book_theme/assets/styles/sections/_header-article.scss @@ -3,27 +3,25 @@ * TODO: Rename much of these to article-header to conform with sphinx-basic-ng *********************************************/ .header-article { - height: $topbar-height; // Fix the height so the TOC doesn't grow it + height: $header-article-height; // Fix the height so the TOC doesn't grow it background-color: white; transition: left 0.2s; - font-size: var(--sbt-topbar-font-size); + font-size: var(--sbt-header-article-font-size); .scrolled & { box-shadow: 0 6px 6px -6px rgba(0, 0, 0, 0.3); } - .topbar-main { - height: $topbar-height; + .header-article-main { + height: $header-article-height; - // Both topbar sections - .topbar-left, - .topbar-right { + .header-article__left, + .header-article__right { display: flex; align-items: center; } - // Right section of topbar - .topbar-right { + .header-article__right { margin-left: auto; } } diff --git a/src/sphinx_book_theme/assets/styles/sections/_sidebars-toggle.scss b/src/sphinx_book_theme/assets/styles/sections/_sidebars-toggle.scss index 9b576a23e..11690d32d 100644 --- a/src/sphinx_book_theme/assets/styles/sections/_sidebars-toggle.scss +++ b/src/sphinx_book_theme/assets/styles/sections/_sidebars-toggle.scss @@ -63,11 +63,11 @@ label.overlay { @media (max-width: $breakpoint-md) { input:checked + label.overlay { &.overlay-navbar { - z-index: 1040; // This puts it above the topbar, below the sidebar + z-index: 1040; // This puts it above the header, below the sidebar } &.overlay-pagetoc { - z-index: 1010; // This puts it *below* the topbar so the TOC shows + z-index: 1010; // This puts it *below* the header so the TOC shows } height: 100%; opacity: 1; diff --git a/src/sphinx_book_theme/header_buttons/__init__.py b/src/sphinx_book_theme/header_buttons/__init__.py new file mode 100644 index 000000000..adf8ce3e0 --- /dev/null +++ b/src/sphinx_book_theme/header_buttons/__init__.py @@ -0,0 +1,177 @@ +"""Generate metadata for header buttons.""" + +from sphinx.errors import SphinxError + + +def _as_bool(var): + """Cast string as a boolean with some extra checks. + + If var is a string, it will be matched to 'true'/'false' + If var is a bool, it will be returned + If var is None, it will return False. + """ + if isinstance(var, str): + return var.lower() == "true" + elif isinstance(var, bool): + return var + else: + return False + + +def prep_header_buttons(app, pagename, templatename, context, doctree): + """Prep an empty list that we'll populate with header buttons.""" + context["header_buttons"] = [] + + +def add_header_buttons(app, pagename, templatename, context, doctree): + """Populate the context with header button metadata we'll insert in templates.""" + opts = app.config.html_theme_options + pathto = context["pathto"] + header_buttons = context["header_buttons"] + + # If we have a suffix, then we have a source file + suff = context.get("page_source_suffix") + + # Full screen button + if _as_bool(opts.get("use_fullscreen_button", True)): + header_buttons.append( + { + "type": "javascript", + "javascript": "toggleFullScreen()", + "tooltip": "Fullscreen mode", + "icon": "fas fa-expand", + } + ) + + # Edit this page button + # Add HTML context variables that the pydata theme uses that we configure elsewhere + # For some reason the source_suffix sometimes isn't there even when doctree is + repo_keywords = [ + "use_issues_button", + "use_edit_page_button", + "use_repository_button", + ] + for key in repo_keywords: + opts[key] = _as_bool(opts.get(key)) + + if any(opts.get(kw) for kw in repo_keywords): + repo_url = opts.get("repository_url", "") + if not repo_url: + raise SphinxError( + "Repository buttons enabled, but repository_url not given. " + "Please add a repository_url." + ) + repo_buttons = [] + if opts.get("use_repository_button"): + repo_buttons.append( + { + "type": "link", + "url": repo_url, + "tooltip": "Source repository", + "text": "repository", + "icon": "fab fa-github", + } + ) + + if opts.get("use_issues_button"): + repo_buttons.append( + { + "type": "link", + "url": f"{repo_url}/issues/new?title=Issue%20on%20page%20%2F{context['pagename']}.html&body=Your%20issue%20content%20here.", # noqa: E501 + "text": "open issue", + "tooltip": "Open an issue", + "icon": "fas fa-lightbulb", + } + ) + + if opts.get("use_edit_page_button") and doctree and suff: + branch = opts.get("repository_branch", "") + if branch == "": + branch = "master" + relpath = opts.get("path_to_docs", "") + org, repo = repo_url.strip("/").split("/")[-2:] + + # Update the context because this is what the get_edit_url function uses. + context.update( + { + "github_user": org, + "github_repo": repo, + "github_version": branch, + "doc_path": relpath, + } + ) + + repo_buttons.append( + { + "type": "link", + "url": context["get_edit_url"](), + "tooltip": "Edit this page", + "text": "suggest edit", + "icon": "fas fa-pencil-alt", + } + ) + + # If we have multiple repo buttons enabled, add a group, otherwise just 1 button + if len(repo_buttons) > 1: + for rb in repo_buttons: + rb["tooltip_placement"] = "left" + header_buttons.append( + { + "type": "group", + "tooltip": "Source repositories", + "icon": "fab fa-github", + "buttons": repo_buttons, + "label": "repository-buttons", + } + ) + else: + header_buttons.extend(repo_buttons) + + # Download buttons for various source content. + if _as_bool(opts.get("use_download_button", True)) and suff: + download_buttons = [] + + # Create the dropdown list of buttons + if context.get("ipynb_source"): + download_buttons.append( + { + "type": "link", + "url": f'{pathto("_sources", 1)}/{context.get("ipynb_source")}', + "text": ".ipynb", + "icon": "fas fa-code", + "tooltip": "Download notebook file", + "tooltip_placement": "left", + } + ) + + download_buttons.append( + { + "type": "link", + "url": f'{pathto("_sources", 1)}/{context["sourcename"]}', + "text": suff, + "tooltip": "Download source file", + "tooltip_placement": "left", + "icon": "fas fa-file", + } + ) + download_buttons.append( + { + "type": "javascript", + "javascript": "printPdf(this)", + "text": ".pdf", + "tooltip": "Print to PDF", + "tooltip_placement": "left", + "icon": "fas fa-file-pdf", + } + ) + + # Add the group + header_buttons.append( + { + "type": "group", + "tooltip": "Download this page", + "icon": "fas fa-download", + "buttons": download_buttons, + "label": "download-buttons", + } + ) diff --git a/src/sphinx_book_theme/launch.py b/src/sphinx_book_theme/header_buttons/launch.py similarity index 71% rename from src/sphinx_book_theme/launch.py rename to src/sphinx_book_theme/header_buttons/launch.py index 65237f91b..6e6e905f3 100644 --- a/src/sphinx_book_theme/launch.py +++ b/src/sphinx_book_theme/header_buttons/launch.py @@ -10,7 +10,7 @@ SPHINX_LOGGER = logging.getLogger(__name__) -def add_hub_urls( +def add_launch_buttons( app: Sphinx, pagename: str, templatename: str, @@ -39,6 +39,9 @@ def add_hub_urls( if not launch_buttons or not _is_notebook(app, pagename): return + # Grab the header buttons from context as it should already exist. + header_buttons = context["header_buttons"] + # Check if we have a markdown notebook, and if so then add a link to the context if _is_notebook(app, pagename) and ( context["sourcename"].endswith(".md") @@ -91,6 +94,9 @@ def add_hub_urls( book_relpath += "/" path_rel_repo = f"{book_relpath}{pagename}{extension}" + # Container for launch buttons + launch_buttons_list = [] + # Now build infrastructure-specific links jupyterhub_url = launch_buttons.get("jupyterhub_url") binderhub_url = launch_buttons.get("binderhub_url") @@ -101,28 +107,86 @@ def add_hub_urls( f"{binderhub_url}/v2/gh/{org}/{repo}/{branch}?" f"urlpath={ui_pre}/{path_rel_repo}" ) - context["binder_url"] = url + launch_buttons_list.append( + { + "type": "link", + "text": "Binder", + "tooltip": "Launch on Binder", + "icon": "_static/images/logo_binder.svg", + "url": url, + } + ) if jupyterhub_url: url = ( f"{jupyterhub_url}/hub/user-redirect/git-pull?" f"repo={repo_url}&urlpath={ui_pre}/{repo}/{path_rel_repo}&branch={branch}" ) - context["jupyterhub_url"] = url + launch_buttons_list.append( + { + "type": "link", + "text": "JupyterHub", + "tooltip": "Launch on JupyterHub", + "icon": "_static/images/logo_jupyterhub.svg", + "url": url, + } + ) if colab_url: url = f"{colab_url}/github/{org}/{repo}/blob/{branch}/{path_rel_repo}" - context["colab_url"] = url + launch_buttons_list.append( + { + "type": "link", + "text": "Colab", + "tooltip": "Launch on Colab", + "icon": "_static/images/logo_colab.png", + "url": url, + } + ) if deepnote_url: github_path = f"%2F{org}%2F{repo}%2Fblob%2F{branch}%2F{path_rel_repo}" url = f"{deepnote_url}/launch?url=https%3A%2F%2Fgithub.com{github_path}" - context["deepnote_url"] = url + launch_buttons_list.append( + { + "type": "link", + "text": "Deepnote", + "tooltip": "Launch on Deepnote", + "icon": "_static/images/logo_deepnote.svg", + "url": url, + } + ) # Add thebe flag in context if launch_buttons.get("thebe", False): + launch_buttons_list.append( + { + "type": "javascript", + "text": "Live Code", + "tooltip": "Launch Thebe", + "javascript": "initThebeSBT()", + "icon": "fas fa-play", + "label": "launch-thebe", + } + ) context["use_thebe"] = True + # Add the buttons to header_buttons + if len(launch_buttons_list) == 1: + header_buttons.extend(launch_buttons_list) + else: + for lb in launch_buttons_list: + lb["tooltip_placement"] = "left" + header_buttons.append( + { + "type": "group", + "tooltip": "Launch interactive content", + "icon": "fas fa-rocket", + "buttons": launch_buttons_list, + "label": "launch-buttons", + } + ) + def _split_repo_url(url): """Split a repository URL into an org / repo combination.""" diff --git a/src/sphinx_book_theme/theme/sphinx_book_theme/components/download.html b/src/sphinx_book_theme/theme/sphinx_book_theme/components/download.html deleted file mode 100644 index 7aa9491b0..000000000 --- a/src/sphinx_book_theme/theme/sphinx_book_theme/components/download.html +++ /dev/null @@ -1,21 +0,0 @@ -{% if page_source_suffix and theme_use_download_button %} - -{% endif %} diff --git a/src/sphinx_book_theme/theme/sphinx_book_theme/components/fullscreen.html b/src/sphinx_book_theme/theme/sphinx_book_theme/components/fullscreen.html deleted file mode 100644 index d784e2b7a..000000000 --- a/src/sphinx_book_theme/theme/sphinx_book_theme/components/fullscreen.html +++ /dev/null @@ -1,7 +0,0 @@ - -{% if theme_use_fullscreen_button %} - -{% endif %} diff --git a/src/sphinx_book_theme/theme/sphinx_book_theme/components/launchbuttons.html b/src/sphinx_book_theme/theme/sphinx_book_theme/components/launchbuttons.html deleted file mode 100644 index cec561154..000000000 --- a/src/sphinx_book_theme/theme/sphinx_book_theme/components/launchbuttons.html +++ /dev/null @@ -1,40 +0,0 @@ - -{% macro launch_button(name, url, path_img) -%} - - - -{%- endmacro %} -{% if binder_url or jupyterhub_url or colab_url or deepnote_url or use_thebe %} - -{% endif %} diff --git a/src/sphinx_book_theme/theme/sphinx_book_theme/components/repobuttons.html b/src/sphinx_book_theme/theme/sphinx_book_theme/components/repobuttons.html deleted file mode 100644 index 579aaf005..000000000 --- a/src/sphinx_book_theme/theme/sphinx_book_theme/components/repobuttons.html +++ /dev/null @@ -1,20 +0,0 @@ - -{% if theme_repository_url and (theme_use_repository_button or theme_use_issues_button or theme_use_edit_page_button) %} - -{% endif %} diff --git a/src/sphinx_book_theme/theme/sphinx_book_theme/components/toc-button.html b/src/sphinx_book_theme/theme/sphinx_book_theme/components/toc-button.html index dddd66402..87da67249 100644 --- a/src/sphinx_book_theme/theme/sphinx_book_theme/components/toc-button.html +++ b/src/sphinx_book_theme/theme/sphinx_book_theme/components/toc-button.html @@ -1,4 +1,4 @@ -