diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5c39aa38a..a998b6343 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,31 @@ repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: "v5.0.0" hooks: - - id: trailing-whitespace + - id: check-builtin-literals + - id: check-case-conflict + - id: check-toml + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: trailing-whitespace + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.9.7 + hooks: + - id: ruff-format + exclude: examples + - id: ruff + args: ["--fix", "--show-fixes"] + + - repo: https://github.com/rbubley/mirrors-prettier + rev: "v3.4.2" + hooks: + - id: prettier + types_or: [yaml, markdown, html, css, scss, javascript, json] + + - repo: https://github.com/abravalheri/validate-pyproject + rev: "v0.23" + hooks: + - id: validate-pyproject + additional_dependencies: ["validate-pyproject-schema-store[all]"] diff --git a/Makefile b/Makefile index dba1164f0..8cf13b9e9 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: docs clean +make.PHONY: docs clean SPHINXOPTS = diff --git a/docs/_scripts/prep_docs.py b/docs/_scripts/prep_docs.py index 1098567d1..bc502f407 100644 --- a/docs/_scripts/prep_docs.py +++ b/docs/_scripts/prep_docs.py @@ -1,39 +1,70 @@ """ALL pre-rendering and pre-preparation of docs should occur in this file. +This script is called **before** Sphinx builds the documentation. + Note: make no assumptions about the working directory from which this script will be called. """ + +import os import sys -from pathlib import Path from importlib.metadata import version +from pathlib import Path from packaging.version import parse +from scripts_logger import setup_logger + +logger = setup_logger(__name__) + +# Set up paths to docs and npe2 docs source DOCS = Path(__file__).parent.parent.absolute() -NPE = DOCS.parent.absolute() / 'npe2' +logger.debug(f"DOCS: {DOCS}") +NPE = DOCS.parent.absolute() / "npe2" +logger.debug(f"NPE: {NPE}") + def prep_npe2(): - # some plugin docs live in npe2 for testing purposes + """Preps the npe2 plugin engine prior to Sphinx docs build. + + Some plugin-related docs live in the npe2 repo to simplify + plugin testing. + """ + logger.debug("Preparing npe2 plugin") + # Checks if the path to npe2 repo exist. If so, bail. if NPE.exists(): + logger.debug("NPE2 plugin already present") return from subprocess import check_call npe2_version = version("npe2") - + logger.debug(f"npe2 version: {npe2_version}") check_call(f"rm -rf {NPE}".split()) + logger.debug("removing NPE directory succeeded") check_call(f"git clone https://github.com/napari/npe2 {NPE}".split()) + if not parse(npe2_version).is_devrelease: check_call(f"git checkout tags/v{npe2_version}".split(), cwd=NPE) - check_call([sys.executable, f"{NPE}/_docs/render.py", DOCS / 'plugins']) + + check_call([sys.executable, f"{NPE}/_docs/render.py", DOCS / "plugins"]) check_call(f"rm -rf {NPE}".split()) def main(): prep_npe2() - __import__('update_preference_docs').main() - __import__('update_event_docs').main() - __import__('update_ui_sections_docs').main() + logger.debug("Prep npe2 complete") + __import__("update_preference_docs").main() + logger.debug("update_preference_docs succeeded") + __import__("update_event_docs").main() + logger.debug("update_event_docs succeeded") + __import__("update_ui_sections_docs").main() + logger.debug("update_ui_sections_docs succeeded") if __name__ == "__main__": + # Example usage within a script + current_script_name = os.path.basename(__file__) + # Get the name of the current script + logger = setup_logger(current_script_name) + main() diff --git a/docs/_scripts/scripts_logger.py b/docs/_scripts/scripts_logger.py new file mode 100644 index 000000000..8ba3a4923 --- /dev/null +++ b/docs/_scripts/scripts_logger.py @@ -0,0 +1,59 @@ +"""Create a logger for a directory of scripts to aid in debugging.""" + +import logging +import os +import sys + + +def setup_logger(script_name, log_directory="logs"): + """Sets up a logger for a specific script. + + Args: + script_name (str): The name of the script (e.g., "my_script.py"). + log_directory (str, optional): The directory to store log files. Defaults to "logs". + + Returns: + logging.Logger: A configured logger instance. + """ + # Create log directory if it doesn't exist + if not os.path.exists(log_directory): + os.makedirs(log_directory) + + # Extract the script name without the extension + script_name_no_ext = os.path.splitext(script_name)[0] + + # Create a logger + logger = logging.getLogger(script_name_no_ext) + logger.setLevel(logging.DEBUG) # Set the minimum logging level + + # Create a file handler + # log_file_path = os.path.join(log_directory, f"{script_name_no_ext}.log") + # file_handler = logging.FileHandler(log_file_path) + # file_handler.setLevel(logging.DEBUG) + + handler = logging.StreamHandler(sys.stdout) + + # Create a formatter + formatter = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + ) + # file_handler.setFormatter(formatter) + handler.setFormatter(formatter) + + # Add the file handler to the logger + # logger.addHandler(file_handler) + logger.addHandler(handler) + return logger + + +if __name__ == "__main__": + # Example usage within a script + current_script_name = os.path.basename(__file__) + # Get the name of the current script + logger = setup_logger(current_script_name) + + logger.debug("This is a debug message.") + logger.info("This is an info message.") + logger.warning("This is a warning message.") + logger.error("This is an error message.") + logger.critical("This is a critical message.") diff --git a/docs/_scripts/update_event_docs.py b/docs/_scripts/update_event_docs.py index 572c5469c..5e1f40b15 100644 --- a/docs/_scripts/update_event_docs.py +++ b/docs/_scripts/update_event_docs.py @@ -1,5 +1,6 @@ import ast import inspect +import os from dataclasses import dataclass from pathlib import Path from types import ModuleType @@ -15,6 +16,10 @@ from napari.components.viewer_model import ViewerModel from napari.utils.events import EventedModel +from scripts_logger import setup_logger + +logger = setup_logger(__name__) + DOCS = Path(__file__).parent.parent @@ -28,49 +33,50 @@ class Ev: def access_at(self): """Where this event can be accessed (in code)""" if issubclass(self.model, layers.Layer): - return f'layer.events.{self.name}' + return f"layer.events.{self.name}" if issubclass(self.model, LayerList): - if self.name.startswith('selection.'): - return f'layers.selection.events.{self.name[10:]}' - return f'layers.events.{self.name}' + if self.name.startswith("selection."): + return f"layers.selection.events.{self.name[10:]}" + return f"layers.events.{self.name}" if issubclass(self.model, ViewerModel): - return f'viewer.events.{self.name}' + return f"viewer.events.{self.name}" for name, field_ in napari.Viewer.__fields__.items(): if field_.type_ is self.model: - return f'viewer.{name}.events.{self.name}' - return '' + return f"viewer.{name}.events.{self.name}" + return "" def type_name(self): - if cls_name := getattr(self.type_, '__name__', None): + if cls_name := getattr(self.type_, "__name__", None): return cls_name - name = str(self.type_) if self.type_ else '' + name = str(self.type_) if self.type_ else "" return name.replace("typing.", "") def ev_model_row(self) -> List[str]: return [ - f'`{self.model.__name__}`', - f'`{self.name}`', - f'`{self.access_at()}`', - self.description or '', - f'`{self.type_name()}`', + f"`{self.model.__name__}`", + f"`{self.name}`", + f"`{self.access_at()}`", + self.description or "", + f"`{self.type_name()}`", ] def layer_row(self) -> List[str]: return [ - f'`{self.model.__name__}`', - f'`{self.name}`', - f'`{self.access_at()}`', - self.description or '', - '', + f"`{self.model.__name__}`", + f"`{self.name}`", + f"`{self.access_at()}`", + self.description or "", + "", ] def walk_modules( - module: ModuleType, pkg='napari', _walked=None + module: ModuleType, pkg="napari", _walked=None ) -> Iterator[ModuleType]: """walk all modules in pkg, starting with `module`.""" + logger.debug(f"walking {pkg}") if not _walked: _walked = set() yield module @@ -87,6 +93,7 @@ def walk_modules( def iter_classes(module: ModuleType) -> Iterator[Type]: """iter all classes in module""" + logger.debug(f"walking {module}") for name in dir(module): attr = getattr(module, name) if inspect.isclass(attr) and attr.__module__ == module.__name__: @@ -94,14 +101,14 @@ def iter_classes(module: ModuleType) -> Iterator[Type]: def class_doc_attrs(kls: Type) -> Dict[str, Parameter]: - docs = {p.name: " ".join(p.desc) for p in ClassDoc(kls).get('Attributes')} - docs.update( - {p.name: " ".join(p.desc) for p in ClassDoc(kls).get('Parameters')} - ) + logger.debug(f"walking {kls}") + docs = {p.name: " ".join(p.desc) for p in ClassDoc(kls).get("Attributes")} + docs.update({p.name: " ".join(p.desc) for p in ClassDoc(kls).get("Parameters")}) return docs def iter_evented_model_events(module: ModuleType = napari) -> Iterator[Ev]: + logger.debug(f"walking evented model events {module}") for mod in walk_modules(module): for kls in iter_classes(mod): if not issubclass(kls, EventedModel): @@ -110,17 +117,14 @@ def iter_evented_model_events(module: ModuleType = napari) -> Iterator[Ev]: for name, field_ in kls.__fields__.items(): finfo = field_.field_info if finfo.allow_mutation: - descr = ( - f"{finfo.title.lower()}" - if finfo.title - else docs.get(name) - ) + descr = f"{finfo.title.lower()}" if finfo.title else docs.get(name) yield Ev(name, kls, descr, field_.type_) def iter_evented_container_events( module: ModuleType = napari, container_class=LayerList ) -> Iterator[Ev]: + logger.debug(f"walking evented container events {module}") for mod in walk_modules(module): for kls in iter_classes(mod): if not issubclass(kls, container_class): @@ -130,13 +134,13 @@ def iter_evented_container_events( for name, emitter in kls_instance.events._emitters.items(): descr = docs.get(name) yield Ev(name, kls, descr, type_=None) - if hasattr(kls_instance, 'selection'): + if hasattr(kls_instance, "selection"): selection = kls_instance.selection for name, emitter in selection.events._emitters.items(): - if name.startswith('_'): + if name.startswith("_"): # skip private emitters continue - name = 'selection.' + name + name = "selection." + name descr = docs.get(name) yield Ev(name, kls, descr, type_=None) @@ -147,11 +151,12 @@ def __init__(self) -> None: self._emitters: List[str] = [] def visit_Call(self, node: ast.Call): - if getattr(node.func, 'id', None) == 'EmitterGroup': + if getattr(node.func, "id", None) == "EmitterGroup": self._emitters.extend([name.arg for name in node.keywords]) # type: ignore def base_event_names() -> List[str]: + logger.debug("walking base event names") from napari.layers.base import base root = ast.parse(Path(base.__file__).read_text()) @@ -161,6 +166,7 @@ def base_event_names() -> List[str]: def iter_layer_events() -> Iterator[Ev]: + logger.debug("walking layer events") basenames = base_event_names() docs = class_doc_attrs(layers.Layer) for name in basenames: @@ -194,10 +200,11 @@ def iter_layer_events() -> Iterator[Ev]: def merge_image_and_label_rows(rows: List[List[str]]): """Merge events common to _ImageBase or IntensityVisualizationMixin.""" + logger.debug(f"merging {len(rows)} rows") # find events that are common across both Image, Labels and Surface layers. - image_events = {r[1] for r in rows if r[0] == '`Image`'} - labels_events = {r[1] for r in rows if r[0] == '`Labels`'} - surface_events = {r[1] for r in rows if r[0] == '`Surface`'} + image_events = {r[1] for r in rows if r[0] == "`Image`"} + labels_events = {r[1] for r in rows if r[0] == "`Labels`"} + surface_events = {r[1] for r in rows if r[0] == "`Surface`"} common_events = image_events & labels_events & surface_events # common only to Image and Labels imagebase_events = (image_events & labels_events) - common_events @@ -206,24 +213,24 @@ def merge_image_and_label_rows(rows: List[List[str]]): rows = [ r for r in rows - if not (r[0] in ['`Labels`', '`Surface`'] and r[1] in common_events) + if not (r[0] in ["`Labels`", "`Surface`"] and r[1] in common_events) ] rows = [ r for r in rows - if not (r[0] in ['`Labels`', '`Surface`'] and r[1] in imagebase_events) + if not (r[0] in ["`Labels`", "`Surface`"] and r[1] in imagebase_events) ] # modify the class name of the Image entries to mention Labels, Surface rows = [ - ['`Image`, `Labels`'] + r[1:] - if r[0] == '`Image`' and r[1] in imagebase_events + ["`Image`, `Labels`"] + r[1:] + if r[0] == "`Image`" and r[1] in imagebase_events else r for r in rows ] rows = [ - ['`Image`, `Labels`, `Surface`'] + r[1:] - if r[0] == '`Image`' and r[1] in common_events + ["`Image`, `Labels`, `Surface`"] + r[1:] + if r[0] == "`Image`" and r[1] in common_events else r for r in rows ] @@ -232,45 +239,43 @@ def merge_image_and_label_rows(rows: List[List[str]]): def main(): HEADER = [ - 'Event', - 'Description', - 'Event.value type', + "Event", + "Description", + "Event.value type", ] # Do viewer events rows = [ - ev.ev_model_row()[2:] - for ev in iter_evented_model_events() - if ev.access_at() + ev.ev_model_row()[2:] for ev in iter_evented_model_events() if ev.access_at() ] table1 = table_repr(rows, padding=2, header=HEADER, divide_rows=False) - (DOCS / 'guides' / '_viewer_events.md').write_text(table1) + (DOCS / "guides" / "_viewer_events.md").write_text(table1) # Do LayerList events rows = [ ev.layer_row()[2:] - for ev in iter_evented_container_events( - napari, container_class=LayerList - ) + for ev in iter_evented_container_events(napari, container_class=LayerList) if ev.access_at() ] table2 = table_repr(rows, padding=2, header=HEADER, divide_rows=False) - (DOCS / 'guides' / '_layerlist_events.md').write_text(table2) + (DOCS / "guides" / "_layerlist_events.md").write_text(table2) # Do layer events HEADER = [ - 'Class', - 'Event', - 'Description', - 'Event.value type', - ] - rows = [ - [ev.layer_row()[0]] + ev.layer_row()[2:] for ev in iter_layer_events() + "Class", + "Event", + "Description", + "Event.value type", ] + rows = [[ev.layer_row()[0]] + ev.layer_row()[2:] for ev in iter_layer_events()] rows = merge_image_and_label_rows(rows) table3 = table_repr(rows, padding=2, header=HEADER, divide_rows=False) - (DOCS / 'guides' / '_layer_events.md').write_text(table3) + (DOCS / "guides" / "_layer_events.md").write_text(table3) -if __name__ == '__main__': +if __name__ == "__main__": + # Example usage within a script + current_script_name = os.path.basename(__file__) + # Get the name of the current script + logger = setup_logger(current_script_name) main() diff --git a/docs/_scripts/update_preference_docs.py b/docs/_scripts/update_preference_docs.py index ef60c7fef..5c15c16a5 100644 --- a/docs/_scripts/update_preference_docs.py +++ b/docs/_scripts/update_preference_docs.py @@ -1,15 +1,20 @@ +import os from pathlib import Path from jinja2 import Template -from napari._pydantic_compat import ModelMetaclass from qtpy.QtCore import QTimer from qtpy.QtWidgets import QMessageBox from napari._qt.dialogs.preferences_dialog import PreferencesDialog from napari._qt.qt_event_loop import get_qapp from napari._qt.qt_resources import get_stylesheet +from napari._pydantic_compat import ModelMetaclass from napari.settings import NapariSettings +from scripts_logger import setup_logger + +logger = setup_logger(__name__) + DOCS = REPO_ROOT_PATH = Path(__file__).resolve().parent.parent GUIDES_PATH = DOCS / "guides" IMAGES_PATH = DOCS / "images" / "_autogenerated" @@ -98,7 +103,7 @@ def generate_images(): Generate images from `CORE_SETTINGS`. and save them in the developer section of the docs. """ - + logger.debug("Generating images") app = get_qapp() pref = PreferencesDialog() pref.setStyleSheet(get_stylesheet("dark")) @@ -131,10 +136,10 @@ def grab(): def create_preferences_docs(): """Create preferences docs from SETTINGS using a jinja template.""" + logger.debug("Creating preferences docs") sections = {} for name, field in NapariSettings.__fields__.items(): - if not isinstance(field.type_, ModelMetaclass): continue @@ -142,7 +147,7 @@ def create_preferences_docs(): title = field.field_info.title or name sections[title.lower()] = { "title": title, - "description": field.field_info.description or '', + "description": field.field_info.description or "", "fields": [ { "field": n, @@ -150,10 +155,10 @@ def create_preferences_docs(): "description": f.field_info.description, "default": repr(f.get_default()), "ui": n not in excluded, - "type": repr(f._type_display()).replace('.typing', ''), + "type": repr(f._type_display()).replace(".typing", ""), } for n, f in sorted(field.type_.__fields__.items()) - if n not in ('schema_version') + if n not in ("schema_version") ], } @@ -176,4 +181,9 @@ def main(): if __name__ == "__main__": + # Example usage within a script + current_script_name = os.path.basename(__file__) + # Get the name of the current script + logger = setup_logger(current_script_name) + main() diff --git a/docs/_scripts/update_ui_sections_docs.py b/docs/_scripts/update_ui_sections_docs.py index 9fd8f6bb9..a5ab07246 100644 --- a/docs/_scripts/update_ui_sections_docs.py +++ b/docs/_scripts/update_ui_sections_docs.py @@ -1,21 +1,26 @@ # ---- Standard library imports import json +import os from pathlib import Path +# ---- Third-party imports +import seedir as sd +from pydeps import cli, colors, dot, py2depgraph +from pydeps.pydeps import depgraph_to_dotsrc +from pydeps.target import Target + # ---- Napari imports +from napari._qt import dialogs +from napari._qt import qt_viewer from napari._qt.containers import qt_layer_list from napari._qt.layer_controls import qt_layer_controls_container -from napari._qt.widgets import qt_viewer_status_bar from napari._qt._qapp_model import qactions -from napari._qt import qt_viewer -from napari._qt import dialogs +from napari._qt.widgets import qt_viewer_status_bar from napari_console import qt_console -# ---- Third-party imports -from pydeps import cli, colors, dot, py2depgraph -from pydeps.pydeps import depgraph_to_dotsrc -from pydeps.target import Target -import seedir as sd +from scripts_logger import setup_logger + +logger = setup_logger(__name__) # ---- General constants # Docs paths @@ -32,6 +37,8 @@ DIALOGS_MODULE_PATH = Path(dialogs.__file__).parent CONSOLE_MODULE_PATH = Path(qt_console.__file__).parent +logger.debug("paths successfully set in update ui sections") + # ---- Utility functions def generate_dependencies_graph(options): @@ -206,13 +213,8 @@ def generate_mermaid_diagram( if "imports" in dependency: dep_imports = dependency["imports"] for dep_import_name in dep_imports: - if ( - dep_import_name != dep_name - and dep_import_name not in dep_name - ): - mermaid_diagram_content += ( - f"\t{dep_name} --> {dep_import_name}\n" - ) + if dep_import_name != dep_name and dep_import_name not in dep_name: + mermaid_diagram_content += f"\t{dep_name} --> {dep_import_name}\n" if graph_urls_prefix and dependency["path"]: module_path = Path(dependency["path"]) # Check if module is outside napari, like @@ -222,9 +224,7 @@ def generate_mermaid_diagram( NAPARI_ROOT_DIRECTORY_PATH ).as_posix() module_url = f"{graph_urls_prefix}{module_relative_path}" - mermaid_diagram_content += ( - f'\tclick {dep_name} "{module_url}" _blank\n' - ) + mermaid_diagram_content += f'\tclick {dep_name} "{module_url}" _blank\n' dep_name_parent = ".".join(dep_name.split(".")[:-1]) if dep_name_parent not in subgraphs: subgraphs[dep_name_parent] = [dep_name] @@ -234,18 +234,14 @@ def generate_mermaid_diagram( external_nodes.append(dep_name) for subgraph_key, subgraph_value in subgraphs.items(): - mermaid_diagram_content += ( - f"\tsubgraph module.{subgraph_key}[{subgraph_key}]\n" - ) + mermaid_diagram_content += f"\tsubgraph module.{subgraph_key}[{subgraph_key}]\n" for dep_subgraph_name in subgraph_value: mermaid_diagram_content += f"\t\t {dep_subgraph_name}\n" mermaid_diagram_content += "\tend\n" mermaid_diagram_content += f"\tclass module.{subgraph_key} subgraphs\n" if external_nodes: - mermaid_diagram_content += ( - "\tsubgraph module.external[external]\n" - ) + mermaid_diagram_content += "\tsubgraph module.external[external]\n" for external_node in external_nodes: mermaid_diagram_content += f"\t\t {external_node}\n" mermaid_diagram_content += "\tend\n" @@ -256,17 +252,11 @@ def generate_mermaid_diagram( "\tclassDef subgraphs fill:white,strock:black,color:black;" ) if graph_node_default_style: - mermaid_diagram_content += ( - f"\tclassDef default {graph_node_default_style}\n" - ) + mermaid_diagram_content += f"\tclassDef default {graph_node_default_style}\n" if graph_link_default_style: - mermaid_diagram_content += ( - f"\tlinkStyle default {graph_link_default_style}\n" - ) + mermaid_diagram_content += f"\tlinkStyle default {graph_link_default_style}\n" if graph_node_external_style: - mermaid_diagram_content += ( - f"\tclassDef external {graph_node_external_style}\n" - ) + mermaid_diagram_content += f"\tclassDef external {graph_node_external_style}\n" for external_dep in external_nodes: mermaid_diagram_content += f"\tclass {external_dep} external\n" @@ -310,7 +300,9 @@ def generate_docs_ui_section_page( page_content = f"## {section_name}\n" page_content += "### Dependencies diagram (related `napari` modules)\n" page_content += mermaid_diagram - page_content += "### Source code directory layout (related to modules inside `napari`)\n" + page_content += ( + "### Source code directory layout (related to modules inside `napari`)\n" + ) page_content += directory_layout if output_file: output_file.parent.mkdir(exist_ok=True, parents=True) @@ -388,7 +380,10 @@ def generate_docs_ui_section( # ---- Main and UI sections parameters def main(): - # General 'settings' + logger.debug("Empty ui sections list created") + ui_sections = [] + + # --- mermaid settings mermaid_graph_base_settings = { "graph_orientation": "LR", "graph_node_default_style": "fill:#00c3ff,color:black;", @@ -396,9 +391,8 @@ def main(): "graph_link_default_style": "stroke:#00c3ff", "graph_urls_prefix": "https://github.com/napari/napari/tree/main/napari/", } - ui_sections = [] - # ---- Layer list section parameters + # --- Layer list section parameters layer_list_section_name = "Layers list" layer_list_output_page = UI_SECTIONS_DOCS_ROOT_PATH / "layers_list_ui.md" layer_list_pydeps_args = [ @@ -433,6 +427,8 @@ def main(): "--show-deps", "--no-output", ] + + logger.debug("adding layer list to ui section") ui_sections.append( ( layer_list_section_name, @@ -443,10 +439,9 @@ def main(): ) # ---- Layer controls section parameters + logger.debug("start layer controls") layer_controls_section_name = "Layers controls" - layer_controls_output_page = ( - UI_SECTIONS_DOCS_ROOT_PATH / "layers_controls_ui.md" - ) + layer_controls_output_page = UI_SECTIONS_DOCS_ROOT_PATH / "layers_controls_ui.md" layer_controls_pydeps_args = [ f"{LAYER_CONTROLS_MODULE_PATH}", "--exclude", @@ -475,6 +470,8 @@ def main(): "--show-deps", "--no-output", ] + + logger.debug("adding layer controls to ui section") ui_sections.append( ( layer_controls_section_name, @@ -514,6 +511,7 @@ def main(): "--show-deps", "--no-output", ] + logger.debug("adding application status bar to ui section") ui_sections.append( ( application_status_bar_section_name, @@ -565,6 +563,7 @@ def main(): "--show-deps", "--no-output", ] + logger.debug("adding application menus to ui section") ui_sections.append( ( application_menus_section_name, @@ -606,6 +605,7 @@ def main(): "--show-deps", "--no-output", ] + logger.debug("adding viewer to ui section") ui_sections.append( ( viewer_section_name, @@ -660,6 +660,7 @@ def main(): "--show-deps", "--no-output", ] + logger.debug("adding dialogs to ui section") ui_sections.append( ( dialogs_section_name, @@ -687,6 +688,7 @@ def main(): "--show-deps", "--no-output", ] + logger.debug("adding console to ui section") ui_sections.append( ( console_section_name, @@ -696,6 +698,8 @@ def main(): ) ) + logger.debug("getting ready to iterate over sections") + logger.debug(f"ui sections {ui_sections}") for ( section_name, output_page, @@ -711,5 +715,9 @@ def main(): if __name__ == "__main__": - # ---- Call main + # Example usage within a script + current_script_name = os.path.basename(__file__) + # Get the name of the current script + logger = setup_logger(current_script_name) + main() diff --git a/docs/_static/custom.css b/docs/_static/custom.css index ecd8fabcf..d4c258b44 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -1,3 +1,5 @@ +@import url("https://fonts.googleapis.com/css2?family=Barlow:wght@400;500;600;700&display=swap"); + /* Sphinx-Gallery has compatible CSS to fix default sphinx themes Tested for Sphinx 1.3.1 for all themes: default, alabaster, sphinxdoc, @@ -6,7 +8,11 @@ Tested for Read the Docs theme 0.1.7 */ div.sphx-glr-download a { background-color: rgb(255, 255, 255) !important; - background-image: linear-gradient(to bottom, rgb(255, 255, 255), #ffffff) !important; + background-image: linear-gradient( + to bottom, + rgb(255, 255, 255), + #ffffff + ) !important; border-radius: 4px; border: 1px solid #ffffff !important; color: #000; @@ -17,7 +23,8 @@ div.sphx-glr-download a { } div.sphx-glr-download a:hover { - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 5px rgba(0, 0, 0, 0.25); text-decoration: none; background-image: none; @@ -25,18 +32,26 @@ div.sphx-glr-download a:hover { } /* Workaround for https: //github.com/napari/docs/pull/423#issuecomment-2141165872 */ -.bd-content .sd-tab-set>label { +.bd-content .sd-tab-set > label { background-color: var(--napari-gray); color: #222832; } /* Version warning banner color */ #bd-header-version-warning { - background-color: color-mix(in srgb, var(--pst-color-secondary-bg), transparent 30%); + background-color: color-mix( + in srgb, + var(--pst-color-secondary-bg), + transparent 30% + ); } #bd-header-version-warning .pst-button-link-to-stable-version { - background-color: color-mix(in srgb, var(--pst-color-secondary-bg), transparent 0%); + background-color: color-mix( + in srgb, + var(--pst-color-secondary-bg), + transparent 0% + ); border-color: var(--pst-color-secondary-bg); color: #222832; font-weight: 700; @@ -47,4 +62,972 @@ div.sphx-glr-download a:hover { border-color: var(--pst-color-secondary-bg); color: #222832; font-weight: 700; -} \ No newline at end of file +} + +/*************************** + napari custom colors +***************************/ + +html { + --napari-primary-blue: #80d1ff; + --napari-deep-blue: #009bf2; + --napari-light-blue: #d2efff; + --napari-dark-gray: #686868; + --napari-gray: #f7f7f7; + --pst-font-family-base: "Barlow", var(--pst-font-family-base-system); + --pst-font-family-heading: "Barlow", var(--pst-font-family-base-system); + --pst-font-family-monospace: "JetBrains Mono", + var(--pst-font-family-monospace-system); + --pst-font-size-base: 16px; + --pst-color-headerlink: var(--napari-dark-gray); + --pst-color-headerlink-hover: var(--napari-deep-blue); + --pst-color-surface: var(--napari-gray) !important; +} + +html[data-theme="light"] { + --pst-color-primary: black; + --pst-color-secondary: var(--napari-primary-blue); + --pst-color-background: white; + --napari-color-text-base: black; + --pst-color-text-base: var(--napari-color-text-base); + --pst-color-link: black; + --pst-color-link-hover: black !important; + --pst-color-inline-code: black !important; + --pst-color-inline-code-links: black !important; + --pst-color-on-background: white; + --pst-color-text-muted: var(--napari-dark-gray); + --pst-color-border: var(--napari-gray); + --pst-color-target: var(--napari-gray); +} + +/* Dark theme is currently unset - design/accessibility assessment is needed here */ +html[data-theme="dark"] { + --pst-color-primary: black; + --pst-color-secondary: var(--napari-primary-blue); + --pst-color-background: white; + --napari-color-text-base: black; + --pst-color-text-base: var(--napari-color-text-base); + --pst-color-link: black; + --pst-color-link-hover: black !important; + --pst-color-inline-code: black !important; + --pst-color-inline-code-links: black !important; + --pst-color-on-background: white; + --pst-color-text-muted: var(--napari-dark-gray); + --pst-color-border: var(--napari-gray); + --pst-color-target: var(--napari-gray); + --pst-color-secondary-bg: #e0c7ff; + --pst-color-primary-text: white; +} + +/* HTML elements */ +body { + margin: 0; + font-family: + "Barlow", + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Roboto, + "Helvetica Neue", + Arial, + "Noto Sans", + "Liberation Sans", + sans-serif, + "Apple Color Emoji", + "Segoe UI Emoji", + "Segoe UI Symbol", + "Noto Color Emoji"; + text-align: left; +} + +p, +.line-block .line { + line-height: 1.5; + font-size: 1.0625rem; +} + +h1 { + font-size: 2.1875rem; + line-height: 125%; + font-weight: bolder; +} + +/*************************** + Textual elements +***************************/ + +h1 { + font-weight: 700; + color: var(--napari-color-text-base) !important; +} + +h2 { + font-weight: 700; + color: var(--napari-color-text-base) !important; +} + +h3 { + font-weight: 700; + color: var(--napari-color-text-base) !important; +} + +h4 { + font-weight: 700; + color: var(--napari-color-text-base) !important; +} + +h5 { + font-weight: 700; + color: var(--napari-color-text-base) !important; +} + +h6 { + font-weight: 700; + color: var(--napari-color-text-base) !important; +} + +a.headerlink { + color: var(--napari-dark-gray); +} + +.sd-card-hover:hover { + border-color: var(--napari-color-text-base) !important; + transform: scale(1.01); +} + +.prev-next-area p { + color: var(--napari-color-text-base); +} + +/*************************** + napari footer +***************************/ + +.napari-footer { + display: flex; + flex-shrink: 0; + flex-direction: row; + flex-wrap: wrap; + width: 100%; + align-items: center; + justify-content: space-between; + background-color: black; + padding-top: 1.5rem; + padding-bottom: 0.5rem; + margin-top: 75px; +} + +@media (min-width: 495px) { + .napari-footer { + padding-left: 3rem; + padding-right: 3rem; + } + + .napari-footer a > span { + font-size: 0.875rem; + line-height: 1.25rem; + } +} + +.napari-footer p { + color: white; +} + +.napari-footer a { + white-space: nowrap; + text-decoration-line: none; + margin-right: 1rem; + margin-bottom: 1rem; + color: white !important; + display: flex; + flex-direction: row; + align-items: center; +} + +.napari-footer a:hover { + color: white; +} + +.napari-footer a:last-child { + margin-right: 0; +} + +.napari-footer a > svg { + margin-right: 0.25rem; + display: inline-block; + height: 1em; + width: 1em; +} + +.napari-footer .footer-item { + display: flex; + flex-wrap: wrap; + align-items: center; +} + +.napari-footer .footer-item { + display: flex; + flex-wrap: wrap; + align-items: center; +} + +.napari-footer .footer-item--with-napari-copyright { + width: 100%; + justify-content: flex-end; +} + +.napari-footer .napari-copyright { + display: flex; + flex-direction: column; +} + +.napari-footer .napari-copyright, +.napari-footer .napari-copyright .copyright { + font-weight: 600; + font-size: 0.5625rem; + margin-bottom: auto; +} + +@media (min-width: 780px) { + .napari-footer .footer-item--with-napari-copyright { + width: max-content; + } + + .napari-footer .footer-item--with-napari-copyright { + justify-content: flex-start; + } +} + +@media (min-width: 495px) { + .napari-footer .napari-copyright, + .napari-footer .napari-copyright .copyright { + font-size: 0.875rem; + line-height: 1.25rem; + } +} + +.napari-footer .napari-copyright .sphinx-link, +.napari-footer .napari-copyright .sphinx-version { + display: flex; + justify-content: flex-start; +} + +.napari-footer, +.napari-copyright, +.sphinx-link, +> :not([hidden]), +~ :not([hidden]) { + margin-right: calc(0.25rem); + margin-left: calc(0.25rem); +} + +.napari-footer .napari-copyright .sphinx-version { + color: white; +} + +.napari-footer .footer-icon__hover-blue .footer-icon__light-blue { + display: none; +} + +.napari-footer .footer-icon__hover-blue:hover .footer-icon__regular { + display: none; +} + +.napari-footer .footer-icon__hover-blue:hover .footer-icon__light-blue { + display: block; +} + +/* Recommended by PST so that components will display horizontally */ +.footer-items__start, +.footer-items__end { + flex-direction: row; +} + +.bd-footer__inner { + display: flex; + flex-shrink: 0; + flex-direction: row; + flex-wrap: wrap; + width: 100%; + max-width: 100%; + align-items: center; + justify-content: space-between; + background-color: black; +} + +/**************************** + Nav bar section +*****************************/ + +.navbar { + background-color: var(--napari-primary-blue) !important; + box-shadow: none; +} + +.navbar-brand { + vertical-align: middle; + font-size: 1.25rem; + color: black !important; + position: relative; + font-weight: bolder; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none !important; + color: var(--napari-color-text-base) !important; +} + +/* Navbar text */ +.bd-header ul.navbar-nav > li.nav-item > .nav-link { + color: var(--napari-color-text-base); + font-size: 1.0625rem; + font-weight: 500 !important; + border-bottom: 3px solid transparent; + padding: 16px 1.0625rem 16px !important; +} + +.bd-header ul.navbar-nav > li.nav-item > .nav-link:hover { + color: var(--napari-color-text-base); + font-size: 1.0625rem; + font-weight: 500 !important; + border-bottom: 3px solid var(--napari-color-text-base); +} + +.bd-header ul.navbar-nav > li.nav-item.current > .nav-link::before { + border-bottom: 0 solid var(--napari-color-text-base); +} + +.bd-header ul.navbar-nav > li.nav-item.current > .nav-link { + border-bottom: 3px solid var(--napari-color-text-base); + font-weight: 700 !important; +} + +.bd-header ul.navbar-nav { + height: var(--pst-header-height); +} + +.bd-header ul.navbar-nav > li.nav-item { + margin-inline: 0; +} + +.bd-header ul.navbar-nav > li.nav-item.dropdown > .dropdown-toggle { + color: var(--napari-color-text-base); + font-size: 1.0625rem; + font-weight: 500 !important; + border-bottom: 3px solid transparent; + padding: 16px 1.0625rem 16px !important; + height: var(--pst-header-height); +} + +.bd-header ul.navbar-nav > li.nav-item.dropdown > .dropdown-toggle:hover { + box-shadow: none; + text-decoration: none; + border-bottom: 3px solid var(--napari-color-text-base); +} + +.bd-header ul.navbar-nav li a.nav-link.dropdown-item { + color: var(--napari-color-text-base); + font-weight: 500; +} + +html .pst-navbar-icon, +html .pst-navbar-icon:hover { + color: var(--napari-color-text-base); +} + +/*************************** + version switcher +***************************/ + +button.btn.version-switcher__button { + padding-top: 0; + font-size: 0.875rem; + font-weight: 600; + border-style: none; + color: var(--napari-color-text-base); +} + +button.btn.version-switcher__button:hover { + color: var(--napari-color-text-base); +} + +.version-switcher__menu a.list-group-item { + background-color: var(--pst-color-background); + color: var(--napari-color-text-base); + padding: 0.5rem 0.5rem; + font-size: 0.875rem; +} + +.version-switcher__menu a.list-group-item:hover { + color: var(--napari-color-text-base); +} + +.version-switcher__menu, +button.version-switcher__button { + min-width: max-content; + border-radius: unset; +} + +/*************************** + sidebar +***************************/ + +/* Remove "Section Navigation" caption */ +.bd-links__title { + display: none; +} + +/* Move chevron to the left */ +.bd-sidebar-primary li.has-children > details > summary .toctree-toggle { + right: unset; +} + +/* Fonts and styles */ +.bd-sidebar a.reference, +.bd-sidebar .caption-text { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.bd-sidebar-primary .sidebar-primary-items__end { + margin-bottom: 0; + margin-top: 0; +} + +.bd-sidebar .toctree-l1 a { + padding-left: 32px; +} + +.bd-sidebar .toctree-l2 { + margin-left: -0.2rem; + border-left: 1px solid var(--napari-color-text-base); +} + +.bd-sidebar .toctree-l2 label { + left: 4px; +} + +.bd-sidebar .toctree-l2 a { + font-size: 0.875rem; + line-height: 1.5rem; + text-decoration-line: none; + border-left: 2px solid transparent; + color: var(--napari-color-text-base) !important; +} + +.bd-sidebar .toctree-l2 a:hover, +.bd-sidebar .toctree-l2 a.current:hover { + border-left: 2px solid var(--napari-primary-blue); +} + +.bd-sidebar .toctree-l2 a.current { + border-left: 2px solid var(--napari-color-text-base); +} + +.bd-sidebar .toctree-l3 label { + left: 6px; +} + +.bd-sidebar .toctree-l3 a { + font-size: 0.875rem; + line-height: 1.5rem; + text-decoration-line: none; + border-left: 2px solid transparent; + color: var(--napari-color-text-base) !important; + padding-left: 36px; +} + +.bd-sidebar .toctree-l3 a:hover, +.bd-sidebar .toctree-l3 a.current:hover { + border-left: 2px solid var(--napari-primary-blue); + margin-left: -1rem; + padding-left: 52px; +} + +.bd-sidebar .toctree-l3 a.current { + border-left: 2px solid var(--napari-color-text-base); + margin-left: -1rem; + padding-left: 52px; +} + +.bd-sidebar .toctree-l4 label { + left: 8px; +} + +.bd-sidebar .toctree-l4 a { + font-size: 0.875rem; + line-height: 1.5rem; + text-decoration-line: none; + border-left: 2px solid transparent; + color: var(--napari-color-text-base) !important; +} + +.bd-sidebar .toctree-l4 a:hover, +.bd-sidebar .toctree-l4 a.current:hover { + border-left: 2px solid var(--napari-primary-blue); + margin-left: -2rem; + padding-left: 68px; +} + +.bd-sidebar .toctree-l4 a.current { + border-left: 2px solid var(--napari-color-text-base); + margin-left: -2rem; + padding-left: 68px; +} + +.bd-sidebar .toctree-l5 label { + left: 10px; +} + +.bd-sidebar .toctree-l5 a { + font-size: 0.875rem; + line-height: 1.5rem; + text-decoration-line: none; + border-left: 2px solid transparent; + color: var(--napari-color-text-base) !important; +} + +.bd-sidebar .toctree-l5 a:hover, +.bd-sidebar .toctree-l5 a.current:hover { + border-left: 2px solid var(--napari-primary-blue); +} + +.bd-sidebar .toctree-l5 a.current { + border-left: 2px solid var(--napari-color-text-base); +} + +/* also related to navbar */ +.navbar-nav li a:focus, +.navbar-nav li a:hover, +.navbar-nav li.current > a { + color: var(--napari-color-text-base); +} + +nav.bd-links li > a { + color: var(--napari-color-text-base); + display: block; + line-height: 1.25rem; +} + +nav.bd-links li > a:active, +nav.bd-links li > a:hover { + color: var(--napari-color-text-base); +} + +nav.bd-links li > a:hover { + text-decoration: none !important; +} + +nav.bd-links .current > a { + box-shadow: none !important; +} + +/*************************** +search +***************************/ + +.bd-search { + border: 1px solid transparent; +} + +.bd-search:focus-within { + box-shadow: 0 0 0 0.1875rem var(--napari-primary-blue); +} + +.form-control { + border: 1px transparent; +} + +.form-control:focus, +.form-control:focus-visible { + background-color: var(--pst-color-background); + border: none; + box-shadow: none; + color: var(--pst-color-text-muted); + outline: none; +} + +/*************************** +page toc sidebar +***************************/ + +.onthispage { + border-style: none; + padding: 1px 0 5px; + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 600; + text-transform: uppercase; + color: var(--napari-color-text-base) !important; + margin: 0 !important; +} + +.page-toc { + .section-nav { + padding-left: 0; + border-bottom: none; + } + + .onthispage { + color: var(--napari-color-text-base); + font-weight: var(--pst-sidebar-header-font-weight); + margin-bottom: 1rem; + } +} + +.toc-entry a.nav-link.active:hover { + color: var(--napari-color-text-base); +} + +.toc-entry a.nav-link:active, +.toc-entry a.nav-link:hover { + color: var(--napari-color-text-base); +} + +nav.page-toc { + border-left: 1px solid var(--napari-color-text-base); + padding-left: 1rem; +} + +.sidebar-secondary-item { + border-left: none !important; +} + +.toc-entry a.nav-link, +.toc-entry a > code { + color: var(--napari-color-text-base); +} + +.toc-entry > .nav-link { + border-left: 3px solid transparent; +} + +.toc-entry > .nav-link:hover { + border-left: 3px solid var(--napari-primary-blue); +} + +.toc-entry a.nav-link:hover { + text-decoration: none !important; +} + +.toc-entry a.nav-link.active { + box-shadow: none !important; + border-left: 3px solid var(--napari-color-text-base); +} + +.toc-h3.nav-item.toc-entry.active a { + margin-left: -2rem; + padding-left: 2rem; +} + +.toc-h3.nav-item.toc-entry a { + margin-left: -2rem; + padding-left: 2rem; +} + +.toc-h4.nav-item.toc-entry.active a { + margin-left: -3rem; + padding-left: 3rem; +} + +.toc-h4.nav-item.toc-entry a { + margin-left: -3rem; + padding-left: 3rem; +} + +.toc-h4.nav-item.toc-entry a:hover { + margin-left: -3rem; + padding-left: 3rem; +} + +/*************************** + napari calendar +***************************/ + +:root { + --fc-border-color: var(--napari-primary-blue); + --fc-daygrid-event-dot-width: 5px; + --fc-button-bg-color: var(--napari-primary-blue); + --fc-button-border-color: var(--napari-primary-blue); + --fc-button-text-color: var(--napari-color-text-base); + --fc-button-active-bg-color: var(--napari-deep-blue); + --fc-button-active-border-color: var(--napari-deep-blue); + --fc-button-hover-bg-color: var(--napari-deep-blue); + --fc-button-hover-border-color: var(--napari-deep-blue); + --fc-event-bg-color: var(--napari-light-blue); + --fc-event-border-color: var(--napari-light-blue); + --fc-event-text-color: var(--napari-color-text-base); +} + +.fc .fc-button:focus { + box-shadow: none; +} + +.fc-event-time { + margin-right: 3px; + min-width: fit-content; +} + +.fc-day-today .fc-daygrid-day-number { + background-color: var(--napari-primary-blue); +} + +.fc .fc-daygrid-day.fc-day-today { + background-color: unset; +} + +/*************************** + Admonitions +***************************/ + +.admonition, +div.admonition { + --color: #80d1ff; + border: var(--color) solid 1px !important; + border-radius: 0 !important; + box-shadow: none !important; + /*border-color: rgba(var(--pst-color-admonition-default), 1); */ + padding-left: 0 !important; + font-size: 0.938rem; + font-weight: 500; +} + +.admonition > .admonition-title, +div.admonition > .admonition-title { + text-transform: uppercase; + background: var(--color) !important; + font-size: 0.938rem !important; + font-weight: 700; +} + +/* Remove admonition title icon */ +div.admonition > .admonition-title:after, +.admonition > .admonition-title:after { + display: none; +} + +/* Padding and spacing */ +div.admonition.warning > ul.simple { + padding: 1.1rem !important; +} + +div.admonition > p, +div.admonition > ul.simple p { + font-size: 0.938rem; +} + +div.admonition > p { + padding-top: 0.5rem; + padding-bottom: 0.4rem; +} + +/* Toggle button */ +.admonition.toggle-hidden { + height: 40px; +} + +.admonition .toggle-button { + top: 0; + right: 0; + z-index: 10; + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + float: unset; +} + +.admonition .toggle-button::before { + display: none; +} + +.admonition .toggle-button svg { + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transform: rotate(45deg); +} + +.admonition .toggle-button.toggle-button-hidden svg { + transform: rotate(0); +} + +/* Attention */ + +.admonition.attention { + --color: #d8f97d; +} + +/* Caution */ + +.admonition.caution { + --color: #ffc580; +} + +/* Warning */ + +.admonition.warning { + --color: #ffa680; +} + +/* Danger */ + +.admonition.danger { + --color: #ff8080; +} + +/* Error */ + +.admonition.error { + --color: #fade7d; +} + +/* Hint */ + +.admonition.hint { + --color: #8094ff; +} + +/* Tip */ + +.admonition.tip { + --color: #cf80ff; +} + +/* Important */ + +.admonition.important { + --color: #f1f379; +} + +/* Note */ + +.admonition.note { + --color: #80ffe0; +} + +/*************************** + Page container +***************************/ + +#pst-back-to-top { + background-color: var(--napari-light-blue); + color: var(--napari-dark-gray); +} + +/*************************** + Calendar popup +***************************/ + +/* The Modal (background) */ +.modal { + /* Hidden by default */ + position: fixed; + /* Stay in place */ + z-index: 1; + /* Sit on top */ + padding-top: 100px; + /* Location of the box */ + left: 0; + top: 0; + width: 100%; + /* Full width */ + height: 100%; + /* Full height */ + overflow: auto; + /* Enable scroll if needed */ + background-color: rgb(0, 0, 0); + /* Fallback color */ + background-color: rgba(0, 0, 0, 0.4); + /* Black w/ opacity */ +} + +/* Modal Content */ +.modal-content { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: #fefefe; + margin: auto; + padding: 0; + border: 1px solid #888; + box-shadow: + 0 4px 8px 0 rgba(0, 0, 0, 0.2), + 0 6px 20px 0 rgba(0, 0, 0, 0.19); +} + +/* Modal Content */ +.modal-content { + width: 30%; +} + +@media (max-width: 780px) { + .modal-content { + width: 50%; + } +} + +@media (max-width: 495px) { + .modal-content { + width: 80%; + } +} + +/* Add Animation */ +@-webkit-keyframes animatetop { + from { + top: -300px; + opacity: 0; + } + + to { + top: 0; + opacity: 1; + } +} + +@keyframes animatetop { + from { + top: -300px; + opacity: 0; + } + + to { + top: 0; + opacity: 1; + } +} + +/* The Close Button */ +.close { + color: white; + float: right; + font-size: 28px; + font-weight: bold; + padding-right: 12px; + padding-top: 4px; +} + +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; +} + +.modal-header { + padding: 0 0 0 12px; + background-color: var(--napari-primary-blue); + color: white; + display: block; +} + +.modal-header h3 { + margin-top: 1rem; +} + +.modal-body { + padding: 12px; +} diff --git a/docs/_templates/footer.html b/docs/_templates/footer.html new file mode 100644 index 000000000..ff6eee7b2 --- /dev/null +++ b/docs/_templates/footer.html @@ -0,0 +1,23 @@ +{% if theme_footer_start or theme_footer_center or theme_footer_end %} +<div class="bd-footer__inner bd-page-width"> + {% if theme_footer_start %} + <div class="footer-items__start"> + {% for item in theme_footer_start %} + <div class="footer-item">{% include item %}</div> + {% endfor %} + </div> + {% endif %} {% if theme_footer_center %} + <div class="footer-items__center"> + {% for item in theme_footer_center %} + <div class="footer-item">{% include item %}</div> + {% endfor %} + </div> + {% endif %} {% if theme_footer_end %} + <div class="footer-items__end"> + {% for item in theme_footer_end %} + <div class="footer-item">{% include item %}</div> + {% endfor %} + </div> + {% endif %} +</div> +{% endif %} diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html new file mode 100644 index 000000000..a715e0f18 --- /dev/null +++ b/docs/_templates/layout.html @@ -0,0 +1,3 @@ +{%- extends "pydata_sphinx_theme/layout.html" %} {%- block footer %} +<footer class="napari-footer">{%- include "footer.html" %}</footer> +{%- endblock %} diff --git a/docs/_templates/napari-copyright.html b/docs/_templates/napari-copyright.html new file mode 100644 index 000000000..a6f1538b1 --- /dev/null +++ b/docs/_templates/napari-copyright.html @@ -0,0 +1,21 @@ +<div class="napari-copyright"> + <p class="copyright"> + {%- if hasdoc('copyright') %} {% trans path=pathto('copyright'), + copyright=copyright|e %}<a href="{{ path }}">Copyright</a> {{ copyright }}{% + endtrans %}<br /> + {%- else %} {% trans copyright=copyright|e %}Copyright {{ copyright }}{% + endtrans %}<br /> + {%- endif %} + </p> + + <div class="sphinx-version"> + <div class="sphinx-link"> + <span>Created using</span> + <a href="http://sphinx-doc.org/">Sphinx</a> + </div> + + {% trans sphinx_version=sphinx_version|e %} + <span>{{ sphinx_version }}</span> + {% endtrans %} + </div> +</div> diff --git a/docs/_templates/napari-footer-links.html b/docs/_templates/napari-footer-links.html new file mode 100644 index 000000000..96dae323b --- /dev/null +++ b/docs/_templates/napari-footer-links.html @@ -0,0 +1,70 @@ +<a + class="footer-icon__hover-blue" + href="https://napari.org" + title="napari.org" + target="_blank" + rel="noreferrer" +> + {% include "partials/napari.html" %} + <span>napari</span> +</a> + +<a + class="footer-icon__hover-blue" + href="https://napari-hub.org" + title="napari hub" + target="_blank" + rel="noreferrer" +> + {% include "partials/napari-hub.html" %} {% include + "partials/napari-hub-light-blue.html" %} + <span>Plugins</span> +</a> + +<a + class="footer-icon__hover-blue" + href="https://github.com/napari/napari" + title="GitHub" + target="_blank" + rel="noreferrer" +> + {% include "partials/github.html" %} {% include + "partials/github-light-blue.html" %} + <span>Code</span> +</a> + +<a + class="footer-icon__hover-blue" + href="https://fosstodon.org/@napari" + title="Mastodon" + target="_blank" + rel="noreferrer" +> + {% include "partials/mastodon.html" %} {% include + "partials/mastodon-light-blue.html" %} + <span>Mastodon</span> +</a> + +<a + class="footer-icon__hover-blue" + href="https://forum.image.sc/tag/napari" + title="image.sc" + target="_blank" + rel="noreferrer" +> + {% include "partials/image-sc.html" %} {% include + "partials/image-sc-light-blue.html" %} + <span>Forum</span> +</a> + +<a + class="footer-icon__hover-blue" + href="https://napari.zulipchat.com" + title="Zulip" + target="_blank" + rel="noreferrer" +> + {% include "partials/zulip.html" %} {% include + "partials/zulip-light-blue.html" %} + <span>Chat</span> +</a> diff --git a/docs/_templates/page-toc.html b/docs/_templates/page-toc.html new file mode 100644 index 000000000..dd6639ab1 --- /dev/null +++ b/docs/_templates/page-toc.html @@ -0,0 +1,4 @@ +{% set page_toc = generate_toc_html() %} {%- if page_toc | length >= 1 %} +<div class="page-toc tocsection onthispage">{{ _("On this page") }}</div> +<nav class="bd-toc-nav page-toc">{{ page_toc }}</nav> +{%- endif %} diff --git a/docs/_templates/partials/github-light-blue.html b/docs/_templates/partials/github-light-blue.html new file mode 100644 index 000000000..2735a7dc3 --- /dev/null +++ b/docs/_templates/partials/github-light-blue.html @@ -0,0 +1,13 @@ +<svg + class="footer-icon__light-blue" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" + xmlns="http://www.w3.org/2000/svg" +> + <path + d="M8 0C3.58 0 0 3.582 0 8C0 11.5353 2.292 14.5333 5.47 15.59C5.87 15.6653 6.01667 15.418 6.01667 15.2053C6.01667 15.0153 6.01 14.512 6.00667 13.8453C3.78133 14.328 3.312 12.772 3.312 12.772C2.948 11.8487 2.422 11.602 2.422 11.602C1.69733 11.106 2.478 11.116 2.478 11.116C3.28133 11.172 3.70333 11.94 3.70333 11.94C4.41667 13.1633 5.576 12.81 6.03333 12.6053C6.10533 12.088 6.31133 11.7353 6.54 11.5353C4.76333 11.3353 2.896 10.6473 2.896 7.582C2.896 6.70867 3.206 5.99533 3.71933 5.43533C3.62933 5.23333 3.35933 4.42 3.78933 3.318C3.78933 3.318 4.45933 3.10333 5.98933 4.138C6.62933 3.96 7.30933 3.872 7.98933 3.868C8.66933 3.872 9.34933 3.96 9.98933 4.138C11.5093 3.10333 12.1793 3.318 12.1793 3.318C12.6093 4.42 12.3393 5.23333 12.2593 5.43533C12.7693 5.99533 13.0793 6.70867 13.0793 7.582C13.0793 10.6553 11.2093 11.332 9.42933 11.5287C9.70933 11.7687 9.96933 12.2593 9.96933 13.0087C9.96933 14.0793 9.95933 14.9393 9.95933 15.1993C9.95933 15.4093 10.0993 15.6593 10.5093 15.5793C13.71 14.53 16 11.53 16 8C16 3.582 12.418 0 8 0" + fill="#D2EFFF" + /> +</svg> diff --git a/docs/_templates/partials/github.html b/docs/_templates/partials/github.html new file mode 100644 index 000000000..6898a5df4 --- /dev/null +++ b/docs/_templates/partials/github.html @@ -0,0 +1,14 @@ +<svg + class="footer-icon__regular" + width="14" + height="14" + viewBox="0 0 14 14" + fill="none" + xmlns="http://www.w3.org/2000/svg" +> + <title>Visit GitHub repository</title> + <path + d="M7 0.173252C3.1325 0.173252 0 3.3075 0 7.17325C0 10.2667 2.0055 12.8899 4.78625 13.8145C5.13625 13.8804 5.26458 13.664 5.26458 13.4779C5.26458 13.3117 5.25875 12.8713 5.25583 12.2879C3.30867 12.7103 2.898 11.3488 2.898 11.3488C2.5795 10.5408 2.11925 10.325 2.11925 10.325C1.48517 9.891 2.16825 9.89975 2.16825 9.89975C2.87117 9.94875 3.24042 10.6208 3.24042 10.6208C3.86458 11.6912 4.879 11.382 5.27917 11.2029C5.34217 10.7503 5.52242 10.4417 5.7225 10.2667C4.16792 10.0917 2.534 9.48967 2.534 6.8075C2.534 6.04334 2.80525 5.41917 3.25442 4.92917C3.17567 4.75242 2.93942 4.04075 3.31567 3.0765C3.31567 3.0765 3.90192 2.88867 5.24067 3.794C5.80067 3.63825 6.39567 3.56125 6.99067 3.55775C7.58567 3.56125 8.18067 3.63825 8.74067 3.794C10.0707 2.88867 10.6569 3.0765 10.6569 3.0765C11.0332 4.04075 10.7969 4.75242 10.7269 4.92917C11.1732 5.41917 11.4444 6.04334 11.4444 6.8075C11.4444 9.49667 9.80817 10.0888 8.25067 10.2608C8.49567 10.4708 8.72317 10.9002 8.72317 11.5558C8.72317 12.4927 8.71442 13.2452 8.71442 13.4727C8.71442 13.6564 8.83692 13.8752 9.19567 13.8052C11.9962 12.887 14 10.262 14 7.17325C14 3.3075 10.8657 0.173252 7 0.173252Z" + fill="white" + ></path> +</svg> diff --git a/docs/_templates/partials/image-sc-light-blue.html b/docs/_templates/partials/image-sc-light-blue.html new file mode 100644 index 000000000..fc7c82f95 --- /dev/null +++ b/docs/_templates/partials/image-sc-light-blue.html @@ -0,0 +1,39 @@ +<svg + class="footer-icon__light-blue" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" + xmlns="http://www.w3.org/2000/svg" +> + <path + d="M10.3989 3.72445V1.61071C10.3989 1.60566 10.3948 1.60156 10.3898 1.60156H5.56237C5.55732 1.60156 5.55322 1.60566 5.55322 1.61071V6.43813C5.55322 6.44319 5.55732 6.44727 5.56237 6.44727H8.77605" + stroke="#D2EFFF" + stroke-width="1.87535" + /> + <path + d="M5.55322 12.2794V14.3932C5.55322 14.3982 5.55732 14.4023 5.56237 14.4023H10.3898C10.3948 14.4023 10.3989 14.3982 10.3989 14.3932V9.56576C10.3989 9.56072 10.3948 9.55664 10.3898 9.55664H7.17608" + stroke="#D2EFFF" + stroke-width="1.87535" + /> + <path + d="M12.8916 7.96164L14.7221 6.90477C14.7265 6.90224 14.728 6.89665 14.7255 6.89228L12.3118 2.7116C12.3092 2.70723 12.3036 2.70573 12.2993 2.70826L8.1186 5.12197C8.11428 5.12449 8.11276 5.13008 8.11524 5.13446L9.72212 7.91762" + stroke="#D2EFFF" + stroke-width="1.87535" + /> + <path + d="M3.06062 8.04297L1.23007 9.09985C1.22569 9.10233 1.2242 9.10793 1.22672 9.11233L3.64044 13.293C3.64296 13.2974 3.64856 13.2989 3.65292 13.2963L7.8336 10.8826C7.83797 10.8801 7.83948 10.8745 7.83695 10.8701L6.23009 8.08697" + stroke="#D2EFFF" + stroke-width="1.87535" + /> + <path + d="M10.4688 12.2401L12.2994 13.297C12.3037 13.2996 12.3093 13.298 12.3119 13.2936L14.7255 9.11299C14.7281 9.10859 14.7266 9.10299 14.7222 9.10051L10.5415 6.68677C10.5371 6.68425 10.5315 6.68575 10.5291 6.69012L8.92218 9.47331" + stroke="#D2EFFF" + stroke-width="1.87535" + /> + <path + d="M5.48348 3.76513L3.65292 2.70825C3.64855 2.70573 3.64296 2.70723 3.64043 2.71161L1.22672 6.89228C1.22419 6.89665 1.2257 6.90225 1.23007 6.90477L5.41075 9.3185C5.41511 9.32098 5.42071 9.31954 5.42323 9.31514L7.03009 6.53198" + stroke="#D2EFFF" + stroke-width="1.87535" + /> +</svg> diff --git a/docs/_templates/partials/image-sc.html b/docs/_templates/partials/image-sc.html new file mode 100644 index 000000000..449a2f383 --- /dev/null +++ b/docs/_templates/partials/image-sc.html @@ -0,0 +1,40 @@ +<svg + class="footer-icon__regular" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + xmlns="http://www.w3.org/2000/svg" +> + <title>Visit image.sc forum</title> + <path + d="M12.9987 4.65361V2.01143C12.9987 2.00512 12.9936 2 12.9873 2H6.95296C6.94665 2 6.94153 2.00512 6.94153 2.01143V8.04571C6.94153 8.05203 6.94665 8.05714 6.95296 8.05714H10.9701" + stroke="white" + stroke-width="2.34286" + ></path> + <path + d="M6.94153 15.3464V17.9886C6.94153 17.9949 6.94665 18 6.95296 18H12.9873C12.9936 18 12.9987 17.9949 12.9987 17.9886V11.9543C12.9987 11.948 12.9936 11.9429 12.9873 11.9429H8.9701" + stroke="white" + stroke-width="2.34286" + ></path> + <path + d="M16.1145 9.9496L18.4027 8.62852C18.4082 8.62536 18.4101 8.61837 18.4069 8.6129L15.3898 3.38706C15.3866 3.38159 15.3796 3.37972 15.3742 3.38288L10.1483 6.40002C10.1429 6.40317 10.141 6.41016 10.1441 6.41563L12.1527 9.89458" + stroke="white" + stroke-width="2.34286" + ></path> + <path + d="M3.82577 10.0503L1.53758 11.3714C1.53211 11.3745 1.53024 11.3815 1.5334 11.387L4.55054 16.6128C4.5537 16.6183 4.56069 16.6202 4.56615 16.617L9.792 13.5999C9.79746 13.5967 9.79934 13.5897 9.79618 13.5842L7.78761 10.1053" + stroke="white" + stroke-width="2.34286" + ></path> + <path + d="M13.086 15.2962L15.3742 16.6173C15.3796 16.6205 15.3866 16.6186 15.3898 16.6131L18.4069 11.3873C18.4101 11.3818 18.4082 11.3748 18.4027 11.3717L13.1769 8.35456C13.1714 8.35141 13.1644 8.35328 13.1613 8.35875L11.1527 11.8377" + stroke="white" + stroke-width="2.34286" + ></path> + <path + d="M6.85434 4.70397L4.56614 3.38288C4.56068 3.37973 4.55369 3.3816 4.55053 3.38707L1.53339 8.61291C1.53023 8.61838 1.53211 8.62537 1.53757 8.62852L6.76342 11.6457C6.76888 11.6488 6.77587 11.647 6.77903 11.6415L8.7876 8.16254" + stroke="white" + stroke-width="2.34286" + ></path> +</svg> diff --git a/docs/_templates/partials/mastodon-light-blue.html b/docs/_templates/partials/mastodon-light-blue.html new file mode 100644 index 000000000..e5e7a32e6 --- /dev/null +++ b/docs/_templates/partials/mastodon-light-blue.html @@ -0,0 +1,13 @@ +<svg + class="footer-icon__light-blue" + width="74" + height="79" + viewBox="0 0 74 79" + fill="none" + xmlns="http://www.w3.org/2000/svg" +> + <path + d="m 73.64677,18.147319 c -1.1398,-8.33886 -8.524,-14.91044 -17.2774,-16.18384 -1.4768,-0.21517 -7.0723,-0.99825999 -20.0339,-0.99825999 h -0.0968 c -12.9652,0 -15.7468,0.78308999 -17.2236,0.99825999 -8.5097,1.23812 -16.2809303,7.14302 -18.16639429,15.58064 -0.906885,4.1553 -1.003667,8.7621 -0.835194,12.9879 0.240163,6.0601 0.286762,12.1096 0.845948,18.1451 0.38657999,4.009 1.06095999,7.9861 2.01808999,11.9015 1.79226,7.2312 9.0473503,13.249 16.1554503,15.704 7.6103,2.5603 15.7945,2.9854 23.6364,1.2276 0.8626,-0.1976 1.7158,-0.4268 2.5593,-0.6879 1.9034,-0.5961 4.1366,-1.2628 5.7783,-2.4339 0.0225,-0.0164 0.041,-0.0376 0.054,-0.0621 0.013,-0.0244 0.0203,-0.0514 0.0212,-0.079 v -5.8484 c -4e-4,-0.0258 -0.0066,-0.0512 -0.0183,-0.0743 -0.0116,-0.0231 -0.0283,-0.0433 -0.049,-0.0592 -0.0206,-0.0159 -0.0446,-0.027 -0.0701,-0.0326 -0.0256,-0.0056 -0.0521,-0.0055 -0.0776,3e-4 -5.0242,1.181 -10.1727,1.773 -15.3382,1.7637 -8.8896,0 -11.2805,-4.1518 -11.9652,-5.8802 -0.5503,-1.4938 -0.8998,-3.0521 -1.0395,-4.6351 -0.0014,-0.0265 0.0036,-0.0531 0.0145,-0.0774 0.0109,-0.0244 0.0276,-0.0458 0.0485,-0.0627 0.021,-0.0168 0.0457,-0.0285 0.0721,-0.0342 0.0264,-0.0057 0.0538,-0.0052 0.08,0.0015 4.9405,1.173 10.005,1.765 15.0873,1.7637 1.2223,0 2.441,0 3.6634,-0.0317 5.1115,-0.1411 10.499,-0.3986 15.5281,-1.3652 0.1255,-0.0246 0.2509,-0.0458 0.3585,-0.0776 7.9325,-1.4991 15.4815,-6.2047 16.2486,-18.1203 0.0287,-0.4691 0.1004,-4.9137 0.1004,-5.4005 0.0036,-1.6543 0.5413,-11.7357 -0.0789,-17.9298 z m -12.2089,29.7326 h -8.3411 v -20.1063 c 0,-4.2329 -1.7923,-6.3917 -5.4378,-6.3917 -4.0075,0 -6.0148,2.5538 -6.0148,7.5981 v 11.0055 h -8.291 v -11.0055 c 0,-5.0443 -2.0109,-7.5981 -6.0184,-7.5981 -3.624,0 -5.4342,2.1588 -5.4378,6.3917 v 20.1063 h -8.334 v -20.7166 c 0,-4.2329 1.0981,-7.5957 3.2942,-10.0884 2.2654,-2.4868 5.237,-3.7637 8.9255,-3.7637 4.2691,0 7.4952,1.6155 9.6459,4.8431 l 2.0755,3.4287 2.079,-3.4287 c 2.1507,-3.2276 5.3768,-4.8431 9.6388,-4.8431 3.6849,0 6.6564,1.2769 8.929,3.7637 2.1962,2.4904 3.2942,5.8532 3.2942,10.0884 z" + fill="#D2EFFF" + ></path> +</svg> diff --git a/docs/_templates/partials/mastodon.html b/docs/_templates/partials/mastodon.html new file mode 100644 index 000000000..563231f19 --- /dev/null +++ b/docs/_templates/partials/mastodon.html @@ -0,0 +1,14 @@ +<svg + class="footer-icon__regular" + width="74" + height="79" + viewBox="0 0 74 79" + fill="none" + xmlns="http://www.w3.org/2000/svg" +> + <title>Visit Mastodon page</title> + <path + d="M73.7014 17.9592C72.5616 9.62034 65.1774 3.04876 56.424 1.77536C54.9472 1.56019 49.3517 0.7771 36.3901 0.7771H36.2933C23.3281 0.7771 20.5465 1.56019 19.0697 1.77536C10.56 3.01348 2.78877 8.91838 0.903306 17.356C-0.00357857 21.5113 -0.100361 26.1181 0.068112 30.3439C0.308275 36.404 0.354874 42.4535 0.91406 48.489C1.30064 52.498 1.97502 56.4751 2.93215 60.3905C4.72441 67.6217 11.9795 73.6395 19.0876 76.0945C26.6979 78.6548 34.8821 79.0799 42.724 77.3221C43.5866 77.1245 44.4398 76.8953 45.2833 76.6342C47.1867 76.0381 49.4199 75.3714 51.0616 74.2003C51.0841 74.1839 51.1026 74.1627 51.1156 74.1382C51.1286 74.1138 51.1359 74.0868 51.1368 74.0592V68.2108C51.1364 68.185 51.1302 68.1596 51.1185 68.1365C51.1069 68.1134 51.0902 68.0932 51.0695 68.0773C51.0489 68.0614 51.0249 68.0503 50.9994 68.0447C50.9738 68.0391 50.9473 68.0392 50.9218 68.045C45.8976 69.226 40.7491 69.818 35.5836 69.8087C26.694 69.8087 24.3031 65.6569 23.6184 63.9285C23.0681 62.4347 22.7186 60.8764 22.5789 59.2934C22.5775 59.2669 22.5825 59.2403 22.5934 59.216C22.6043 59.1916 22.621 59.1702 22.6419 59.1533C22.6629 59.1365 22.6876 59.1248 22.714 59.1191C22.7404 59.1134 22.7678 59.1139 22.794 59.1206C27.7345 60.2936 32.799 60.8856 37.8813 60.8843C39.1036 60.8843 40.3223 60.8843 41.5447 60.8526C46.6562 60.7115 52.0437 60.454 57.0728 59.4874C57.1983 59.4628 57.3237 59.4416 57.4313 59.4098C65.3638 57.9107 72.9128 53.2051 73.6799 41.2895C73.7086 40.8204 73.7803 36.3758 73.7803 35.889C73.7839 34.2347 74.3216 24.1533 73.7014 17.9592ZM61.4925 47.6918H53.1514V27.5855C53.1514 23.3526 51.3591 21.1938 47.7136 21.1938C43.7061 21.1938 41.6988 23.7476 41.6988 28.7919V39.7974H33.4078V28.7919C33.4078 23.7476 31.3969 21.1938 27.3894 21.1938C23.7654 21.1938 21.9552 23.3526 21.9516 27.5855V47.6918H13.6176V26.9752C13.6176 22.7423 14.7157 19.3795 16.9118 16.8868C19.1772 14.4 22.1488 13.1231 25.8373 13.1231C30.1064 13.1231 33.3325 14.7386 35.4832 17.9662L37.5587 21.3949L39.6377 17.9662C41.7884 14.7386 45.0145 13.1231 49.2765 13.1231C52.9614 13.1231 55.9329 14.4 58.2055 16.8868C60.4017 19.3772 61.4997 22.74 61.4997 26.9752L61.4925 47.6918Z" + fill="white" + ></path> +</svg> diff --git a/docs/_templates/partials/menu.html b/docs/_templates/partials/menu.html new file mode 100644 index 000000000..63e56be20 --- /dev/null +++ b/docs/_templates/partials/menu.html @@ -0,0 +1,11 @@ +<svg + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + xmlns="http://www.w3.org/2000/svg" +> + <path d="M20 2H0" stroke="black" stroke-width="3" /> + <path d="M20 10H0" stroke="black" stroke-width="3" /> + <path d="M20 18H0" stroke="black" stroke-width="3" /> +</svg> diff --git a/docs/_templates/partials/napari-hub-light-blue.html b/docs/_templates/partials/napari-hub-light-blue.html new file mode 100644 index 000000000..67d49111c --- /dev/null +++ b/docs/_templates/partials/napari-hub-light-blue.html @@ -0,0 +1,96 @@ +<svg + class="footer-icon__light-blue" + width="17" + height="17" + viewBox="0 0 17 17" + fill="none" + xmlns="http://www.w3.org/2000/svg" +> + <g clip-path="url(#clip0_307_186)"> + <circle + cx="8.50002" + cy="8.49935" + r="2.83333" + fill="#D2EFFF" + stroke="#D2EFFF" + stroke-width="1.86421" + /> + <circle cx="8.50004" cy="1.41667" r="1.41667" fill="#D2EFFF" /> + <circle cx="8.50004" cy="15.5846" r="1.41667" fill="#D2EFFF" /> + <path + d="M8.50006 0.943359L8.50006 4.72114" + stroke="#D2EFFF" + stroke-width="1.86421" + stroke-linecap="round" + stroke-linejoin="round" + /> + <path + d="M8.50006 12.2773L8.50006 16.0551" + stroke="#D2EFFF" + stroke-width="1.86421" + stroke-linecap="round" + stroke-linejoin="round" + /> + <circle + cx="2.36574" + cy="4.95803" + r="1.41667" + transform="rotate(-60 2.36574 4.95803)" + fill="#D2EFFF" + /> + <circle + cx="14.6344" + cy="12.0401" + r="1.41667" + transform="rotate(-60 14.6344 12.0401)" + fill="#D2EFFF" + /> + <path + d="M1.95673 4.72266L5.22838 6.61155" + stroke="#D2EFFF" + stroke-width="1.86421" + stroke-linecap="round" + stroke-linejoin="round" + /> + <path + d="M11.7717 10.3887L15.0434 12.2776" + stroke="#D2EFFF" + stroke-width="1.86421" + stroke-linecap="round" + stroke-linejoin="round" + /> + <circle + cx="2.3657" + cy="12.0433" + r="1.41667" + transform="rotate(-120 2.3657 12.0433)" + fill="#D2EFFF" + /> + <circle + cx="14.6345" + cy="4.95933" + r="1.41667" + transform="rotate(-120 14.6345 4.95933)" + fill="#D2EFFF" + /> + <path + d="M1.95673 12.2773L5.22838 10.3885" + stroke="#D2EFFF" + stroke-width="1.86421" + stroke-linecap="round" + stroke-linejoin="round" + /> + <path + d="M11.7717 6.61133L15.0434 4.72244" + stroke="#D2EFFF" + stroke-width="1.86421" + stroke-linecap="round" + stroke-linejoin="round" + /> + </g> + <defs> + <clipPath id="clip0_307_186"> + <rect width="17" height="17" fill="white" /> + </clipPath> + </defs> +</svg> diff --git a/docs/_templates/partials/napari-hub.html b/docs/_templates/partials/napari-hub.html new file mode 100644 index 000000000..18e063444 --- /dev/null +++ b/docs/_templates/partials/napari-hub.html @@ -0,0 +1,90 @@ +<svg + class="footer-icon__regular" + width="487" + height="512" + viewBox="0 0 487 512" + fill="none" + xmlns="http://www.w3.org/2000/svg" +> + <title>Visit napari hub</title> + <circle + cx="243.036" + cy="256" + r="85.3333" + fill="white" + stroke="white" + stroke-width="56.8889" + ></circle> + <circle cx="243.036" cy="42.6667" r="42.6667" fill="white"></circle> + <circle cx="243.036" cy="469.333" r="42.6667" fill="white"></circle> + <path + d="M243.036 28.4444L243.036 142.222" + stroke="white" + stroke-width="56.8889" + stroke-linecap="round" + stroke-linejoin="round" + ></path> + <path + d="M243.036 369.778L243.036 483.556" + stroke="white" + stroke-width="56.8889" + stroke-linecap="round" + stroke-linejoin="round" + ></path> + <circle + cx="58.2838" + cy="149.333" + r="42.6667" + transform="rotate(-60 58.2838 149.333)" + fill="white" + ></circle> + <circle + cx="427.788" + cy="362.667" + r="42.6667" + transform="rotate(-60 427.788 362.667)" + fill="white" + ></circle> + <path + d="M45.967 142.222L144.501 199.111" + stroke="white" + stroke-width="56.8889" + stroke-linecap="round" + stroke-linejoin="round" + ></path> + <path + d="M341.57 312.889L440.105 369.778" + stroke="white" + stroke-width="56.8889" + stroke-linecap="round" + stroke-linejoin="round" + ></path> + <circle + cx="58.2838" + cy="362.667" + r="42.6667" + transform="rotate(-120 58.2838 362.667)" + fill="white" + ></circle> + <circle + cx="427.788" + cy="149.333" + r="42.6667" + transform="rotate(-120 427.788 149.333)" + fill="white" + ></circle> + <path + d="M45.967 369.778L144.501 312.889" + stroke="white" + stroke-width="56.8889" + stroke-linecap="round" + stroke-linejoin="round" + ></path> + <path + d="M341.57 199.111L440.105 142.222" + stroke="white" + stroke-width="56.8889" + stroke-linecap="round" + stroke-linejoin="round" + ></path> +</svg> diff --git a/docs/_templates/partials/napari.html b/docs/_templates/partials/napari.html new file mode 100644 index 000000000..d581265a7 --- /dev/null +++ b/docs/_templates/partials/napari.html @@ -0,0 +1,67 @@ +<svg + width="30" + height="30" + viewBox="0 0 30 30" + fill="none" + xmlns="http://www.w3.org/2000/svg" +> + <path + d="M27.8387 0H2.16127C0.967636 0 0 0.967636 0 2.16127V27.8387C0 29.0324 0.967636 30 2.16127 30H27.8387C29.0324 30 30 29.0324 30 27.8387V2.16127C30 0.967636 29.0324 0 27.8387 0Z" + fill="#26283D" + ></path> + <path + d="M27.7779 1.03139H2.22356C1.56514 1.03139 1.03139 1.56514 1.03139 2.22357V27.7779C1.03139 28.4363 1.56514 28.9701 2.22356 28.9701H27.7779C28.4363 28.9701 28.9701 28.4363 28.9701 27.7779V2.22357C28.9701 1.56514 28.4363 1.03139 27.7779 1.03139Z" + fill="url(#paint0_radial)" + ></path> + <path + d="M17.4655 27.3361C17.099 27.3361 16.7542 27.3201 16.4124 27.2897C16.0096 27.2645 15.61 27.202 15.2187 27.1028C14.4296 26.8759 13.6918 26.4986 13.0459 25.9918C12.1394 25.2971 11.3826 24.4265 10.8209 23.4322C10.5949 23.0338 10.3863 22.4775 9.83293 20.9899C9.71125 20.661 9.61999 20.4104 9.54321 20.2091C9.36359 19.7209 9.3042 19.5558 9.15789 19.2385C8.96705 18.7853 8.73274 18.3516 8.45823 17.9435C8.12277 17.4768 7.73384 17.0509 7.29937 16.6745C7.13279 16.5297 7.05601 16.4616 6.91405 16.3515C6.73732 16.2067 6.49396 16.0183 6.02752 15.6098C5.2395 14.9174 4.94254 14.5958 4.66876 14.2453C4.25597 13.7417 3.92726 13.1748 3.69532 12.5664C3.49242 12.0272 3.37416 11.4599 3.34476 10.8846C3.32978 10.3829 3.38289 9.88145 3.50265 9.39401C3.69467 8.45513 4.01235 7.54642 4.44713 6.69241C4.7481 6.05734 5.13833 5.46858 5.60599 4.94398C6.16001 4.34736 6.8007 3.83754 7.50652 3.43167C7.8302 3.24114 8.16941 3.07832 8.52052 2.94495C9.04221 2.75721 9.59283 2.66258 10.1473 2.66537C10.6436 2.66652 11.1376 2.73375 11.6161 2.86528C12.5756 3.10049 13.433 3.64024 14.0599 4.40366C14.7204 5.22066 14.987 6.2477 15.2028 7.63978C15.2578 8.00193 15.2796 8.18879 15.2984 8.35248C15.31 8.46547 15.323 8.57846 15.352 8.77836C15.5273 9.97199 15.6142 10.5167 15.8141 10.9324C16.1038 11.5408 16.6572 11.8957 17.0208 12.1304C17.9131 12.7098 18.8344 12.788 19.4442 12.8416C19.6847 12.8633 19.9585 12.8749 20.2569 12.8749C20.703 12.8749 21.0739 12.8503 21.2115 12.8402C21.4853 12.8199 21.7141 12.7938 21.9358 12.7677C22.272 12.7223 22.6106 12.6972 22.9498 12.6924C23.3746 12.6921 23.7976 12.7491 24.2071 12.8619C25.0966 13.067 25.8989 13.547 26.5002 14.2337C27.1101 14.958 27.2738 15.6721 27.4795 16.5804C27.6193 17.187 27.707 17.8044 27.7417 18.4259C27.7764 19.1313 27.8242 20.0946 27.4925 21.2303C27.1727 22.2747 26.6228 23.234 25.8831 24.0377C25.1073 24.908 24.1734 25.6233 23.1309 26.1453C22.5393 26.4354 21.9207 26.6669 21.2839 26.8363C20.4006 27.0835 19.4935 27.2365 18.578 27.2926C18.1912 27.3216 17.8175 27.3361 17.4655 27.3361Z" + fill="#26283D" + ></path> + <path + d="M17.4655 26.2265C17.1323 26.2265 16.8194 26.2134 16.5109 26.1859C16.1688 26.1658 15.8293 26.1139 15.4969 26.0309C14.8468 25.8412 14.2394 25.5279 13.7079 25.1082C12.924 24.5046 12.2698 23.7494 11.7842 22.8875C11.6016 22.5645 11.3873 21.9908 10.8701 20.6031C10.7484 20.2786 10.6586 20.0323 10.5804 19.8295C10.4008 19.3385 10.3269 19.1342 10.1618 18.7764C9.94359 18.2639 9.6768 17.7735 9.36504 17.3119C8.98218 16.7792 8.53831 16.293 8.04249 15.8633C7.84694 15.6852 7.74264 15.6026 7.59778 15.4882C7.41961 15.3433 7.19942 15.1738 6.75471 14.7842C6.03042 14.1497 5.76968 13.8672 5.53791 13.5703C5.19569 13.1552 4.92274 12.6875 4.7296 12.1854C4.41134 11.3736 4.36059 10.4814 4.58474 9.63882C4.75558 8.79295 5.04066 7.97424 5.43216 7.20521C5.68904 6.6581 6.02321 6.15074 6.42443 5.69869C6.89941 5.18449 7.44931 4.74497 8.05553 4.39498C8.32534 4.23625 8.6078 4.1001 8.90005 3.98793C9.29948 3.84394 9.72125 3.77185 10.1458 3.77499C10.5448 3.77727 10.9417 3.83182 11.3264 3.93723C12.0597 4.10871 12.7166 4.51577 13.1965 5.09609C13.6311 5.6422 13.8846 6.37663 14.1062 7.80492C14.1584 8.14389 14.1772 8.31048 14.1946 8.47127C14.2076 8.5944 14.2221 8.71753 14.254 8.93481C14.4336 10.1531 14.5321 10.8252 14.8146 11.4104C15.2492 12.2984 15.9734 12.7735 16.4182 13.056C17.5394 13.7803 18.6692 13.8817 19.3443 13.9425C19.6166 13.9672 19.9237 13.9788 20.2554 13.9788C20.7393 13.9788 21.142 13.9527 21.2941 13.9411C21.5838 13.9194 21.8387 13.8904 22.0604 13.8643C22.3529 13.8246 22.6474 13.8019 22.9425 13.7962C23.2691 13.7958 23.5942 13.8401 23.9087 13.9281C24.2796 14.028 25.0502 14.238 25.647 14.9421C26.0816 15.4592 26.2062 16.001 26.393 16.8252C26.5186 17.3689 26.5976 17.9223 26.6292 18.4795C26.661 19.14 26.7016 19.9614 26.422 20.9174C26.1469 21.8088 25.6746 22.6267 25.0401 23.3105C24.3582 24.0719 23.5378 24.6968 22.6224 25.1516C22.0928 25.4117 21.5387 25.6184 20.9681 25.7687C20.1555 25.9947 19.3214 26.1345 18.4795 26.1859C18.1333 26.2192 17.7929 26.2265 17.4655 26.2265Z" + fill="#A59678" + ></path> + <path + d="M9.21149 4.83535C10.0328 4.53405 10.8513 4.74409 11.0946 4.80783C11.6453 4.93454 12.1384 5.24019 12.4969 5.67697C12.8199 6.07533 13.0212 6.69098 13.2211 7.95558C13.3124 8.5437 13.295 8.56978 13.366 9.07823C13.5529 10.3472 13.6644 11.1062 14.0077 11.816C14.548 12.9314 15.4563 13.521 15.9329 13.8267C17.2366 14.6726 18.5403 14.7885 19.2646 14.8537C19.9624 14.9053 20.663 14.9053 21.3607 14.8537C22.4718 14.7711 22.9121 14.6045 23.6712 14.8117C24.0058 14.9015 24.549 15.0579 24.9546 15.536C25.2443 15.8851 25.337 16.2603 25.5109 17.0324C25.625 17.5246 25.6967 18.0257 25.7253 18.5302C25.7571 19.169 25.7904 19.86 25.5543 20.6697C25.3157 21.4363 24.906 22.1387 24.3563 22.7238C23.7531 23.3969 23.027 23.9485 22.2168 24.3491C21.736 24.5834 21.2327 24.7683 20.7146 24.901C19.9602 25.1106 19.1857 25.2397 18.4042 25.2863C17.7992 25.3373 17.1911 25.3373 16.5862 25.2863C16.2975 25.2698 16.0109 25.2267 15.7301 25.1574C15.1926 24.9997 14.6912 24.7384 14.254 24.3882C13.5654 23.8599 12.9908 23.1976 12.5649 22.4413C12.4201 22.1806 12.1811 21.5476 11.7088 20.2801C11.2916 19.1603 11.2323 18.943 10.9845 18.3969C10.7451 17.8345 10.451 17.297 10.1067 16.7919C9.68854 16.2019 9.20233 15.6632 8.65814 15.1869C8.19314 14.7639 8.16417 14.8044 7.35442 14.0961C6.64751 13.4761 6.43168 13.2342 6.25205 13.0053C5.96697 12.6629 5.73947 12.2764 5.57846 11.8609C5.45202 11.5217 5.37825 11.1651 5.35973 10.8035C5.35202 10.4734 5.39004 10.1439 5.47272 9.82425C5.62675 9.05475 5.88556 8.31001 6.24191 7.61082C6.46342 7.13752 6.75173 6.69847 7.09802 6.3071C7.32105 6.06667 7.56328 5.84479 7.82231 5.64366C8.03967 5.47474 8.26863 5.3213 8.50748 5.18446C8.73275 5.05002 8.96813 4.93329 9.21149 4.83535V4.83535Z" + fill="url(#paint1_linear)" + ></path> + <defs> + <radialGradient + id="paint0_radial" + cx="0" + cy="0" + r="1" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(15 15) scale(28.521)" + > + <stop stop-color="#746B7F"></stop> + <stop offset="0.12" stop-color="#615B6F"></stop> + <stop offset="0.26" stop-color="#514C60"></stop> + <stop offset="0.37" stop-color="#4B475B"></stop> + <stop offset="0.44" stop-color="#4F4B5F"></stop> + <stop offset="0.51" stop-color="#5A566C"></stop> + <stop offset="0.59" stop-color="#6D6881"></stop> + <stop offset="0.67" stop-color="#87829E"></stop> + <stop offset="0.74" stop-color="#A9A2C3"></stop> + <stop offset="0.78" stop-color="#BCB5D9"></stop> + </radialGradient> + <linearGradient + id="paint1_linear" + x1="5.54949" + y1="7.79624" + x2="23.9884" + y2="23.7393" + gradientUnits="userSpaceOnUse" + > + <stop offset="0.05" stop-color="#448491"></stop> + <stop offset="0.15" stop-color="#4A7986"></stop> + <stop offset="0.33" stop-color="#506E7B"></stop> + <stop offset="0.52" stop-color="#526A77"></stop> + <stop offset="0.6" stop-color="#56707C"></stop> + <stop offset="0.7" stop-color="#607F8B"></stop> + <stop offset="0.82" stop-color="#7099A3"></stop> + <stop offset="0.94" stop-color="#87BDC4"></stop> + <stop offset="1" stop-color="#94D1D6"></stop> + </linearGradient> + </defs> +</svg> diff --git a/docs/_templates/partials/zulip-light-blue.html b/docs/_templates/partials/zulip-light-blue.html new file mode 100644 index 000000000..e91e7051c --- /dev/null +++ b/docs/_templates/partials/zulip-light-blue.html @@ -0,0 +1,20 @@ +<svg + class="footer-icon__light-blue" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" + xmlns="http://www.w3.org/2000/svg" +> + <g clip-path="url(#clip0_307_219)"> + <path + d="M15.178 2.39267C15.178 3.19867 14.816 3.91467 14.2647 4.34867L8.90866 9.13133C8.80932 9.21667 8.67999 9.07933 8.75199 8.96467L10.716 5.03133C10.7713 4.92133 10.7 4.786 10.5867 4.786H2.96799C1.78799 4.786 0.821991 3.70933 0.821991 2.39267C0.821991 1.07733 1.78799 3.09431e-07 2.96799 3.09431e-07H13.032C14.212 -0.000666357 15.178 1.076 15.178 2.39267V2.39267ZM2.96799 16H13.032C14.212 16 15.178 14.9227 15.178 13.6067C15.178 12.2907 14.212 11.2133 13.032 11.2133H5.41332C5.29999 11.2133 5.22866 11.0787 5.28399 10.9687L7.24799 7.03533C7.31999 6.92067 7.19066 6.78333 7.09132 6.86867L1.73599 11.6507C1.18399 12.084 0.822658 12.8007 0.822658 13.6067C0.822658 14.9227 1.78799 16 2.96799 16V16Z" + fill="#D2EFFF" + /> + </g> + <defs> + <clipPath id="clip0_307_219"> + <rect width="16" height="16" fill="white" /> + </clipPath> + </defs> +</svg> diff --git a/docs/_templates/partials/zulip.html b/docs/_templates/partials/zulip.html new file mode 100644 index 000000000..37e8a5b77 --- /dev/null +++ b/docs/_templates/partials/zulip.html @@ -0,0 +1,21 @@ +<svg + class="footer-icon__regular" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" + xmlns="http://www.w3.org/2000/svg" +> + <title>Visit Zulip chatroom</title> + <g clip-path="url(#clip0)"> + <path + d="M15.178 2.39267C15.178 3.19867 14.816 3.91467 14.2647 4.34867L8.90869 9.13133C8.80935 9.21667 8.68002 9.07933 8.75202 8.96467L10.716 5.03133C10.7714 4.92133 10.7 4.786 10.5867 4.786H2.96802C1.78802 4.786 0.822021 3.70933 0.822021 2.39267C0.822021 1.07733 1.78802 3.09431e-07 2.96802 3.09431e-07H13.032C14.212 -0.000666357 15.178 1.076 15.178 2.39267ZM2.96802 16H13.032C14.212 16 15.178 14.9227 15.178 13.6067C15.178 12.2907 14.212 11.2133 13.032 11.2133H5.41335C5.30002 11.2133 5.22869 11.0787 5.28402 10.9687L7.24802 7.03533C7.32002 6.92067 7.19069 6.78333 7.09135 6.86867L1.73602 11.6507C1.18402 12.084 0.822688 12.8007 0.822688 13.6067C0.822688 14.9227 1.78802 16 2.96802 16Z" + fill="white" + ></path> + </g> + <defs> + <clipPath id="clip0"> + <rect width="16" height="16" fill="white"></rect> + </clipPath> + </defs> +</svg> diff --git a/docs/_templates/search-button-field.html b/docs/_templates/search-button-field.html new file mode 100644 index 000000000..6ca02fb32 --- /dev/null +++ b/docs/_templates/search-button-field.html @@ -0,0 +1,10 @@ +{# Behaves the same as `search-button.html` but looks more like a search field. +# # As this function will only work when JavaScript is enabled, we add it +through JavaScript. #} +<script> + document.write(` + <button class="btn navbar-btn search-button-field search-button__button" title="{{ _('Search') }}" aria-label="{{ _('Search') }}" data-bs-placement="bottom" data-bs-toggle="tooltip"> + <i class="fa-solid fa-magnifying-glass"></i> + </button> + `); +</script> diff --git a/docs/_templates/search-field.html b/docs/_templates/search-field.html new file mode 100644 index 000000000..ab2682b1c --- /dev/null +++ b/docs/_templates/search-field.html @@ -0,0 +1,21 @@ +{# A bootstrap-styled field that will direct to the `search.html` page when +submitted #} +<form + class="bd-search d-flex align-items-center" + action="{{ pathto('search') }}" + method="get" +> + <i class="fa-solid fa-magnifying-glass"></i> + <input + type="search" + class="form-control" + name="q" + id="search-input" + placeholder="{{ theme_search_bar_text }}" + aria-label="{{ theme_search_bar_text }}" + autocomplete="off" + autocorrect="off" + autocapitalize="off" + spellcheck="false" + /> +</form> diff --git a/docs/conf.py b/docs/conf.py index f107e0c8c..7e0a8bb23 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,100 +1,164 @@ # Configuration file for the Sphinx documentation builder. # -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html +# For sphinx config settings available, see: +# See https://www.sphinx-doc.org/en/master/usage/configuration.html +# For theme specific config, see: +# https://pydata-sphinx-theme.readthedocs.io/en/stable/index.html -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - -import re +import logging import os +import re from datetime import datetime from importlib import import_module from importlib.metadata import distribution from pathlib import Path from urllib.parse import urlparse, urlunparse -import logging from jinja2.filters import FILTERS +from packaging.version import parse as parse_version +from pygments.lexers import TOMLLexer +from qtpy.QtWidgets import QApplication +from sphinx_gallery import gen_rst from sphinx_gallery import scrapers from sphinx_gallery.sorting import ExampleTitleSortKey from sphinx.highlighting import lexers from sphinx.util import logging as sphinx_logging -from packaging.version import parse as parse_version -from pygments.lexers import TOMLLexer import napari +from napari.settings import get_settings from napari._version import __version_tuple__ -release = napari.__version__ -if "dev" in release: - version = "dev" -else: - version = release +logger = logging.getLogger(__name__) # -- Project information ----------------------------------------------------- -project = 'napari' -copyright = f'{datetime.now().year}, The napari team' -author = 'The napari team' +project = "napari" +copyright = f"{datetime.now().year}, The napari team" +author = "The napari team" # -- General configuration --------------------------------------------------- -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -autosummary_generate = True -autosummary_imported_members = True -comments_config = {'hypothesis': False, 'utterances': False} - -# execution_allow_errors = False -# execution_excludepatterns = [] -# execution_in_temp = False -# execution_timeout = 30 - +# Add sphinx extensions here, as strings. extensions = [ - "sphinx.ext.napoleon", "sphinx.ext.autodoc", "sphinx.ext.autosummary", "sphinx.ext.intersphinx", - "sphinx_external_toc", - "sphinx_design", - 'myst_nb', - # "sphinx_comments", + "sphinx.ext.napoleon", "sphinx.ext.viewcode", - "sphinx_favicon", + "myst_nb", "sphinx_copybutton", + "sphinx_design", + "sphinx_external_toc", + "sphinx_favicon", "sphinx_gallery.gen_gallery", "sphinx_tags", "sphinxcontrib.mermaid", ] +# Config for sphinx.ext.autosummary + +autosummary_generate = True +autosummary_imported_members = True +autosummary_ignore_module_all = False + +# Config for sphinx_copybutton + +# Specify how to identify the prompt when copying code snippets +copybutton_prompt_text = r">>> |\.\.\. " +copybutton_prompt_is_regexp = True +copybutton_exclude = "style" + +# Config for sphinx_external_toc + external_toc_path = "_toc.yml" external_toc_exclude_missing = False +# Config for sphinx.ext.intersphinx + +intersphinx_mapping = { + "python": ["https://docs.python.org/3", None], + "numpy": ["https://numpy.org/doc/stable/", None], + # napari_plugin_engine is deprecated + "napari_plugin_engine": [ + "https://napari-plugin-engine.readthedocs.io/en/latest/", + "https://napari-plugin-engine.readthedocs.io/en/latest/objects.inv", + ], + "magicgui": [ + "https://pyapp-kit.github.io/magicgui/", + "https://pyapp-kit.github.io/magicgui/objects.inv", + ], + "app-model": [ + "http://app-model.readthedocs.io/en/latest/", + "http://app-model.readthedocs.io/en/latest/objects.inv", + ], + "vispy": [ + "https://vispy.org/", + "https://vispy.org/objects.inv", + ], +} + +# Config for sphinx_tags + tags_create_tags = True tags_output_dir = "_tags" tags_overview_title = "Tags" tags_extension = ["md", "rst"] +# Config for sphinxcontrib.mermaid + mermaid_d3_zoom = True mermaid_version = "11.4.1" mermaid_include_elk = "" -# -- Options for HTML output ------------------------------------------------- +# Config for myst_nb -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'napari_sphinx_theme' +myst_enable_extensions = [ + "colon_fence", + "dollarmath", + "substitution", + "tasklist", + "attrs_inline", + "linkify", +] + +myst_heading_anchors = 4 + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [ + "_build", + "Thumbs.db", + ".DS_Store", + ".jupyter_cache", + "jupyter_execute", + "plugins/_*.md", + "plugins/building_a_plugin/_layer_data_guide.md", + "gallery/index.rst", + "refactor.md", +] + +myst_footnote_transition = False + +nb_output_stderr = "show" + +panels_add_bootstrap_css = False +pygments_style = "solarized-dark" +suppress_warnings = ["myst.header", "etoc.toctree", "config.cache"] + +napoleon_custom_sections = [("Events", "params_style")] +lexers["toml"] = TOMLLexer(startinline=True) + +# -- Config for versions ---------------------------------------------------- + +release = napari.__version__ +if "dev" in release: + version = "dev" +else: + version = release # Define the json_url for our version switcher. json_url = "https://napari.org/dev/_static/version_switcher.json" @@ -104,25 +168,73 @@ else: version_match = release + +def get_supported_python_versions(project_name): + """ + Get the supported Python versions for a given project + based on the classifiers in its distribution metadata. + """ + dist = distribution(project_name) + classifiers = [ + value + for key, value in dist.metadata.items() + if key == "Classifier" and value.startswith("Programming Language :: Python ::") + ] + return [ + parse_version(c.split(" :: ")[-1]) + for c in classifiers + if not c.endswith("Only") + ] + + +napari_supported_python_versions = get_supported_python_versions("napari") + +min_python_version = min(napari_supported_python_versions) +max_python_version = max(napari_supported_python_versions) + +version_string = ".".join(str(x) for x in __version_tuple__[:3]) +# when updating the version below, ensure to also update napari/napari README +python_version = "3.11" +python_version_range = f"{min_python_version}-{max_python_version}" + +myst_substitutions = { + "napari_conda_version": f"`napari={version_string}`", + "napari_version": version_string, + "python_version": python_version, + "python_version_range": python_version_range, + "python_version_code": f"`python={python_version}`", + "conda_create_env": f"```sh\nconda create -y -n napari-env -c conda-forge python={python_version}\nconda activate napari-env\n```", +} + +# -- Options for HTML output ------------------------------------------------- + +html_theme = "pydata_sphinx_theme" + html_theme_options = { "external_links": [ {"name": "napari hub", "url": "https://napari-hub.org"}, {"name": "Island Dispatch", "url": "https://napari.org/island-dispatch"}, {"name": "Community chat", "url": "https://napari.zulipchat.com"}, - {"name": "workshop template", "url": "https://napari.org/napari-workshop-template"}, + { + "name": "workshop template", + "url": "https://napari.org/napari-workshop-template", + }, ], "github_url": "https://github.com/napari/napari", + "navbar_align": "content", "navbar_start": ["navbar-logo", "navbar-project"], + "navbar_center": ["navbar-nav"], "navbar_end": ["version-switcher", "navbar-icon-links"], "switcher": { "json_url": json_url, "version_match": version_match, }, "navbar_persistent": [], + "navigation_with_keys": True, "header_links_before_dropdown": 6, - "secondary_sidebar_items": ["page-toc"], - "pygment_light_style": "napari", - "pygment_dark_style": "napari", + "secondary_sidebar_items": ["page-toc", "sourcelink"], + "pygments_light_style": "napari", + "pygments_dark_style": "napari", "announcement": "", "back_to_top_button": False, "analytics": { @@ -138,21 +250,21 @@ html_sidebars = { "**": ["search-field.html", "sidebar-nav-bs"], - "index": ["search-field.html" , "calendar-template"], + "index": ["search-field.html", "calendar-template"], } html_context = { - # use Light theme only, don't auto switch (default) - "default_mode": "light" + # use Light theme only, don't auto switch (default) + "default_mode": "light" } # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] html_logo = "_static/images/logo.png" -html_sourcelink_suffix = '' -html_title = 'napari' +html_sourcelink_suffix = "" +html_title = "napari" favicons = [ { @@ -177,106 +289,13 @@ ] html_css_files = [ - 'custom.css', -] - -intersphinx_mapping = { - 'python': ['https://docs.python.org/3', None], - 'numpy': ['https://numpy.org/doc/stable/', None], - 'napari_plugin_engine': [ - 'https://napari-plugin-engine.readthedocs.io/en/latest/', - 'https://napari-plugin-engine.readthedocs.io/en/latest/objects.inv', - ], - 'magicgui': [ - 'https://pyapp-kit.github.io/magicgui/', - 'https://pyapp-kit.github.io/magicgui/objects.inv', - ], - 'app-model': [ - 'http://app-model.readthedocs.io/en/latest/', - 'http://app-model.readthedocs.io/en/latest/objects.inv', - ], - 'vispy': [ - 'https://vispy.org/', - 'https://vispy.org/objects.inv', - ], -} - -myst_enable_extensions = [ - 'colon_fence', - 'dollarmath', - 'substitution', - 'tasklist', - 'attrs_inline', - 'linkify', -] - -myst_heading_anchors = 4 - - -def get_supported_python_versions(project_name): - """ - Get the supported Python versions for a given project - based on the classifiers in its distribution metadata. - """ - dist = distribution(project_name) - classifiers = [value for key, value in dist.metadata.items() if key == 'Classifier' and value.startswith('Programming Language :: Python ::')] - return [parse_version(c.split(' :: ')[-1]) for c in classifiers if not c.endswith('Only')] - - -napari_supported_python_versions = get_supported_python_versions('napari') - -min_python_version = min(napari_supported_python_versions) -max_python_version = max(napari_supported_python_versions) - -version_string = '.'.join(str(x) for x in __version_tuple__[:3]) -# when updating the version below, ensure to also update napari/napari README -python_version = '3.11' -python_version_range = f"{min_python_version}-{max_python_version}" - -myst_substitutions = { - "napari_conda_version": f"`napari={version_string}`", - "napari_version": version_string, - "python_version": python_version, - "python_version_range": python_version_range, - "python_version_code": f"`python={python_version}`", - "conda_create_env": f"```sh\nconda create -y -n napari-env -c conda-forge python={python_version}\nconda activate napari-env\n```", -} - -myst_footnote_transition = False - -nb_output_stderr = 'show' - -panels_add_bootstrap_css = False -pygments_style = 'solarized-dark' -suppress_warnings = ['myst.header', 'etoc.toctree', 'config.cache'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [ - '_build', - 'Thumbs.db', - '.DS_Store', - '.jupyter_cache', - 'jupyter_execute', - 'plugins/_*.md', - 'plugins/building_a_plugin/_layer_data_guide.md', - 'gallery/index.rst', + "custom.css", ] -napoleon_custom_sections = [('Events', 'params_style')] -lexers['toml'] = TOMLLexer(startinline=True) - def reset_napari(gallery_conf, fname): - from napari.settings import get_settings - from qtpy.QtWidgets import QApplication - settings = get_settings() - settings.appearance.theme = 'dark' + settings.appearance.theme = "dark" # Disabling `QApplication.exec_` means example scripts can call `exec_` # (scripts work when run normally) without blocking example execution by @@ -291,7 +310,7 @@ def napari_scraper(block, block_vars, gallery_conf): `app.processEvents()` allows Qt events to propagateo and prevents hanging. """ - imgpath_iter = block_vars['image_path_iterator'] + imgpath_iter = block_vars["image_path_iterator"] if app := napari.qt.get_qapp(): app.processEvents() @@ -309,10 +328,10 @@ def napari_scraper(block, block_vars, gallery_conf): napari.Viewer.close_all() app.processEvents() - return scrapers.figure_rst(img_paths, gallery_conf['src_dir']) + return scrapers.figure_rst(img_paths, gallery_conf["src_dir"]) -from sphinx_gallery import gen_rst +# -- Sphinx gallery ---------------------------------------------------- gen_rst.EXAMPLE_HEADER = """ .. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. @@ -338,22 +357,27 @@ def napari_scraper(block, block_vars, gallery_conf): sphinx_gallery_conf = { # path to your example scripts (this value is set in the Makefile) # 'examples_dirs': '../../napari/examples', - 'gallery_dirs': 'gallery', # path to where to save gallery generated output - 'filename_pattern': '/*.py', - 'ignore_pattern': 'README.rst|/*_.py', - 'default_thumb_file': Path(__file__).parent / '_static' / 'images' / 'logo.png', - 'plot_gallery': "'True'", # https://github.com/sphinx-gallery/sphinx-gallery/pull/304/files - 'download_all_examples': False, - 'min_reported_time': 10, - 'only_warn_on_example_error': False, - 'abort_on_example_error': True, - 'image_scrapers': ("matplotlib", napari_scraper,), - 'reset_modules': (reset_napari,), - 'reference_url': {'napari': None}, - 'within_subsection_order': ExampleTitleSortKey, + "gallery_dirs": "gallery", # path to where to save gallery generated output + "filename_pattern": "/*.py", + "ignore_pattern": "README.rst|/*_.py", + "default_thumb_file": Path(__file__).parent / "_static" / "images" / "logo.png", + "plot_gallery": "'True'", # https://github.com/sphinx-gallery/sphinx-gallery/pull/304/files + "download_all_examples": False, + "min_reported_time": 10, + "only_warn_on_example_error": False, + "abort_on_example_error": True, + "image_scrapers": ( + "matplotlib", + napari_scraper, + ), + "reset_modules": (reset_napari,), + "reference_url": {"napari": None}, + "within_subsection_order": ExampleTitleSortKey, } -GOOGLE_CALENDAR_API_KEY = os.environ.get('GOOGLE_CALENDAR_API_KEY', '') +# -- Google calendar integration ---------------------------------------- + +GOOGLE_CALENDAR_API_KEY = os.environ.get("GOOGLE_CALENDAR_API_KEY", "") def add_google_calendar_secrets(app, docname, source): @@ -363,8 +387,8 @@ def add_google_calendar_secrets(app, docname, source): source file. You can process the contents and replace this item to implement source-level transformations. """ - if docname == 'community/meeting_schedule': - source[0] = source[0].replace('{API_KEY}', GOOGLE_CALENDAR_API_KEY) + if docname == "community/meeting_schedule": + source[0] = source[0].replace("{API_KEY}", GOOGLE_CALENDAR_API_KEY) class FilterSphinxWarnings(logging.Filter): @@ -375,8 +399,8 @@ class FilterSphinxWarnings(logging.Filter): The warnings are not useful - they don't result in any missing documentation or rendering issues, so we can safely ignore them. - """ + def __init__(self, app): self.app = app super().__init__() @@ -384,9 +408,7 @@ def __init__(self, app): def filter(self, record: logging.LogRecord) -> bool: msg = record.getMessage() - filter_out = ( - "duplicate object description", - ) + filter_out = ("duplicate object description",) if msg.strip().startswith(filter_out): return False @@ -417,18 +439,20 @@ def setup(app): """ app.registry.source_suffix.pop(".ipynb", None) - app.connect('source-read', add_google_calendar_secrets) - app.connect('linkcheck-process-uri', rewrite_github_anchor) - app.connect('autodoc-process-docstring', qt_docstrings) + app.connect("source-read", add_google_calendar_secrets) + app.connect("linkcheck-process-uri", rewrite_github_anchor) + app.connect("autodoc-process-docstring", qt_docstrings) logger = logging.getLogger("sphinx") warning_handler, *_ = [ - h for h in logger.handlers - if isinstance(h, sphinx_logging.WarningStreamHandler) + h for h in logger.handlers if isinstance(h, sphinx_logging.WarningStreamHandler) ] warning_handler.filters.insert(0, FilterSphinxWarnings(app)) +# -- Attributes for autosummary -------------------------------------- + + def get_attributes(item, obj, modulename): """Filters attributes to be used in autosummary. @@ -447,9 +471,9 @@ def get_attributes(item, obj, modulename): FILTERS["get_attributes"] = get_attributes -autosummary_ignore_module_all = False +# -- Config for linkcheck --------------------------------------------------- -linkcheck_anchors_ignore = [r'^!', r'L\d+-L\d+', r'r\d+', r'issuecomment-\d+'] +linkcheck_anchors_ignore = [r"^!", r"L\d+-L\d+", r"r\d+", r"issuecomment-\d+"] linkcheck_ignore = [ "https://napari.zulipchat.com/", "../_tags", @@ -468,6 +492,8 @@ def get_attributes(item, obj, modulename): r"https://github\.com/napari/napari/releases/download/.*": r"https://objects\.githubusercontent\.com/.*", } +# -- GitHub Anchors for Links ----------------------------------------- + def rewrite_github_anchor(app, uri: str): """Rewrite anchor name of the hyperlink to github.com @@ -486,10 +512,10 @@ def rewrite_github_anchor(app, uri: str): ]: if parsed.fragment.startswith(text): return None - if re.match(r'r\d+', parsed.fragment): + if re.match(r"r\d+", parsed.fragment): return None - prefixed = parsed.fragment.startswith('user-content-') + prefixed = parsed.fragment.startswith("user-content-") if not prefixed: - fragment = f'user-content-{parsed.fragment}' + fragment = f"user-content-{parsed.fragment}" return urlunparse(parsed._replace(fragment=fragment)) return None diff --git a/docs/refactor.md b/docs/refactor.md new file mode 100644 index 000000000..1e4a13dd0 --- /dev/null +++ b/docs/refactor.md @@ -0,0 +1,21 @@ +# Refactor custom theme to pydata theme plus config + +## Motivation + +- Maintaining a separate theme adds more burden. +- Adding more complexity than needed. +- Frustrating to contribute since builds aren't clear to do locally. + +## Done + +- added logging to scripts +- fixed code copy + +## TODO + +- reduce warnings (no warnings when make no plot) +- ~~add scripts from nap theme here maybe~~ +- pygments highlighting +- headless on mac +- fix styling for This Page source +- search wtf keyboard shortcut (where did you go) diff --git a/requirements.txt b/requirements.txt index 515edfd9f..322b3170b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -sphinx<8 # break build docs because of warnings +sphinx<8 # break build docs because of warnings sphinx-autobuild sphinx-tabs sphinx-tags @@ -10,7 +10,7 @@ sphinx-gallery sphinx_autodoc_typehints==1.12.0 sphinxcontrib-mermaid>=1.0.0 myst-nb -napari-sphinx-theme>=0.3.0 +pydata-sphinx-theme matplotlib lxml_html_clean imageio-ffmpeg