mkdocs-material
Advanced tools
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| from __future__ import annotations | ||
| import logging | ||
| from material.utilities.filter import FileFilter, FilterConfig | ||
| from mkdocs.structure.pages import _RelativePathTreeprocessor | ||
| from markdown import Extension, Markdown | ||
| from markdown.treeprocessors import Treeprocessor | ||
| from mkdocs.exceptions import ConfigurationError | ||
| from urllib.parse import urlparse | ||
| from xml.etree.ElementTree import Element | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
| # ----------------------------------------------------------------------------- | ||
| class PreviewProcessor(Treeprocessor): | ||
| """ | ||
| A Markdown treeprocessor to enable instant previews on links. | ||
| Note that this treeprocessor is dependent on the `relpath` treeprocessor | ||
| registered programmatically by MkDocs before rendering a page. | ||
| """ | ||
| def __init__(self, md: Markdown, config: dict): | ||
| """ | ||
| Initialize the treeprocessor. | ||
| Arguments: | ||
| md: The Markdown instance. | ||
| config: The configuration. | ||
| """ | ||
| super().__init__(md) | ||
| self.config = config | ||
| def run(self, root: Element): | ||
| """ | ||
| Run the treeprocessor. | ||
| Arguments: | ||
| root: The root element of the parsed Markdown document. | ||
| """ | ||
| at = self.md.treeprocessors.get_index_for_name("relpath") | ||
| # Hack: Python Markdown has no notion of where it is, i.e., which file | ||
| # is being processed. This seems to be a deliberate design decision, as | ||
| # it is not possible to access the file path of the current page, but | ||
| # it might also be an oversight that is now impossible to fix. However, | ||
| # since this extension is only useful in the context of Material for | ||
| # MkDocs, we can assume that the _RelativePathTreeprocessor is always | ||
| # present, telling us the file path of the current page. If that ever | ||
| # changes, we would need to wrap this extension in a plugin, but for | ||
| # the time being we are sneaky and will probably get away with it. | ||
| processor = self.md.treeprocessors[at] | ||
| if not isinstance(processor, _RelativePathTreeprocessor): | ||
| raise TypeError("Relative path processor not registered") | ||
| # Normalize configurations | ||
| configurations = self.config["configurations"] | ||
| configurations.append({ | ||
| "sources": self.config.get("sources"), | ||
| "targets": self.config.get("targets") | ||
| }) | ||
| # Walk through all configurations - @todo refactor so that we don't | ||
| # iterate multiple times over the same elements | ||
| for configuration in configurations: | ||
| # Skip, if the configuration defines nothing – we could also fix | ||
| # this in the file filter, but we first fix it here and check if | ||
| # it generalizes well enough to other inclusion/exclusion sites, | ||
| # because here, it would hinder the ability to automaticaly | ||
| # include all sources, while excluding specific targets. | ||
| if ( | ||
| not configuration.get("sources") and | ||
| not configuration.get("targets") | ||
| ): | ||
| continue | ||
| # Skip if page should not be considered | ||
| filter = get_filter(configuration, "sources") | ||
| if not filter(processor.file): | ||
| continue | ||
| # Walk through all links and add preview attributes | ||
| filter = get_filter(configuration, "targets") | ||
| for el in root.iter("a"): | ||
| href = el.get("href") | ||
| if not href: | ||
| continue | ||
| # Skip footnotes | ||
| if "footnote-ref" in el.get("class", ""): | ||
| continue | ||
| # Skip external links | ||
| url = urlparse(href) | ||
| if url.scheme or url.netloc: | ||
| continue | ||
| # Add preview attribute to internal links | ||
| for path in processor._possible_target_uris( | ||
| processor.file, url.path, | ||
| processor.config.use_directory_urls | ||
| ): | ||
| target = processor.files.get_file_from_path(path) | ||
| if not target: | ||
| continue | ||
| # Include, if filter matches | ||
| if filter(target): | ||
| el.set("data-preview", "") | ||
| # ----------------------------------------------------------------------------- | ||
| class PreviewExtension(Extension): | ||
| """ | ||
| A Markdown extension to enable instant previews on links. | ||
| This extensions allows to automatically add the `data-preview` attribute to | ||
| internal links matching specific criteria, so Material for MkDocs renders a | ||
| nice preview on hover as part of a tooltip. It is the recommended way to | ||
| add previews to links in a programmatic way. | ||
| """ | ||
| def __init__(self, *args, **kwargs): | ||
| """ | ||
| """ | ||
| self.config = { | ||
| "configurations": [[], "Filter configurations"], | ||
| "sources": [{}, "Link sources"], | ||
| "targets": [{}, "Link targets"] | ||
| } | ||
| super().__init__(*args, **kwargs) | ||
| def extendMarkdown(self, md: Markdown): | ||
| """ | ||
| Register Markdown extension. | ||
| Arguments: | ||
| md: The Markdown instance. | ||
| """ | ||
| md.registerExtension(self) | ||
| # Create and register treeprocessor - we use the same priority as the | ||
| # `relpath` treeprocessor, the latter of which is guaranteed to run | ||
| # after our treeprocessor, so we can check the original Markdown URIs | ||
| # before they are resolved to URLs. | ||
| processor = PreviewProcessor(md, self.getConfigs()) | ||
| md.treeprocessors.register(processor, "preview", 0) | ||
| # ----------------------------------------------------------------------------- | ||
| # Functions | ||
| # ----------------------------------------------------------------------------- | ||
| def get_filter(settings: dict, key: str): | ||
| """ | ||
| Get file filter from settings. | ||
| Arguments: | ||
| settings: The settings. | ||
| key: The key in the settings. | ||
| Returns: | ||
| The file filter. | ||
| """ | ||
| config = FilterConfig() | ||
| config.load_dict(settings.get(key) or {}) | ||
| # Validate filter configuration | ||
| errors, warnings = config.validate() | ||
| for _, w in warnings: | ||
| log.warning( | ||
| f"Error reading filter configuration in '{key}':\n" | ||
| f"{w}" | ||
| ) | ||
| for _, e in errors: | ||
| raise ConfigurationError( | ||
| f"Error reading filter configuration in '{key}':\n" | ||
| f"{e}" | ||
| ) | ||
| # Return file filter | ||
| return FileFilter(config = config) # type: ignore | ||
| def makeExtension(**kwargs): | ||
| """ | ||
| Register Markdown extension. | ||
| Arguments: | ||
| **kwargs: Configuration options. | ||
| Returns: | ||
| The Markdown extension. | ||
| """ | ||
| return PreviewExtension(**kwargs) | ||
| # ----------------------------------------------------------------------------- | ||
| # Data | ||
| # ----------------------------------------------------------------------------- | ||
| # Set up logging | ||
| log = logging.getLogger("mkdocs.material.extensions.preview") |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| import os | ||
| from mkdocs.config.base import Config | ||
| from mkdocs.config.config_options import ListOfItems, Type | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
| # ----------------------------------------------------------------------------- | ||
| # Optimize plugin configuration | ||
| class OptimizeConfig(Config): | ||
| enabled = Type(bool, default = True) | ||
| concurrency = Type(int, default = max(1, os.cpu_count() - 1)) | ||
| # Settings for caching | ||
| cache = Type(bool, default = True) | ||
| cache_dir = Type(str, default = ".cache/plugin/optimize") | ||
| # Settings for optimization | ||
| optimize = Type(bool, default = True) | ||
| optimize_png = Type(bool, default = True) | ||
| optimize_png_speed = Type(int, default = 3) | ||
| optimize_png_strip = Type(bool, default = True) | ||
| optimize_jpg = Type(bool, default = True) | ||
| optimize_jpg_quality = Type(int, default = 60) | ||
| optimize_jpg_progressive = Type(bool, default = True) | ||
| optimize_include = ListOfItems(Type(str), default = []) | ||
| optimize_exclude = ListOfItems(Type(str), default = []) | ||
| # Settings for reporting | ||
| print_gain = Type(bool, default = True) | ||
| print_gain_summary = Type(bool, default = True) |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| from __future__ import annotations | ||
| import functools | ||
| import json | ||
| import logging | ||
| import os | ||
| import subprocess | ||
| import sys | ||
| from fnmatch import fnmatch | ||
| from colorama import Fore, Style | ||
| from concurrent.futures import Future | ||
| from concurrent.futures.thread import ThreadPoolExecutor | ||
| from hashlib import sha1 | ||
| from mkdocs import utils | ||
| from mkdocs.config.defaults import MkDocsConfig | ||
| from mkdocs.exceptions import PluginError | ||
| from mkdocs.plugins import BasePlugin | ||
| from mkdocs.structure.files import File | ||
| from shutil import which | ||
| try: | ||
| from PIL import Image | ||
| except ImportError: | ||
| pass | ||
| from .config import OptimizeConfig | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
| # ----------------------------------------------------------------------------- | ||
| # Optimize plugin | ||
| class OptimizePlugin(BasePlugin[OptimizeConfig]): | ||
| supports_multiple_instances = True | ||
| # Manifest | ||
| manifest: dict[str, str] = {} | ||
| # Initialize plugin | ||
| def __init__(self, *args, **kwargs): | ||
| super().__init__(*args, **kwargs) | ||
| # Initialize incremental builds | ||
| self.is_serve = False | ||
| # Determine whether we're serving the site | ||
| def on_startup(self, *, command, dirty): | ||
| self.is_serve = command == "serve" | ||
| # Initialize thread pool | ||
| self.pool = ThreadPoolExecutor(self.config.concurrency) | ||
| self.pool_jobs: dict[str, Future] = {} | ||
| # Resolve and load manifest | ||
| def on_config(self, config): | ||
| if not self.config.enabled: | ||
| return | ||
| # Resolve cache directory (once) - this is necessary, so the cache is | ||
| # always relative to the configuration file, and thus project, and not | ||
| # relative to the current working directory, or it would not work with | ||
| # the projects plugin. | ||
| path = os.path.abspath(self.config.cache_dir) | ||
| if path != self.config.cache_dir: | ||
| self.config.cache_dir = os.path.join( | ||
| os.path.dirname(config.config_file_path), | ||
| os.path.normpath(self.config.cache_dir) | ||
| ) | ||
| # Ensure cache directory exists | ||
| os.makedirs(self.config.cache_dir, exist_ok = True) | ||
| # Initialize manifest | ||
| self.manifest_file = os.path.join( | ||
| self.config.cache_dir, "manifest.json" | ||
| ) | ||
| # Load manifest if it exists and the cache should be used | ||
| if os.path.isfile(self.manifest_file) and self.config.cache: | ||
| try: | ||
| with open(self.manifest_file) as f: | ||
| self.manifest = json.load(f) | ||
| except: | ||
| pass | ||
| # Initialize optimization pipeline | ||
| def on_env(self, env, *, config, files): | ||
| if not self.config.enabled: | ||
| return | ||
| # Skip if media files should not be optimized | ||
| if not self.config.optimize: | ||
| return | ||
| # Filter all optimizable media files and steal reponsibility from MkDocs | ||
| # by removing them from the files collection. Then, start a concurrent | ||
| # job that checks if an image was already optimized and can be returned | ||
| # from the cache, or optimize it accordingly. | ||
| for file in files.media_files(): | ||
| if self._is_excluded(file): | ||
| continue | ||
| # Spawn concurrent job to optimize the given image and add future | ||
| # to job dictionary, as it returns the file we need to copy later | ||
| path = os.path.join(self.config.cache_dir, file.src_path) | ||
| self.pool_jobs[file.abs_src_path] = self.pool.submit( | ||
| self._optimize_image, file, path, config | ||
| ) | ||
| # Steal responsibility from MkDocs | ||
| files.remove(file) | ||
| # Finish optimization pipeline | ||
| def on_post_build(self, *, config): | ||
| if not self.config.enabled: | ||
| return | ||
| # Skip if media files should not be optimized | ||
| if not self.config.optimize: | ||
| return | ||
| # Reconcile concurrent jobs - we need to wait for all jobs to finish | ||
| # before we can copy the optimized files to the output directory. If an | ||
| # exception occurred in one of the jobs, we raise it here, so the build | ||
| # fails and the author can fix the issue. | ||
| for path, future in self.pool_jobs.items(): | ||
| if future.exception(): | ||
| raise future.exception() | ||
| else: | ||
| file: File = future.result() | ||
| file.copy_file() | ||
| # Save manifest if cache should be used | ||
| if self.config.cache: | ||
| with open(self.manifest_file, "w") as f: | ||
| f.write(json.dumps(self.manifest, indent = 2, sort_keys = True)) | ||
| # Compute and print gains through optimization | ||
| if self.config.print_gain_summary: | ||
| print(Style.NORMAL) | ||
| print(f" Optimizations:") | ||
| # Print summary for file extension | ||
| for seek in [".png", ".jpg"]: | ||
| size = size_opt = 0 | ||
| for path, future in self.pool_jobs.items(): | ||
| file: File = future.result() | ||
| # Skip files that are not of the given type | ||
| _, extension = os.path.splitext(path) | ||
| extension = ".jpg" if extension == ".jpeg" else extension | ||
| if extension != seek: | ||
| continue | ||
| # Compute size before and after optimization | ||
| size += os.path.getsize(path) | ||
| size_opt += os.path.getsize(file.abs_dest_path) | ||
| # Compute absolute and relative gain | ||
| if size and size_opt: | ||
| gain_abs = size - size_opt | ||
| gain_rel = (1 - size_opt / size) * 100 | ||
| # Print summary for files | ||
| print( | ||
| f" *{seek} {Fore.GREEN}{_size(size_opt)}" | ||
| f"{Fore.WHITE}{Style.DIM} ↓ " | ||
| f"{_size(gain_abs)} [{gain_rel:3.1f}%]" | ||
| f"{Style.RESET_ALL}" | ||
| ) | ||
| # Reset all styles | ||
| print(Style.RESET_ALL) | ||
| # Save manifest on shutdown | ||
| def on_shutdown(self): | ||
| if not self.config.enabled: | ||
| return | ||
| # Shutdown thread pool - if we're on Python 3.9 and above, cancel all | ||
| # pending futures that have not yet been scheduled | ||
| if sys.version_info >= (3, 9): | ||
| self.pool.shutdown(cancel_futures = True) | ||
| else: | ||
| self.pool.shutdown() | ||
| # Save manifest if cache should be used | ||
| if self.manifest and self.config.cache: | ||
| with open(self.manifest_file, "w") as f: | ||
| f.write(json.dumps(self.manifest, indent = 2, sort_keys = True)) | ||
| # ------------------------------------------------------------------------- | ||
| # Check if a file can be optimized | ||
| def _is_optimizable(self, file: File): | ||
| # Check if PNG images should be optimized | ||
| if file.url.endswith((".png")): | ||
| return self.config.optimize_png | ||
| # Check if JPG images should be optimized | ||
| if file.url.endswith((".jpg", ".jpeg")): | ||
| return self.config.optimize_jpg | ||
| # File can not be optimized by the plugin | ||
| return False | ||
| # Check if the given file is excluded | ||
| def _is_excluded(self, file: File): | ||
| if not self._is_optimizable(file): | ||
| return True | ||
| # Check if file matches one of the inclusion patterns | ||
| path = file.src_path | ||
| if self.config.optimize_include: | ||
| for pattern in self.config.optimize_include: | ||
| if fnmatch(file.src_uri, pattern): | ||
| return False | ||
| # File is not included | ||
| log.debug(f"Excluding file '{path}' due to inclusion patterns") | ||
| return True | ||
| # Check if file matches one of the exclusion patterns | ||
| for pattern in self.config.optimize_exclude: | ||
| if fnmatch(file.src_uri, pattern): | ||
| log.debug(f"Excluding file '{path}' due to exclusion patterns") | ||
| return True | ||
| # File is not excluded | ||
| return False | ||
| # Optimize image and write to cache | ||
| def _optimize_image(self, file: File, path: str, config: MkDocsConfig): | ||
| with open(file.abs_src_path, "rb") as f: | ||
| data = f.read() | ||
| hash = sha1(data).hexdigest() | ||
| # Check if file hash changed, so we need to optimize again | ||
| prev = self.manifest.get(file.url, "") | ||
| if hash != prev or not os.path.isfile(path): | ||
| os.makedirs(os.path.dirname(path), exist_ok = True) | ||
| # Optimize PNG image using pngquant | ||
| if file.url.endswith((".png")): | ||
| self._optimize_image_png(file, path, config) | ||
| # Optimize JPG image using pillow | ||
| if file.url.endswith((".jpg", ".jpeg")): | ||
| self._optimize_image_jpg(file, path, config) | ||
| # Compute size before and after optimization | ||
| size = len(data) | ||
| size_opt = os.path.getsize(path) | ||
| # Compute absolute and relative gain | ||
| gain_abs = size - size_opt | ||
| gain_rel = (1 - size_opt / size) * 100 | ||
| # Print how much we gained, if we did and desired | ||
| gain = "" | ||
| if gain_abs and self.config.print_gain: | ||
| gain += " ↓ " | ||
| gain += " ".join([_size(gain_abs), f"[{gain_rel:3.1f}%]"]) | ||
| # Print summary for file | ||
| log.info( | ||
| f"Optimized media file: {file.src_uri} " | ||
| f"{Fore.GREEN}{_size(size_opt)}" | ||
| f"{Fore.WHITE}{Style.DIM}{gain}" | ||
| f"{Style.RESET_ALL}" | ||
| ) | ||
| # Update manifest by associating file with hash | ||
| self.manifest[file.url] = hash | ||
| # Compute project root | ||
| root = os.path.dirname(config.config_file_path) | ||
| # Compute source file system path | ||
| file.abs_src_path = path | ||
| file.src_path = os.path.relpath(path, root) | ||
| # Return file to be copied from cache | ||
| return file | ||
| # Optimize PNG image - we first tried to use libimagequant, but encountered | ||
| # the occassional segmentation fault, which means it's probably not a good | ||
| # choice. Instead, we just rely on pngquant which seems much more stable. | ||
| def _optimize_image_png(self, file: File, path: str, config: MkDocsConfig): | ||
| # Check if the required dependencies for optimizing are available, which | ||
| # is, at the absolute minimum, the 'pngquant' binary, and raise an error | ||
| # to the caller, so he can decide what to do with the error. The caller | ||
| # can treat this as a warning or an error to abort the build. | ||
| if not which("pngquant"): | ||
| docs = os.path.relpath(config.docs_dir) | ||
| path = os.path.relpath(file.abs_src_path, docs) | ||
| raise PluginError( | ||
| f"Couldn't optimize image '{path}' in '{docs}': 'pngquant' " | ||
| f"not found. Make sure 'pngquant' is installed and in your path" | ||
| ) | ||
| # Build command line arguments | ||
| args = ["pngquant", | ||
| "--force", "--skip-if-larger", | ||
| "--output", path, | ||
| "--speed", f"{self.config.optimize_png_speed}" | ||
| ] | ||
| # Add flag to remove optional metadata | ||
| if self.config.optimize_png_strip: | ||
| args.append("--strip") | ||
| # Set input file and run, then check if pngquant actually wrote a file, | ||
| # as we instruct it not to if the size of the optimized file is larger. | ||
| # This can happen if files are already compressed and optimized by | ||
| # the author. In that case, just copy the original file. | ||
| subprocess.run([*args, file.abs_src_path]) | ||
| if not os.path.isfile(path): | ||
| utils.copy_file(file.abs_src_path, path) | ||
| # Optimize JPG image | ||
| def _optimize_image_jpg(self, file: File, path: str, config: MkDocsConfig): | ||
| # Check if the required dependencies for optimizing are available, which | ||
| # is, at the absolute minimum, the 'pillow' package, and raise an error | ||
| # to the caller, so he can decide what to do with the error. The caller | ||
| # can treat this as a warning or an error to abort the build. | ||
| if not _supports("Image"): | ||
| docs = os.path.relpath(config.docs_dir) | ||
| path = os.path.relpath(file.abs_src_path, docs) | ||
| raise PluginError( | ||
| f"Couldn't optimize image '{path}' in '{docs}': install " | ||
| f"required dependencies – pip install 'mkdocs-material[imaging]'" | ||
| ) | ||
| # Open and save optimized image | ||
| image = Image.open(file.abs_src_path) | ||
| image.save(path, "jpeg", | ||
| quality = self.config.optimize_jpg_quality, | ||
| progressive = self.config.optimize_jpg_progressive | ||
| ) | ||
| # ----------------------------------------------------------------------------- | ||
| # Helper functions | ||
| # ----------------------------------------------------------------------------- | ||
| # Check for presence of optional imports | ||
| @functools.lru_cache(maxsize = None) | ||
| def _supports(name: str): | ||
| return name in globals() | ||
| # ----------------------------------------------------------------------------- | ||
| # Print human-readable size | ||
| def _size(value): | ||
| for unit in ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB"]: | ||
| if abs(value) < 1000.0: | ||
| return f"{value:3.1f} {unit}" | ||
| value /= 1000.0 | ||
| # ----------------------------------------------------------------------------- | ||
| # Data | ||
| # ----------------------------------------------------------------------------- | ||
| # Set up logging | ||
| log = logging.getLogger("mkdocs.material.optimize") |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| from __future__ import annotations | ||
| import logging | ||
| import multiprocessing | ||
| import os | ||
| import posixpath | ||
| from collections.abc import Callable | ||
| from concurrent.futures import Future, as_completed | ||
| from concurrent.futures.process import ProcessPoolExecutor | ||
| from logging import Logger | ||
| from material.plugins.projects.config import ProjectsConfig | ||
| from material.plugins.projects.structure import Project, ProjectJob | ||
| from mkdocs.commands.build import build | ||
| from mkdocs.config.base import ConfigErrors, ConfigWarnings | ||
| from mkdocs.config.defaults import MkDocsConfig | ||
| from mkdocs.exceptions import Abort | ||
| from mkdocs.livereload import LiveReloadServer | ||
| from urllib.parse import urlparse | ||
| from .log import ( | ||
| get_log_for, | ||
| get_log_formatter, | ||
| get_log_handler, | ||
| get_log_level_for | ||
| ) | ||
| from .watcher import ProjectsWatcher | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
| # ----------------------------------------------------------------------------- | ||
| # Projects builder | ||
| class ProjectsBuilder: | ||
| # Set of projects | ||
| projects: set[Project] = set() | ||
| # Projects watcher | ||
| watcher: ProjectsWatcher | None = None | ||
| # Initialize projects builder - compared to our other concurrent plugins, | ||
| # this plugin is forced to use a process pool in order to guarantee proper | ||
| # isolation, as MkDocs itself is not thread-safe. We also need to recreate | ||
| # the process pool on every build, or CTRL-C is broken | ||
| def __init__(self, config: MkDocsConfig, plugin: ProjectsConfig): | ||
| # Initialize root project | ||
| self.root = Project(config.config_file_path, plugin) | ||
| self.root.config = config | ||
| # Initialize process pool | ||
| self.pool = ProcessPoolExecutor | ||
| self.pool_jobs: dict[str, Future] = {} | ||
| # Build projects | ||
| def build(self, serve: bool = False, dirty: bool = False): | ||
| self.pool = ProcessPoolExecutor( | ||
| self.root.plugin.concurrency, | ||
| mp_context = multiprocessing.get_context("spawn") | ||
| ) | ||
| # Determine projects in topological order and prepare for building | ||
| built: list[str] = [] | ||
| queue = [*self.root.jobs()] | ||
| for job in queue: | ||
| _setup(job.project, self.root, serve) | ||
| if serve: | ||
| self._link(job.project) | ||
| # Schedule projects for building | ||
| for job in reversed(queue): | ||
| if self._schedule(job, serve, dirty): | ||
| queue.remove(job) | ||
| # Build loop - iteratively build more projects while there are still | ||
| # projects to be built, sticking to the topological order. | ||
| while len(built) < len(self.pool_jobs): | ||
| for future in as_completed(self.pool_jobs.values()): | ||
| slug, errors, warnings = future.result() | ||
| if slug in built: | ||
| continue | ||
| # Mark project as built | ||
| built.append(slug) | ||
| # Schedule projects for building | ||
| for job in reversed(queue): | ||
| if self._schedule(job, serve, dirty): | ||
| queue.remove(job) | ||
| # Print errors and warnings | ||
| for project in self.projects: | ||
| if project.slug == slug: | ||
| _print(get_log_for(project), errors, warnings) | ||
| break | ||
| # Shutdown process pool | ||
| self.pool.shutdown() | ||
| if self.watcher: | ||
| # Update watched paths | ||
| for project in self.projects: | ||
| if project.slug not in built: | ||
| self.pool_jobs.pop(project.slug, None) | ||
| self.watcher.unwatch(project) | ||
| else: | ||
| self.watcher.watch(project, self.taint) | ||
| # Taint a project to schedule it for building | ||
| def taint(self, project: Project): | ||
| self.pool_jobs.pop(project.slug, None) | ||
| # Watch and serve projects | ||
| def serve(self, server: LiveReloadServer, is_dirty: bool = False): | ||
| self.watcher = ProjectsWatcher(server) | ||
| self.watcher.watch(self.root, self.taint) | ||
| for project in self.projects: | ||
| self.watcher.watch(project, self.taint) | ||
| # ------------------------------------------------------------------------- | ||
| # Create symlink for project if we're serving the site | ||
| def _link(self, project: Project): | ||
| # Compute path for slug from current project - normalize path, | ||
| # as paths computed from slugs or site URLs use forward slashes | ||
| path = project.path(self.root) | ||
| path = os.path.normpath(path) | ||
| # Create symbolic link, if we haven't already | ||
| path = os.path.join(self.root.config.site_dir, path) | ||
| if not os.path.islink(path): | ||
| # Ensure link target exists | ||
| target = os.path.realpath(os.path.dirname(path)) | ||
| if not os.path.exists(target): | ||
| os.makedirs(target, exist_ok = True) | ||
| # Create symbolic link | ||
| os.makedirs(os.path.dirname(path), exist_ok = True) | ||
| os.symlink(project.config.site_dir, path) | ||
| # Schedule project for building - spawn concurrent job to build the project | ||
| # and add a future to the jobs dictionary to link build results to projects | ||
| def _schedule(self, job: ProjectJob, serve: bool, dirty: bool): | ||
| self.projects.add(job.project) | ||
| # Exit early, if project doesn't need to be re built | ||
| if job.project.slug in self.pool_jobs: | ||
| return True | ||
| # Check if dependencies have been built already, and if so, remove | ||
| # them from the list of dependencies. If a dependency has failed to | ||
| # build, we'll raise an exception, which will be caught by the main | ||
| # process, and the entire build will be aborted. | ||
| for dependency in [*job.dependencies]: | ||
| future = self.pool_jobs[dependency.slug] | ||
| if future.running(): | ||
| continue | ||
| # If the dependency has failed to build, we'll raise an exception | ||
| # to abort the entire build, as we can't build the project itself | ||
| # without the dependency. This will be caught by the main process. | ||
| # Otherwise, we'll remove the dependency from the list. | ||
| if future.exception(): | ||
| raise future.exception() | ||
| elif future.done(): | ||
| job.dependencies.remove(dependency) | ||
| # If all dependencies of the project have been built, we can build | ||
| # the project itself by spawning a concurrent job | ||
| if not job.dependencies: | ||
| self.pool_jobs[job.project.slug] = self.pool.submit( | ||
| _build, job.project, serve, dirty, | ||
| get_log_level_for(job.project) | ||
| ) | ||
| # Return whether the project has been scheduled | ||
| return not job.dependencies | ||
| # ----------------------------------------------------------------------------- | ||
| # Helper functions | ||
| # ----------------------------------------------------------------------------- | ||
| # Setup project by preparing it for building | ||
| def _setup(project: Project, root: Project, serve: bool): | ||
| assert project.slug != "." | ||
| # Retrieve configuration of top-level project and transform project | ||
| transform = root.plugin.projects_config_transform | ||
| if isinstance(transform, Callable): | ||
| transform(project, root) | ||
| # If the top-level project defines a site URL, we need to make sure that the | ||
| # site URL of the project is set as well, setting it to the path we derive | ||
| # from the slug. This allows to define the URL independent of the entire | ||
| # project's directory structure. If the top-level project doesn't define a | ||
| # site URL, it might be the case that the author is building a consolidated | ||
| # project of several nested projects that are independent, but which should | ||
| # be bundled together for distribution. As this is a case that is quite | ||
| # common, we're not raising a warning or error. | ||
| path = project.path(root) | ||
| if root.config.site_url: | ||
| # If the project doesn't have a site URL, compute it from the site URL | ||
| # of the top-level project and the path derived from the slug | ||
| if not project.config.site_url: | ||
| project.config.site_url = posixpath.join( | ||
| root.config.site_url, | ||
| path | ||
| ) | ||
| # If we're serving the site, replace the project's host name with the | ||
| # dev server address, so we can serve nested projects as well | ||
| if serve: | ||
| url = urlparse(project.config.site_url) | ||
| url = url._replace( | ||
| scheme = "http", | ||
| netloc = str(root.config.dev_addr) | ||
| ) | ||
| # Update site URL with dev server address | ||
| project.config.site_url = url.geturl() | ||
| # If we're building the site, the project's output must be written to the | ||
| # site directory of the top-level project, so we can serve it from there | ||
| if not serve: | ||
| project.config.site_dir = os.path.join( | ||
| root.config.site_dir, | ||
| os.path.normpath(path) | ||
| ) | ||
| # If we're serving the site, we must fall back to symbolic links, as MkDocs | ||
| # will empty the entire site directory every time it performs a build | ||
| else: | ||
| project.config.site_dir = os.path.join( | ||
| os.path.dirname(project.config.config_file_path), | ||
| project.config.site_dir | ||
| ) | ||
| # Build project - note that regardless of whether MkDocs was started in build | ||
| # or serve mode, projects must always be built, as they're served by the root | ||
| def _build(project: Project, serve: bool, dirty: bool, level = logging.WARN): | ||
| config = project.config | ||
| # Change working directory to project root - this is necessary, or relative | ||
| # paths used in extensions and plugins will be resolved incorrectly | ||
| os.chdir(os.path.dirname(config.config_file_path)) | ||
| # Validate configuration | ||
| errors, warnings = config.validate() | ||
| if not errors: | ||
| # Retrieve and configure MkDocs' logger | ||
| log = logging.getLogger("mkdocs") | ||
| log.setLevel(level) | ||
| # Hack: there seems to be an inconsistency between operating systems, | ||
| # and it's yet unclear where this is coming from - on macOS, the MkDocs | ||
| # default logger has no designated handler registered, but on Linux it | ||
| # does. If there's no handler, we need to create one. If there is, we | ||
| # must only set the formatter, as otherwise we'll end up with the same | ||
| # message printed on two log handlers - see https://t.ly/q7UEq | ||
| handler = get_log_handler(project) | ||
| if not log.hasHandlers(): | ||
| log.addHandler(handler) | ||
| else: | ||
| for handler in log.handlers: | ||
| handler.setFormatter(get_log_formatter(project)) | ||
| # Build project and dispatch startup and shutdown plugin events - note | ||
| # that we must pass the correct command to the event handler, but run | ||
| # the build command anyway, because otherwise some plugins will not | ||
| # run in serve mode. | ||
| command = "serve" if serve else "build" | ||
| config.plugins.run_event("startup", command = command, dirty = dirty) | ||
| try: | ||
| build(config, dirty = dirty) | ||
| finally: | ||
| config.plugins.run_event("shutdown") | ||
| log.removeHandler(handler) | ||
| # Return slug, errors and warnings | ||
| return project.slug, errors, warnings | ||
| # Print errors and warnings resulting from building a project | ||
| def _print(log: Logger, errors: ConfigErrors, warnings: ConfigWarnings): | ||
| # Print warnings | ||
| for value, message in warnings: | ||
| log.warning(f"Config value '{value}': {message}") | ||
| # Print errors | ||
| for value, message in errors: | ||
| log.error(f"Config value '{value}': {message}") | ||
| # Abort if there were errors | ||
| if errors: | ||
| raise Abort(f"Aborted with {len(errors)} configuration errors") |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| from __future__ import annotations | ||
| import logging | ||
| from click import style | ||
| from logging import Filter | ||
| from material.plugins.projects.structure import Project | ||
| from mkdocs.__main__ import ColorFormatter | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
| # ----------------------------------------------------------------------------- | ||
| # Dirty build warning filter | ||
| class ProjectsFilter(Filter): | ||
| # Filter log messages | ||
| def filter(self, record): | ||
| message = record.getMessage() | ||
| return not message.startswith("A 'dirty' build") | ||
| # ----------------------------------------------------------------------------- | ||
| # Functions | ||
| # ----------------------------------------------------------------------------- | ||
| # Retrieve logger for project | ||
| def get_log_for(project: Project): | ||
| log = logging.getLogger("".join(["mkdocs.material.projects", project.slug])) | ||
| # Ensure logger does not propagate messags to parent logger, or messages | ||
| # will be printed multiple times, and attach handler with color formatter | ||
| log.propagate = False | ||
| if not log.hasHandlers(): | ||
| log.addHandler(get_log_handler(project)) | ||
| log.setLevel(get_log_level_for(project)) | ||
| # Return logger | ||
| return log | ||
| # Retrieve log level for project | ||
| def get_log_level_for(project: Project): | ||
| level = logging.INFO | ||
| # Determine log level as set in MkDocs - if the build is started with the | ||
| # `--quiet` flag, the log level is set to `ERROR` to suppress all messages, | ||
| # except for errors. If it's started with `--verbose`, MkDocs sets the log | ||
| # level to `DEBUG`, the most verbose of all log levels. | ||
| log = logging.getLogger("mkdocs") | ||
| for handler in log.handlers: | ||
| level = handler.level | ||
| break | ||
| # Determine if MkDocs was invoked with the `--quiet` flag and the log level | ||
| # as configured in the plugin configuration. When `--quiet` is set, or the | ||
| # projects plugin configuration disables logging, ignore the configured log | ||
| # level and set it to `ERROR` to suppress all messages. | ||
| quiet = level == logging.ERROR | ||
| level = project.plugin.log_level.upper() | ||
| if quiet or not project.plugin.log: | ||
| level = logging.ERROR | ||
| # Retun log level | ||
| return level | ||
| # ----------------------------------------------------------------------------- | ||
| # Retrieve log handler for project | ||
| def get_log_handler(project: Project): | ||
| handler = logging.StreamHandler() | ||
| handler.setFormatter(get_log_formatter(project)) | ||
| # Add filter to suppress dirty build warning, or we'll get as many of those | ||
| # as projects are built - one warning is surely enough, KTHXBYE | ||
| handler.addFilter(ProjectsFilter()) | ||
| return handler | ||
| # Retrieve log formatter for project | ||
| def get_log_formatter(project: Project): | ||
| prefix = style(f"project://{project.slug}", underline = True) | ||
| return ColorFormatter(f"[{prefix}] %(message)s") |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| from __future__ import annotations | ||
| import os | ||
| from collections.abc import Callable | ||
| from material.plugins.projects.structure import Project | ||
| from mkdocs.livereload import LiveReloadServer | ||
| from .handler import ProjectChanged, ProjectAddedOrRemoved | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
| # ----------------------------------------------------------------------------- | ||
| # Projects watcher | ||
| class ProjectsWatcher: | ||
| # Set of watched paths | ||
| watched: set[str] = set() | ||
| # Initialize projects watcher | ||
| def __init__(self, server: LiveReloadServer): | ||
| self.server = server | ||
| # Watch project and invoke function on change | ||
| def watch(self, project: Project, fn: Callable): | ||
| self._on_project_changed(project, fn) | ||
| self._on_project_added_or_removed(project, fn) | ||
| # Stop watching project | ||
| def unwatch(self, project: Project): | ||
| # Traverse all watched paths | ||
| root = os.path.dirname(project.config.config_file_path) | ||
| for path in [*self.watched]: | ||
| # Remove path from watched paths | ||
| if path.startswith(root): | ||
| self.server.unwatch(path) | ||
| self.watched.remove(path) | ||
| # ------------------------------------------------------------------------- | ||
| # Register event handler for project changes | ||
| def _on_project_changed(self, project: Project, fn: Callable): | ||
| # Resolve project root and docs directory | ||
| root = os.path.dirname(project.config.config_file_path) | ||
| docs = os.path.join(root, project.config.docs_dir) | ||
| # Collect all paths to watch | ||
| paths = set([docs, project.config.config_file_path]) | ||
| for path in project.config.watch: | ||
| paths.add(os.path.join(root, path)) | ||
| # Register event handler for unwatched paths | ||
| handler = ProjectChanged(project, fn) | ||
| for path in paths - self.watched: | ||
| self.server.watch(path) | ||
| # Add path and its descendents to watched paths | ||
| self.server.observer.schedule(handler, path, recursive = True) | ||
| self.watched.add(path) | ||
| # Register event handler for project additions and removals | ||
| def _on_project_added_or_removed(self, project: Project, fn: Callable): | ||
| # Resolve project root and path to projects directory | ||
| root = os.path.dirname(project.config.config_file_path) | ||
| path = os.path.join(root, project.plugin.projects_dir) | ||
| # Register event handler for unwatched paths | ||
| handler = ProjectAddedOrRemoved(project, fn) | ||
| if path not in self.watched and os.path.isdir(path): | ||
| # Add path to watched paths | ||
| self.server.observer.schedule(handler, path) | ||
| self.watched.add(path) |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| from __future__ import annotations | ||
| import logging | ||
| import os | ||
| from collections.abc import Callable | ||
| from material.plugins.projects.structure import Project | ||
| from watchdog.events import FileSystemEvent, FileSystemEventHandler | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
| # ----------------------------------------------------------------------------- | ||
| # Project changed | ||
| class ProjectChanged(FileSystemEventHandler): | ||
| # Initialize event handler | ||
| def __init__(self, project: Project, handler: Callable): | ||
| self.project = project | ||
| self.handler = handler | ||
| # Handle file event | ||
| def on_any_event(self, event: FileSystemEvent): | ||
| self._handle(event) | ||
| # ------------------------------------------------------------------------- | ||
| # Invoke file event handler | ||
| def _handle(self, event: FileSystemEvent): | ||
| config = self.project.config | ||
| # Resolve path to docs directory | ||
| base = os.path.dirname(config.config_file_path) | ||
| docs = os.path.join(base, config.docs_dir) | ||
| # Resolve project root and path to changed file | ||
| root = os.path.relpath(base) | ||
| path = os.path.relpath(event.src_path, root) | ||
| # Check if mkdocs.yml or docs directory was deleted | ||
| if event.src_path in [docs, config.config_file_path]: | ||
| if event.event_type == "deleted": | ||
| return | ||
| # Invoke handler and print message that we're scheduling a build | ||
| log.info(f"Schedule build due to '{path}' in '{root}'") | ||
| self.handler(self.project) | ||
| # ----------------------------------------------------------------------------- | ||
| # Project added or removed | ||
| class ProjectAddedOrRemoved(FileSystemEventHandler): | ||
| # Initialize event handler | ||
| def __init__(self, project: Project, handler: Callable): | ||
| self.project = project | ||
| self.handler = handler | ||
| # Handle file creation event | ||
| def on_created(self, event: FileSystemEvent): | ||
| self._handle(event) | ||
| # Handle file deletion event | ||
| def on_deleted(self, event: FileSystemEvent): | ||
| self._handle(event) | ||
| # ------------------------------------------------------------------------ | ||
| # Invoke file event handler | ||
| def _handle(self, event: FileSystemEvent): | ||
| config = self.project.config | ||
| # Touch mkdocs.yml to trigger rebuild | ||
| if os.path.isfile(config.config_file_path): | ||
| os.utime(config.config_file_path, None) | ||
| # Invoke handler | ||
| self.handler(self.project) | ||
| # ----------------------------------------------------------------------------- | ||
| # Data | ||
| # ----------------------------------------------------------------------------- | ||
| # Set up logging | ||
| log = logging.getLogger("mkdocs") |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| import os | ||
| from collections.abc import Callable | ||
| from mkdocs.config.config_options import Choice, Optional, Type | ||
| from mkdocs.config.base import Config | ||
| # ----------------------------------------------------------------------------- | ||
| # Options | ||
| # ----------------------------------------------------------------------------- | ||
| # Options for log level | ||
| LogLevel = ( | ||
| "error", | ||
| "warn", | ||
| "info", | ||
| "debug" | ||
| ) | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
| # ----------------------------------------------------------------------------- | ||
| # Projects plugin configuration | ||
| class ProjectsConfig(Config): | ||
| enabled = Type(bool, default = True) | ||
| concurrency = Type(int, default = max(1, os.cpu_count() - 1)) | ||
| # Settings for caching | ||
| cache = Type(bool, default = True) | ||
| cache_dir = Type(str, default = ".cache/plugin/projects") | ||
| # Settings for logging | ||
| log = Type(bool, default = True) | ||
| log_level = Choice(LogLevel, default = "info") | ||
| # Settings for projects | ||
| projects = Type(bool, default = True) | ||
| projects_dir = Type(str, default = "projects") | ||
| projects_config_files = Type(str, default = "*/mkdocs.yml") | ||
| projects_config_transform = Optional(Type(Callable)) | ||
| projects_root_dir = Optional(Type(str)) | ||
| # Settings for hoisting | ||
| hoisting = Type(bool, default = True) |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| from __future__ import annotations | ||
| import json | ||
| import os | ||
| import posixpath | ||
| from jinja2 import pass_context | ||
| from jinja2.runtime import Context | ||
| from mkdocs.config.defaults import MkDocsConfig | ||
| from mkdocs.exceptions import PluginError | ||
| from mkdocs import utils | ||
| from mkdocs.plugins import BasePlugin, event_priority | ||
| from mkdocs.structure import StructureItem | ||
| from mkdocs.structure.files import Files | ||
| from mkdocs.structure.nav import Link, Section | ||
| from mkdocs.utils import get_theme_dir | ||
| from urllib.parse import ParseResult as URL, urlparse | ||
| from .builder import ProjectsBuilder | ||
| from .config import ProjectsConfig | ||
| from .structure import Project, ProjectLink | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
| # ----------------------------------------------------------------------------- | ||
| # Projects plugin | ||
| class ProjectsPlugin(BasePlugin[ProjectsConfig]): | ||
| # Projects builder | ||
| builder: ProjectsBuilder = None | ||
| # Initialize plugin | ||
| def __init__(self, *args, **kwargs): | ||
| super().__init__(*args, **kwargs) | ||
| # Initialize incremental builds | ||
| self.is_serve = False | ||
| self.is_dirty = False | ||
| # Hack: Since we're building in topological order, we cannot let MkDocs | ||
| # clean the directory, because it means that nested projects are always | ||
| # deleted before a project is built. We also don't need to restore this | ||
| # functionality, because it's only used once in the process. | ||
| utils.clean_directory = lambda _: _ | ||
| # Determine whether we're serving the site | ||
| def on_startup(self, *, command, dirty): | ||
| self.is_serve = command == "serve" | ||
| self.is_dirty = dirty | ||
| # Resolve projects – compared to our other concurrent plugins, this plugin | ||
| # is forced to use a process pool in order to guarantee proper isolation, as | ||
| # MkDocs itself is not thread-safe. Additionally, all project configurations | ||
| # are resolved and written to the cache (if enabled), as it's sufficient to | ||
| # resolve them once on the top-level before projects are built. We might | ||
| # need adjacent project configurations for interlinking projects. | ||
| def on_config(self, config): | ||
| if not self.config.enabled: | ||
| return | ||
| # Skip if projects should not be built - we can only exit here if we're | ||
| # at the top-level, but not when building a nested project | ||
| root = self.config.projects_root_dir is None | ||
| if root and not self.config.projects: | ||
| return | ||
| # Set projects root directory to the top-level project | ||
| if not self.config.projects_root_dir: | ||
| self.config.projects_root_dir = os.path.dirname( | ||
| config.config_file_path | ||
| ) | ||
| # Initialize manifest | ||
| self.manifest: dict[str, str] = {} | ||
| self.manifest_file = os.path.join( | ||
| self.config.projects_root_dir, | ||
| self.config.cache_dir, | ||
| "manifest.json" | ||
| ) | ||
| # Load manifest if it exists and the cache should be used | ||
| if os.path.isfile(self.manifest_file): | ||
| try: | ||
| with open(self.manifest_file) as f: | ||
| self.manifest = json.load(f) | ||
| except: | ||
| pass | ||
| # Building the top-level project, we must resolve and load all project | ||
| # configurations, as we need all information upfront to build them in | ||
| # the correct order, and to resolve links between projects. Furthermore, | ||
| # the author might influence a project's path by setting the site URL. | ||
| if root: | ||
| if not self.builder: | ||
| self.builder = ProjectsBuilder(config, self.config) | ||
| # @todo: detach project resolution from build | ||
| self.manifest = { ".": os.path.relpath(config.config_file_path) } | ||
| for job in self.builder.root.jobs(): | ||
| path = os.path.relpath(job.project.config.config_file_path) | ||
| self.manifest[job.project.slug] = path | ||
| # Save manifest, a we need it in nested projects | ||
| os.makedirs(os.path.dirname(self.manifest_file), exist_ok = True) | ||
| with open(self.manifest_file, "w") as f: | ||
| f.write(json.dumps(self.manifest, indent = 2, sort_keys = True)) | ||
| # Schedule projects for building - the general case is that all projects | ||
| # can be considered independent of each other, so we build them in parallel | ||
| def on_pre_build(self, config): | ||
| if not self.config.enabled: | ||
| return | ||
| # Skip if projects should not be built or we're not at the top-level | ||
| if not self.config.projects or not self.builder: | ||
| return | ||
| # Build projects | ||
| self.builder.build(self.is_serve, self.is_dirty) | ||
| # Patch environment to allow for hoisting of media files provided by the | ||
| # theme itself, which will also work for other themes, not only this one | ||
| def on_env(self, env, *, config, files): | ||
| if not self.config.enabled: | ||
| return | ||
| # Skip if projects should not be built or we're at the top-level | ||
| if not self.config.projects or self.builder: | ||
| return | ||
| # If hoisting is enabled and we're building a project, remove all media | ||
| # files that are provided by the theme and hoist them to the top | ||
| if self.config.hoisting: | ||
| theme = get_theme_dir(config.theme.name) | ||
| hoist = Files([]) | ||
| # Retrieve top-level project and check if the current project uses | ||
| # the same theme as the top-level project - if not, don't hoist | ||
| root = Project("mkdocs.yml", self.config) | ||
| if config.theme.name != root.config.theme["name"]: | ||
| return | ||
| # Remove all media files that are provided by the theme | ||
| for file in files.media_files(): | ||
| if file.abs_src_path.startswith(theme): | ||
| files.remove(file) | ||
| hoist.append(file) | ||
| # Resolve source and target project | ||
| source: Project | None = None | ||
| target: Project | None = None | ||
| for ref, file in self.manifest.items(): | ||
| base = os.path.join(self.config.projects_root_dir, file) | ||
| if file == os.path.relpath( | ||
| config.config_file_path, self.config.projects_root_dir | ||
| ): | ||
| source = Project(base, self.config, ref) | ||
| if "." == ref: | ||
| target = Project(base, self.config, ref) | ||
| # Compute path for slug from source and target project | ||
| path = target.path(source) | ||
| # Fetch URL template filter from environment - the filter might | ||
| # be overridden by other plugins, so we must retrieve and wrap it | ||
| url_filter = env.filters["url"] | ||
| # Patch URL template filter to add support for correctly resolving | ||
| # media files that were hoisted to the top-level project | ||
| @pass_context | ||
| def url_filter_with_hoisting(context: Context, url: str | None): | ||
| if url and hoist.get_file_from_path(url): | ||
| return posixpath.join(path, url_filter(context, url)) | ||
| else: | ||
| return url_filter(context, url) | ||
| # Register custom template filters | ||
| env.filters["url"] = url_filter_with_hoisting | ||
| # Adjust project navigation in page (run latest) - as always, allow | ||
| # other plugins to alter the navigation before we process it here | ||
| @event_priority(-100) | ||
| def on_page_context(self, context, *, page, config, nav): | ||
| if not self.config.enabled: | ||
| return | ||
| # Skip if projects should not be built | ||
| if not self.config.projects: | ||
| return | ||
| # Replace project URLs in navigation | ||
| self._replace(nav.items, config) | ||
| # Adjust project navigation in template (run latest) - as always, allow | ||
| # other plugins to alter the navigation before we process it here | ||
| @event_priority(-100) | ||
| def on_template_context(self, context, *, template_name, config): | ||
| if not self.config.enabled: | ||
| return | ||
| # Skip if projects should not be built | ||
| if not self.config.projects: | ||
| return | ||
| # Replace project URLs in navigation | ||
| self._replace(context["nav"].items, config) | ||
| # Serve projects | ||
| def on_serve(self, server, *, config, builder): | ||
| if self.config.enabled: | ||
| self.builder.serve(server, self.is_dirty) | ||
| # ------------------------------------------------------------------------- | ||
| # Replace project links in the given list of navigation items | ||
| def _replace(self, items: list[StructureItem], config: MkDocsConfig): | ||
| for index, item in enumerate(items): | ||
| # Handle section | ||
| if isinstance(item, Section): | ||
| self._replace(item.children, config) | ||
| # Handle link | ||
| if isinstance(item, Link): | ||
| url = urlparse(item.url) | ||
| if url.scheme == "project": | ||
| project, url = self._resolve_project_url(url, config) | ||
| # Append file name if directory URLs are disabled | ||
| if not project.config.use_directory_urls: | ||
| url += "index.html" | ||
| # Replace link with project link | ||
| items[index] = ProjectLink( | ||
| item.title or project.config.site_name, | ||
| url | ||
| ) | ||
| # Resolve project URL and slug | ||
| def _resolve_project_url(self, url: URL, config: MkDocsConfig): | ||
| # Abort if the project URL contains a path, as we first need to collect | ||
| # use cases for when, how and whether we need and want to support this | ||
| if url.path != "": | ||
| raise PluginError( | ||
| f"Couldn't resolve project URL: paths currently not supported\n" | ||
| f"Please only use 'project://{url.hostname}'" | ||
| ) | ||
| # Compute slug from host name and convert to dot notation | ||
| slug = url.hostname | ||
| slug = slug if slug.startswith(".") else f".{slug}" | ||
| # Resolve source and target project | ||
| source: Project | None = None | ||
| target: Project | None = None | ||
| for ref, file in self.manifest.items(): | ||
| base = os.path.join(self.config.projects_root_dir, file) | ||
| if file == os.path.relpath( | ||
| config.config_file_path, self.config.projects_root_dir | ||
| ): | ||
| source = Project(base, self.config, ref) | ||
| if slug == ref: | ||
| target = Project(base, self.config, ref) | ||
| # Abort if slug doesn't match a known project | ||
| if not target: | ||
| raise PluginError(f"Couldn't find project '{slug}'") | ||
| # Return project slug and path | ||
| return target, target.path(source) |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| from __future__ import annotations | ||
| import os | ||
| import posixpath | ||
| import re | ||
| from copy import deepcopy | ||
| from glob import iglob | ||
| from material.plugins.projects.config import ProjectsConfig | ||
| from mkdocs.structure.nav import Link | ||
| from mkdocs.config.defaults import MkDocsConfig | ||
| from mkdocs.config.config_options import Plugins | ||
| from urllib.parse import urlparse | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
| # ----------------------------------------------------------------------------- | ||
| # Project | ||
| class Project: | ||
| # Initialize project - note that the configuration of the projects plugin | ||
| # of the enclosing project is necessary to resolve nested projects | ||
| def __init__(self, file: str, plugin: ProjectsConfig, slug = "."): | ||
| self.config, self.plugin = self._resolve(file, plugin) | ||
| # The slug should not be changed after initialization, as it's used for | ||
| # correct resolution of projects and nested projects | ||
| self.slug = slug | ||
| # Find and yield nested projects of the current project - the project's | ||
| # slug is prepended to the computed slug for a simple resolution of nested | ||
| # projects, allowing authors to use the project:// protocol for linking to | ||
| # projects from the top-level project or nested and adjacent projects | ||
| def __iter__(self): | ||
| seen: list[str] = [] | ||
| # Compute project root and base directory | ||
| root = os.path.dirname(self.config.config_file_path) | ||
| base = os.path.join(root, self.plugin.projects_dir) | ||
| # Find and yield all projects - note that we need to filter for nested | ||
| # projects at this point, as we're only interested in the projects on | ||
| # the next level, not in projects inside projects as they are resolved | ||
| # recursively to preserve topological ordering. This is also why we must | ||
| # sort the list of projects by path, ordering shorted paths first which | ||
| # ensures that nested projects are resolved before their parents. | ||
| glob = os.path.join(base, self.plugin.projects_config_files) | ||
| glob = iglob(os.path.normpath(glob), recursive = True) | ||
| for file in sorted(glob, key = os.path.dirname): | ||
| path = os.path.join(os.path.dirname(file), "") | ||
| if any(path.startswith(_) for _ in seen): | ||
| continue | ||
| else: | ||
| seen.append(path) | ||
| # Extract the first level of the project's directory relative to | ||
| # the projects directory as the computed slug of the project. This | ||
| # allows authors to build projects whose mkdocs.yml files are not | ||
| # located at the project root, e.g., when using git submodules. | ||
| slug = os.path.relpath(file, base) | ||
| slug, *_ = slug.split(os.path.sep) | ||
| # Normalize slug to an internal dot notation which we convert to | ||
| # file system or URL paths when necessary. Each slug starts with | ||
| # a dot to denote that it is resolved from the top-level project, | ||
| # which also allows for resolving slugs in nested projects. | ||
| root = self.slug.rstrip(".") | ||
| slug = f"{root}.{slug}" | ||
| # Create and yield project | ||
| yield Project(file, self.plugin, slug) | ||
| # Compute project hash | ||
| def __hash__(self): | ||
| return hash(self.slug) | ||
| # Find and yield all nested projects (excluding this project) in reverse | ||
| # topological order, by performing a post-order traversal on the tree of | ||
| # projects. This function returns project jobs, which are projects with | ||
| # their immediate dependencies, to build them in the correct order. | ||
| def jobs(self): | ||
| stack = [*self] | ||
| while stack: | ||
| # Pop project from stack and get its dependencies | ||
| project = stack.pop() | ||
| dependencies = [*project] | ||
| # Add project dependencies to stack and yield job | ||
| stack.extend(dependencies) | ||
| yield ProjectJob(project, dependencies) | ||
| # Compute relative path between two projects | ||
| def path(self, that: Project): | ||
| # If both, the top-level and the current project have a site URL set, | ||
| # compute slug from the common path of both site URLs | ||
| if self.config.site_url and that.config.site_url: | ||
| source = self._path_from_config(that.config) | ||
| target = self._path_from_config(self.config) | ||
| # Edge case: the author has set a site URL that does not include a | ||
| # path, so the path of the project is equal to the top-level path. | ||
| # In this case, we need to fall back to the path computed from the | ||
| # slug - see https://t.ly/5vqMr | ||
| if target == source: | ||
| target = self._path_from_slug(self.slug) | ||
| # Otherwise, always compute the path from the slugs of both projects, | ||
| # as we want to support consolidation of unrelated projects | ||
| else: | ||
| source = self._path_from_slug(that.slug) | ||
| target = self._path_from_slug(self.slug) | ||
| # Compute path between projects, and add trailing slash | ||
| path = posixpath.relpath(target, source) | ||
| return posixpath.join(path, "") | ||
| # ------------------------------------------------------------------------- | ||
| # Resolve project and plugin configuration | ||
| def _resolve(self, file: str, plugin: ProjectsConfig): | ||
| config = self._resolve_config(file) | ||
| plugin = self._resolve_plugin(config, plugin) | ||
| # Return project and plugin configuration | ||
| return config, plugin | ||
| # Resolve project configuration | ||
| def _resolve_config(self, file: str): | ||
| with open(file, encoding = "utf-8-sig") as f: | ||
| config: MkDocsConfig = MkDocsConfig(config_file_path = file) | ||
| config.load_file(f) | ||
| # Return project configuration | ||
| return config | ||
| # Resolve project plugin configuration | ||
| def _resolve_plugin(self, config: MkDocsConfig, plugin: ProjectsConfig): | ||
| # Make sure that every project has a plugin configuration set - we need | ||
| # to deep copy the configuration object, as it's mutated during parsing. | ||
| # We're using an internal method of the Plugins class to ensure that we | ||
| # always stick to the syntaxes allowed by MkDocs (list and dictionary). | ||
| plugins = Plugins._parse_configs(deepcopy(config.plugins)) | ||
| for index, (key, settings) in enumerate(plugins): | ||
| if not re.match(r"^(material/)?projects$", key): | ||
| continue | ||
| # Forward these settings of the plugin configuration to the project, | ||
| # as we need to build nested projects consistently | ||
| for name in ["cache", "projects", "projects_root_dir", "hoisting"]: | ||
| settings[name] = plugin[name] | ||
| # Forward these settings only if they have not been set in the | ||
| # project configuration, as they might be overwritten by the author | ||
| for name in ["log", "log_level"]: | ||
| if not name in settings: | ||
| settings[name] = plugin[name] | ||
| # Initialize and expand the plugin configuration, and mutate the | ||
| # plugin collection to persist the patched configuration | ||
| plugin: ProjectsConfig = ProjectsConfig() | ||
| plugin.load_dict(settings) | ||
| if isinstance(config.plugins, list): | ||
| config.plugins[index] = { key: dict(plugin.items()) } | ||
| else: | ||
| config.plugins[key] = dict(plugin.items()) | ||
| # Return project plugin configuration | ||
| return plugin | ||
| # If no plugin configuration was found, add the default configuration | ||
| # and call this function recursively to ensure that it's present | ||
| config.plugins.append("material/projects") | ||
| return self._resolve_plugin(config, plugin) | ||
| # ------------------------------------------------------------------------- | ||
| # Compute path from given slug - split slug at dots, ignoring the first one, | ||
| # and join the segments to a path, prefixed with a dot. This is necessary | ||
| # to compute the common path correctly, so we can use the same logic for | ||
| # when the path is computed from the site URL (see below). | ||
| def _path_from_slug(self, slug: str): | ||
| _, *segments = slug.split(".") | ||
| return posixpath.join(".", *segments) | ||
| # Compute path from given project configuration - parse site URL and return | ||
| # canonicalized path. Paths always start with a dot and trailing slashes are | ||
| # always removed. This is necessary so that we can compute the common path | ||
| # correctly, since the site URL might or might not contain a trailing slash. | ||
| def _path_from_config(self, config: MkDocsConfig): | ||
| url = urlparse(config.site_url) | ||
| # Remove leading slash, if any | ||
| path = url.path | ||
| if path.startswith("/"): | ||
| path = path[1:] | ||
| # Return normalized path | ||
| path = posixpath.normpath(path) if path else path | ||
| return posixpath.join(".", path) | ||
| # ----------------------------------------------------------------------------- | ||
| # Project job | ||
| class ProjectJob: | ||
| # Initialize project job | ||
| def __init__(self, project: Project, dependencies: list[Project]): | ||
| self.project = project | ||
| self.dependencies = dependencies | ||
| # ----------------------------------------------------------------------------- | ||
| # Project link | ||
| class ProjectLink(Link): | ||
| # Indicate that the link points to a project | ||
| is_project = True |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| from __future__ import annotations | ||
| import re | ||
| from mkdocs.config.base import Config | ||
| from mkdocs.config.config_options import ( | ||
| Choice, DictOfItems, ListOfItems, SubConfig, Type | ||
| ) | ||
| try: | ||
| from PIL.Image import Image as _Image | ||
| except ImportError: | ||
| pass | ||
| # ----------------------------------------------------------------------------- | ||
| # Options | ||
| # ----------------------------------------------------------------------------- | ||
| # Options for origin | ||
| Origin = ( | ||
| "start top", "center top", "end top", | ||
| "start center", "center", "end center", | ||
| "start bottom", "center bottom", "end bottom", | ||
| "start", "end" | ||
| ) | ||
| # Options for overflow | ||
| Overflow = ( | ||
| "truncate", | ||
| "shrink" | ||
| ) | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
| # ----------------------------------------------------------------------------- | ||
| # Size | ||
| class Size(Config): | ||
| width = Type(int, default = 0) | ||
| height = Type(int, default = 0) | ||
| # Offset | ||
| class Offset(Config): | ||
| x = Type(int, default = 0) | ||
| y = Type(int, default = 0) | ||
| # # ----------------------------------------------------------------------------- | ||
| # Background | ||
| class Background(Config): | ||
| color = Type(str, default = "") | ||
| image = Type(str, default = "") | ||
| # # ----------------------------------------------------------------------------- | ||
| # Icon | ||
| class Icon(Config): | ||
| value = Type(str, default = "") | ||
| color = Type(str, default = "") | ||
| # # ----------------------------------------------------------------------------- | ||
| # Line | ||
| class Line(Config): | ||
| amount = Type((int, float), default = 1) | ||
| height = Type((int, float), default = 1) | ||
| # Font | ||
| class Font(Config): | ||
| family = Type(str, default = "Roboto") | ||
| variant = Type(str, default = "") | ||
| style = Type(str, default = "Regular") | ||
| # Typography | ||
| class Typography(Config): | ||
| content = Type(str, default = "") | ||
| align = Choice(Origin, default = "start top") | ||
| overflow = Choice(Overflow, default = "truncate") | ||
| color = Type(str, default = "") | ||
| line = SubConfig(Line) | ||
| font = SubConfig(Font) | ||
| # ----------------------------------------------------------------------------- | ||
| # Layer | ||
| class Layer(Config): | ||
| size = SubConfig(Size) | ||
| offset = SubConfig(Offset) | ||
| origin = Choice(Origin, default = "start top") | ||
| background = SubConfig(Background) | ||
| icon = SubConfig(Icon) | ||
| typography = SubConfig(Typography) | ||
| # ----------------------------------------------------------------------------- | ||
| # Layout | ||
| class Layout(Config): | ||
| definitions = ListOfItems(Type(str), default = []) | ||
| tags = DictOfItems(Type(str), default = {}) | ||
| size = SubConfig(Size) | ||
| layers = ListOfItems(SubConfig(Layer), default = []) | ||
| # ----------------------------------------------------------------------------- | ||
| # Functions | ||
| # ----------------------------------------------------------------------------- | ||
| # Get layer or layout size as tuple | ||
| def get_size(layer: Layer | Layout): | ||
| return layer.size.width, layer.size.height | ||
| # Get layer offset as tuple | ||
| def get_offset(layer: Layer, image: _Image): | ||
| x, y = layer.offset.x, layer.offset.y | ||
| # Compute offset from origin - if an origin is given, compute the offset | ||
| # relative to the image and layer size to allow for flexible positioning | ||
| if layer.origin != "start top": | ||
| origin = re.split(r"\s+", layer.origin) | ||
| # Get layer size | ||
| w, h = get_size(layer) | ||
| # Compute origin on x-axis | ||
| if "start" in origin: pass | ||
| elif "end" in origin: x += (image.width - w) - 2 * x | ||
| elif "center" in origin: x += (image.width - w) >> 1 | ||
| # Compute origin on y-axis | ||
| if "top" in origin: pass | ||
| elif "bottom" in origin: y += (image.height - h) - 2 * y | ||
| elif "center" in origin: y += (image.height - h) >> 1 | ||
| # Return offset | ||
| return x, y |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| from __future__ import annotations | ||
| # ----------------------------------------------------------------------------- | ||
| # Functions | ||
| # ----------------------------------------------------------------------------- | ||
| # Filter for coercing everthing that is falsy to an empty string | ||
| def x_filter(value: str | None): | ||
| return value or "" |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| # ----------------------------------------------------------------------------- | ||
| # Configuration | ||
| # ----------------------------------------------------------------------------- | ||
| # Definitions | ||
| definitions: | ||
| # Background image | ||
| - &background_image >- | ||
| {{ layout.background_image | x }} | ||
| # Background color (default: indigo) | ||
| - &background_color >- | ||
| {%- if layout.background_color -%} | ||
| {{ layout.background_color }} | ||
| {%- else -%} | ||
| {%- set palette = config.theme.palette or {} -%} | ||
| {%- if not palette is mapping -%} | ||
| {%- set list = palette | selectattr("primary") | list + palette -%} | ||
| {%- set palette = list | first -%} | ||
| {%- endif -%} | ||
| {%- set primary = palette.get("primary", "indigo") -%} | ||
| {%- set primary = primary.replace(" ", "-") -%} | ||
| {{ { | ||
| "red": "#ef5552", | ||
| "pink": "#e92063", | ||
| "purple": "#ab47bd", | ||
| "deep-purple": "#7e56c2", | ||
| "indigo": "#4051b5", | ||
| "blue": "#2094f3", | ||
| "light-blue": "#02a6f2", | ||
| "cyan": "#00bdd6", | ||
| "teal": "#009485", | ||
| "green": "#4cae4f", | ||
| "light-green": "#8bc34b", | ||
| "lime": "#cbdc38", | ||
| "yellow": "#ffec3d", | ||
| "amber": "#ffc105", | ||
| "orange": "#ffa724", | ||
| "deep-orange": "#ff6e42", | ||
| "brown": "#795649", | ||
| "grey": "#757575", | ||
| "blue-grey": "#546d78", | ||
| "black": "#000000", | ||
| "white": "#ffffff" | ||
| }[primary] or "#4051b5" }} | ||
| {%- endif -%} | ||
| # Text color (default: white) | ||
| - &color >- | ||
| {%- if layout.color -%} | ||
| {{ layout.color }} | ||
| {%- else -%} | ||
| {%- set palette = config.theme.palette or {} -%} | ||
| {%- if not palette is mapping -%} | ||
| {%- set list = palette | selectattr("primary") | list + palette -%} | ||
| {%- set palette = list | first -%} | ||
| {%- endif -%} | ||
| {%- set primary = palette.get("primary", "indigo") -%} | ||
| {%- set primary = primary.replace(" ", "-") -%} | ||
| {{ { | ||
| "red": "#ffffff", | ||
| "pink": "#ffffff", | ||
| "purple": "#ffffff", | ||
| "deep-purple": "#ffffff", | ||
| "indigo": "#ffffff", | ||
| "blue": "#ffffff", | ||
| "light-blue": "#ffffff", | ||
| "cyan": "#ffffff", | ||
| "teal": "#ffffff", | ||
| "green": "#ffffff", | ||
| "light-green": "#ffffff", | ||
| "lime": "#000000", | ||
| "yellow": "#000000", | ||
| "amber": "#000000", | ||
| "orange": "#000000", | ||
| "deep-orange": "#ffffff", | ||
| "brown": "#ffffff", | ||
| "grey": "#ffffff", | ||
| "blue-grey": "#ffffff", | ||
| "black": "#ffffff", | ||
| "white": "#000000" | ||
| }[primary] or "#ffffff" }} | ||
| {%- endif -%} | ||
| # Font family (default: Roboto) | ||
| - &font_family >- | ||
| {%- if layout.font_family -%} | ||
| {{ layout.font_family }} | ||
| {%- elif config.theme.font is mapping -%} | ||
| {{ config.theme.font.get("text", "Roboto") }} | ||
| {%- else -%} | ||
| Roboto | ||
| {%- endif -%} | ||
| # Font variant | ||
| - &font_variant >- | ||
| {%- if layout.font_variant -%} | ||
| {{ layout.font_variant }} | ||
| {%- endif -%} | ||
| # Site name | ||
| - &site_name >- | ||
| {{ config.site_name }} | ||
| # Page title | ||
| - &page_title >- | ||
| {%- if layout.title -%} | ||
| {{ layout.title }} | ||
| {%- else -%} | ||
| {{ page.meta.get("title", page.title) }} | ||
| {%- endif -%} | ||
| # Page title with site name | ||
| - &page_title_with_site_name >- | ||
| {%- if not page.is_homepage -%} | ||
| {{ page.meta.get("title", page.title) }} - {{ config.site_name }} | ||
| {%- else -%} | ||
| {{ config.site_name }} | ||
| {%- endif -%} | ||
| # Page description | ||
| - &page_description >- | ||
| {%- if layout.description -%} | ||
| {{ layout.description }} | ||
| {%- else -%} | ||
| {{ page.meta.get("description", config.site_description) | x }} | ||
| {%- endif -%} | ||
| # Logo | ||
| - &logo >- | ||
| {%- if layout.logo -%} | ||
| {{ layout.logo }} | ||
| {%- elif config.theme.logo -%} | ||
| {{ config.docs_dir }}/{{ config.theme.logo }} | ||
| {%- endif -%} | ||
| # Logo (icon) | ||
| - &logo_icon >- | ||
| {%- if not layout.logo and config.theme.icon -%} | ||
| {{ config.theme.icon.logo | x }} | ||
| {%- endif -%} | ||
| # Meta tags | ||
| tags: | ||
| # Open Graph | ||
| og:type: website | ||
| og:title: *page_title_with_site_name | ||
| og:description: *page_description | ||
| og:image: "{{ image.url }}" | ||
| og:image:type: "{{ image.type }}" | ||
| og:image:width: "{{ image.width }}" | ||
| og:image:height: "{{ image.height }}" | ||
| og:url: "{{ page.canonical_url }}" | ||
| twitter:card: summary_large_image | ||
| twitter:title: *page_title_with_site_name | ||
| twitter:description: *page_description | ||
| twitter:image: "{{ image.url }}" | ||
| # ----------------------------------------------------------------------------- | ||
| # Specification | ||
| # ----------------------------------------------------------------------------- | ||
| # Card size and layers | ||
| size: { width: 1200, height: 630 } | ||
| layers: | ||
| # Background | ||
| - background: | ||
| image: *background_image | ||
| color: *background_color | ||
| # Logo | ||
| - size: { width: 144, height: 144 } | ||
| offset: { x: 992, y: 64 } | ||
| background: | ||
| image: *logo | ||
| icon: | ||
| value: *logo_icon | ||
| color: *color | ||
| # Site name | ||
| - size: { width: 832, height: 42 } | ||
| offset: { x: 64, y: 64 } | ||
| typography: | ||
| content: *site_name | ||
| color: *color | ||
| font: | ||
| family: *font_family | ||
| variant: *font_variant | ||
| style: Bold | ||
| # Page title | ||
| - size: { width: 832, height: 310 } | ||
| offset: { x: 62, y: 160 } | ||
| typography: | ||
| content: *page_title | ||
| align: start | ||
| color: *color | ||
| line: | ||
| amount: 3 | ||
| height: 1.25 | ||
| font: | ||
| family: *font_family | ||
| variant: *font_variant | ||
| style: Bold | ||
| # Page description | ||
| - size: { width: 832, height: 64 } | ||
| offset: { x: 64, y: 512 } | ||
| typography: | ||
| content: *page_description | ||
| align: start | ||
| color: *color | ||
| line: | ||
| amount: 2 | ||
| height: 1.5 | ||
| font: | ||
| family: *font_family | ||
| variant: *font_variant | ||
| style: Regular |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| # ----------------------------------------------------------------------------- | ||
| # Configuration | ||
| # ----------------------------------------------------------------------------- | ||
| # Definitions | ||
| definitions: | ||
| # Background image | ||
| - &background_image >- | ||
| {{ layout.background_image | x }} | ||
| # Background color (default: indigo) | ||
| - &background_color >- | ||
| {%- if layout.background_color -%} | ||
| {{ layout.background_color }} | ||
| {%- else -%} | ||
| {%- set palette = config.theme.palette or {} -%} | ||
| {%- if not palette is mapping -%} | ||
| {%- set list = palette | selectattr("accent") | list + palette -%} | ||
| {%- set palette = list | first -%} | ||
| {%- endif -%} | ||
| {%- set accent = palette.get("accent", "indigo") -%} | ||
| {%- set accent = accent.replace(" ", "-") -%} | ||
| {{ { | ||
| "red": "#ff1a47", | ||
| "pink": "#f50056", | ||
| "purple": "#df41fb", | ||
| "deep-purple": "#7c4dff", | ||
| "indigo": "#526cfe", | ||
| "blue": "#4287ff", | ||
| "light-blue": "#0091eb", | ||
| "cyan": "#00bad6", | ||
| "teal": "#00bda4", | ||
| "green": "#00c753", | ||
| "light-green": "#63de17", | ||
| "lime": "#b0eb00", | ||
| "yellow": "#ffd500", | ||
| "amber": "#ffaa00", | ||
| "orange": "#ff9100", | ||
| "deep-orange": "#ff6e42" | ||
| }[accent] or "#4051b5" }} | ||
| {%- endif -%} | ||
| # Text color (default: white) | ||
| - &color >- | ||
| {%- if layout.color -%} | ||
| {{ layout.color }} | ||
| {%- else -%} | ||
| {%- set palette = config.theme.palette or {} -%} | ||
| {%- if not palette is mapping -%} | ||
| {%- set list = palette | selectattr("accent") | list + palette -%} | ||
| {%- set palette = list | first -%} | ||
| {%- endif -%} | ||
| {%- set accent = palette.get("accent", "indigo") -%} | ||
| {%- set accent = accent.replace(" ", "-") -%} | ||
| {{ { | ||
| "red": "#ffffff", | ||
| "pink": "#ffffff", | ||
| "purple": "#ffffff", | ||
| "deep-purple": "#ffffff", | ||
| "indigo": "#ffffff", | ||
| "blue": "#ffffff", | ||
| "light-blue": "#ffffff", | ||
| "cyan": "#ffffff", | ||
| "teal": "#ffffff", | ||
| "green": "#ffffff", | ||
| "light-green": "#ffffff", | ||
| "lime": "#000000", | ||
| "yellow": "#000000", | ||
| "amber": "#000000", | ||
| "orange": "#000000", | ||
| "deep-orange": "#ffffff" | ||
| }[accent] or "#ffffff" }} | ||
| {%- endif -%} | ||
| # Font family (default: Roboto) | ||
| - &font_family >- | ||
| {%- if layout.font_family -%} | ||
| {{ layout.font_family }} | ||
| {%- elif config.theme.font is mapping -%} | ||
| {{ config.theme.font.get("text", "Roboto") }} | ||
| {%- else -%} | ||
| Roboto | ||
| {%- endif -%} | ||
| # Font variant | ||
| - &font_variant >- | ||
| {%- if layout.font_variant -%} | ||
| {{ layout.font_variant }} | ||
| {%- endif -%} | ||
| # Site name | ||
| - &site_name >- | ||
| {{ config.site_name }} | ||
| # Page title | ||
| - &page_title >- | ||
| {%- if layout.title -%} | ||
| {{ layout.title }} | ||
| {%- else -%} | ||
| {{ page.meta.get("title", page.title) }} | ||
| {%- endif -%} | ||
| # Page title with site name | ||
| - &page_title_with_site_name >- | ||
| {%- if not page.is_homepage -%} | ||
| {{ page.meta.get("title", page.title) }} - {{ config.site_name }} | ||
| {%- else -%} | ||
| {{ config.site_name }} | ||
| {%- endif -%} | ||
| # Page description | ||
| - &page_description >- | ||
| {%- if layout.description -%} | ||
| {{ layout.description }} | ||
| {%- else -%} | ||
| {{ page.meta.get("description", config.site_description) | x }} | ||
| {%- endif -%} | ||
| # Logo | ||
| - &logo >- | ||
| {%- if layout.logo -%} | ||
| {{ layout.logo }} | ||
| {%- elif config.theme.logo -%} | ||
| {{ config.docs_dir }}/{{ config.theme.logo }} | ||
| {%- endif -%} | ||
| # Logo (icon) | ||
| - &logo_icon >- | ||
| {%- if not layout.logo and config.theme.icon -%} | ||
| {{ config.theme.icon.logo | x }} | ||
| {%- endif -%} | ||
| # Meta tags | ||
| tags: | ||
| # Open Graph | ||
| og:type: website | ||
| og:title: *page_title_with_site_name | ||
| og:description: *page_description | ||
| og:image: "{{ image.url }}" | ||
| og:image:type: "{{ image.type }}" | ||
| og:image:width: "{{ image.width }}" | ||
| og:image:height: "{{ image.height }}" | ||
| og:url: "{{ page.canonical_url }}" | ||
| twitter:card: summary_large_image | ||
| twitter:title: *page_title_with_site_name | ||
| twitter:description: *page_description | ||
| twitter:image: "{{ image.url }}" | ||
| # ----------------------------------------------------------------------------- | ||
| # Specification | ||
| # ----------------------------------------------------------------------------- | ||
| # Card size and layers | ||
| size: { width: 1200, height: 630 } | ||
| layers: | ||
| # Background | ||
| - background: | ||
| image: *background_image | ||
| color: *background_color | ||
| # Logo | ||
| - size: { width: 144, height: 144 } | ||
| offset: { x: 992, y: 64 } | ||
| background: | ||
| image: *logo | ||
| icon: | ||
| value: *logo_icon | ||
| color: *color | ||
| # Site name | ||
| - size: { width: 832, height: 42 } | ||
| offset: { x: 64, y: 64 } | ||
| typography: | ||
| content: *site_name | ||
| color: *color | ||
| font: | ||
| family: *font_family | ||
| variant: *font_variant | ||
| style: Bold | ||
| # Page title | ||
| - size: { width: 832, height: 310 } | ||
| offset: { x: 62, y: 160 } | ||
| typography: | ||
| content: *page_title | ||
| align: start | ||
| color: *color | ||
| line: | ||
| amount: 3 | ||
| height: 1.25 | ||
| font: | ||
| family: *font_family | ||
| variant: *font_variant | ||
| style: Bold | ||
| # Page description | ||
| - size: { width: 832, height: 64 } | ||
| offset: { x: 64, y: 512 } | ||
| typography: | ||
| content: *page_description | ||
| align: start | ||
| color: *color | ||
| line: | ||
| amount: 2 | ||
| height: 1.5 | ||
| font: | ||
| family: *font_family | ||
| variant: *font_variant | ||
| style: Regular |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| # ----------------------------------------------------------------------------- | ||
| # Configuration | ||
| # ----------------------------------------------------------------------------- | ||
| # Definitions | ||
| definitions: | ||
| # Background image | ||
| - &background_image >- | ||
| {{ layout.background_image | x }} | ||
| # Background color (default: white) | ||
| - &background_color >- | ||
| {%- if layout.background_color -%} | ||
| {{ layout.background_color }} | ||
| {%- else -%} | ||
| {%- set palette = config.theme.palette or {} -%} | ||
| {%- if not palette is mapping -%} | ||
| {%- set list = palette | selectattr("primary") | list + palette -%} | ||
| {%- set palette = list | first -%} | ||
| {%- endif -%} | ||
| {%- set primary = palette.get("primary", "indigo") -%} | ||
| {%- set primary = primary.replace(" ", "-") -%} | ||
| {{ { | ||
| "red": "#ffffff", | ||
| "pink": "#ffffff", | ||
| "purple": "#ffffff", | ||
| "deep-purple": "#ffffff", | ||
| "indigo": "#ffffff", | ||
| "blue": "#ffffff", | ||
| "light-blue": "#ffffff", | ||
| "cyan": "#ffffff", | ||
| "teal": "#ffffff", | ||
| "green": "#ffffff", | ||
| "light-green": "#ffffff", | ||
| "lime": "#000000", | ||
| "yellow": "#000000", | ||
| "amber": "#000000", | ||
| "orange": "#000000", | ||
| "deep-orange": "#ffffff", | ||
| "brown": "#ffffff", | ||
| "grey": "#ffffff", | ||
| "blue-grey": "#ffffff", | ||
| "black": "#ffffff", | ||
| "white": "#000000" | ||
| }[primary] or "#ffffff" }} | ||
| {%- endif -%} | ||
| # Text color (default: indigo) | ||
| - &color >- | ||
| {%- if layout.color -%} | ||
| {{ layout.color }} | ||
| {%- else -%} | ||
| {%- set palette = config.theme.palette or {} -%} | ||
| {%- if not palette is mapping -%} | ||
| {%- set list = palette | selectattr("primary") | list + palette -%} | ||
| {%- set palette = list | first -%} | ||
| {%- endif -%} | ||
| {%- set primary = palette.get("primary", "indigo") -%} | ||
| {%- set primary = primary.replace(" ", "-") -%} | ||
| {{ { | ||
| "red": "#ef5552", | ||
| "pink": "#e92063", | ||
| "purple": "#ab47bd", | ||
| "deep-purple": "#7e56c2", | ||
| "indigo": "#4051b5", | ||
| "blue": "#2094f3", | ||
| "light-blue": "#02a6f2", | ||
| "cyan": "#00bdd6", | ||
| "teal": "#009485", | ||
| "green": "#4cae4f", | ||
| "light-green": "#8bc34b", | ||
| "lime": "#cbdc38", | ||
| "yellow": "#ffec3d", | ||
| "amber": "#ffc105", | ||
| "orange": "#ffa724", | ||
| "deep-orange": "#ff6e42", | ||
| "brown": "#795649", | ||
| "grey": "#757575", | ||
| "blue-grey": "#546d78", | ||
| "black": "#000000", | ||
| "white": "#ffffff" | ||
| }[primary] or "#4051b5" }} | ||
| {%- endif -%} | ||
| # Font family (default: Roboto) | ||
| - &font_family >- | ||
| {%- if layout.font_family -%} | ||
| {{ layout.font_family }} | ||
| {%- elif config.theme.font is mapping -%} | ||
| {{ config.theme.font.get("text", "Roboto") }} | ||
| {%- else -%} | ||
| Roboto | ||
| {%- endif -%} | ||
| # Font variant | ||
| - &font_variant >- | ||
| {%- if layout.font_variant -%} | ||
| {{ layout.font_variant }} | ||
| {%- endif -%} | ||
| # Site name | ||
| - &site_name >- | ||
| {{ config.site_name }} | ||
| # Page title | ||
| - &page_title >- | ||
| {%- if layout.title -%} | ||
| {{ layout.title }} | ||
| {%- else -%} | ||
| {{ page.meta.get("title", page.title) }} | ||
| {%- endif -%} | ||
| # Page title with site name | ||
| - &page_title_with_site_name >- | ||
| {%- if not page.is_homepage -%} | ||
| {{ page.meta.get("title", page.title) }} - {{ config.site_name }} | ||
| {%- else -%} | ||
| {{ config.site_name }} | ||
| {%- endif -%} | ||
| # Page description | ||
| - &page_description >- | ||
| {%- if layout.description -%} | ||
| {{ layout.description }} | ||
| {%- else -%} | ||
| {{ page.meta.get("description", config.site_description) | x }} | ||
| {%- endif -%} | ||
| # Logo | ||
| - &logo >- | ||
| {%- if layout.logo -%} | ||
| {{ layout.logo }} | ||
| {%- elif config.theme.logo -%} | ||
| {{ config.docs_dir }}/{{ config.theme.logo }} | ||
| {%- endif -%} | ||
| # Logo (icon) | ||
| - &logo_icon >- | ||
| {%- if not layout.logo and config.theme.icon -%} | ||
| {{ config.theme.icon.logo | x }} | ||
| {%- endif -%} | ||
| # Meta tags | ||
| tags: | ||
| # Open Graph | ||
| og:type: website | ||
| og:title: *page_title_with_site_name | ||
| og:description: *page_description | ||
| og:image: "{{ image.url }}" | ||
| og:image:type: "{{ image.type }}" | ||
| og:image:width: "{{ image.width }}" | ||
| og:image:height: "{{ image.height }}" | ||
| og:url: "{{ page.canonical_url }}" | ||
| twitter:card: summary_large_image | ||
| twitter:title: *page_title_with_site_name | ||
| twitter:description: *page_description | ||
| twitter:image: "{{ image.url }}" | ||
| # ----------------------------------------------------------------------------- | ||
| # Specification | ||
| # ----------------------------------------------------------------------------- | ||
| # Card size and layers | ||
| size: { width: 1200, height: 630 } | ||
| layers: | ||
| # Background | ||
| - background: | ||
| image: *background_image | ||
| color: *background_color | ||
| # Logo | ||
| - size: { width: 144, height: 144 } | ||
| offset: { x: 992, y: 64 } | ||
| background: | ||
| image: *logo | ||
| icon: | ||
| value: *logo_icon | ||
| color: *color | ||
| # Site name | ||
| - size: { width: 832, height: 42 } | ||
| offset: { x: 64, y: 64 } | ||
| typography: | ||
| content: *site_name | ||
| color: *color | ||
| font: | ||
| family: *font_family | ||
| variant: *font_variant | ||
| style: Bold | ||
| # Page title | ||
| - size: { width: 832, height: 310 } | ||
| offset: { x: 62, y: 160 } | ||
| typography: | ||
| content: *page_title | ||
| align: start | ||
| color: *color | ||
| line: | ||
| amount: 3 | ||
| height: 1.25 | ||
| font: | ||
| family: *font_family | ||
| variant: *font_variant | ||
| style: Bold | ||
| # Page description | ||
| - size: { width: 832, height: 64 } | ||
| offset: { x: 64, y: 512 } | ||
| typography: | ||
| content: *page_description | ||
| align: start | ||
| color: *color | ||
| line: | ||
| amount: 2 | ||
| height: 1.5 | ||
| font: | ||
| family: *font_family | ||
| variant: *font_variant | ||
| style: Regular |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| # ----------------------------------------------------------------------------- | ||
| # Configuration | ||
| # ----------------------------------------------------------------------------- | ||
| # Definitions | ||
| definitions: | ||
| # Background image | ||
| - &background_image >- | ||
| {{ layout.background_image }} | ||
| # Page title with site name | ||
| - &page_title_with_site_name >- | ||
| {%- if not page.is_homepage -%} | ||
| {{ page.meta.get("title", page.title) }} - {{ config.site_name }} | ||
| {%- else -%} | ||
| {{ page.meta.get("title", page.title) }} | ||
| {%- endif -%} | ||
| # Page description | ||
| - &page_description >- | ||
| {%- if layout.description -%} | ||
| {{ layout.description }} | ||
| {%- else -%} | ||
| {{ page.meta.get("description", config.site_description) | x }} | ||
| {%- endif -%} | ||
| # Meta tags | ||
| tags: | ||
| # Open Graph | ||
| og:type: website | ||
| og:title: *page_title_with_site_name | ||
| og:description: *page_description | ||
| og:image: "{{ image.url }}" | ||
| og:image:type: "{{ image.type }}" | ||
| og:image:width: "{{ image.width }}" | ||
| og:image:height: "{{ image.height }}" | ||
| og:url: "{{ page.canonical_url }}" | ||
| twitter:card: summary_large_image | ||
| twitter:title: *page_title_with_site_name | ||
| twitter:description: *page_description | ||
| twitter:image: "{{ image.url }}" | ||
| # ----------------------------------------------------------------------------- | ||
| # Specification | ||
| # ----------------------------------------------------------------------------- | ||
| # Card size and layers | ||
| size: { width: 1200, height: 630 } | ||
| layers: | ||
| # Background | ||
| - background: | ||
| image: *background_image |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| # ----------------------------------------------------------------------------- | ||
| # Configuration | ||
| # ----------------------------------------------------------------------------- | ||
| # Definitions | ||
| definitions: | ||
| # Background image | ||
| - &background_image >- | ||
| {{ layout.background_image | x }} | ||
| # Background color (default: indigo) | ||
| - &background_color >- | ||
| {%- if layout.background_color -%} | ||
| {{ layout.background_color }} | ||
| {%- else -%} | ||
| {%- set palette = config.theme.palette or {} -%} | ||
| {%- if not palette is mapping -%} | ||
| {%- set list = palette | selectattr("primary") | list + palette -%} | ||
| {%- set palette = list | first -%} | ||
| {%- endif -%} | ||
| {%- set primary = palette.get("primary", "indigo") -%} | ||
| {%- set primary = primary.replace(" ", "-") -%} | ||
| {{ { | ||
| "red": "#ef5552", | ||
| "pink": "#e92063", | ||
| "purple": "#ab47bd", | ||
| "deep-purple": "#7e56c2", | ||
| "indigo": "#4051b5", | ||
| "blue": "#2094f3", | ||
| "light-blue": "#02a6f2", | ||
| "cyan": "#00bdd6", | ||
| "teal": "#009485", | ||
| "green": "#4cae4f", | ||
| "light-green": "#8bc34b", | ||
| "lime": "#cbdc38", | ||
| "yellow": "#ffec3d", | ||
| "amber": "#ffc105", | ||
| "orange": "#ffa724", | ||
| "deep-orange": "#ff6e42", | ||
| "brown": "#795649", | ||
| "grey": "#757575", | ||
| "blue-grey": "#546d78", | ||
| "black": "#000000", | ||
| "white": "#ffffff" | ||
| }[primary] or "#4051b5" }} | ||
| {%- endif -%} | ||
| # Text color (default: white) | ||
| - &color >- | ||
| {%- if layout.color -%} | ||
| {{ layout.color }} | ||
| {%- else -%} | ||
| {%- set palette = config.theme.palette or {} -%} | ||
| {%- if not palette is mapping -%} | ||
| {%- set list = palette | selectattr("primary") | list + palette -%} | ||
| {%- set palette = list | first -%} | ||
| {%- endif -%} | ||
| {%- set primary = palette.get("primary", "indigo") -%} | ||
| {%- set primary = primary.replace(" ", "-") -%} | ||
| {{ { | ||
| "red": "#ffffff", | ||
| "pink": "#ffffff", | ||
| "purple": "#ffffff", | ||
| "deep-purple": "#ffffff", | ||
| "indigo": "#ffffff", | ||
| "blue": "#ffffff", | ||
| "light-blue": "#ffffff", | ||
| "cyan": "#ffffff", | ||
| "teal": "#ffffff", | ||
| "green": "#ffffff", | ||
| "light-green": "#ffffff", | ||
| "lime": "#000000", | ||
| "yellow": "#000000", | ||
| "amber": "#000000", | ||
| "orange": "#000000", | ||
| "deep-orange": "#ffffff", | ||
| "brown": "#ffffff", | ||
| "grey": "#ffffff", | ||
| "blue-grey": "#ffffff", | ||
| "black": "#ffffff", | ||
| "white": "#000000" | ||
| }[primary] or "#ffffff" }} | ||
| {%- endif -%} | ||
| # Font family (default: Roboto) | ||
| - &font_family >- | ||
| {%- if layout.font_family -%} | ||
| {{ layout.font_family }} | ||
| {%- elif config.theme.font is mapping -%} | ||
| {{ config.theme.font.get("text", "Roboto") }} | ||
| {%- else -%} | ||
| Roboto | ||
| {%- endif -%} | ||
| # Font variant | ||
| - &font_variant >- | ||
| {%- if layout.font_variant -%} | ||
| {{ layout.font_variant }} | ||
| {%- endif -%} | ||
| # Site name | ||
| - &site_name >- | ||
| {{ config.site_name }} | ||
| # Page title | ||
| - &page_title >- | ||
| {%- if layout.title -%} | ||
| {{ layout.title }} | ||
| {%- else -%} | ||
| {{ page.meta.get("title", page.title) }} | ||
| {%- endif -%} | ||
| # Page title with site name | ||
| - &page_title_with_site_name >- | ||
| {%- if not page.is_homepage -%} | ||
| {{ page.meta.get("title", page.title) }} - {{ config.site_name }} | ||
| {%- else -%} | ||
| {{ config.site_name }} | ||
| {%- endif -%} | ||
| # Page description | ||
| - &page_description >- | ||
| {%- if layout.description -%} | ||
| {{ layout.description }} | ||
| {%- else -%} | ||
| {{ page.meta.get("description", config.site_description) | x }} | ||
| {%- endif -%} | ||
| # Page icon | ||
| - &page_icon >- | ||
| {{ page.meta.icon | x }} | ||
| # Logo | ||
| - &logo >- | ||
| {%- if layout.logo -%} | ||
| {{ layout.logo }} | ||
| {%- elif config.theme.logo -%} | ||
| {{ config.docs_dir }}/{{ config.theme.logo }} | ||
| {%- endif -%} | ||
| # Logo (icon) | ||
| - &logo_icon >- | ||
| {%- if not layout.logo and config.theme.icon -%} | ||
| {{ config.theme.icon.logo | x }} | ||
| {%- endif -%} | ||
| # Meta tags | ||
| tags: | ||
| # Open Graph | ||
| og:type: website | ||
| og:title: *page_title_with_site_name | ||
| og:description: *page_description | ||
| og:image: "{{ image.url }}" | ||
| og:image:type: "{{ image.type }}" | ||
| og:image:width: "{{ image.width }}" | ||
| og:image:height: "{{ image.height }}" | ||
| og:url: "{{ page.canonical_url }}" | ||
| twitter:card: summary_large_image | ||
| twitter:title: *page_title_with_site_name | ||
| twitter:description: *page_description | ||
| twitter:image: "{{ image.url }}" | ||
| # ----------------------------------------------------------------------------- | ||
| # Specification | ||
| # ----------------------------------------------------------------------------- | ||
| # Card size and layers | ||
| size: { width: 1200, height: 630 } | ||
| layers: | ||
| # Background | ||
| - background: | ||
| image: *background_image | ||
| color: *background_color | ||
| # Page icon | ||
| - size: { width: 630, height: 630 } | ||
| offset: { x: 800, y: 0 } | ||
| icon: | ||
| value: *page_icon | ||
| color: "#00000033" | ||
| # Logo | ||
| - size: { width: 64, height: 64 } | ||
| offset: { x: 64, y: 64 } | ||
| background: | ||
| image: *logo | ||
| icon: | ||
| value: *logo_icon | ||
| color: *color | ||
| # Site name | ||
| - size: { width: 768, height: 42 } | ||
| offset: { x: 160, y: 74 } | ||
| typography: | ||
| content: *site_name | ||
| color: *color | ||
| font: | ||
| family: *font_family | ||
| variant: *font_variant | ||
| style: Bold | ||
| # Page title | ||
| - size: { width: 864, height: 256 } | ||
| offset: { x: 62, y: 192 } | ||
| typography: | ||
| content: *page_title | ||
| align: start | ||
| color: *color | ||
| line: | ||
| amount: 3 | ||
| height: 1.25 | ||
| font: | ||
| family: *font_family | ||
| variant: *font_variant | ||
| style: Bold | ||
| # Page description | ||
| - size: { width: 864, height: 64 } | ||
| offset: { x: 64, y: 512 } | ||
| typography: | ||
| content: *page_description | ||
| align: start | ||
| color: *color | ||
| line: | ||
| amount: 2 | ||
| height: 1.5 | ||
| font: | ||
| family: *font_family | ||
| variant: *font_variant | ||
| style: Regular |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| from mkdocs.config.config_options import Type | ||
| from mkdocs.config.base import Config | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
| # ----------------------------------------------------------------------------- | ||
| # Typeset plugin configuration | ||
| class TypesetConfig(Config): | ||
| enabled = Type(bool, default = True) |
| # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to | ||
| # deal in the Software without restriction, including without limitation the | ||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| # sell copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # The above copyright notice and this permission notice shall be included in | ||
| # all copies or substantial portions of the Software. | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| # IN THE SOFTWARE. | ||
| import re | ||
| from mkdocs.plugins import BasePlugin | ||
| from .config import TypesetConfig | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
| # ----------------------------------------------------------------------------- | ||
| # Typeset plugin | ||
| class TypesetPlugin(BasePlugin[TypesetConfig]): | ||
| # Initialize plugin | ||
| def on_config(self, config): | ||
| if not self.config.enabled: | ||
| return | ||
| # Initialize titles | ||
| self.title_map: dict[str, str] = {} | ||
| # Extract source of page title before it's lost | ||
| def on_pre_page(self, page, *, config, files): | ||
| if not self.config.enabled: | ||
| return | ||
| # Check if page title was set in configuration | ||
| if page.title: | ||
| path = page.file.src_uri | ||
| self.title_map[path] = "config" | ||
| # Extract typeset content for headlines | ||
| def on_page_content(self, html, *, page, config, files): | ||
| if not self.config.enabled: | ||
| return | ||
| # Check if page title was set in metadata | ||
| path = page.file.src_uri | ||
| if path not in self.title_map: | ||
| if "title" in page.meta: | ||
| self.title_map[path] = "meta" | ||
| # Flatten anchors and map to headlines | ||
| anchors = _flatten(page.toc.items) | ||
| for (level, id, title) in re.findall( | ||
| r"<h(\d)[^>]+id=\"([^\"]+)[^>]*>(.*?)</h\1>", | ||
| html, flags = re.I | re.M | ||
| ): | ||
| if id not in anchors: | ||
| continue | ||
| # If the author uses `data-toc-label` to override a heading (which | ||
| # doesn't support adding of HTML tags), we can abort here, since | ||
| # the headline will be rendered as-is. It's more or less a hack, so | ||
| # we should check if we can improve it in the future. | ||
| label = re.escape(anchors[id].title) | ||
| if re.search(rf"data-toc-label=['\"]{label}", page.markdown): | ||
| continue | ||
| # Remove anchor links from headlines – we need to do that, or we | ||
| # end up with anchor links inside anchor links, which is invalid | ||
| # HTML5. There are two cases we need to account for here: | ||
| # | ||
| # 1. If toc.anchorlink is enabled, the entire headline is wrapped | ||
| # in an anchor link, so we unpack its contents | ||
| # | ||
| # 2. If toc.permalink is enabled, an anchor link is appended to the | ||
| # contents of the headline, so we just remove it | ||
| # | ||
| # Albeit it doesn't make much sense, both options can be used at | ||
| # the same time, so we need to account for both cases. This problem | ||
| # was first reported in https://bit.ly/456AjUm | ||
| title = re.sub(r"^<a\s+[^>]+>(.*?)</a>", r"\1", title) | ||
| title = re.sub(r"<a\s+[^>]+>[^<]+?</a>$", "", title) | ||
| # Remove author-provided ids - see https://bit.ly/3ngiZea | ||
| title = re.sub(r"id=\"?[^\">]+\"?", "", title) | ||
| # Assign headline content to anchor | ||
| anchors[id].typeset = { "title": title } | ||
| if path not in self.title_map: | ||
| # Assign first top-level headline to page | ||
| if not hasattr(page, "typeset") and int(level) == 1: | ||
| page.typeset = anchors[id].typeset | ||
| page.title = re.sub(r"<[^>]+>", "", title) | ||
| # ----------------------------------------------------------------------------- | ||
| # Helper functions | ||
| # ----------------------------------------------------------------------------- | ||
| # Flatten a tree of anchors | ||
| def _flatten(items): | ||
| anchors = {} | ||
| for item in items: | ||
| anchors[item.id] = item | ||
| # Recursively expand children | ||
| if item.children: | ||
| anchors.update(_flatten(item.children)) | ||
| # Return anchors | ||
| return anchors |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
| "use strict";(()=>{var xe=Object.create;var G=Object.defineProperty,ve=Object.defineProperties,Se=Object.getOwnPropertyDescriptor,Te=Object.getOwnPropertyDescriptors,Qe=Object.getOwnPropertyNames,J=Object.getOwnPropertySymbols,Ee=Object.getPrototypeOf,Z=Object.prototype.hasOwnProperty,be=Object.prototype.propertyIsEnumerable;var K=Math.pow,X=(t,e,r)=>e in t?G(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,_=(t,e)=>{for(var r in e||(e={}))Z.call(e,r)&&X(t,r,e[r]);if(J)for(var r of J(e))be.call(e,r)&&X(t,r,e[r]);return t},B=(t,e)=>ve(t,Te(e));var Le=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var we=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Qe(e))!Z.call(t,i)&&i!==r&&G(t,i,{get:()=>e[i],enumerable:!(n=Se(e,i))||n.enumerable});return t};var Pe=(t,e,r)=>(r=t!=null?xe(Ee(t)):{},we(e||!t||!t.__esModule?G(r,"default",{value:t,enumerable:!0}):r,t));var W=(t,e,r)=>new Promise((n,i)=>{var s=u=>{try{a(r.next(u))}catch(c){i(c)}},o=u=>{try{a(r.throw(u))}catch(c){i(c)}},a=u=>u.done?n(u.value):Promise.resolve(u.value).then(s,o);a((r=r.apply(t,e)).next())});var re=Le((ee,te)=>{/** | ||
| * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| * @license MIT | ||
| */(function(){var t=function(e){var r=new t.Builder;return r.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),r.searchPipeline.add(t.stemmer),e.call(r,r),r.build()};t.version="2.3.9";/*! | ||
| * lunr.utils | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.utils={},t.utils.warn=(function(e){return function(r){e.console&&console.warn&&console.warn(r)}})(this),t.utils.asString=function(e){return e==null?"":e.toString()},t.utils.clone=function(e){if(e==null)return e;for(var r=Object.create(null),n=Object.keys(e),i=0;i<n.length;i++){var s=n[i],o=e[s];if(Array.isArray(o)){r[s]=o.slice();continue}if(typeof o=="string"||typeof o=="number"||typeof o=="boolean"){r[s]=o;continue}throw new TypeError("clone is not deep and does not support nested objects")}return r},t.FieldRef=function(e,r,n){this.docRef=e,this.fieldName=r,this._stringValue=n},t.FieldRef.joiner="/",t.FieldRef.fromString=function(e){var r=e.indexOf(t.FieldRef.joiner);if(r===-1)throw"malformed field ref string";var n=e.slice(0,r),i=e.slice(r+1);return new t.FieldRef(i,n,e)},t.FieldRef.prototype.toString=function(){return this._stringValue==null&&(this._stringValue=this.fieldName+t.FieldRef.joiner+this.docRef),this._stringValue};/*! | ||
| * lunr.Set | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.Set=function(e){if(this.elements=Object.create(null),e){this.length=e.length;for(var r=0;r<this.length;r++)this.elements[e[r]]=!0}else this.length=0},t.Set.complete={intersect:function(e){return e},union:function(){return this},contains:function(){return!0}},t.Set.empty={intersect:function(){return this},union:function(e){return e},contains:function(){return!1}},t.Set.prototype.contains=function(e){return!!this.elements[e]},t.Set.prototype.intersect=function(e){var r,n,i,s=[];if(e===t.Set.complete)return this;if(e===t.Set.empty)return e;this.length<e.length?(r=this,n=e):(r=e,n=this),i=Object.keys(r.elements);for(var o=0;o<i.length;o++){var a=i[o];a in n.elements&&s.push(a)}return new t.Set(s)},t.Set.prototype.union=function(e){return e===t.Set.complete?t.Set.complete:e===t.Set.empty?this:new t.Set(Object.keys(this.elements).concat(Object.keys(e.elements)))},t.idf=function(e,r){var n=0;for(var i in e)i!="_index"&&(n+=Object.keys(e[i]).length);var s=(r-n+.5)/(n+.5);return Math.log(1+Math.abs(s))},t.Token=function(e,r){this.str=e||"",this.metadata=r||{}},t.Token.prototype.toString=function(){return this.str},t.Token.prototype.update=function(e){return this.str=e(this.str,this.metadata),this},t.Token.prototype.clone=function(e){return e=e||function(r){return r},new t.Token(e(this.str,this.metadata),this.metadata)};/*! | ||
| * lunr.tokenizer | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.tokenizer=function(e,r){if(e==null||e==null)return[];if(Array.isArray(e))return e.map(function(g){return new t.Token(t.utils.asString(g).toLowerCase(),t.utils.clone(r))});for(var n=e.toString().toLowerCase(),i=n.length,s=[],o=0,a=0;o<=i;o++){var u=n.charAt(o),c=o-a;if(u.match(t.tokenizer.separator)||o==i){if(c>0){var f=t.utils.clone(r)||{};f.position=[a,c],f.index=s.length,s.push(new t.Token(n.slice(a,o),f))}a=o+1}}return s},t.tokenizer.separator=/[\s\-]+/;/*! | ||
| * lunr.Pipeline | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions=Object.create(null),t.Pipeline.registerFunction=function(e,r){r in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+r),e.label=r,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var r=e.label&&e.label in this.registeredFunctions;r||t.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. | ||
| `,e)},t.Pipeline.load=function(e){var r=new t.Pipeline;return e.forEach(function(n){var i=t.Pipeline.registeredFunctions[n];if(i)r.add(i);else throw new Error("Cannot load unregistered function: "+n)}),r},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(r){t.Pipeline.warnIfFunctionNotRegistered(r),this._stack.push(r)},this)},t.Pipeline.prototype.after=function(e,r){t.Pipeline.warnIfFunctionNotRegistered(r);var n=this._stack.indexOf(e);if(n==-1)throw new Error("Cannot find existingFn");n=n+1,this._stack.splice(n,0,r)},t.Pipeline.prototype.before=function(e,r){t.Pipeline.warnIfFunctionNotRegistered(r);var n=this._stack.indexOf(e);if(n==-1)throw new Error("Cannot find existingFn");this._stack.splice(n,0,r)},t.Pipeline.prototype.remove=function(e){var r=this._stack.indexOf(e);r!=-1&&this._stack.splice(r,1)},t.Pipeline.prototype.run=function(e){for(var r=this._stack.length,n=0;n<r;n++){for(var i=this._stack[n],s=[],o=0;o<e.length;o++){var a=i(e[o],o,e);if(!(a==null||a===""))if(Array.isArray(a))for(var u=0;u<a.length;u++)s.push(a[u]);else s.push(a)}e=s}return e},t.Pipeline.prototype.runString=function(e,r){var n=new t.Token(e,r);return this.run([n]).map(function(i){return i.toString()})},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})};/*! | ||
| * lunr.Vector | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.Vector=function(e){this._magnitude=0,this.elements=e||[]},t.Vector.prototype.positionForIndex=function(e){if(this.elements.length==0)return 0;for(var r=0,n=this.elements.length/2,i=n-r,s=Math.floor(i/2),o=this.elements[s*2];i>1&&(o<e&&(r=s),o>e&&(n=s),o!=e);)i=n-r,s=r+Math.floor(i/2),o=this.elements[s*2];if(o==e||o>e)return s*2;if(o<e)return(s+1)*2},t.Vector.prototype.insert=function(e,r){this.upsert(e,r,function(){throw"duplicate index"})},t.Vector.prototype.upsert=function(e,r,n){this._magnitude=0;var i=this.positionForIndex(e);this.elements[i]==e?this.elements[i+1]=n(this.elements[i+1],r):this.elements.splice(i,0,e,r)},t.Vector.prototype.magnitude=function(){if(this._magnitude)return this._magnitude;for(var e=0,r=this.elements.length,n=1;n<r;n+=2){var i=this.elements[n];e+=i*i}return this._magnitude=Math.sqrt(e)},t.Vector.prototype.dot=function(e){for(var r=0,n=this.elements,i=e.elements,s=n.length,o=i.length,a=0,u=0,c=0,f=0;c<s&&f<o;)a=n[c],u=i[f],a<u?c+=2:a>u?f+=2:a==u&&(r+=n[c+1]*i[f+1],c+=2,f+=2);return r},t.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},t.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),r=1,n=0;r<this.elements.length;r+=2,n++)e[n]=this.elements[r];return e},t.Vector.prototype.toJSON=function(){return this.elements};/*! | ||
| * lunr.stemmer | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt | ||
| */t.stemmer=(function(){var e={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},r={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",s=n+"[^aeiouy]*",o=i+"[aeiou]*",a="^("+s+")?"+o+s,u="^("+s+")?"+o+s+"("+o+")?$",c="^("+s+")?"+o+s+o+s,f="^("+s+")?"+i,g=new RegExp(a),l=new RegExp(c),m=new RegExp(u),x=new RegExp(f),v=/^(.+?)(ss|i)es$/,d=/^(.+?)([^s])s$/,y=/^(.+?)eed$/,b=/^(.+?)(ed|ing)$/,E=/.$/,w=/(at|bl|iz)$/,R=new RegExp("([^aeiouylsz])\\1$"),j=new RegExp("^"+s+i+"[^aeiouwxy]$"),C=/^(.+?[^aeiou])y$/,D=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,N=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,M=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,V=/^(.+?)(s|t)(ion)$/,P=/^(.+?)e$/,z=/ll$/,$=new RegExp("^"+s+i+"[^aeiouwxy]$"),A=function(h){var S,k,L,p,T,O,F;if(h.length<3)return h;if(L=h.substr(0,1),L=="y"&&(h=L.toUpperCase()+h.substr(1)),p=v,T=d,p.test(h)?h=h.replace(p,"$1$2"):T.test(h)&&(h=h.replace(T,"$1$2")),p=y,T=b,p.test(h)){var Q=p.exec(h);p=g,p.test(Q[1])&&(p=E,h=h.replace(p,""))}else if(T.test(h)){var Q=T.exec(h);S=Q[1],T=x,T.test(S)&&(h=S,T=w,O=R,F=j,T.test(h)?h=h+"e":O.test(h)?(p=E,h=h.replace(p,"")):F.test(h)&&(h=h+"e"))}if(p=C,p.test(h)){var Q=p.exec(h);S=Q[1],h=S+"i"}if(p=D,p.test(h)){var Q=p.exec(h);S=Q[1],k=Q[2],p=g,p.test(S)&&(h=S+e[k])}if(p=N,p.test(h)){var Q=p.exec(h);S=Q[1],k=Q[2],p=g,p.test(S)&&(h=S+r[k])}if(p=M,T=V,p.test(h)){var Q=p.exec(h);S=Q[1],p=l,p.test(S)&&(h=S)}else if(T.test(h)){var Q=T.exec(h);S=Q[1]+Q[2],T=l,T.test(S)&&(h=S)}if(p=P,p.test(h)){var Q=p.exec(h);S=Q[1],p=l,T=m,O=$,(p.test(S)||T.test(S)&&!O.test(S))&&(h=S)}return p=z,T=l,p.test(h)&&T.test(h)&&(p=E,h=h.replace(p,"")),L=="y"&&(h=L.toLowerCase()+h.substr(1)),h};return function(I){return I.update(A)}})(),t.Pipeline.registerFunction(t.stemmer,"stemmer");/*! | ||
| * lunr.stopWordFilter | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.generateStopWordFilter=function(e){var r=e.reduce(function(n,i){return n[i]=i,n},{});return function(n){if(n&&r[n.toString()]!==n.toString())return n}},t.stopWordFilter=t.generateStopWordFilter(["a","able","about","across","after","all","almost","also","am","among","an","and","any","are","as","at","be","because","been","but","by","can","cannot","could","dear","did","do","does","either","else","ever","every","for","from","get","got","had","has","have","he","her","hers","him","his","how","however","i","if","in","into","is","it","its","just","least","let","like","likely","may","me","might","most","must","my","neither","no","nor","not","of","off","often","on","only","or","other","our","own","rather","said","say","says","she","should","since","so","some","than","that","the","their","them","then","there","these","they","this","tis","to","too","twas","us","wants","was","we","were","what","when","where","which","while","who","whom","why","will","with","would","yet","you","your"]),t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter");/*! | ||
| * lunr.trimmer | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.trimmer=function(e){return e.update(function(r){return r.replace(/^\W+/,"").replace(/\W+$/,"")})},t.Pipeline.registerFunction(t.trimmer,"trimmer");/*! | ||
| * lunr.TokenSet | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.TokenSet=function(){this.final=!1,this.edges={},this.id=t.TokenSet._nextId,t.TokenSet._nextId+=1},t.TokenSet._nextId=1,t.TokenSet.fromArray=function(e){for(var r=new t.TokenSet.Builder,n=0,i=e.length;n<i;n++)r.insert(e[n]);return r.finish(),r.root},t.TokenSet.fromClause=function(e){return"editDistance"in e?t.TokenSet.fromFuzzyString(e.term,e.editDistance):t.TokenSet.fromString(e.term)},t.TokenSet.fromFuzzyString=function(e,r){for(var n=new t.TokenSet,i=[{node:n,editsRemaining:r,str:e}];i.length;){var s=i.pop();if(s.str.length>0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new t.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new t.TokenSet;s.node.edges["*"]=u}if(s.str.length==0&&(u.final=!0),i.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var c=s.node.edges["*"];else{var c=new t.TokenSet;s.node.edges["*"]=c}s.str.length==1&&(c.final=!0),i.push({node:c,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var f=s.str.charAt(0),g=s.str.charAt(1),l;g in s.node.edges?l=s.node.edges[g]:(l=new t.TokenSet,s.node.edges[g]=l),s.str.length==1&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:f+s.str.slice(2)})}}}return n},t.TokenSet.fromString=function(e){for(var r=new t.TokenSet,n=r,i=0,s=e.length;i<s;i++){var o=e[i],a=i==s-1;if(o=="*")r.edges[o]=r,r.final=a;else{var u=new t.TokenSet;u.final=a,r.edges[o]=u,r=u}}return n},t.TokenSet.prototype.toArray=function(){for(var e=[],r=[{prefix:"",node:this}];r.length;){var n=r.pop(),i=Object.keys(n.node.edges),s=i.length;n.node.final&&(n.prefix.charAt(0),e.push(n.prefix));for(var o=0;o<s;o++){var a=i[o];r.push({prefix:n.prefix.concat(a),node:n.node.edges[a]})}}return e},t.TokenSet.prototype.toString=function(){if(this._str)return this._str;for(var e=this.final?"1":"0",r=Object.keys(this.edges).sort(),n=r.length,i=0;i<n;i++){var s=r[i],o=this.edges[s];e=e+s+o.id}return e},t.TokenSet.prototype.intersect=function(e){for(var r=new t.TokenSet,n=void 0,i=[{qNode:e,output:r,node:this}];i.length;){n=i.pop();for(var s=Object.keys(n.qNode.edges),o=s.length,a=Object.keys(n.node.edges),u=a.length,c=0;c<o;c++)for(var f=s[c],g=0;g<u;g++){var l=a[g];if(l==f||f=="*"){var m=n.node.edges[l],x=n.qNode.edges[f],v=m.final&&x.final,d=void 0;l in n.output.edges?(d=n.output.edges[l],d.final=d.final||v):(d=new t.TokenSet,d.final=v,n.output.edges[l]=d),i.push({qNode:x,output:d,node:m})}}}return r},t.TokenSet.Builder=function(){this.previousWord="",this.root=new t.TokenSet,this.uncheckedNodes=[],this.minimizedNodes={}},t.TokenSet.Builder.prototype.insert=function(e){var r,n=0;if(e<this.previousWord)throw new Error("Out of order word insertion");for(var i=0;i<e.length&&i<this.previousWord.length&&e[i]==this.previousWord[i];i++)n++;this.minimize(n),this.uncheckedNodes.length==0?r=this.root:r=this.uncheckedNodes[this.uncheckedNodes.length-1].child;for(var i=n;i<e.length;i++){var s=new t.TokenSet,o=e[i];r.edges[o]=s,this.uncheckedNodes.push({parent:r,char:o,child:s}),r=s}r.final=!0,this.previousWord=e},t.TokenSet.Builder.prototype.finish=function(){this.minimize(0)},t.TokenSet.Builder.prototype.minimize=function(e){for(var r=this.uncheckedNodes.length-1;r>=e;r--){var n=this.uncheckedNodes[r],i=n.child.toString();i in this.minimizedNodes?n.parent.edges[n.char]=this.minimizedNodes[i]:(n.child._str=i,this.minimizedNodes[i]=n.child),this.uncheckedNodes.pop()}};/*! | ||
| * lunr.Index | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(r){var n=new t.QueryParser(e,r);n.parse()})},t.Index.prototype.query=function(e){for(var r=new t.Query(this.fields),n=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u<this.fields.length;u++)i[this.fields[u]]=new t.Vector;e.call(r,r);for(var u=0;u<r.clauses.length;u++){var c=r.clauses[u],f=null,g=t.Set.empty;c.usePipeline?f=this.pipeline.runString(c.term,{fields:c.fields}):f=[c.term];for(var l=0;l<f.length;l++){var m=f[l];c.term=m;var x=t.TokenSet.fromClause(c),v=this.tokenSet.intersect(x).toArray();if(v.length===0&&c.presence===t.Query.presence.REQUIRED){for(var d=0;d<c.fields.length;d++){var y=c.fields[d];o[y]=t.Set.empty}break}for(var b=0;b<v.length;b++)for(var E=v[b],w=this.invertedIndex[E],R=w._index,d=0;d<c.fields.length;d++){var y=c.fields[d],j=w[y],C=Object.keys(j),D=E+"/"+y,N=new t.Set(C);if(c.presence==t.Query.presence.REQUIRED&&(g=g.union(N),o[y]===void 0&&(o[y]=t.Set.complete)),c.presence==t.Query.presence.PROHIBITED){a[y]===void 0&&(a[y]=t.Set.empty),a[y]=a[y].union(N);continue}if(i[y].upsert(R,c.boost,function(ye,me){return ye+me}),!s[D]){for(var M=0;M<C.length;M++){var V=C[M],P=new t.FieldRef(V,y),z=j[V],$;($=n[P])===void 0?n[P]=new t.MatchData(E,y,z):$.add(E,y,z)}s[D]=!0}}}if(c.presence===t.Query.presence.REQUIRED)for(var d=0;d<c.fields.length;d++){var y=c.fields[d];o[y]=o[y].intersect(g)}}for(var A=t.Set.complete,I=t.Set.empty,u=0;u<this.fields.length;u++){var y=this.fields[u];o[y]&&(A=A.intersect(o[y])),a[y]&&(I=I.union(a[y]))}var h=Object.keys(n),S=[],k=Object.create(null);if(r.isNegated()){h=Object.keys(this.fieldVectors);for(var u=0;u<h.length;u++){var P=h[u],L=t.FieldRef.fromString(P);n[P]=new t.MatchData}}for(var u=0;u<h.length;u++){var L=t.FieldRef.fromString(h[u]),p=L.docRef;if(A.contains(p)&&!I.contains(p)){var T=this.fieldVectors[L],O=i[L.fieldName].similarity(T),F;if((F=k[p])!==void 0)F.score+=O,F.matchData.combine(n[L]);else{var Q={ref:p,score:O,matchData:n[L]};k[p]=Q,S.push(Q)}}}return S.sort(function(pe,ge){return ge.score-pe.score})},t.Index.prototype.toJSON=function(){var e=Object.keys(this.invertedIndex).sort().map(function(n){return[n,this.invertedIndex[n]]},this),r=Object.keys(this.fieldVectors).map(function(n){return[n,this.fieldVectors[n].toJSON()]},this);return{version:t.version,fields:this.fields,fieldVectors:r,invertedIndex:e,pipeline:this.pipeline.toJSON()}},t.Index.load=function(e){var r={},n={},i=e.fieldVectors,s=Object.create(null),o=e.invertedIndex,a=new t.TokenSet.Builder,u=t.Pipeline.load(e.pipeline);e.version!=t.version&&t.utils.warn("Version mismatch when loading serialised index. Current version of lunr '"+t.version+"' does not match serialized index '"+e.version+"'");for(var c=0;c<i.length;c++){var f=i[c],g=f[0],l=f[1];n[g]=new t.Vector(l)}for(var c=0;c<o.length;c++){var f=o[c],m=f[0],x=f[1];a.insert(m),s[m]=x}return a.finish(),r.fields=e.fields,r.fieldVectors=n,r.invertedIndex=s,r.tokenSet=a.root,r.pipeline=u,new t.Index(r)};/*! | ||
| * lunr.Builder | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.Builder=function(){this._ref="id",this._fields=Object.create(null),this._documents=Object.create(null),this.invertedIndex=Object.create(null),this.fieldTermFrequencies={},this.fieldLengths={},this.tokenizer=t.tokenizer,this.pipeline=new t.Pipeline,this.searchPipeline=new t.Pipeline,this.documentCount=0,this._b=.75,this._k1=1.2,this.termIndex=0,this.metadataWhitelist=[]},t.Builder.prototype.ref=function(e){this._ref=e},t.Builder.prototype.field=function(e,r){if(/\//.test(e))throw new RangeError("Field '"+e+"' contains illegal character '/'");this._fields[e]=r||{}},t.Builder.prototype.b=function(e){e<0?this._b=0:e>1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,r){var n=e[this._ref],i=Object.keys(this._fields);this._documents[n]=r||{},this.documentCount+=1;for(var s=0;s<i.length;s++){var o=i[s],a=this._fields[o].extractor,u=a?a(e):e[o],c=this.tokenizer(u,{fields:[o]}),f=this.pipeline.run(c),g=new t.FieldRef(n,o),l=Object.create(null);this.fieldTermFrequencies[g]=l,this.fieldLengths[g]=0,this.fieldLengths[g]+=f.length;for(var m=0;m<f.length;m++){var x=f[m];if(l[x]==null&&(l[x]=0),l[x]+=1,this.invertedIndex[x]==null){var v=Object.create(null);v._index=this.termIndex,this.termIndex+=1;for(var d=0;d<i.length;d++)v[i[d]]=Object.create(null);this.invertedIndex[x]=v}this.invertedIndex[x][o][n]==null&&(this.invertedIndex[x][o][n]=Object.create(null));for(var y=0;y<this.metadataWhitelist.length;y++){var b=this.metadataWhitelist[y],E=x.metadata[b];this.invertedIndex[x][o][n][b]==null&&(this.invertedIndex[x][o][n][b]=[]),this.invertedIndex[x][o][n][b].push(E)}}}},t.Builder.prototype.calculateAverageFieldLengths=function(){for(var e=Object.keys(this.fieldLengths),r=e.length,n={},i={},s=0;s<r;s++){var o=t.FieldRef.fromString(e[s]),a=o.fieldName;i[a]||(i[a]=0),i[a]+=1,n[a]||(n[a]=0),n[a]+=this.fieldLengths[o]}for(var u=Object.keys(this._fields),s=0;s<u.length;s++){var c=u[s];n[c]=n[c]/i[c]}this.averageFieldLength=n},t.Builder.prototype.createFieldVectors=function(){for(var e={},r=Object.keys(this.fieldTermFrequencies),n=r.length,i=Object.create(null),s=0;s<n;s++){for(var o=t.FieldRef.fromString(r[s]),a=o.fieldName,u=this.fieldLengths[o],c=new t.Vector,f=this.fieldTermFrequencies[o],g=Object.keys(f),l=g.length,m=this._fields[a].boost||1,x=this._documents[o.docRef].boost||1,v=0;v<l;v++){var d=g[v],y=f[d],b=this.invertedIndex[d]._index,E,w,R;i[d]===void 0?(E=t.idf(this.invertedIndex[d],this.documentCount),i[d]=E):E=i[d],w=E*((this._k1+1)*y)/(this._k1*(1-this._b+this._b*(u/this.averageFieldLength[a]))+y),w*=m,w*=x,R=Math.round(w*1e3)/1e3,c.insert(b,R)}e[o]=c}this.fieldVectors=e},t.Builder.prototype.createTokenSet=function(){this.tokenSet=t.TokenSet.fromArray(Object.keys(this.invertedIndex).sort())},t.Builder.prototype.build=function(){return this.calculateAverageFieldLengths(),this.createFieldVectors(),this.createTokenSet(),new t.Index({invertedIndex:this.invertedIndex,fieldVectors:this.fieldVectors,tokenSet:this.tokenSet,fields:Object.keys(this._fields),pipeline:this.searchPipeline})},t.Builder.prototype.use=function(e){var r=Array.prototype.slice.call(arguments,1);r.unshift(this),e.apply(this,r)},t.MatchData=function(e,r,n){for(var i=Object.create(null),s=Object.keys(n||{}),o=0;o<s.length;o++){var a=s[o];i[a]=n[a].slice()}this.metadata=Object.create(null),e!==void 0&&(this.metadata[e]=Object.create(null),this.metadata[e][r]=i)},t.MatchData.prototype.combine=function(e){for(var r=Object.keys(e.metadata),n=0;n<r.length;n++){var i=r[n],s=Object.keys(e.metadata[i]);this.metadata[i]==null&&(this.metadata[i]=Object.create(null));for(var o=0;o<s.length;o++){var a=s[o],u=Object.keys(e.metadata[i][a]);this.metadata[i][a]==null&&(this.metadata[i][a]=Object.create(null));for(var c=0;c<u.length;c++){var f=u[c];this.metadata[i][a][f]==null?this.metadata[i][a][f]=e.metadata[i][a][f]:this.metadata[i][a][f]=this.metadata[i][a][f].concat(e.metadata[i][a][f])}}}},t.MatchData.prototype.add=function(e,r,n){if(!(e in this.metadata)){this.metadata[e]=Object.create(null),this.metadata[e][r]=n;return}if(!(r in this.metadata[e])){this.metadata[e][r]=n;return}for(var i=Object.keys(n),s=0;s<i.length;s++){var o=i[s];o in this.metadata[e][r]?this.metadata[e][r][o]=this.metadata[e][r][o].concat(n[o]):this.metadata[e][r][o]=n[o]}},t.Query=function(e){this.clauses=[],this.allFields=e},t.Query.wildcard=new String("*"),t.Query.wildcard.NONE=0,t.Query.wildcard.LEADING=1,t.Query.wildcard.TRAILING=2,t.Query.presence={OPTIONAL:1,REQUIRED:2,PROHIBITED:3},t.Query.prototype.clause=function(e){return"fields"in e||(e.fields=this.allFields),"boost"in e||(e.boost=1),"usePipeline"in e||(e.usePipeline=!0),"wildcard"in e||(e.wildcard=t.Query.wildcard.NONE),e.wildcard&t.Query.wildcard.LEADING&&e.term.charAt(0)!=t.Query.wildcard&&(e.term="*"+e.term),e.wildcard&t.Query.wildcard.TRAILING&&e.term.slice(-1)!=t.Query.wildcard&&(e.term=""+e.term+"*"),"presence"in e||(e.presence=t.Query.presence.OPTIONAL),this.clauses.push(e),this},t.Query.prototype.isNegated=function(){for(var e=0;e<this.clauses.length;e++)if(this.clauses[e].presence!=t.Query.presence.PROHIBITED)return!1;return!0},t.Query.prototype.term=function(e,r){if(Array.isArray(e))return e.forEach(function(i){this.term(i,t.utils.clone(r))},this),this;var n=r||{};return n.term=e.toString(),this.clause(n),this},t.QueryParseError=function(e,r,n){this.name="QueryParseError",this.message=e,this.start=r,this.end=n},t.QueryParseError.prototype=new Error,t.QueryLexer=function(e){this.lexemes=[],this.str=e,this.length=e.length,this.pos=0,this.start=0,this.escapeCharPositions=[]},t.QueryLexer.prototype.run=function(){for(var e=t.QueryLexer.lexText;e;)e=e(this)},t.QueryLexer.prototype.sliceString=function(){for(var e=[],r=this.start,n=this.pos,i=0;i<this.escapeCharPositions.length;i++)n=this.escapeCharPositions[i],e.push(this.str.slice(r,n)),r=n+1;return e.push(this.str.slice(r,this.pos)),this.escapeCharPositions.length=0,e.join("")},t.QueryLexer.prototype.emit=function(e){this.lexemes.push({type:e,str:this.sliceString(),start:this.start,end:this.pos}),this.start=this.pos},t.QueryLexer.prototype.escapeCharacter=function(){this.escapeCharPositions.push(this.pos-1),this.pos+=1},t.QueryLexer.prototype.next=function(){if(this.pos>=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,r;do e=this.next(),r=e.charCodeAt(0);while(r>47&&r<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos<this.length},t.QueryLexer.EOS="EOS",t.QueryLexer.FIELD="FIELD",t.QueryLexer.TERM="TERM",t.QueryLexer.EDIT_DISTANCE="EDIT_DISTANCE",t.QueryLexer.BOOST="BOOST",t.QueryLexer.PRESENCE="PRESENCE",t.QueryLexer.lexField=function(e){return e.backup(),e.emit(t.QueryLexer.FIELD),e.ignore(),t.QueryLexer.lexText},t.QueryLexer.lexTerm=function(e){if(e.width()>1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var r=e.next();if(r==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(r.charCodeAt(0)==92){e.escapeCharacter();continue}if(r==":")return t.QueryLexer.lexField;if(r=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(r=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(r=="+"&&e.width()===1||r=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(r.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,r){this.lexer=new t.QueryLexer(e),this.query=r,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var r=e.peekLexeme();if(r!=null)switch(r.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var n="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(n+=" with value '"+r.str+"'"),new t.QueryParseError(n,r.start,r.end)}},t.QueryParser.parsePresence=function(e){var r=e.consumeLexeme();if(r!=null){switch(r.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var n="unrecognised presence operator'"+r.str+"'";throw new t.QueryParseError(n,r.start,r.end)}var i=e.peekLexeme();if(i==null){var n="expecting term or field, found nothing";throw new t.QueryParseError(n,r.start,r.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var n="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(n,i.start,i.end)}}},t.QueryParser.parseField=function(e){var r=e.consumeLexeme();if(r!=null){if(e.query.allFields.indexOf(r.str)==-1){var n=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+r.str+"', possible fields: "+n;throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.fields=[r.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,r.start,r.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var r=e.consumeLexeme();if(r!=null){e.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var n=e.peekLexeme();if(n==null){e.nextClause();return}switch(n.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+n.type+"'";throw new t.QueryParseError(i,n.start,n.end)}}},t.QueryParser.parseEditDistance=function(e){var r=e.consumeLexeme();if(r!=null){var n=parseInt(r.str,10);if(isNaN(n)){var i="edit distance must be numeric";throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.editDistance=n;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var r=e.consumeLexeme();if(r!=null){var n=parseInt(r.str,10);if(isNaN(n)){var i="boost must be numeric";throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.boost=n;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},(function(e,r){typeof define=="function"&&define.amd?define(r):typeof ee=="object"?te.exports=r():e.lunr=r()})(this,function(){return t})})()});var Y=Pe(re());function ne(t,e=document){let r=ke(t,e);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${t}" to be present`);return r}function ke(t,e=document){return e.querySelector(t)||void 0}Object.entries||(Object.entries=function(t){let e=[];for(let r of Object.keys(t))e.push([r,t[r]]);return e});Object.values||(Object.values=function(t){let e=[];for(let r of Object.keys(t))e.push(t[r]);return e});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(t,e){typeof t=="object"?(this.scrollLeft=t.left,this.scrollTop=t.top):(this.scrollLeft=t,this.scrollTop=e)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...t){let e=this.parentNode;if(e){t.length===0&&e.removeChild(this);for(let r=t.length-1;r>=0;r--){let n=t[r];typeof n=="string"?n=document.createTextNode(n):n.parentNode&&n.parentNode.removeChild(n),r?e.insertBefore(this.previousSibling,n):e.replaceChild(n,this)}}}));function ie(t){let e=new Map;for(let r of t){let[n]=r.location.split("#"),i=e.get(n);typeof i=="undefined"?e.set(n,r):(e.set(r.location,r),r.parent=i)}return e}function H(t,e,r){var s;e=new RegExp(e,"g");let n,i=0;do{n=e.exec(t);let o=(s=n==null?void 0:n.index)!=null?s:t.length;if(i<o&&r(i,o),n){let[a]=n;i=n.index+a.length,a.length===0&&(e.lastIndex=n.index+1)}}while(n)}function se(t,e){let r=0,n=0,i=0;for(let s=0;i<t.length;i++)t.charAt(i)==="<"&&i>n?e(r,1,n,n=i):t.charAt(i)===">"&&(t.charAt(n+1)==="/"?--s===0&&e(r++,2,n,i+1):t.charAt(i-1)!=="/"&&s++===0&&e(r,0,n,i+1),n=i+1);i>n&&e(r,1,n,i)}function oe(t,e,r,n=!1){return q([t],e,r,n).pop()}function q(t,e,r,n=!1){let i=[0];for(let s=1;s<e.length;s++){let o=e[s-1],a=e[s],u=o[o.length-1]>>>2&1023,c=a[0]>>>12;i.push(+(u>c)+i[i.length-1])}return t.map((s,o)=>{let a=0,u=new Map;for(let f of r.sort((g,l)=>g-l)){let g=f&1048575,l=f>>>20;if(i[l]!==o)continue;let m=u.get(l);typeof m=="undefined"&&u.set(l,m=[]),m.push(g)}if(u.size===0)return s;let c=[];for(let[f,g]of u){let l=e[f],m=l[0]>>>12,x=l[l.length-1]>>>12,v=l[l.length-1]>>>2&1023;n&&m>a&&c.push(s.slice(a,m));let d=s.slice(m,x+v);for(let y of g.sort((b,E)=>E-b)){let b=(l[y]>>>12)-m,E=(l[y]>>>2&1023)+b;d=[d.slice(0,b),"<mark>",d.slice(b,E),"</mark>",d.slice(E)].join("")}if(a=x+v,c.push(d)===2)break}return n&&a<s.length&&c.push(s.slice(a)),c.join("")})}function ae(t){let e=[];if(typeof t=="undefined")return e;let r=Array.isArray(t)?t:[t];for(let n=0;n<r.length;n++){let i=lunr.tokenizer.table,s=i.length;se(r[n],(o,a,u,c)=>{var f;switch(i[f=o+=s]||(i[f]=[]),a){case 0:case 2:i[o].push(u<<12|c-u<<2|a);break;case 1:let g=r[n].slice(u,c);H(g,lunr.tokenizer.separator,(l,m)=>{if(typeof lunr.segmenter!="undefined"){let x=g.slice(l,m);if(/^[MHIK]$/.test(lunr.segmenter.ctype_(x))){let v=lunr.segmenter.segment(x);for(let d=0,y=0;d<v.length;d++)i[o]||(i[o]=[]),i[o].push(u+l+y<<12|v[d].length<<2|a),e.push(new lunr.Token(v[d].toLowerCase(),{position:o<<20|i[o].length-1})),y+=v[d].length;return}}i[o].push(u+l<<12|m-l<<2|a),e.push(new lunr.Token(g.slice(l,m).toLowerCase(),{position:o<<20|i[o].length-1}))})}})}return e}function ue(t,e=r=>r){return t.trim().split(/"([^"]+)"/g).map((r,n)=>n&1?r.replace(/^\b|^(?![^\x00-\x7F]|$)|\s+/g," +"):r).join("").replace(/"|(?:^|\s+)[*+\-:^~]+(?=\s+|$)/g,"").split(/\s+/g).reduce((r,n)=>{let i=e(n);return[...r,...Array.isArray(i)?i:[i]]},[]).map(r=>/([~^]$)/.test(r)?`${r}1`:r).map(r=>/(^[+-]|[~^]\d+$)/.test(r)?r:`${r}*`).join(" ")}function ce(t){return ue(t,e=>{let r=[],n=new lunr.QueryLexer(e);n.run();for(let{type:i,str:s,start:o,end:a}of n.lexemes)switch(i){case"FIELD":["title","text","tags"].includes(s)||(e=[e.slice(0,a)," ",e.slice(a+1)].join(""));break;case"TERM":H(s,lunr.tokenizer.separator,(...u)=>{r.push([e.slice(0,o),s.slice(...u),e.slice(a)].join(""))})}return r})}function le(t){let e=new lunr.Query(["title","text","tags"]);new lunr.QueryParser(t,e).parse();for(let n of e.clauses)n.usePipeline=!0,n.term.startsWith("*")&&(n.wildcard=lunr.Query.wildcard.LEADING,n.term=n.term.slice(1)),n.term.endsWith("*")&&(n.wildcard=lunr.Query.wildcard.TRAILING,n.term=n.term.slice(0,-1));return e.clauses}function he(t,e){var i;let r=new Set(t),n={};for(let s=0;s<e.length;s++)for(let o of r)e[s].startsWith(o.term)&&(n[o.term]=!0,r.delete(o));for(let s of r)(i=lunr.stopWordFilter)!=null&&i.call(lunr,s.term)&&(n[s.term]=!1);return n}function fe(t,e){let r=new Set,n=new Uint16Array(t.length);for(let s=0;s<t.length;s++)for(let o=s+1;o<t.length;o++)t.slice(s,o)in e&&(n[s]=o-s);let i=[0];for(let s=i.length;s>0;){let o=i[--s];for(let u=1;u<n[o];u++)n[o+u]>n[o]-u&&(r.add(t.slice(o,o+u)),i[s++]=o+u);let a=o+n[o];n[a]&&a<t.length-1&&(i[s++]=a),r.add(t.slice(o,a))}return r.has("")?new Set([t]):r}function Oe(t){return e=>r=>{if(typeof r[e]=="undefined")return;let n=[r.location,e].join(":");return t.set(n,lunr.tokenizer.table=[]),r[e]}}function Re(t,e){let[r,n]=[new Set(t),new Set(e)];return[...new Set([...r].filter(i=>!n.has(i)))]}var U=class{constructor({config:e,docs:r,options:n}){let i=Oe(this.table=new Map);this.map=ie(r),this.options=n,this.index=lunr(function(){this.metadataWhitelist=["position"],this.b(0),e.lang.length===1&&e.lang[0]!=="en"?this.use(lunr[e.lang[0]]):e.lang.length>1&&this.use(lunr.multiLanguage(...e.lang)),this.tokenizer=ae,lunr.tokenizer.separator=new RegExp(e.separator),lunr.segmenter="TinySegmenter"in lunr?new lunr.TinySegmenter:void 0;let s=Re(["trimmer","stopWordFilter","stemmer"],e.pipeline);for(let o of e.lang.map(a=>a==="en"?lunr:lunr[a]))for(let a of s)this.pipeline.remove(o[a]),this.searchPipeline.remove(o[a]);this.ref("location");for(let[o,a]of Object.entries(e.fields))this.field(o,B(_({},a),{extractor:i(o)}));for(let o of r)this.add(o,{boost:o.boost})})}search(e){if(e=e.replace(new RegExp("\\p{sc=Han}+","gu"),s=>[...fe(s,this.index.invertedIndex)].join("* ")),e=ce(e),!e)return{items:[]};let r=le(e).filter(s=>s.presence!==lunr.Query.presence.PROHIBITED),n=this.index.search(e).reduce((s,{ref:o,score:a,matchData:u})=>{let c=this.map.get(o);if(typeof c!="undefined"){c=_({},c),c.tags&&(c.tags=[...c.tags]);let f=he(r,Object.keys(u.metadata));for(let l of this.index.fields){if(typeof c[l]=="undefined")continue;let m=[];for(let d of Object.values(u.metadata))typeof d[l]!="undefined"&&m.push(...d[l].position);if(!m.length)continue;let x=this.table.get([c.location,l].join(":")),v=Array.isArray(c[l])?q:oe;c[l]=v(c[l],x,m,l!=="text")}let g=+!c.parent+Object.values(f).filter(l=>l).length/Object.keys(f).length;s.push(B(_({},c),{score:a*(1+K(g,2)),terms:f}))}return s},[]).sort((s,o)=>o.score-s.score).reduce((s,o)=>{let a=this.map.get(o.location);if(typeof a!="undefined"){let u=a.parent?a.parent.location:a.location;s.set(u,[...s.get(u)||[],o])}return s},new Map);for(let[s,o]of n)if(!o.find(a=>a.location===s)){let a=this.map.get(s);o.push(B(_({},a),{score:0,terms:{}}))}let i;if(this.options.suggest){let s=this.index.query(o=>{for(let a of r)o.term(a.term,{fields:["title"],presence:lunr.Query.presence.REQUIRED,wildcard:lunr.Query.wildcard.TRAILING})});i=s.length?Object.keys(s[0].matchData.metadata):[]}return _({items:[...n.values()]},typeof i!="undefined"&&{suggest:i})}};var de;function Ie(t){return W(this,null,function*(){let e="../lunr";if(typeof parent!="undefined"&&"IFrameWorker"in parent){let n=ne("script[src]"),[i]=n.src.split("/worker");e=e.replace("..",i)}let r=[];for(let n of t.lang){switch(n){case"ja":r.push(`${e}/tinyseg.js`);break;case"hi":case"th":r.push(`${e}/wordcut.js`);break}n!=="en"&&r.push(`${e}/min/lunr.${n}.min.js`)}t.lang.length>1&&r.push(`${e}/min/lunr.multi.min.js`),r.length&&(yield importScripts(`${e}/min/lunr.stemmer.support.min.js`,...r))})}function Fe(t){return W(this,null,function*(){switch(t.type){case 0:return yield Ie(t.data.config),de=new U(t.data),{type:1};case 2:let e=t.data;try{return{type:3,data:de.search(e)}}catch(r){return console.warn(`Invalid query: ${e} \u2013 see https://bit.ly/2s3ChXG`),console.warn(r),{type:3,data:{items:[]}}}default:throw new TypeError("Invalid message type")}})}self.lunr=Y.default;Y.default.utils.warn=console.warn;addEventListener("message",t=>W(null,null,function*(){postMessage(yield Fe(t.data))}));})(); | ||
| //# sourceMappingURL=search.7a47a382.min.js.map | ||
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
| {"version":3,"sources":["src/templates/assets/stylesheets/main/components/_meta.scss","../../../../src/templates/assets/stylesheets/main.scss","src/templates/assets/stylesheets/main/_resets.scss","src/templates/assets/stylesheets/main/_colors.scss","src/templates/assets/stylesheets/main/_icons.scss","src/templates/assets/stylesheets/main/_typeset.scss","src/templates/assets/stylesheets/utilities/_break.scss","src/templates/assets/stylesheets/main/components/_author.scss","src/templates/assets/stylesheets/main/components/_banner.scss","src/templates/assets/stylesheets/main/components/_base.scss","src/templates/assets/stylesheets/main/components/_clipboard.scss","src/templates/assets/stylesheets/main/components/_code.scss","src/templates/assets/stylesheets/main/components/_consent.scss","src/templates/assets/stylesheets/main/components/_content.scss","src/templates/assets/stylesheets/main/components/_dialog.scss","src/templates/assets/stylesheets/main/components/_feedback.scss","src/templates/assets/stylesheets/main/components/_footer.scss","src/templates/assets/stylesheets/main/components/_form.scss","src/templates/assets/stylesheets/main/components/_header.scss","node_modules/material-design-color/material-color.scss","src/templates/assets/stylesheets/main/components/_nav.scss","src/templates/assets/stylesheets/main/components/_pagination.scss","src/templates/assets/stylesheets/main/components/_path.scss","src/templates/assets/stylesheets/main/components/_post.scss","src/templates/assets/stylesheets/main/components/_progress.scss","src/templates/assets/stylesheets/main/components/_search.scss","src/templates/assets/stylesheets/main/components/_select.scss","src/templates/assets/stylesheets/main/components/_sidebar.scss","src/templates/assets/stylesheets/main/components/_source.scss","src/templates/assets/stylesheets/main/components/_status.scss","src/templates/assets/stylesheets/main/components/_tabs.scss","src/templates/assets/stylesheets/main/components/_tag.scss","src/templates/assets/stylesheets/main/components/_tooltip.scss","src/templates/assets/stylesheets/main/components/_tooltip2.scss","src/templates/assets/stylesheets/main/components/_top.scss","src/templates/assets/stylesheets/main/components/_version.scss","src/templates/assets/stylesheets/main/extensions/markdown/_admonition.scss","src/templates/assets/stylesheets/main/extensions/markdown/_footnotes.scss","src/templates/assets/stylesheets/main/extensions/markdown/_toc.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_critic.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_details.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_emoji.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_highlight.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_keys.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss","src/templates/assets/stylesheets/main/integrations/_giscus.scss","src/templates/assets/stylesheets/main/integrations/_mermaid.scss","src/templates/assets/stylesheets/main/modifiers/_grid.scss","src/templates/assets/stylesheets/main/modifiers/_inline.scss"],"names":[],"mappings":"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CAmBH,iDAAA,CAAA,yCnCkhHN,CmC9gHI,wEACE,anCghHN,CmCriHE,4DACE,oBnCwiHJ,CmCriHI,sFACE,gCnCuiHN,CmCliHE,oEACE,0BnCqiHJ,CmCliHI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6CnCoiHN,CmChiHI,gFACE,anCkiHN,CmCvjHE,8DACE,oBnC0jHJ,CmCvjHI,wFACE,gCnCyjHN,CmCpjHE,sEACE,0BnCujHJ,CmCpjHI,oFACE,wBAlBG,CAmBH,sDAAA,CAAA,8CnCsjHN,CmCljHI,kFACE,anCojHN,CmCzkHE,4DACE,oBnC4kHJ,CmCzkHI,sFACE,gCnC2kHN,CmCtkHE,oEACE,0BnCykHJ,CmCtkHI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6CnCwkHN,CmCpkHI,gFACE,anCskHN,CmC3lHE,4DACE,oBnC8lHJ,CmC3lHI,sFACE,gCnC6lHN,CmCxlHE,oEACE,0BnC2lHJ,CmCxlHI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6CnC0lHN,CmCtlHI,gFACE,anCwlHN,CmC7mHE,0DACE,oBnCgnHJ,CmC7mHI,oFACE,gCnC+mHN,CmC1mHE,kEACE,0BnC6mHJ,CmC1mHI,gFACE,wBAlBG,CAmBH,oDAAA,CAAA,4CnC4mHN,CmCxmHI,8EACE,anC0mHN,CmC/nHE,oDACE,oBnCkoHJ,CmC/nHI,8EACE,gCnCioHN,CmC5nHE,4DACE,0BnC+nHJ,CmC5nHI,0EACE,wBAlBG,CAmBH,iDAAA,CAAA,yCnC8nHN,CmC1nHI,wEACE,anC4nHN,CmCjpHE,4DACE,oBnCopHJ,CmCjpHI,sFACE,gCnCmpHN,CmC9oHE,oEACE,0BnCipHJ,CmC9oHI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6CnCgpHN,CmC5oHI,gFACE,anC8oHN,CmCnqHE,wDACE,oBnCsqHJ,CmCnqHI,kFACE,gCnCqqHN,CmChqHE,gEACE,0BnCmqHJ,CmChqHI,8EACE,wBAlBG,CAmBH,mDAAA,CAAA,2CnCkqHN,CmC9pHI,4EACE,anCgqHN,CoCp0HA,MACE,qMpCu0HF,CoC9zHE,sBAEE,uCAAA,CADA,g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Cv7IQ,+CACE,Q5Cy7IV,C4C17IQ,+CACE,Q5C47IV,C4C77IQ,+CACE,Q5C+7IV,C4Ch8IQ,+CACE,Q5Ck8IV,C4Cn8IQ,+CACE,Q5Cq8IV,C4Ct8IQ,+CACE,Q5Cw8IV,C4Cz8IQ,+CACE,Q5C28IV,C4C58IQ,+CACE,Q5C88IV,CACF,C4Cz8IM,uCACE,gC5C28IR,C4Cv8IM,oDACE,a5Cy8IR,C4Cp8II,yCACE,S5Cs8IN,C4Cl8IM,2CACE,aAAA,CACA,8B5Co8IR,C4C97IE,4BACE,U5Cg8IJ,C4C77II,aAJF,4BAKI,gB5Cg8IJ,CACF,C4C57IE,0BACE,Y5C87IJ,C4C37II,aAJF,0BAKI,a5C87IJ,C4C17IM,sCACE,O5C47IR,C4C77IM,uCACE,O5C+7IR,C4Ch8IM,uCACE,O5Ck8IR,C4Cn8IM,uCACE,O5Cq8IR,C4Ct8IM,uCACE,O5Cw8IR,C4Cz8IM,uCACE,O5C28IR,C4C58IM,uCACE,O5C88IR,C4C/8IM,uCACE,O5Ci9IR,C4Cl9IM,uCACE,O5Co9IR,C4Cr9IM,wCACE,Q5Cu9IR,C4Cx9IM,wCACE,Q5C09IR,C4C39IM,wCACE,Q5C69IR,C4C99IM,wCACE,Q5Cg+IR,C4Cj+IM,wCACE,Q5Cm+IR,C4Cp+IM,wCACE,Q5Cs+IR,C4Cv+IM,wCACE,Q5Cy+IR,C4C1+IM,wCACE,Q5C4+IR,C4C7+IM,wCACE,Q5C++IR,C4Ch/IM,wCACE,Q5Ck/IR,C4Cn/IM,wCACE,Q5Cq/IR,CACF,C4C/+II,+FAEE,Q5Ci/IN,C4C9+IM,yGACE,wBAAA,CACA,yB5Ci/IR,C4Cx+IM,2DAEE,wBAAA,CACA,yBAAA,CAFA,Q5C4+IR,C4Cr+IM,iEACE,Q5Cu+IR,C4Cp+IQ,qLAGE,wBAAA,CACA,yBAAA,CAFA,Q5Cw+IV,C4Cl+IQ,6FACE,wBAAA,CACA,yB5Co+IV,C4C/9IM,yDACE,kB5Ci+IR,C4C59II,sCACE,Q5C89IN,C4Cz9IE,2BAEE,iBAAA,CAOA,kBAAA,CAHA,uCAAA,CAEA,cAAA,CAPA,aAAA,CAGA,YAAA,CACA,gBAAA,CAEA,mBAAA,CAGA,gCAAA,CAPA,W5Ck+IJ,C4Cx9II,i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","file":"main.css"} |
| @media screen{[data-md-color-scheme=slate]{--md-default-fg-color:hsla(var(--md-hue),15%,90%,0.82);--md-default-fg-color--light:hsla(var(--md-hue),15%,90%,0.56);--md-default-fg-color--lighter:hsla(var(--md-hue),15%,90%,0.32);--md-default-fg-color--lightest:hsla(var(--md-hue),15%,90%,0.12);--md-default-bg-color:hsla(var(--md-hue),15%,14%,1);--md-default-bg-color--light:hsla(var(--md-hue),15%,14%,0.54);--md-default-bg-color--lighter:hsla(var(--md-hue),15%,14%,0.26);--md-default-bg-color--lightest:hsla(var(--md-hue),15%,14%,0.07);--md-code-fg-color:hsla(var(--md-hue),18%,86%,0.82);--md-code-bg-color:hsla(var(--md-hue),15%,18%,1);--md-code-bg-color--light:hsla(var(--md-hue),15%,18%,0.9);--md-code-bg-color--lighter:hsla(var(--md-hue),15%,18%,0.54);--md-code-hl-color:#2977ff;--md-code-hl-color--light:#2977ff1a;--md-code-hl-number-color:#e6695b;--md-code-hl-special-color:#f06090;--md-code-hl-function-color:#c973d9;--md-code-hl-constant-color:#9383e2;--md-code-hl-keyword-color:#6791e0;--md-code-hl-string-color:#2fb170;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-kbd-color:hsla(var(--md-hue),15%,90%,0.12);--md-typeset-kbd-accent-color:hsla(var(--md-hue),15%,90%,0.2);--md-typeset-kbd-border-color:hsla(var(--md-hue),15%,14%,1);--md-typeset-mark-color:#4287ff4d;--md-typeset-table-color:hsla(var(--md-hue),15%,95%,0.12);--md-typeset-table-color--light:hsla(var(--md-hue),15%,95%,0.035);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-footer-bg-color:hsla(var(--md-hue),15%,10%,0.87);--md-footer-bg-color--dark:hsla(var(--md-hue),15%,8%,1);--md-shadow-z1:0 0.2rem 0.5rem #0000000d,0 0 0.05rem #0000001a;--md-shadow-z2:0 0.2rem 0.5rem #00000040,0 0 0.05rem #00000040;--md-shadow-z3:0 0.2rem 0.5rem #0006,0 0 0.05rem #00000059;color-scheme:dark}[data-md-color-scheme=slate] img[src$="#gh-light-mode-only"],[data-md-color-scheme=slate] img[src$="#only-light"]{display:none}[data-md-color-scheme=slate][data-md-color-primary=pink]{--md-typeset-a-color:#ed5487}[data-md-color-scheme=slate][data-md-color-primary=purple]{--md-typeset-a-color:#c46fd3}[data-md-color-scheme=slate][data-md-color-primary=deep-purple]{--md-typeset-a-color:#a47bea}[data-md-color-scheme=slate][data-md-color-primary=indigo]{--md-typeset-a-color:#5488e8}[data-md-color-scheme=slate][data-md-color-primary=teal]{--md-typeset-a-color:#00ccb8}[data-md-color-scheme=slate][data-md-color-primary=green]{--md-typeset-a-color:#71c174}[data-md-color-scheme=slate][data-md-color-primary=deep-orange]{--md-typeset-a-color:#ff764d}[data-md-color-scheme=slate][data-md-color-primary=brown]{--md-typeset-a-color:#c1775c}[data-md-color-scheme=slate][data-md-color-primary=black],[data-md-color-scheme=slate][data-md-color-primary=blue-grey],[data-md-color-scheme=slate][data-md-color-primary=grey],[data-md-color-scheme=slate][data-md-color-primary=white]{--md-typeset-a-color:#5e8bde}[data-md-color-switching] *,[data-md-color-switching] :after,[data-md-color-switching] :before{transition-duration:0ms!important}}[data-md-color-accent=red]{--md-accent-fg-color:#ff1947;--md-accent-fg-color--transparent:#ff19471a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=pink]{--md-accent-fg-color:#f50056;--md-accent-fg-color--transparent:#f500561a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=purple]{--md-accent-fg-color:#df41fb;--md-accent-fg-color--transparent:#df41fb1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=deep-purple]{--md-accent-fg-color:#7c4dff;--md-accent-fg-color--transparent:#7c4dff1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=indigo]{--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:#526cfe1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=blue]{--md-accent-fg-color:#4287ff;--md-accent-fg-color--transparent:#4287ff1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=light-blue]{--md-accent-fg-color:#0091eb;--md-accent-fg-color--transparent:#0091eb1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=cyan]{--md-accent-fg-color:#00bad6;--md-accent-fg-color--transparent:#00bad61a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=teal]{--md-accent-fg-color:#00bda4;--md-accent-fg-color--transparent:#00bda41a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=green]{--md-accent-fg-color:#00c753;--md-accent-fg-color--transparent:#00c7531a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=light-green]{--md-accent-fg-color:#63de17;--md-accent-fg-color--transparent:#63de171a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=lime]{--md-accent-fg-color:#b0eb00;--md-accent-fg-color--transparent:#b0eb001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=yellow]{--md-accent-fg-color:#ffd500;--md-accent-fg-color--transparent:#ffd5001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=amber]{--md-accent-fg-color:#fa0;--md-accent-fg-color--transparent:#ffaa001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=orange]{--md-accent-fg-color:#ff9100;--md-accent-fg-color--transparent:#ff91001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=deep-orange]{--md-accent-fg-color:#ff6e42;--md-accent-fg-color--transparent:#ff6e421a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-primary=red]{--md-primary-fg-color:#ef5552;--md-primary-fg-color--light:#e57171;--md-primary-fg-color--dark:#e53734;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=pink]{--md-primary-fg-color:#e92063;--md-primary-fg-color--light:#ec417a;--md-primary-fg-color--dark:#c3185d;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=purple]{--md-primary-fg-color:#ab47bd;--md-primary-fg-color--light:#bb69c9;--md-primary-fg-color--dark:#8c24a8;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=deep-purple]{--md-primary-fg-color:#7e56c2;--md-primary-fg-color--light:#9574cd;--md-primary-fg-color--dark:#673ab6;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=indigo]{--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=blue]{--md-primary-fg-color:#2094f3;--md-primary-fg-color--light:#42a5f5;--md-primary-fg-color--dark:#1975d2;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=light-blue]{--md-primary-fg-color:#02a6f2;--md-primary-fg-color--light:#28b5f6;--md-primary-fg-color--dark:#0287cf;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=cyan]{--md-primary-fg-color:#00bdd6;--md-primary-fg-color--light:#25c5da;--md-primary-fg-color--dark:#0097a8;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=teal]{--md-primary-fg-color:#009485;--md-primary-fg-color--light:#26a699;--md-primary-fg-color--dark:#007a6c;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=green]{--md-primary-fg-color:#4cae4f;--md-primary-fg-color--light:#68bb6c;--md-primary-fg-color--dark:#398e3d;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=light-green]{--md-primary-fg-color:#8bc34b;--md-primary-fg-color--light:#9ccc66;--md-primary-fg-color--dark:#689f38;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=lime]{--md-primary-fg-color:#cbdc38;--md-primary-fg-color--light:#d3e156;--md-primary-fg-color--dark:#b0b52c;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=yellow]{--md-primary-fg-color:#ffec3d;--md-primary-fg-color--light:#ffee57;--md-primary-fg-color--dark:#fbc02d;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=amber]{--md-primary-fg-color:#ffc105;--md-primary-fg-color--light:#ffc929;--md-primary-fg-color--dark:#ffa200;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=orange]{--md-primary-fg-color:#ffa724;--md-primary-fg-color--light:#ffa724;--md-primary-fg-color--dark:#fa8900;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=deep-orange]{--md-primary-fg-color:#ff6e42;--md-primary-fg-color--light:#ff8a66;--md-primary-fg-color--dark:#f4511f;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=brown]{--md-primary-fg-color:#795649;--md-primary-fg-color--light:#8d6e62;--md-primary-fg-color--dark:#5d4037;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=grey]{--md-primary-fg-color:#757575;--md-primary-fg-color--light:#9e9e9e;--md-primary-fg-color--dark:#616161;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-typeset-a-color:#4051b5}[data-md-color-primary=blue-grey]{--md-primary-fg-color:#546d78;--md-primary-fg-color--light:#607c8a;--md-primary-fg-color--dark:#455a63;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-typeset-a-color:#4051b5}[data-md-color-primary=light-green]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#72ad2e}[data-md-color-primary=lime]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#8b990a}[data-md-color-primary=yellow]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#b8a500}[data-md-color-primary=amber]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#d19d00}[data-md-color-primary=orange]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#e68a00}[data-md-color-primary=white]{--md-primary-fg-color:hsla(var(--md-hue),0%,100%,1);--md-primary-fg-color--light:hsla(var(--md-hue),0%,100%,0.7);--md-primary-fg-color--dark:hsla(var(--md-hue),0%,0%,0.07);--md-primary-bg-color:hsla(var(--md-hue),0%,0%,0.87);--md-primary-bg-color--light:hsla(var(--md-hue),0%,0%,0.54);--md-typeset-a-color:#4051b5}[data-md-color-primary=white] .md-button{color:var(--md-typeset-a-color)}[data-md-color-primary=white] .md-button--primary{background-color:var(--md-typeset-a-color);border-color:var(--md-typeset-a-color);color:hsla(var(--md-hue),0%,100%,1)}@media screen and (min-width:60em){[data-md-color-primary=white] .md-search__form{background-color:hsla(var(--md-hue),0%,0%,.07)}[data-md-color-primary=white] .md-search__form:hover{background-color:hsla(var(--md-hue),0%,0%,.32)}[data-md-color-primary=white] .md-search__input+.md-search__icon{color:hsla(var(--md-hue),0%,0%,.87)}}@media screen and (min-width:76.25em){[data-md-color-primary=white] .md-tabs{border-bottom:.05rem solid #00000012}}[data-md-color-primary=black]{--md-primary-fg-color:hsla(var(--md-hue),15%,9%,1);--md-primary-fg-color--light:hsla(var(--md-hue),15%,9%,0.54);--md-primary-fg-color--dark:hsla(var(--md-hue),15%,9%,1);--md-primary-bg-color:hsla(var(--md-hue),15%,100%,1);--md-primary-bg-color--light:hsla(var(--md-hue),15%,100%,0.7);--md-typeset-a-color:#4051b5}[data-md-color-primary=black] .md-button{color:var(--md-typeset-a-color)}[data-md-color-primary=black] .md-button--primary{background-color:var(--md-typeset-a-color);border-color:var(--md-typeset-a-color);color:hsla(var(--md-hue),0%,100%,1)}[data-md-color-primary=black] .md-header{background-color:hsla(var(--md-hue),15%,9%,1)}@media screen and (max-width:59.984375em){[data-md-color-primary=black] .md-nav__source{background-color:hsla(var(--md-hue),15%,11%,.87)}}@media screen and (max-width:76.234375em){html [data-md-color-primary=black] .md-nav--primary .md-nav__title[for=__drawer]{background-color:hsla(var(--md-hue),15%,9%,1)}}@media screen and (min-width:76.25em){[data-md-color-primary=black] .md-tabs{background-color:hsla(var(--md-hue),15%,9%,1)}} |
| {"version":3,"sources":["src/templates/assets/stylesheets/palette/_scheme.scss","../../../../src/templates/assets/stylesheets/palette.scss","src/templates/assets/stylesheets/palette/_accent.scss","src/templates/assets/stylesheets/palette/_primary.scss","src/templates/assets/stylesheets/utilities/_break.scss"],"names":[],"mappings":"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","file":"palette.css"} |
| {#- | ||
| This file was automatically generated - do not edit | ||
| -#} | ||
| {% macro render_content(nav_item) %} | ||
| <span class="md-ellipsis"> | ||
| {{ nav_item.title }} | ||
| </span> | ||
| {% endmacro %} | ||
| {% macro render(nav_item, ref) %} | ||
| {% set ref = ref or nav_item %} | ||
| {% if nav_item.children %} | ||
| {% set first = nav_item.children | first %} | ||
| {% if first.children %} | ||
| {{ render(first, ref) }} | ||
| {% else %} | ||
| <li class="md-path__item"> | ||
| <a href="{{ first.url | url }}" class="md-path__link"> | ||
| {{ render_content(ref) }} | ||
| </a> | ||
| </li> | ||
| {% endif %} | ||
| {% else %} | ||
| <li class="md-path__item"> | ||
| <a href="{{ nav_item.url | url }}" class="md-path__link"> | ||
| {{ render_content(ref) }} | ||
| </a> | ||
| </li> | ||
| {% endif %} | ||
| {% endmacro %} |
| {#- | ||
| This file was automatically generated - do not edit | ||
| -#} | ||
| {% import "partials/path-item.html" as item with context %} | ||
| {% if page.meta and page.meta.hide %} | ||
| {% set hidden = "hidden" if "path" in page.meta.hide %} | ||
| {% endif %} | ||
| {% set depth = page.ancestors | length %} | ||
| {% if nav.homepage %} | ||
| {% set depth = depth + 1 %} | ||
| {% endif %} | ||
| {% if depth > 1 %} | ||
| <nav class="md-path" aria-label="{{ lang.t('nav') }}" {{ hidden }}> | ||
| <ol class="md-path__list"> | ||
| {% if nav.homepage %} | ||
| {{ item.render(nav.homepage) }} | ||
| {% endif %} | ||
| {% for nav_item in page.ancestors | reverse %} | ||
| {{ item.render(nav_item) }} | ||
| {% endfor %} | ||
| </ol> | ||
| </nav> | ||
| {% endif %} |
@@ -21,2 +21,2 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| __version__ = "9.6.23" | ||
| __version__ = "9.7.0" |
@@ -20,1 +20,15 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # IN THE SOFTWARE. | ||
| from .structure import View | ||
| # ----------------------------------------------------------------------------- | ||
| # Functions | ||
| # ----------------------------------------------------------------------------- | ||
| # Sort views by name | ||
| def view_name(view: View): | ||
| return view.name | ||
| # Sort views by post count | ||
| def view_post_count(view: View): | ||
| return len(view.posts) |
@@ -26,2 +26,4 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| from . import view_name | ||
| # ----------------------------------------------------------------------------- | ||
@@ -60,2 +62,4 @@ # Classes | ||
| archive_url_format = Type(str, default = "archive/{date}") | ||
| archive_pagination = Optional(Type(bool)) | ||
| archive_pagination_per_page = Optional(Type(int)) | ||
| archive_toc = Optional(Type(bool)) | ||
@@ -69,3 +73,7 @@ | ||
| categories_slugify_separator = Type(str, default = "-") | ||
| categories_sort_by = Type(Callable, default = view_name) | ||
| categories_sort_reverse = Type(bool, default = False) | ||
| categories_allowed = Type(list, default = []) | ||
| categories_pagination = Optional(Type(bool)) | ||
| categories_pagination_per_page = Optional(Type(int)) | ||
| categories_toc = Optional(Type(bool)) | ||
@@ -76,2 +84,8 @@ | ||
| authors_file = Type(str, default = "{blog}/.authors.yml") | ||
| authors_profiles = Type(bool, default = False) | ||
| authors_profiles_name = Type(str, default = "blog.authors") | ||
| authors_profiles_url_format = Type(str, default = "author/{slug}") | ||
| authors_profiles_pagination = Optional(Type(bool)) | ||
| authors_profiles_pagination_per_page = Optional(Type(int)) | ||
| authors_profiles_toc = Optional(Type(bool)) | ||
@@ -78,0 +92,0 @@ # Settings for pagination |
@@ -48,6 +48,11 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| from .author import Authors | ||
| from . import view_name | ||
| from .author import Author, Authors | ||
| from .config import BlogConfig | ||
| from .readtime import readtime | ||
| from .structure import Archive, Category, Excerpt, Post, Reference, View | ||
| from .structure import ( | ||
| Archive, Category, Profile, | ||
| Excerpt, Post, View, | ||
| Reference | ||
| ) | ||
@@ -90,8 +95,2 @@ # ----------------------------------------------------------------------------- | ||
| # Initialize table of contents settings | ||
| if not isinstance(self.config.archive_toc, bool): | ||
| self.config.archive_toc = self.config.blog_toc | ||
| if not isinstance(self.config.categories_toc, bool): | ||
| self.config.categories_toc = self.config.blog_toc | ||
| # By default, drafts are rendered when the documentation is served, | ||
@@ -139,3 +138,6 @@ # but not when it is built, for a better user experience | ||
| self._resolve_posts(files, config), | ||
| key = lambda post: post.config.date.created, | ||
| key = lambda post: ( | ||
| post.config.pin, | ||
| post.config.date.created | ||
| ), | ||
| reverse = True | ||
@@ -146,17 +148,27 @@ ) | ||
| if self.config.archive: | ||
| self.blog.views.extend( | ||
| self._generate_archive(config, files) | ||
| ) | ||
| views = self._generate_archive(config, files) | ||
| self.blog.views.extend(views) | ||
| # Generate views for categories | ||
| if self.config.categories: | ||
| views = self._generate_categories(config, files) | ||
| # We always sort the list of categories by name first, so that any | ||
| # custom sorting function that returns the same value for two items | ||
| # returns them in a predictable and logical order, because sorting | ||
| # in Python is stable, i.e., order of equal items is preserved | ||
| self.blog.views.extend(sorted( | ||
| self._generate_categories(config, files), | ||
| key = lambda view: view.name, | ||
| reverse = False | ||
| sorted(views, key = view_name), | ||
| key = self.config.categories_sort_by, | ||
| reverse = self.config.categories_sort_reverse | ||
| )) | ||
| # Generate views for profiles | ||
| if self.config.authors_profiles: | ||
| views = self._generate_profiles(config, files) | ||
| self.blog.views.extend(views) | ||
| # Generate pages for views | ||
| if self.config.pagination: | ||
| for view in self._resolve_views(self.blog): | ||
| for view in self._resolve_views(self.blog): | ||
| if self._config_pagination(view): | ||
| for page in self._generate_pages(view, config, files): | ||
@@ -216,5 +228,14 @@ view.pages.append(page) | ||
| # Attach views for profiles | ||
| if self.config.authors_profiles: | ||
| title = self._translate(self.config.authors_profiles_name, config) | ||
| views = [_ for _ in self.blog.views if isinstance(_, Profile)] | ||
| # Attach and link views for categories, if any | ||
| if self.blog.file.inclusion.is_in_nav() and views: | ||
| self._attach_to(self.blog, Section(title, views), nav) | ||
| # Attach pages for views | ||
| if self.config.pagination: | ||
| for view in self._resolve_views(self.blog): | ||
| for view in self._resolve_views(self.blog): | ||
| if self._config_pagination(view): | ||
| for at in range(1, len(view.pages)): | ||
@@ -235,3 +256,3 @@ self._attach_at(view.parent, view, view.pages[at]) | ||
| if page not in self.blog.posts: | ||
| if not self.config.pagination: | ||
| if not self._config_pagination(page): | ||
| return | ||
@@ -259,8 +280,8 @@ | ||
| if self.config.authors: | ||
| for name in page.config.authors: | ||
| if name not in self.authors: | ||
| raise PluginError(f"Couldn't find author '{name}'") | ||
| for id in page.config.authors: | ||
| if id not in self.authors: | ||
| raise PluginError(f"Couldn't find author '{id}'") | ||
| # Append to list of authors | ||
| page.authors.append(self.authors[name]) | ||
| page.authors.append(self.authors[id]) | ||
@@ -324,3 +345,3 @@ # Extract settings for excerpts | ||
| # Patch URL template filter to add support for paginated views, i.e., | ||
| # that paginated views never link to themselves but to the main view | ||
| # that paginated views never link to themselves but to the main vie | ||
| @pass_context | ||
@@ -601,2 +622,33 @@ def url_filter_with_pagination(context: Context, url: str | None): | ||
| # Generate views for profiles - analyze posts and generate the necessary | ||
| # views to provide a profile page for each author listing all posts | ||
| def _generate_profiles(self, config: MkDocsConfig, files: Files): | ||
| for post in self.blog.posts: | ||
| for id in post.config.authors: | ||
| author = self.authors[id] | ||
| path = self._format_path_for_profile(id, author) | ||
| # Create file for view, if it does not exist | ||
| file = files.get_file_from_path(path) | ||
| if not file: | ||
| file = self._path_to_file(path, config) | ||
| files.append(file) | ||
| # Create file in temporary directory | ||
| self._save_to_file(file.abs_src_path, f"# {author.name}") | ||
| # Temporarily remove view from navigation and assign profile | ||
| # URL to author, if not explicitly set | ||
| file.inclusion = InclusionLevel.EXCLUDED | ||
| if not author.url: | ||
| author.url = file.url | ||
| # Create and yield view | ||
| if not isinstance(file.page, Profile): | ||
| yield Profile(author.name, file, config) | ||
| # Assign post to profile | ||
| assert isinstance(file.page, Profile) | ||
| file.page.posts.append(post) | ||
| # Generate pages for pagination - analyze view and generate the necessary | ||
@@ -609,3 +661,3 @@ # pages, creating a chain of views for simple rendering and replacement | ||
| # handled as copies of a view, as they map to the same source location | ||
| step = self.config.pagination_per_page | ||
| step = self._config_pagination_per_page(view) | ||
| for at in range(step, len(view.posts), step): | ||
@@ -760,7 +812,7 @@ path = self._format_path_for_pagination(view, 1 + at // step) | ||
| # Create pagination, if enabled | ||
| if self.config.pagination: | ||
| if self._config_pagination(view): | ||
| at = view.pages.index(view) | ||
| # Compute pagination boundaries | ||
| step = self.config.pagination_per_page | ||
| step = self._config_pagination_per_page(view) | ||
| p, q = at * step, at * step + step | ||
@@ -785,14 +837,5 @@ | ||
| # Determine whether to add posts to the table of contents of the view - | ||
| # note that those settings can be changed individually for each type of | ||
| # view, which is why we need to check the type of view and the table of | ||
| # contents setting for that type of view | ||
| toc = self.config.blog_toc | ||
| if isinstance(view, Archive): | ||
| toc = self.config.archive_toc | ||
| if isinstance(view, Category): | ||
| toc = self.config.categories_toc | ||
| # Attach top-level table of contents item to view if it should be added | ||
| # and both, the view and excerpt contain table of contents items | ||
| toc = self._config_toc(view) | ||
| if toc and excerpt.toc.items and view.toc.items: | ||
@@ -821,2 +864,44 @@ view.toc.items[0].children.append(excerpt.toc.items[0]) | ||
| # Retrieve configuration value or return default | ||
| def _config(self, key: str, default: any): | ||
| return default if self.config[key] is None else self.config[key] | ||
| # Retrieve configuration value for table of contents | ||
| def _config_toc(self, view: View): | ||
| default = self.config.blog_toc | ||
| if isinstance(view, Archive): | ||
| return self._config("archive_toc", default) | ||
| if isinstance(view, Category): | ||
| return self._config("categories_toc", default) | ||
| if isinstance(view, Profile): | ||
| return self._config("authors_profiles_toc", default) | ||
| else: | ||
| return default | ||
| # Retrieve configuration value for pagination | ||
| def _config_pagination(self, view: View): | ||
| default = self.config.pagination | ||
| if isinstance(view, Archive): | ||
| return self._config("archive_pagination", default) | ||
| if isinstance(view, Category): | ||
| return self._config("categories_pagination", default) | ||
| if isinstance(view, Profile): | ||
| return self._config("authors_profiles_pagination", default) | ||
| else: | ||
| return default | ||
| # Retrieve configuration value for pagination per page | ||
| def _config_pagination_per_page(self, view: View): | ||
| default = self.config.pagination_per_page | ||
| if isinstance(view, Archive): | ||
| return self._config("archive_pagination_per_page", default) | ||
| if isinstance(view, Category): | ||
| return self._config("categories_pagination_per_page", default) | ||
| if isinstance(view, Profile): | ||
| return self._config("authors_profiles_pagination_per_page", default) | ||
| else: | ||
| return default | ||
| # ------------------------------------------------------------------------- | ||
| # Format path for post | ||
@@ -861,2 +946,13 @@ def _format_path_for_post(self, post: Post, config: MkDocsConfig): | ||
| # Format path for profile | ||
| def _format_path_for_profile(self, id: str, author: Author): | ||
| path = self.config.authors_profiles_url_format.format( | ||
| slug = author.slug or id, | ||
| name = author.name | ||
| ) | ||
| # Normalize path and strip slashes at the beginning and end | ||
| path = posixpath.normpath(path.strip("/")) | ||
| return posixpath.join(self.config.blog_dir, f"{path}.md") | ||
| # Format path for pagination | ||
@@ -863,0 +959,0 @@ def _format_path_for_pagination(self, view: View, page: int): |
@@ -276,2 +276,8 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Profile view | ||
| class Profile(View): | ||
| pass | ||
| # ----------------------------------------------------------------------------- | ||
| # Reference | ||
@@ -278,0 +284,0 @@ class Reference(Link): |
@@ -36,4 +36,5 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| draft = Optional(Type(bool)) | ||
| pin = Type(bool, default = False) | ||
| links = Optional(PostLinks()) | ||
| readtime = Optional(Type(int)) | ||
| slug = Optional(Type(str)) |
@@ -39,3 +39,3 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| from mkdocs.plugins import BasePlugin, event_priority | ||
| from mkdocs.utils import get_yaml_loader | ||
| from mkdocs.utils.yaml import get_yaml_loader | ||
| from zipfile import ZipFile, ZIP_DEFLATED | ||
@@ -184,3 +184,3 @@ | ||
| if paths_to_validate: | ||
| log.error(f"One or more paths aren't children of root") | ||
| log.error("One or more paths aren't children of root") | ||
| self._help_on_not_in_cwd(paths_to_validate) | ||
@@ -187,0 +187,0 @@ |
@@ -24,5 +24,19 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| from mkdocs.config.base import Config | ||
| from mkdocs.config.config_options import DictOfItems, Type | ||
| from mkdocs.config.config_options import ( | ||
| Choice, Deprecated, DictOfItems, ListOfItems, Type | ||
| ) | ||
| # ----------------------------------------------------------------------------- | ||
| # Options | ||
| # ----------------------------------------------------------------------------- | ||
| # Options for log level | ||
| LogLevel = ( | ||
| "error", | ||
| "warn", | ||
| "info", | ||
| "debug" | ||
| ) | ||
| # ----------------------------------------------------------------------------- | ||
| # Classes | ||
@@ -40,2 +54,6 @@ # ----------------------------------------------------------------------------- | ||
| # Settings for logging | ||
| log = Type(bool, default = True) | ||
| log_level = Choice(LogLevel, default = "info") | ||
| # Settings for external assets | ||
@@ -45,2 +63,19 @@ assets = Type(bool, default = True) | ||
| assets_fetch_dir = Type(str, default = "assets/external") | ||
| assets_include = ListOfItems(Type(str), default = []) | ||
| assets_exclude = ListOfItems(Type(str), default = []) | ||
| assets_expr_map = DictOfItems(Type(str), default = {}) | ||
| # Settings for external links | ||
| links = Type(bool, default = True) | ||
| links_attr_map = DictOfItems(Type(str), default = {}) | ||
| links_noopener = Type(bool, default = True) | ||
| # Deprecated settings | ||
| external_assets = Deprecated(message = "Deprecated, use 'assets_fetch'") | ||
| external_assets_dir = Deprecated(moved_to = "assets_fetch_dir") | ||
| external_assets_include = Deprecated(moved_to = "assets_include") | ||
| external_assets_exclude = Deprecated(moved_to = "assets_exclude") | ||
| external_assets_expr = Deprecated(moved_to = "assets_expr_map") | ||
| external_links = Deprecated(moved_to = "links") | ||
| external_links_attr_map = Deprecated(moved_to = "links_attr_map") | ||
| external_links_noopener = Deprecated(moved_to = "links_noopener") |
@@ -32,3 +32,5 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| from colorama import Fore, Style | ||
| from concurrent.futures import Future, ThreadPoolExecutor, wait | ||
| from concurrent.futures import Future, wait | ||
| from concurrent.futures.thread import ThreadPoolExecutor | ||
| from fnmatch import fnmatch | ||
| from hashlib import sha1 | ||
@@ -56,2 +58,3 @@ from mkdocs.config.config_options import ExtraScriptValue | ||
| class PrivacyPlugin(BasePlugin[PrivacyConfig]): | ||
| supports_multiple_instances = True | ||
@@ -70,2 +73,3 @@ # Initialize thread pools and asset collections | ||
| self.assets = Files([]) | ||
| self.assets_done: list[File] = [] | ||
| self.assets_expr_map = { | ||
@@ -77,2 +81,9 @@ ".css": r"url\(\s*([\"']?)(?P<url>http?[^)'\"]+)\1\s*\)", | ||
| # Set log level or disable logging altogether - @todo when refactoring | ||
| # this plugin for the next time, we should put this into a factory | ||
| if not self.config.log: | ||
| log.disabled = True | ||
| else: | ||
| log.setLevel(self.config.log_level.upper()) | ||
| # Process external style sheets and scripts (run latest) - run this after | ||
@@ -134,3 +145,9 @@ # all other plugins, so they can add additional assets | ||
| # type of external asset when writing. Thus, we create and enqueue a job for | ||
| # each image we find that checks if the image needs to be downloaded. | ||
| # each image we find that checks if the image needs to be downloaded. Also, | ||
| # downloading all external images at this stage, we reconcile all concurrent | ||
| # jobs in `on_env`, which is the stage in which the optimize plugin will | ||
| # evaluate what images can and need to be optimized. This means we can pass | ||
| # external images through the optimization pipeline. Additionally, we run | ||
| # this after all other plugins, so we allow them to add additional images | ||
| # to the content of the page. How cool is that? | ||
| @event_priority(-100) | ||
@@ -157,3 +174,5 @@ def on_page_content(self, html, *, page, config, files): | ||
| # Sync all concurrent jobs | ||
| # Reconcile jobs and pass external assets to MkDocs (run earlier) - allow | ||
| # other plugins (e.g. optimize plugin) to post-process external assets | ||
| @event_priority(50) | ||
| def on_env(self, env, *, config, files): | ||
@@ -163,5 +182,17 @@ if not self.config.enabled: | ||
| # Wait until all jobs until now are finished | ||
| # Reconcile concurrent jobs and clear thread pool, as we will reuse the | ||
| # same thread pool for fetching all remaining external assets | ||
| wait(self.pool_jobs) | ||
| self.pool_jobs.clear() | ||
| # Append all downloaded assets that are not style sheets or scripts to | ||
| # MkDocs's collection of files, making them available to other plugins | ||
| # for further processing. The remaining exteral assets are patched | ||
| # before copying, which is done at the end of the build process. | ||
| for file in self.assets: | ||
| _, extension = posixpath.splitext(file.dest_uri) | ||
| if extension not in [".css", ".js"]: | ||
| self.assets_done.append(file) | ||
| files.append(file) | ||
| # Process external assets in template (run later) | ||
@@ -190,3 +221,4 @@ @event_priority(-50) | ||
| # Reconcile jobs (run earlier) | ||
| # Reconcile jobs (run earlier) - allow other plugins (e.g. optimize plugin) | ||
| # to process all downloaded assets, which is why we must reconcile here | ||
| @event_priority(50) | ||
@@ -211,6 +243,6 @@ def on_post_build(self, *, config): | ||
| # Otherwise just copy external asset to output directory if it | ||
| # exists, i.e., if the download succeeded | ||
| else: | ||
| if os.path.exists(file.abs_src_path): | ||
| # Otherwise just copy external asset to output directory, if we | ||
| # haven't handed control to MkDocs in `on_env` before | ||
| elif file not in self.assets_done: | ||
| if os.path.exists(str(file.abs_src_path)): | ||
| file.copy_file() | ||
@@ -248,2 +280,24 @@ | ||
| # Check if URL matches one of the inclusion patterns | ||
| if self.config.assets_include: | ||
| for pattern in self.config.assets_include: | ||
| if fnmatch(self._path_from_url(url), pattern): | ||
| return False | ||
| # File is not included | ||
| log.debug( | ||
| f"Excluding external file '{url.geturl()}' {via}due to " | ||
| f"inclusion patterns" | ||
| ) | ||
| return True | ||
| # Check if URL matches one of the exclusion patterns | ||
| for pattern in self.config.assets_exclude: | ||
| if fnmatch(self._path_from_url(url), pattern): | ||
| log.debug( | ||
| f"Excluding external file '{url.geturl()}' {via}due to " | ||
| f"exclusion patterns" | ||
| ) | ||
| return True | ||
| # Print warning if fetching is not enabled | ||
@@ -314,2 +368,17 @@ if not self.config.assets_fetch: | ||
| # Handle external link | ||
| if self.config.links and el.tag == "a": | ||
| for key, value in self.config.links_attr_map.items(): | ||
| el.set(key, value) | ||
| # Set `rel=noopener` if link opens in a new window | ||
| if self.config.links_noopener: | ||
| if el.get("target") == "_blank": | ||
| rel = re.findall(r"\S+", el.get("rel", "")) | ||
| if "noopener" not in rel: | ||
| rel.append("noopener") | ||
| # Set relationships after adding `noopener` | ||
| el.set("rel", " ".join(rel)) | ||
| # Handle external style sheet or preconnect hint | ||
@@ -316,0 +385,0 @@ if el.tag == "link": |
@@ -42,2 +42,6 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Search field configuration | ||
| class SearchFieldConfig(Config): | ||
| boost = Type((int, float), default = 1.0) | ||
| # Search plugin configuration | ||
@@ -51,2 +55,3 @@ class SearchConfig(Config): | ||
| pipeline = Optional(ListOfItems(Choice(pipeline))) | ||
| fields = Type(dict, default = {}) | ||
@@ -53,0 +58,0 @@ # Settings for text segmentation (Chinese) |
@@ -30,5 +30,6 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| from mkdocs import utils | ||
| from mkdocs.config.config_options import SubConfig | ||
| from mkdocs.plugins import BasePlugin | ||
| from .config import SearchConfig | ||
| from .config import SearchConfig, SearchFieldConfig | ||
@@ -85,2 +86,15 @@ try: | ||
| # Validate field configuration | ||
| validator = SubConfig(SearchFieldConfig) | ||
| for config in self.config.fields.values(): | ||
| validator.run_validation(config) | ||
| # Merge with default fields | ||
| if "title" not in self.config.fields: | ||
| self.config.fields["title"] = { "boost": 1e3 } | ||
| if "text" not in self.config.fields: | ||
| self.config.fields["text"] = { "boost": 1e0 } | ||
| if "tags" not in self.config.fields: | ||
| self.config.fields["tags"] = { "boost": 1e6 } | ||
| # Initialize search index | ||
@@ -235,3 +249,3 @@ self.search_index = SearchIndex(**self.config) | ||
| key: self.config[key] | ||
| for key in ["lang", "separator", "pipeline"] | ||
| for key in ["lang", "separator", "pipeline", "fields"] | ||
| } | ||
@@ -238,0 +252,0 @@ |
@@ -21,4 +21,7 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| import os | ||
| from mkdocs.config.base import Config | ||
| from mkdocs.config.config_options import Deprecated, Type | ||
| from mkdocs.config.config_options import Deprecated, ListOfItems, Type | ||
| from mkdocs.config.defaults import _LogLevel | ||
@@ -32,19 +35,36 @@ # ----------------------------------------------------------------------------- | ||
| enabled = Type(bool, default = True) | ||
| concurrency = Type(int, default = max(1, os.cpu_count() - 1)) | ||
| # Settings for caching | ||
| cache = Type(bool, default = True) | ||
| cache_dir = Type(str, default = ".cache/plugin/social") | ||
| # Settings for social cards | ||
| # Settings for logging | ||
| log = Type(bool, default = True) | ||
| log_level = _LogLevel(default = "warn") | ||
| # Settings for cards | ||
| cards = Type(bool, default = True) | ||
| cards_dir = Type(str, default = "assets/images/social") | ||
| cards_layout_dir = Type(str, default = "layouts") | ||
| cards_layout = Type(str, default = "default") | ||
| cards_layout_options = Type(dict, default = {}) | ||
| cards_include = ListOfItems(Type(str), default = []) | ||
| cards_exclude = ListOfItems(Type(str), default = []) | ||
| # Settings for debugging | ||
| debug = Type(bool, default = False) | ||
| debug_on_build = Type(bool, default = False) | ||
| debug_grid = Type(bool, default = True) | ||
| debug_grid_step = Type(int, default = 32) | ||
| debug_color = Type(str, default = "grey") | ||
| # Deprecated settings | ||
| cards_color = Deprecated( | ||
| option_type = Type(dict, default = {}), | ||
| message = | ||
| "Deprecated, use 'cards_layout_options.background_color' " | ||
| "and 'cards_layout_options.color' with 'default' layout" | ||
| ) | ||
| ) | ||
| cards_font = Deprecated( | ||
| option_type = Type(str), | ||
| message = "Deprecated, use 'cards_layout_options.font_family'" | ||
| ) |
@@ -21,19 +21,10 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # ----------------------------------------------------------------------------- | ||
| # Disclaimer | ||
| # ----------------------------------------------------------------------------- | ||
| # Please note: this version of the social plugin is not actively development | ||
| # anymore. Instead, Material for MkDocs Insiders ships a complete rewrite of | ||
| # the plugin which is much more powerful and addresses all shortcomings of | ||
| # this implementation. Additionally, the new social plugin allows to create | ||
| # entirely custom social cards. You can probably imagine, that this was a lot | ||
| # of work to pull off. If you run into problems, or want to have additional | ||
| # functionality, please consider sponsoring the project. You can then use the | ||
| # new version of the plugin immediately. | ||
| # ----------------------------------------------------------------------------- | ||
| from __future__ import annotations | ||
| import concurrent.futures | ||
| import functools | ||
| import html | ||
| import json | ||
| import logging | ||
| import os | ||
| import pickle | ||
| import posixpath | ||
@@ -43,17 +34,31 @@ import re | ||
| import sys | ||
| import yaml | ||
| from collections import defaultdict | ||
| from hashlib import md5 | ||
| from concurrent.futures import Future | ||
| from concurrent.futures.thread import ThreadPoolExecutor | ||
| from copy import copy | ||
| from fnmatch import fnmatch | ||
| from hashlib import sha1 | ||
| from html import unescape | ||
| from io import BytesIO | ||
| from mkdocs.commands.build import DuplicateFilter | ||
| from jinja2 import Environment | ||
| from jinja2.meta import find_undeclared_variables | ||
| from mkdocs.config.base import Config | ||
| from mkdocs.config.defaults import MkDocsConfig | ||
| from mkdocs.exceptions import PluginError | ||
| from mkdocs.plugins import BasePlugin | ||
| from mkdocs.plugins import BasePlugin, event_priority | ||
| from mkdocs.structure.files import File, InclusionLevel | ||
| from mkdocs.structure.pages import Page | ||
| from mkdocs.utils import write_file | ||
| from shutil import copyfile | ||
| from statistics import stdev | ||
| from threading import Lock | ||
| from yaml import SafeLoader | ||
| from .config import SocialConfig | ||
| from .layout import Layer, Layout, Line, get_offset, get_size | ||
| from .templates import x_filter | ||
| try: | ||
| from PIL import Image, ImageDraw, ImageFont | ||
| from PIL import Image, ImageColor, ImageDraw, ImageFont | ||
| from PIL.Image import Image as _Image | ||
| except ImportError as e: | ||
@@ -65,3 +70,2 @@ import_errors = {repr(e)} | ||
| cairosvg_error: str = "" | ||
| try: | ||
@@ -74,3 +78,2 @@ from cairosvg import svg2png | ||
| # ----------------------------------------------------------------------------- | ||
@@ -82,47 +85,87 @@ # Classes | ||
| class SocialPlugin(BasePlugin[SocialConfig]): | ||
| supports_multiple_instances = True | ||
| def __init__(self): | ||
| self._executor = concurrent.futures.ThreadPoolExecutor(4) | ||
| # Manifest | ||
| manifest: dict[str, str] = {} | ||
| # Retrieve configuration | ||
| # Initialize plugin | ||
| def __init__(self, *args, **kwargs): | ||
| super().__init__(*args, **kwargs) | ||
| # Initialize incremental builds | ||
| self.is_serve = False | ||
| # Determine whether we're serving the site, and thus doing an incremental | ||
| # build, and initialize two thread pools for card generation, because it's | ||
| # split into two stages: rendering of layers and composition. We use two | ||
| # thread pools, one for each stage, as we need to make sure that all layers | ||
| # of a card are rendered before we compose the card itself. At the same time | ||
| # we want to off-load as much as possible onto worker threads, as card | ||
| # generation is a problem that can be perfectly solved in parallel. Thus, | ||
| # we leverage the file system to cache the generated images, so we don't | ||
| # re-generate the exact same images again and again, making successive | ||
| # builds of large sites much faster. | ||
| def on_startup(self, *, command, dirty): | ||
| self.is_serve = command == "serve" | ||
| # Initialize thread pool for cards | ||
| self.card_pool = ThreadPoolExecutor(self.config.concurrency) | ||
| self.card_pool_jobs: dict[str, Future] = {} | ||
| # Initialize thread pool for card layers | ||
| self.card_layer_pool = ThreadPoolExecutor(self.config.concurrency) | ||
| self.card_layer_pool_jobs: dict[str, Future] = {} | ||
| # Resolve and load manifest and initialize environment | ||
| def on_config(self, config): | ||
| self.color = colors.get("indigo") | ||
| if not self.config.enabled: | ||
| self.config.cards = False | ||
| if not self.config.cards: | ||
| return | ||
| # Check dependencies | ||
| if import_errors: | ||
| raise PluginError( | ||
| "Required dependencies of \"social\" plugin not found:\n" | ||
| + str("\n".join(map(lambda x: "- " + x, import_errors))) | ||
| + "\n\n--> Install with: pip install \"mkdocs-material[imaging]\"" | ||
| # Resolve cache directory (once) - this is necessary, so the cache is | ||
| # always relative to the configuration file, and thus project, and not | ||
| # relative to the current working directory, or it would not work with | ||
| # the projects plugin. | ||
| path = os.path.abspath(self.config.cache_dir) | ||
| if path != self.config.cache_dir: | ||
| self.config.cache_dir = os.path.join( | ||
| os.path.dirname(config.config_file_path), | ||
| os.path.normpath(self.config.cache_dir) | ||
| ) | ||
| if cairosvg_error: | ||
| raise PluginError( | ||
| "\"cairosvg\" Python module is installed, but it crashed with:\n" | ||
| + cairosvg_error | ||
| + "\n\n--> Check out the troubleshooting guide: https://t.ly/MfX6u" | ||
| ) | ||
| # Ensure cache directory exists | ||
| os.makedirs(self.config.cache_dir, exist_ok = True) | ||
| # Move color options | ||
| if self.config.cards_color: | ||
| # Initialize manifest | ||
| self.manifest_file = os.path.join( | ||
| self.config.cache_dir, "manifest.json" | ||
| ) | ||
| # Move background color to new option | ||
| value = self.config.cards_color.get("fill") | ||
| if value: | ||
| self.config.cards_layout_options["background_color"] = value | ||
| # Load manifest if it exists and the cache should be used | ||
| if os.path.isfile(self.manifest_file) and self.config.cache: | ||
| try: | ||
| with open(self.manifest_file) as f: | ||
| self.manifest = json.load(f) | ||
| except: | ||
| pass | ||
| # Move color to new option | ||
| value = self.config.cards_color.get("text") | ||
| if value: | ||
| self.config.cards_layout_options["color"] = value | ||
| # Initialize lock for synchronizing downloading of fonts | ||
| self.lock = Lock() | ||
| # Move font family to new option | ||
| if self.config.cards_font: | ||
| value = self.config.cards_font | ||
| self.config.cards_layout_options["font_family"] = value | ||
| # Initialize card layouts and variables | ||
| self.card_layouts: dict[str, Layout] = {} | ||
| self.card_variables: dict[str, list[list[str]]] = {} | ||
| # Initialize card environment | ||
| self.card_env = Environment() | ||
| self.card_env.filters["x"] = x_filter | ||
| # Always print a warning when debug mode is active | ||
| if self.config.debug: | ||
| log.warning("Debug mode is enabled for \"social\" plugin.") | ||
| # By default, debug mode is disabled when the documentation is | ||
| # built, but not when it is served, for a better user experience | ||
| if not self.is_serve and not self.config.debug_on_build: | ||
| self.config.debug = False | ||
| # Check if site URL is defined | ||
@@ -135,343 +178,654 @@ if not config.site_url: | ||
| # Ensure presence of cache directory | ||
| self.cache = self.config.cache_dir | ||
| if not os.path.isdir(self.cache): | ||
| os.makedirs(self.cache) | ||
| # Ensure card layouts are not copied to the site directory | ||
| def on_files(self, files, *, config): | ||
| if not self.config.enabled: | ||
| return | ||
| # Retrieve palette from theme configuration | ||
| theme = config.theme | ||
| if "palette" in theme: | ||
| palette = theme["palette"] | ||
| # We must exclude all files related to layouts from here on, so MkDocs | ||
| # doesn't copy them to the site directory when the project is built | ||
| for file in files: | ||
| # Find first palette that includes primary color definition | ||
| if isinstance(palette, list): | ||
| for p in palette: | ||
| if "primary" in p and p["primary"]: | ||
| palette = p | ||
| break | ||
| # As of MkDocs 1.6, abs_src_path is optional for generated files, | ||
| # so we need to exlude them - see https://t.ly/zRYj7 | ||
| if not file.abs_src_path: | ||
| continue | ||
| # Set colors according to palette | ||
| if "primary" in palette and palette["primary"]: | ||
| primary = palette["primary"].replace(" ", "-") | ||
| self.color = colors.get(primary, self.color) | ||
| # Exclude files from layout directory | ||
| if file.abs_src_path.startswith(_templates_dirpath()): | ||
| file.inclusion = InclusionLevel.EXCLUDED | ||
| # Retrieve color overrides | ||
| options = self.config.cards_layout_options | ||
| self.color = { | ||
| "fill": options.get("background_color", self.color["fill"]), | ||
| "text": options.get("color", self.color["text"]) | ||
| } | ||
| # Generate card as soon as metadata is available (run latest) - run this | ||
| # after all other plugins, so they can alter the card configuration | ||
| @event_priority(-100) | ||
| def on_page_markdown(self, markdown, *, page, config, files): | ||
| if not self.config.enabled: | ||
| return | ||
| # Retrieve logo and font | ||
| self._resized_logo_promise = self._executor.submit(self._load_resized_logo, config) | ||
| self.font = self._load_font(config) | ||
| # Skip if cards should not be generated | ||
| if self._is_excluded(page): | ||
| return | ||
| self._image_promises = [] | ||
| # Resolve card layout - we also preload the layout here, so we're not | ||
| # triggering multiple concurrent loads in the worker threads | ||
| name = self._config("cards_layout", page) | ||
| self._resolve_layout(name, config) | ||
| # Create social cards | ||
| def on_page_content(self, html, page, config, files): | ||
| if not self.config.cards: | ||
| # Spawn concurrent job to generate card for page and add future to | ||
| # job dictionary, as it returns the file we need to copy later | ||
| self.card_pool_jobs[page.file.src_uri] = self.card_pool.submit( | ||
| self._generate, name, page, config | ||
| ) | ||
| # Generate card metadata (run earlier) - don't run this too late, as we | ||
| # want plugins like the minify plugin to pick up the HTML we inject | ||
| @event_priority(50) | ||
| def on_post_page(self, output, *, page, config): | ||
| if not self.config.enabled: | ||
| return | ||
| # Resolve image directory | ||
| directory = self.config.cards_dir | ||
| file, _ = os.path.splitext(page.file.src_path) | ||
| # Skip if cards should not be generated | ||
| if self._is_excluded(page): | ||
| return | ||
| # Resolve path of image | ||
| path = "{}.png".format(os.path.join( | ||
| config.site_dir, | ||
| directory, | ||
| file | ||
| )) | ||
| # Reconcile concurrent jobs - we need to wait for the card job to finish | ||
| # before we can copy the generated files to the output directory. If an | ||
| # exception occurred in one of the jobs, we either log it as configured | ||
| # by the user, or raise it, so the build fails. | ||
| future = self.card_pool_jobs[page.file.src_uri] | ||
| if future.exception(): | ||
| e = future.exception() | ||
| if self.config.log and isinstance(e, PluginError): | ||
| log.log(self.config.log_level, e) | ||
| return | ||
| # Resolve path of image directory | ||
| directory = os.path.dirname(path) | ||
| if not os.path.isdir(directory): | ||
| os.makedirs(directory) | ||
| # Otherwise throw error | ||
| raise e | ||
| else: | ||
| file: File = future.result() | ||
| file.copy_file() | ||
| # Compute site name | ||
| site_name = config.site_name | ||
| # Resolve card layout | ||
| name = self._config("cards_layout", page) | ||
| layout, _ = self._resolve_layout(name, config) | ||
| # Compute page title and description | ||
| title = page.meta.get("title", page.title) | ||
| description = config.site_description or "" | ||
| if "description" in page.meta: | ||
| description = page.meta["description"] | ||
| # Stop if no tags are present or site URL is not set | ||
| if not layout.tags or not config.site_url: | ||
| return | ||
| # Check type of meta title - see https://t.ly/m1Us | ||
| if not isinstance(title, str): | ||
| log.error( | ||
| f"Page meta title of page '{page.file.src_uri}' must be a " | ||
| f"string, but is of type \"{type(title)}\"." | ||
| ) | ||
| sys.exit(1) | ||
| # Resolve image dimensions and curate image metadata | ||
| width, height = get_size(layout) | ||
| image = { | ||
| "url": posixpath.join(config.site_url, file.url), | ||
| "type": "image/png", | ||
| "width": width, | ||
| "height": height | ||
| } | ||
| # Check type of meta description - see https://t.ly/m1Us | ||
| if not isinstance(description, str): | ||
| log.error( | ||
| f"Page meta description of '{page.file.src_uri}' must be a " | ||
| f"string, but is of type \"{type(description)}\"." | ||
| ) | ||
| sys.exit(1) | ||
| # Find offset of closing head tag, so we can insert meta tags before | ||
| # it - a bit hacky, but much faster than regular expressions | ||
| at = output.find("</head>") | ||
| return "\n".join([ | ||
| output[:at], | ||
| "\n".join([ | ||
| f"<meta property=\"{property}\" content=\"{content}\" />" | ||
| for property, content in _replace( | ||
| layout.tags, self.card_env, config, | ||
| page = page, image = image, | ||
| layout = self._config("cards_layout_options", page), | ||
| ).items() if content | ||
| ]), | ||
| output[at:] | ||
| ]) | ||
| # Generate social card if not in cache | ||
| hash = md5("".join([ | ||
| site_name, | ||
| str(title), | ||
| description | ||
| ]).encode("utf-8")) | ||
| file = os.path.join(self.cache, f"{hash.hexdigest()}.png") | ||
| self._image_promises.append(self._executor.submit( | ||
| self._cache_image, | ||
| cache_path = file, dest_path = path, | ||
| render_function = lambda: self._render_card(site_name, title, description) | ||
| )) | ||
| # Save manifest after build | ||
| def on_post_build(self, *, config): | ||
| if not self.config.enabled: | ||
| return | ||
| # Inject meta tags into page | ||
| meta = page.meta.get("meta", []) | ||
| page.meta["meta"] = meta + self._generate_meta(page, config) | ||
| # Save manifest if cache should be used | ||
| if self.config.cache: | ||
| with open(self.manifest_file, "w") as f: | ||
| f.write(json.dumps(self.manifest, indent = 2, sort_keys = True)) | ||
| def on_post_build(self, config): | ||
| if not self.config.cards: | ||
| # Add custom layout directory to watched files | ||
| def on_serve(self, server, *, config, builder): | ||
| path = os.path.abspath(self.config.cards_layout_dir) | ||
| if os.path.isdir(path): | ||
| server.watch(path, recursive = True) | ||
| # Reconcile jobs (run latest) - all other plugins do not depend on the | ||
| # generated cards, so we can run this after all of them | ||
| @event_priority(-100) | ||
| def on_shutdown(self): | ||
| if not self.config.enabled: | ||
| return | ||
| # Check for exceptions | ||
| for promise in self._image_promises: | ||
| promise.result() | ||
| # Shutdown thread pools - if we're on Python 3.9 and above, cancel all | ||
| # pending futures that have not yet been scheduled | ||
| for pool in [self.card_layer_pool, self.card_pool]: | ||
| if sys.version_info >= (3, 9): | ||
| pool.shutdown(cancel_futures = True) | ||
| else: | ||
| pool.shutdown() | ||
| # Save manifest if cache should be used | ||
| if self.manifest and self.config.cache: | ||
| with open(self.manifest_file, "w") as f: | ||
| f.write(json.dumps(self.manifest, indent = 2, sort_keys = True)) | ||
| # ------------------------------------------------------------------------- | ||
| # Render image to cache (if not present), then copy from cache to site | ||
| def _cache_image(self, cache_path, dest_path, render_function): | ||
| if not os.path.isfile(cache_path): | ||
| image = render_function() | ||
| image.save(cache_path) | ||
| # Check if the given page is excluded - giving the author the option to | ||
| # include and exclude specific pages is important, as it allows to control | ||
| # which pages should generate social cards, and which shouldn't. Different | ||
| # cards can be built by using multiple instances of the plugin. | ||
| def _is_excluded(self, page: Page): | ||
| path = page.file.src_path | ||
| # Copy file from cache | ||
| copyfile(cache_path, dest_path) | ||
| # Check if card generation is disabled for the given page | ||
| if not self._config("cards", page): | ||
| return True | ||
| @functools.lru_cache(maxsize=None) | ||
| def _get_font(self, kind, size): | ||
| return ImageFont.truetype(self.font[kind], size) | ||
| # Check if page matches one of the inclusion patterns | ||
| if self.config.cards_include: | ||
| for pattern in self.config.cards_include: | ||
| if fnmatch(page.file.src_uri, pattern): | ||
| return False | ||
| # Render social card | ||
| def _render_card(self, site_name, title, description): | ||
| # Render background and logo | ||
| image = self._render_card_background((1200, 630), self.color["fill"]) | ||
| image.alpha_composite( | ||
| self._resized_logo_promise.result(), | ||
| (1200 - 228, 64 - 4) | ||
| ) | ||
| # Page is not included | ||
| log.debug(f"Excluding page '{path}' due to inclusion patterns") | ||
| return True | ||
| # Render site name | ||
| font = self._get_font("Bold", 36) | ||
| image.alpha_composite( | ||
| self._render_text((826, 48), font, site_name, 1, 20), | ||
| (64 + 4, 64) | ||
| ) | ||
| # Check if page matches one of the exclusion patterns | ||
| for pattern in self.config.cards_exclude: | ||
| if fnmatch(page.file.src_uri, pattern): | ||
| log.debug(f"Excluding page '{path}' due to exclusion patterns") | ||
| return True | ||
| # Render page title | ||
| font = self._get_font("Bold", 92) | ||
| image.alpha_composite( | ||
| self._render_text((826, 328), font, title, 3, 30), | ||
| (64, 160) | ||
| ) | ||
| # Page is not excluded | ||
| return False | ||
| # Render page description | ||
| font = self._get_font("Regular", 28) | ||
| image.alpha_composite( | ||
| self._render_text((826, 80), font, description, 2, 14), | ||
| (64 + 4, 512) | ||
| # ------------------------------------------------------------------------- | ||
| # Generate card for the given page - generation of cards does not depend on | ||
| # anything else than the page content (incl. metadata) and configuration, | ||
| # which is why it is an embarrassingly parallel problem and can be solved | ||
| # by delegating the generation of each card to a thread pool | ||
| def _generate(self, name: str, page: Page, config: MkDocsConfig): | ||
| layout, variables = self._resolve_layout(name, config) | ||
| # Each card can consist of multiple layers, many of which are likely | ||
| # the same across cards (like background or logo layers). Some of the | ||
| # input values to generate a card may be dependent on author-provided | ||
| # data, e.g., the site description or card title that is sourced from | ||
| # front matter. Additionally, layouts may allow to define arbitrary | ||
| # text boxes with author-provided metadata like tags or categories. | ||
| # Thus, we generate a hash for each card, which is based on the layers | ||
| # and the values of all variables that are used to generate the card. | ||
| layers: dict[str, Layer] = {} | ||
| for layer, templates in zip(layout.layers, variables): | ||
| fingerprints = [self.config, layer] | ||
| # Compute fingerprints for each layer | ||
| for template in templates: | ||
| template = _compile(template, self.card_env) | ||
| fingerprints.append(template.render( | ||
| config = config, page = page, | ||
| layout = self._config("cards_layout_options", page) | ||
| )) | ||
| # Compute digest of fingerprints | ||
| layers[_digest(fingerprints)] = layer | ||
| # Compute digest of all fingerprints - we use this value to check if | ||
| # the exact same card was already generated and cached | ||
| hash = _digest([layout, *list(layers)]) | ||
| # Determine part of path we need to replace - this depends on whether | ||
| # we're using directory URLs and if the page is an index page or not | ||
| suffix = ".html" | ||
| if config.use_directory_urls and not page.is_index: | ||
| suffix = "/index.html" | ||
| # Compute path to card, which is sourced from the cache directory, and | ||
| # generate file to register it with MkDocs as soon as it was generated | ||
| path = page.file.dest_uri.replace(suffix, ".png") | ||
| file = self._path_to_file(path, config) | ||
| # Check if file hash changed, so we need to re-generate the card - if | ||
| # the hash didn't change, we can just return the existing file | ||
| prev = self.manifest.get(file.url, "") | ||
| if hash == prev and os.path.isfile(file.abs_src_path): | ||
| return file | ||
| # Check if the required dependencies for rendering are available, which | ||
| # is, at the absolute minimum, the 'pillow' package, and raise an error | ||
| # to the caller, so he can decide what to do with the error. The caller | ||
| # can treat this as a warning or an error to abort the build. | ||
| if import_errors: | ||
| # docs = os.path.relpath(config.docs_dir) | ||
| # path = os.path.relpath(page.file.abs_src_path, docs) | ||
| # raise PluginError( | ||
| # f"Couldn't render card for '{path}' in '{docs}': install " | ||
| # f"required dependencies – pip install 'mkdocs-material[imaging]'" | ||
| # ) | ||
| # @todo improve formatting of error handling | ||
| raise PluginError( | ||
| "Required dependencies of \"social\" plugin not found:\n" | ||
| + str("\n".join(map(lambda x: "- " + x, import_errors))) | ||
| + "\n\n" | ||
| + "--> Install with: pip install \"mkdocs-material[imaging]\"" | ||
| ) | ||
| if cairosvg_error: | ||
| # @todo improve formatting of error handling | ||
| raise PluginError( | ||
| "\"cairosvg\" Python module is installed, but it crashed with:\n" | ||
| + cairosvg_error | ||
| + "\n\n" | ||
| + "--> Check out the troubleshooting guide: https://t.ly/MfX6u" | ||
| ) | ||
| # Spawn concurrent jobs to render layers - we only need to render layers | ||
| # that we haven't already dispatched, reducing work by deduplication | ||
| for h, layer in layers.items(): | ||
| sentinel = Future() | ||
| # We need to use a hack here to avoid locking the thread pool while | ||
| # we check if the layer was already dispatched. If we don't do this, | ||
| # layers might be dispatched multiple times. The trick is to use a | ||
| # sentinel value to check if the layer was already dispatched. | ||
| if sentinel == self.card_layer_pool_jobs.setdefault(h, sentinel): | ||
| self.card_layer_pool_jobs[h] = self.card_layer_pool.submit( | ||
| self._render, layer, page, config | ||
| ) | ||
| # Reconcile concurrent jobs to render layers and compose card - since | ||
| # layers are rendered in parallel, we can compose the card as soon as | ||
| # all layers have been rendered. For this, we await each future to | ||
| # resolve with the image of the rendered layer. | ||
| image = Image.new(mode = "RGBA", size = get_size(layout)) | ||
| for h, layer in layers.items(): | ||
| image.alpha_composite( | ||
| self.card_layer_pool_jobs[h].result(), | ||
| get_offset(layer, image) | ||
| ) | ||
| # If debug mode is enabled, render overlay | ||
| if self.config.debug: | ||
| image = self._render_overlay(layout, image) | ||
| # Save composed image to cache - the caller must copy the image from | ||
| # the cache, so we don't need to worry about concurrent access | ||
| os.makedirs(os.path.dirname(file.abs_src_path), exist_ok = True) | ||
| image.save(file.abs_src_path) | ||
| # Update manifest by associating file with hash | ||
| self.manifest[file.url] = hash | ||
| # Return file for generated card | ||
| return file | ||
| # Render layer - this is the core of the plugin, which renders a single | ||
| # layer of a card. Order is: background, icon, and typography. | ||
| def _render(self, layer: Layer, page: Page, config: MkDocsConfig): | ||
| image = Image.new(mode = "RGBA", size = get_size(layer)) | ||
| layer = _replace( | ||
| layer, self.card_env, config, | ||
| page = page, layout = self._config("cards_layout_options", page) | ||
| ) | ||
| # Return social card image | ||
| # Render background, icon, and typography | ||
| image = self._render_background(layer, image) | ||
| image = self._render_icon(layer, image, config) | ||
| image = self._render_typography(layer, image) | ||
| # Return image with layer | ||
| return image | ||
| # Render social card background | ||
| def _render_card_background(self, size, fill): | ||
| return Image.new(mode = "RGBA", size = size, color = fill) | ||
| # Render layer background | ||
| def _render_background(self, layer: Layer, input: _Image): | ||
| background = layer.background | ||
| @functools.lru_cache(maxsize=None) | ||
| def _tmp_context(self): | ||
| image = Image.new(mode = "RGBA", size = (50, 50)) | ||
| return ImageDraw.Draw(image) | ||
| # If given, load background image and resize it proportionally to cover | ||
| # the entire area while retaining the aspect ratio of the input image | ||
| if background.image: | ||
| if not os.path.isfile(background.image): | ||
| raise PluginError(f"Couldn't find image '{background.image}'") | ||
| @functools.lru_cache(maxsize=None) | ||
| def _text_bounding_box(self, text, font): | ||
| return self._tmp_context().textbbox((0, 0), text, font = font) | ||
| # Open file and convert SVGs to PNGs | ||
| with open(background.image, "rb") as f: | ||
| data = f.read() | ||
| if background.image.endswith(".svg"): | ||
| data = svg2png(data, output_width = input.width) | ||
| # Render social card text | ||
| def _render_text(self, size, font, text, lmax, spacing = 0): | ||
| width = size[0] | ||
| lines, words = [], [] | ||
| # Resize image to cover entire area | ||
| image = Image.open(BytesIO(data)).convert("RGBA") | ||
| input.alpha_composite(_resize_cover(image, input)) | ||
| # Remove remnant HTML tags and convert HTML entities | ||
| text = re.sub(r"(<[^>]+>)", "", text) | ||
| text = unescape(text) | ||
| # If given, fill background color - this is done after the image is | ||
| # loaded to allow for transparent tints. How awesome is that? | ||
| if background.color: | ||
| color = background.color | ||
| if color == "transparent": | ||
| return input | ||
| # Retrieve y-offset of textbox to correct for spacing | ||
| yoffset = 0 | ||
| # Create image filled with background color | ||
| image = Image.new(mode = "RGBA", size = input.size, color = color) | ||
| input.alpha_composite(image) | ||
| # Create drawing context and split text into lines | ||
| for word in text.split(" "): | ||
| combine = " ".join(words + [word]) | ||
| textbox = self._text_bounding_box(combine, font = font) | ||
| yoffset = textbox[1] | ||
| if not words or textbox[2] <= width: | ||
| words.append(word) | ||
| # Return image with background | ||
| return input | ||
| # Render layer icon | ||
| def _render_icon(self, layer: Layer, input: _Image, config: MkDocsConfig): | ||
| icon = layer.icon | ||
| if not icon.value: | ||
| return input | ||
| # Resolve icon by searching all configured theme directories and apply | ||
| # the fill color before rendering, if given. Note that the fill color | ||
| # must be converted to rgba() function syntax, or opacity will not work | ||
| # correctly. This way, we don't need to use the fill-opacity property. | ||
| data = self._resolve_icon(icon.value, config) | ||
| if icon.color: | ||
| (r, g, b, *a) = ImageColor.getrgb(icon.color) | ||
| opacity = a[0] / 255 if a else 1 | ||
| # Compute and replace fill color | ||
| fill = f"rgba({r}, {g}, {b}, {opacity})" | ||
| data = data.replace("<svg", f"<svg fill=\"{fill}\"") | ||
| # Rasterize vector image given by icon to match the size of the | ||
| # input image, resize it and render it on top of the input image | ||
| image = Image.open(BytesIO( | ||
| svg2png(data.encode("utf-8"), output_width = input.width) | ||
| )) | ||
| input.alpha_composite(_resize_contain(image.convert("RGBA"), input)) | ||
| # Return image with icon | ||
| return input | ||
| # Render layer typography | ||
| def _render_typography(self, layer: Layer, input: _Image): | ||
| typography = layer.typography | ||
| if not typography.content: | ||
| return input | ||
| # Retrieve font family and font style | ||
| family = typography.font.family | ||
| variant = typography.font.variant | ||
| style = typography.font.style | ||
| # Resolve and load font and compute metrics | ||
| path = self._resolve_font(family, style, variant) | ||
| current, spacing = _metrics(path, typography.line, input) | ||
| font = ImageFont.truetype(path, current) | ||
| # Create image and initialize drawing context | ||
| image = Image.new(mode = "RGBA", size = input.size) | ||
| context = ImageDraw.Draw(image) | ||
| # Compute length of whitespace and ellipsis - in the next step, we will | ||
| # distribute the words across the lines we have available, which means | ||
| # we need to compute the length of each word and intersperse it with | ||
| # whitespace. Note that lengths of words are perfectly additive, so we | ||
| # can compute the length of a line by adding the lengths of all words | ||
| # and the whitespace between them. | ||
| space = context.textlength(" ", font = font) | ||
| ellipsis = context.textlength("...", font = font) | ||
| # Initialize lists to hold the lengths of words and indexes of lines. | ||
| # Tracking line indexes allows us to improve splitting using heuristics. | ||
| lengths: list[int] = [] | ||
| indexes, current = [0], 0 | ||
| # Split words at whitespace, and successively add words to the current | ||
| # line. For every other than the first word, account for the whitespace | ||
| # between words. If the next word would exceed the width of the input | ||
| # image, and thus overflow the line, start a new one. | ||
| words = re.split(r"\s+", unescape(typography.content)) | ||
| for word in words: | ||
| length = context.textlength(word, font = font) | ||
| lengths.append(length) | ||
| # Start new line if current line overflows | ||
| whitespace = space if current else 0 | ||
| if current + whitespace + length > input.width: | ||
| indexes.append(len(lengths) - 1) | ||
| current = length | ||
| # Add word to current line | ||
| else: | ||
| lines.append(words) | ||
| words = [word] | ||
| current += whitespace + length | ||
| # Join words for each line and create image | ||
| lines.append(words) | ||
| lines = [" ".join(line) for line in lines] | ||
| image = Image.new(mode = "RGBA", size = size) | ||
| # Add terminating index, if not already present | ||
| if len(lengths) != indexes[-1]: | ||
| indexes.append(len(lengths)) | ||
| # Create drawing context and split text into lines | ||
| context = ImageDraw.Draw(image) | ||
| # If the number of lines exceeds the maximum amount we are able to | ||
| # render, either shrink or truncate the text and add an ellipsis | ||
| amount = typography.line.amount | ||
| if amount < len(indexes) - 1: | ||
| # If overflow mode is set to 'shrink', decrease the font size and | ||
| # try to render the typography again to see if it fits | ||
| overflow = typography.overflow | ||
| if overflow == "shrink": | ||
| typography.line.amount += 1 | ||
| # Render layer with new typography metrics by calling this | ||
| # function recursively and returning immediately from it | ||
| return self._render_typography(layer, input) | ||
| # Determine last and penultimate line indexes | ||
| indexes = indexes[:amount + 1] | ||
| p, q = indexes[-2:] | ||
| # Compute the length of the last line, and check whether we can add | ||
| # the ellipsis after the last word. If not, replace the last word. | ||
| current = sum(lengths[p:q]) + (q - p) * space | ||
| if current + ellipsis < input.width: | ||
| q += 1 | ||
| # Update line indexes and replace word with ellipsis | ||
| indexes[-1] = q | ||
| words[q - 1] = "..." | ||
| # If there are exactly two lines, check if we can improve splitting by | ||
| # moving the last word of the first line to the last line | ||
| elif len(indexes) == 3: | ||
| p, q, r = indexes[-3:] | ||
| # Create two configurations of lines, one with the last word of the | ||
| # first line moved to the last line, and one without the change | ||
| a = [len(" ".join(l)) for l in [words[p:q], words[q:r]]] | ||
| b = [len(" ".join(l)) for l in [words[p:q - 1], words[q - 1:r]]] | ||
| # Compute standard deviation of line lengths before and after the | ||
| # change, and if the standard deviation decreases, move the word | ||
| if stdev(b) < stdev(a): | ||
| indexes[-2] -= 1 | ||
| # Compute anchor and deduce alignment, as well as offset. The anchor | ||
| # is computed as a string of two characters, where the first character | ||
| # denotes the horizontal alignment and the second character denotes | ||
| # the vertical alignment. | ||
| anchor = _anchor(typography.align) | ||
| # Compute horizontal alignment | ||
| if anchor[0] == "l": align, x = "left", 0 | ||
| elif anchor[0] == "m": align, x = "center", input.width >> 1 | ||
| else: align, x = "right", input.width >> 0 | ||
| # Compute vertical alignment | ||
| if anchor[1] == "a": y = 0 | ||
| elif anchor[1] == "m": y = input.height >> 1 | ||
| else: y = input.height >> 0 | ||
| # Join words with whitespace and lines with line breaks | ||
| text = "\n".join([ | ||
| " ".join(words[p:q]) | ||
| for p, q in zip(indexes, indexes[1:]) | ||
| ]) | ||
| # Draw text onto image | ||
| context.text( | ||
| (0, spacing / 2 - yoffset), "\n".join(lines[:lmax]), | ||
| font = font, fill = self.color["text"], spacing = spacing - yoffset | ||
| (x, y), text, | ||
| font = font, | ||
| anchor = anchor, | ||
| spacing = spacing, | ||
| fill = typography.color, | ||
| align = align | ||
| ) | ||
| # Return text image | ||
| return image | ||
| # Return image with typography | ||
| input.alpha_composite(image) | ||
| return input | ||
| # ------------------------------------------------------------------------- | ||
| # Render overlay for debugging | ||
| def _render_overlay(self, layout: Layout, input: _Image): | ||
| path = self._resolve_font("Roboto", "Regular") | ||
| font = ImageFont.truetype(path, 12) | ||
| # Generate meta tags | ||
| def _generate_meta(self, page, config): | ||
| directory = self.config.cards_dir | ||
| file, _ = os.path.splitext(page.file.src_uri) | ||
| # Create image and initialize drawing context | ||
| image = Image.new(mode = "RGBA", size = input.size) | ||
| context = ImageDraw.Draw(image) | ||
| # Compute page title | ||
| if page.is_homepage: | ||
| title = config.site_name | ||
| else: | ||
| page_title = page.meta.get("title", page.title) | ||
| title = f"{page_title} - {config.site_name}" | ||
| # Draw overlay grid | ||
| fill = self.config.debug_color | ||
| if self.config.debug_grid: | ||
| step = self.config.debug_grid_step | ||
| for i in range(0, input.width, step): | ||
| for j in range(0, input.height, step): | ||
| context.ellipse( | ||
| ((i - 1, j - 1), (i + 1, j + 1)), | ||
| fill = fill | ||
| ) | ||
| # Compute page description | ||
| description = config.site_description | ||
| if "description" in page.meta: | ||
| description = page.meta["description"] | ||
| # Compute luminosity of debug color and use it to determine the color | ||
| # of the text that will be drawn on top of the debug color | ||
| (r, g, b, *_) = ImageColor.getrgb(fill) | ||
| color = "black" if r * 0.299 + g * 0.587 + b * 0.114 > 150 else "white" | ||
| # Resolve image URL | ||
| url = "{}.png".format(posixpath.join( | ||
| config.site_url or ".", | ||
| directory, | ||
| file | ||
| )) | ||
| # Draw overlay outline for each layer | ||
| for i, layer in enumerate(layout.layers): | ||
| x, y = get_offset(layer, image) | ||
| w, h = get_size(layer) | ||
| # Ensure forward slashes | ||
| url = url.replace(os.path.sep, "/") | ||
| # Draw overlay outline | ||
| context.rectangle(outline = fill, xy = (x, y, | ||
| min(x + w, input.width - 1), | ||
| min(y + h, input.height - 1) | ||
| )) | ||
| # Return meta tags | ||
| return [ | ||
| # Assemble text and compute its width and height - we only use the | ||
| # coordinates denoting the width and height of the text, as we need | ||
| # to compute the coordinates of the text box manually in order to | ||
| # have the rectangle align perfectly with the outline | ||
| text = f"{i} – {x}, {y}" | ||
| (_, _, x1, y1) = context.textbbox((x, y), text, font = font) | ||
| # Meta tags for Open Graph | ||
| { "property": "og:type", "content": "website" }, | ||
| { "property": "og:title", "content": title }, | ||
| { "property": "og:description", "content": description }, | ||
| { "property": "og:image", "content": url }, | ||
| { "property": "og:image:type", "content": "image/png" }, | ||
| { "property": "og:image:width", "content": "1200" }, | ||
| { "property": "og:image:height", "content": "630" }, | ||
| { "property": "og:url", "content": page.canonical_url }, | ||
| # Draw text on a small rectangle in the top left corner of the | ||
| # layer denoting the number of the layer and its offset | ||
| context.rectangle(fill = fill, xy = (x, y, x1 + 8, y1 + 4)) | ||
| context.text((x + 4, y + 2), text, font = font, fill = color) | ||
| # Meta tags for Twitter | ||
| { "name": "twitter:card", "content": "summary_large_image" }, | ||
| # { "name": "twitter:site", "content": user }, | ||
| # { "name": "twitter:creator", "content": user }, | ||
| { "name": "twitter:title", "content": title }, | ||
| { "name": "twitter:description", "content": description }, | ||
| { "name": "twitter:image", "content": url } | ||
| ] | ||
| # Return image with overlay | ||
| input.alpha_composite(image) | ||
| return input | ||
| def _load_resized_logo(self, config, width = 144): | ||
| logo = self._load_logo(config) | ||
| height = int(width * logo.height / logo.width) | ||
| return logo.resize((width, height)) | ||
| # ------------------------------------------------------------------------- | ||
| # Retrieve logo image or icon | ||
| def _load_logo(self, config): | ||
| theme = config.theme | ||
| # Resolve layout - authors can specify a custom directory for layouts in | ||
| # the configuration, which is checked prior to the layout directory shipped | ||
| # with this plugin. If the layout cannot be resolved in any of the known | ||
| # directories, the plugin must abort with an error. | ||
| def _resolve_layout(self, name: str, config: MkDocsConfig): | ||
| name, _ = os.path.splitext(name) | ||
| if name in self.card_layouts: | ||
| return self.card_layouts[name], self.card_variables[name] | ||
| # Handle images (precedence over icons) | ||
| if "logo" in theme: | ||
| _, extension = os.path.splitext(theme["logo"]) | ||
| # If the author specified a custom directory, try to resolve the layout | ||
| # from this directory first, otherwise fall back to the default | ||
| for base in [ | ||
| os.path.relpath(self.config.cards_layout_dir), | ||
| _templates_dirpath() | ||
| ]: | ||
| path = os.path.join(base, f"{name}.yml") | ||
| path = os.path.normpath(path) | ||
| path = os.path.join(config.docs_dir, theme["logo"]) | ||
| # Skip if layout does not exist and try next directory | ||
| if not os.path.isfile(path): | ||
| continue | ||
| # Allow users to put the logo inside their custom_dir (theme["logo"] case) | ||
| if theme.custom_dir: | ||
| custom_dir_logo = os.path.join(theme.custom_dir, theme["logo"]) | ||
| if os.path.exists(custom_dir_logo): | ||
| path = custom_dir_logo | ||
| # Open file and parse as YAML | ||
| with open(path, encoding = "utf-8-sig") as f: | ||
| layout: Layout = Layout(config_file_path = path) | ||
| try: | ||
| layout.load_dict(yaml.load(f, SafeLoader) or {}) | ||
| # Load SVG and convert to PNG | ||
| if extension == ".svg": | ||
| return self._load_logo_svg(path) | ||
| # The layout could not be loaded because of a syntax error, | ||
| # which we display to the author with a nice error message | ||
| except Exception as e: | ||
| path = os.path.relpath(path, base) | ||
| raise PluginError( | ||
| f"Error reading layout file '{path}' in '{base}':\n" | ||
| f"{e}" | ||
| ) | ||
| # Load PNG, JPEG, etc. | ||
| return Image.open(path).convert("RGBA") | ||
| # Validate layout and abort if errors occurred | ||
| errors, warnings = layout.validate() | ||
| for _, w in warnings: | ||
| log.warning(w) | ||
| for _, e in errors: | ||
| path = os.path.relpath(path, base) | ||
| raise PluginError( | ||
| f"Error reading layout file '{path}' in '{base}':\n" | ||
| f"{e}" | ||
| ) | ||
| # Handle icons | ||
| icon = theme.get("icon") or {} | ||
| if "logo" in icon and icon["logo"]: | ||
| logo = icon["logo"] | ||
| else: | ||
| logo = "material/library" | ||
| # Store layout and variables | ||
| self.card_layouts[name] = layout | ||
| self.card_variables[name] = [] | ||
| # Resolve path of package | ||
| base = os.path.abspath(os.path.join( | ||
| os.path.dirname(__file__), | ||
| "../.." | ||
| )) | ||
| # Extract variables for each layer from layout | ||
| for layer in layout.layers: | ||
| variables = _extract(layer, self.card_env, config) | ||
| self.card_variables[name].append(variables) | ||
| path = f"{base}/templates/.icons/{logo}.svg" | ||
| # Set default values for for layer size, if not given | ||
| for key, value in layer.size.items(): | ||
| if value == 0: | ||
| layer.size[key] = layout.size[key] | ||
| # Allow users to put the logo inside their custom_dir (theme["icon"]["logo"] case) | ||
| if theme.custom_dir: | ||
| custom_dir_logo = os.path.join(theme.custom_dir, ".icons", f"{logo}.svg") | ||
| if os.path.exists(custom_dir_logo): | ||
| path = custom_dir_logo | ||
| # Abort, since we're done | ||
| break | ||
| # Load icon data and fill with color | ||
| return self._load_logo_svg(path, self.color["text"]) | ||
| # Abort if the layout could not be resolved | ||
| if name not in self.card_layouts: | ||
| raise PluginError(f"Couldn't find layout '{name}'") | ||
| # Load SVG file and convert to PNG | ||
| def _load_logo_svg(self, path, fill = None): | ||
| file = BytesIO() | ||
| data = open(path).read() | ||
| # Return layout and variables | ||
| return self.card_layouts[name], self.card_variables[name] | ||
| # Fill with color, if given | ||
| if fill: | ||
| data = data.replace("<svg", f"<svg fill=\"{fill}\"") | ||
| # Resolve icon with given name - this function searches for the icon in all | ||
| # known theme directories, including custom directories specified by the | ||
| # author, which allows for using custom icons in cards. If the icon cannot | ||
| # be resolved, the plugin must abort with an error. | ||
| def _resolve_icon(self, name: str, config: MkDocsConfig): | ||
| for base in config.theme.dirs: | ||
| path = os.path.join(base, ".icons", f"{name}.svg") | ||
| path = os.path.normpath(path) | ||
| # Convert to PNG and return image | ||
| svg2png(bytestring = data.encode("utf-8"), write_to = file, scale = 10) | ||
| return Image.open(file) | ||
| # Skip if icon does not exist and try next directory | ||
| if not os.path.isfile(path): | ||
| continue | ||
| # Retrieve font either from the card layout option or from the Material | ||
| # font defintion. If no font is defined for Material or font is False | ||
| # then choose a default. | ||
| def _load_font(self, config): | ||
| name = self.config.cards_layout_options.get("font_family") | ||
| if not name: | ||
| material_name = config.theme.get("font", False) | ||
| if material_name is False: | ||
| name = "Roboto" | ||
| else: | ||
| name = material_name.get("text", "Roboto") | ||
| # Open and return icon | ||
| with open(path, encoding = "utf-8") as f: | ||
| return f.read() | ||
| # Resolve relevant fonts | ||
| font = {} | ||
| for style in ["Regular", "Bold"]: | ||
| font[style] = self._resolve_font(name, style) | ||
| # Abort if the icon could not be resolved | ||
| raise PluginError(f"Couldn't find icon '{name}'") | ||
| # Return available font weights with fallback | ||
| return defaultdict(lambda: font["Regular"], font) | ||
| # Resolve font family with specific style - if we haven't already done it, | ||
@@ -481,9 +835,20 @@ # the font family is first downloaded from Google Fonts and the styles are | ||
| # must abort with an error. | ||
| def _resolve_font(self, family: str, style: str): | ||
| def _resolve_font(self, family: str, style: str, variant = ""): | ||
| path = os.path.join(self.config.cache_dir, "fonts", family) | ||
| # Fetch font family, if it hasn't been fetched yet | ||
| # Fetch font family, if it hasn't been fetched yet - we use a lock to | ||
| # synchronize access, so the font is not downloaded multiple times, but | ||
| # all other threads wait for the font being available. This is also why | ||
| # we need the double path check, which makes sure that we only use the | ||
| # lock when we actually need to download a font that doesn't exist. If | ||
| # we already downloaded it, we don't want to block at all. | ||
| if not os.path.isdir(path): | ||
| self._fetch_font_from_google_fonts(family) | ||
| with self.lock: | ||
| if not os.path.isdir(path): | ||
| self._fetch_font_from_google_fonts(family) | ||
| # Assemble fully qualified style - see https://t.ly/soDF0 | ||
| if variant: | ||
| style = f"{variant} {style}" | ||
| # Check for availability of font style | ||
@@ -517,2 +882,4 @@ list = sorted(os.listdir(path)) | ||
| # ------------------------------------------------------------------------- | ||
| # Fetch font family from Google Fonts | ||
@@ -543,16 +910,221 @@ def _fetch_font_from_google_fonts(self, family: str): | ||
| # Extract font family name and style using the content in the | ||
| # response via ByteIO to avoid writing a temp file. Done to fix | ||
| # problems with passing a NamedTemporaryFile to | ||
| # ImageFont.truetype() on Windows, see https://t.ly/LiF_k | ||
| with BytesIO(res.content) as fontdata: | ||
| font = ImageFont.truetype(fontdata) | ||
| name, style = font.getname() | ||
| name = " ".join([name.replace(family, ""), style]).strip() | ||
| target = os.path.join(path, family, f"{name}.ttf") | ||
| # Construct image font for analysis by directly reading the | ||
| # contents from the response without priorily writing to a | ||
| # temporary file (like we did before), as this might lead to | ||
| # problems on Windows machines, see https://t.ly/LiF_k | ||
| with BytesIO(res.content) as f: | ||
| font = ImageFont.truetype(f) | ||
| # write file to cache | ||
| # Extract font family name and style | ||
| name, style = font.getname() | ||
| name = " ".join([name.replace(family, ""), style]).strip() | ||
| # Write file to cache directory | ||
| target = os.path.join(path, family, f"{name}.ttf") | ||
| write_file(res.content, target) | ||
| # ------------------------------------------------------------------------- | ||
| # Retrieve configuration value - each page can override certain parts of | ||
| # the site configuration, depending on the type and structure of the value | ||
| def _config(self, name: str, page: Page): | ||
| meta = page.meta.get("social", {}) | ||
| # Primitive values: choose page- over site-level configuration | ||
| if isinstance(self.config[name], (bool, str, int, float)): | ||
| return meta.get(name, self.config[name]) | ||
| # Dictionary values: merge site- with page-level configuration | ||
| if isinstance(self.config[name], (dict)): | ||
| return { **self.config[name], **meta.get(name, {}) } | ||
| # Create a file for the given path | ||
| def _path_to_file(self, path: str, config: MkDocsConfig): | ||
| assert path.endswith(".png") | ||
| return File( | ||
| posixpath.join(self.config.cards_dir, path), | ||
| self.config.cache_dir, | ||
| config.site_dir, | ||
| False | ||
| ) | ||
| # ----------------------------------------------------------------------------- | ||
| # Helper functions | ||
| # ----------------------------------------------------------------------------- | ||
| # Compute a stable hash from an object - since we're doing compositing, we can | ||
| # leverage caching to omit re-generating layers when their parameters stay the | ||
| # same. Additionally, we can identify identical layers between images, e.g., | ||
| # background, logos, or avatars, but also unchanged text. Note that we need to | ||
| # convert the data to a string prior to hashing, because configuration objects | ||
| # are inherently unstable, always resulting in new hashes. | ||
| def _digest(data: object): | ||
| return sha1(pickle.dumps(str(data))).hexdigest() | ||
| # ----------------------------------------------------------------------------- | ||
| # Extract all variables recursively | ||
| def _extract(data: any, env: Environment, config: MkDocsConfig): | ||
| # Traverse configuration or dictionary | ||
| if isinstance(data, (Config, dict)): | ||
| return [ | ||
| variable for value in data.values() | ||
| for variable in _extract(value, env, config) | ||
| ] | ||
| # Traverse list | ||
| elif isinstance(data, list): | ||
| return [ | ||
| variable for value in data | ||
| for variable in _extract(value, env, config) | ||
| ] | ||
| # Retrieve variables from string | ||
| elif isinstance(data, str): | ||
| if find_undeclared_variables(env.parse(data)): | ||
| return [data] | ||
| # Return nothing | ||
| return [] | ||
| # Replace all variables recursively and return a copy of the given data | ||
| def _replace(data: any, env: Environment, config: MkDocsConfig, **kwargs): | ||
| # Traverse configuration or dictionary | ||
| if isinstance(data, (Config, dict)): | ||
| data = copy(data) | ||
| for key, value in data.items(): | ||
| data[key] = _replace(value, env, config, **kwargs) | ||
| # Traverse list | ||
| elif isinstance(data, list): | ||
| return [ | ||
| _replace(value, env, config, **kwargs) | ||
| for value in data | ||
| ] | ||
| # Retrieve variables from string | ||
| elif isinstance(data, str): | ||
| return _compile(data, env).render( | ||
| config = config, **kwargs | ||
| ) or None | ||
| # Return data | ||
| return data | ||
| # Compile template and cache it indefinitely | ||
| @functools.lru_cache(maxsize = None) | ||
| def _compile(data: str, env: Environment): | ||
| return env.from_string(html.unescape(data)) | ||
| # Compute absolute path to internal templates directory, | ||
| # we need to do it this way to assure compatibility with Python 3.8, | ||
| # and also to allow users to install their Python site-packages | ||
| # to a different mount root than their documentation - see https://t.ly/GMeYP | ||
| def _templates_dirpath(): | ||
| return os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates") | ||
| # ----------------------------------------------------------------------------- | ||
| # Resize image to match the size of the reference image and align it to the | ||
| # center of the reference image so that it is fully covered | ||
| def _resize_cover(image: _Image, ref: _Image): | ||
| ratio = max( | ||
| ref.width / image.width, | ||
| ref.height / image.height | ||
| ) | ||
| # Compute aspect ratios of both images and choose the larger one, then | ||
| # resize the image so that it covers the entire reference image | ||
| image = image.resize(( | ||
| int(image.width * ratio), | ||
| int(image.height * ratio) | ||
| )) | ||
| # Align image to the center of the reference image - we also need to crop | ||
| # the image if it's larger than the given reference image | ||
| return image.crop(( | ||
| image.width - ref.width >> 1, | ||
| image.height - ref.height >> 1, | ||
| image.width + ref.width >> 1, | ||
| image.height + ref.height >> 1 | ||
| )) | ||
| # Resize image to match the size of the reference image and align it to the | ||
| # center of the reference image so that it is fully contained | ||
| def _resize_contain(image: _Image, ref: _Image): | ||
| ratio = min( | ||
| ref.width / image.width, | ||
| ref.height / image.height | ||
| ) | ||
| # Resize image according to minimum ratio | ||
| image = image.resize(( | ||
| int(image.width * ratio), | ||
| int(image.height * ratio) | ||
| )) | ||
| # Create a blank image and paste the resized image into it | ||
| blank = Image.new(mode = "RGBA", size = ref.size) | ||
| blank.paste(image, ( | ||
| ref.width - image.width >> 1, | ||
| ref.height - image.height >> 1 | ||
| )) | ||
| # Return resized image | ||
| return blank | ||
| # ----------------------------------------------------------------------------- | ||
| # Resolve font metrics for given truetype font - this function computes the | ||
| # font size and spacing between lines based on the number of lines and height. | ||
| # In order to omit rounding errors, we compute the ascender and descender based | ||
| # on a font size of 1,000. | ||
| def _metrics(path: str, line: Line, ref: _Image): | ||
| font = ImageFont.truetype(path, 1000) | ||
| ascender, descender = font.getmetrics() | ||
| # It would be too complex to let the author define the font size, since this | ||
| # would involve a lot of fiddling to find the right value. Instead, we let | ||
| # the author define the number of lines and the line height, and we compute | ||
| # the font size from that. This is much more intuitive. As a basis, we use | ||
| # the ascender as the actual line height and also add the descender to | ||
| # account for the last line. It's no secret that correctly handling font | ||
| # metrics is super tricky - see https://bit.ly/31u9bh6 | ||
| extent = line.amount * ascender + 1 * descender | ||
| # Now, we still need to account for spacing between lines, which is why we | ||
| # take the number of lines - 1, and multiply that with the line height we | ||
| # computed from the ascender. We add this to the extent we computed before, | ||
| # which we use as a basis for the final font size. | ||
| extent += (line.amount - 1) * (line.height - 1) * ascender | ||
| size = (1000 * ref.height) / extent | ||
| # From this, we can compute the spacing between lines, and we're done. We | ||
| # then return both, the font size and spacing between lines. | ||
| spacing = (line.height - 1) * ascender * size / 1000 | ||
| return int(size), spacing | ||
| # Compute anchor, determining the alignment of text relative to the given | ||
| # coordinates, with the default being "top left" - see https://bit.ly/3NEfr07 | ||
| def _anchor(data: str): | ||
| axis = re.split(r"\s+", data) | ||
| # Determine anchor on x-axis | ||
| if "start" in axis: anchor = "l" | ||
| elif "end" in axis: anchor = "r" | ||
| elif "center" in axis: anchor = "m" | ||
| else: anchor = "l" | ||
| # Determine anchor on y-axis | ||
| if "top" in axis: anchor += "a" | ||
| elif "bottom" in axis: anchor += "d" | ||
| elif "center" in axis: anchor += "m" | ||
| else: anchor += "a" | ||
| # Return anchor | ||
| return anchor | ||
| # ----------------------------------------------------------------------------- | ||
| # Data | ||
@@ -562,28 +1134,2 @@ # ----------------------------------------------------------------------------- | ||
| # Set up logging | ||
| log = logging.getLogger("mkdocs") | ||
| log.addFilter(DuplicateFilter()) | ||
| # Color palette | ||
| colors = { | ||
| "red": { "fill": "#ef5552", "text": "#ffffff" }, | ||
| "pink": { "fill": "#e92063", "text": "#ffffff" }, | ||
| "purple": { "fill": "#ab47bd", "text": "#ffffff" }, | ||
| "deep-purple": { "fill": "#7e56c2", "text": "#ffffff" }, | ||
| "indigo": { "fill": "#4051b5", "text": "#ffffff" }, | ||
| "blue": { "fill": "#2094f3", "text": "#ffffff" }, | ||
| "light-blue": { "fill": "#02a6f2", "text": "#ffffff" }, | ||
| "cyan": { "fill": "#00bdd6", "text": "#ffffff" }, | ||
| "teal": { "fill": "#009485", "text": "#ffffff" }, | ||
| "green": { "fill": "#4cae4f", "text": "#ffffff" }, | ||
| "light-green": { "fill": "#8bc34b", "text": "#ffffff" }, | ||
| "lime": { "fill": "#cbdc38", "text": "#000000" }, | ||
| "yellow": { "fill": "#ffec3d", "text": "#000000" }, | ||
| "amber": { "fill": "#ffc105", "text": "#000000" }, | ||
| "orange": { "fill": "#ffa724", "text": "#000000" }, | ||
| "deep-orange": { "fill": "#ff6e42", "text": "#ffffff" }, | ||
| "brown": { "fill": "#795649", "text": "#ffffff" }, | ||
| "grey": { "fill": "#757575", "text": "#ffffff" }, | ||
| "blue-grey": { "fill": "#546d78", "text": "#ffffff" }, | ||
| "black": { "fill": "#000000", "text": "#ffffff" }, | ||
| "white": { "fill": "#ffffff", "text": "#000000" } | ||
| } | ||
| log = logging.getLogger("mkdocs.material.social") |
@@ -49,2 +49,4 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| tags_slugify_format = Type(str, default = "tag:{slug}") | ||
| tags_hierarchy = Type(bool, default = False) | ||
| tags_hierarchy_separator = Type(str, default = "/") | ||
| tags_sort_by = Type(Callable, default = tag_name) | ||
@@ -65,3 +67,16 @@ tags_sort_reverse = Type(bool, default = False) | ||
| listings_layout = Type(str, default = "default") | ||
| listings_toc = Type(bool, default = True) | ||
| # Settings for shadow tags | ||
| shadow = Type(bool, default = False) | ||
| shadow_on_serve = Type(bool, default = True) | ||
| shadow_tags = TagSet() | ||
| shadow_tags_prefix = Type(str, default = "") | ||
| shadow_tags_suffix = Type(str, default = "") | ||
| # Settings for export | ||
| export = Type(bool, default = True) | ||
| export_file = Type(str, default = "tags.json") | ||
| export_only = Type(bool, default = False) | ||
| # Deprecated settings | ||
@@ -72,5 +87,5 @@ tags_compare = Deprecated(moved_to = "tags_sort_by") | ||
| tags_pages_compare_reverse = Deprecated(moved_to = "listings_sort_reverse") | ||
| tags_file = Deprecated( | ||
| option_type = Type(str), | ||
| message = "This setting is not required anymore" | ||
| tags_file = Deprecated(option_type = Type(str)) | ||
| tags_extra_files = Deprecated( | ||
| option_type = DictOfItems(ListOfItems(Type(str)), default = {}) | ||
| ) |
@@ -39,2 +39,3 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| from .structure.mapping.manager import MappingManager | ||
| from .structure.mapping.storage import MappingStorage | ||
@@ -48,2 +49,7 @@ # ----------------------------------------------------------------------------- | ||
| A tags plugin. | ||
| This plugin collects tags from the front matter of pages, and builds a tag | ||
| structure from them. The tag structure can be used to render listings on | ||
| pages, or to just create a site-wide tags index and export all tags and | ||
| mappings to a JSON file for consumption in another project. | ||
| """ | ||
@@ -128,2 +134,13 @@ | ||
| # If the author only wants to extract and export mappings, we allow to | ||
| # disable the rendering of all tags and listings with a single setting | ||
| if self.config.export_only: | ||
| self.config.tags = False | ||
| self.config.listings = False | ||
| # By default, shadow tags are rendered when the documentation is served, | ||
| # but not when it is built, for a better user experience | ||
| if self.is_serve and self.config.shadow_on_serve: | ||
| self.config.shadow = True | ||
| @event_priority(-50) | ||
@@ -157,2 +174,6 @@ def on_page_markdown( | ||
| # Handle deprecation of `tags_extra_files` setting | ||
| if self.config.tags_extra_files: | ||
| markdown = self._handle_deprecated_tags_extra_files(page, markdown) | ||
| # Collect tags from page | ||
@@ -193,2 +214,11 @@ try: | ||
| # Export mappings to file, if enabled | ||
| if self.config.export: | ||
| path = os.path.join(config.site_dir, self.config.export_file) | ||
| path = os.path.normpath(path) | ||
| # Serialize mappings and save to file | ||
| storage = MappingStorage(self.config) | ||
| storage.save(path, self.mappings) | ||
| def on_page_context( | ||
@@ -251,2 +281,34 @@ self, context: TemplateContext, *, page: Page, **kwargs | ||
| def _handle_deprecated_tags_extra_files( | ||
| self, page: Page, markdown: str | ||
| ) -> str: | ||
| """ | ||
| Handle deprecation of `tags_extra_files` setting. | ||
| Arguments: | ||
| page: The page. | ||
| """ | ||
| directive = self.config.listings_directive | ||
| if page.file.src_uri not in self.config.tags_extra_files: | ||
| return markdown | ||
| # Compute tags to render on page | ||
| tags = self.config.tags_extra_files[page.file.src_uri] | ||
| if tags: | ||
| directive += f" {{ include: [{', '.join(tags)}] }}" | ||
| # Try to find the legacy tags marker and replace with directive | ||
| if "[TAGS]" in markdown: | ||
| markdown = markdown.replace( | ||
| "[TAGS]", f"<!-- {directive} -->" | ||
| ) | ||
| # Try to find the directive and add it if not present | ||
| pattern = r"<!--\s+{directive}".format(directive = re.escape(directive)) | ||
| if not re.search(pattern, markdown): | ||
| markdown += f"\n<!-- {directive} -->" | ||
| # Return markdown | ||
| return markdown | ||
| # ----------------------------------------------------------------------------- | ||
@@ -253,0 +315,0 @@ # Data |
@@ -195,2 +195,7 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| Mappings are only added to listings, if the listing features tags that | ||
| are also featured in the mapping. The caller can decide whether hidden | ||
| tags should be rendered or not, e.g., automatically set by the plugin | ||
| when shadow tags are disabled. | ||
| Arguments: | ||
@@ -197,0 +202,0 @@ mapping: The mapping. |
@@ -47,2 +47,10 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| shadow = Optional(Type(bool)) | ||
| """ | ||
| Whether to include shadow tags. | ||
| This setting allows to override the global setting for shadow tags. If this | ||
| setting is not specified, the global `shadow` setting is used. | ||
| """ | ||
| layout = Optional(Type(str)) | ||
@@ -56,2 +64,10 @@ """ | ||
| toc = Optional(Type(bool)) | ||
| """ | ||
| Whether to populate the table of contents with anchor links to tags. | ||
| This setting allows to override the global setting for the layout. If this | ||
| setting is not specified, the global `listings_toc` setting is used. | ||
| """ | ||
| include = TagSet() | ||
@@ -58,0 +74,0 @@ """ |
@@ -246,5 +246,5 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Add mappings to listing | ||
| # Add mappings to listing, passing shadow tags configuration | ||
| for mapping in mappings: | ||
| listing.add(mapping) | ||
| listing.add(mapping, hidden = listing.config.shadow) | ||
@@ -380,2 +380,6 @@ # Sort listings and tags - we can only do this after all mappings have | ||
| # Inherit shadow tags configuration, unless explicitly set | ||
| if not isinstance(config.shadow, bool): | ||
| config.shadow = self.config.shadow | ||
| # Inherit layout configuration, unless explicitly set | ||
@@ -385,2 +389,6 @@ if not isinstance(config.layout, str): | ||
| # Inherit table of contents configuration, unless explicitly set | ||
| if not isinstance(config.toc, bool): | ||
| config.toc = self.config.listings_toc | ||
| # Return listing configuration | ||
@@ -395,2 +403,7 @@ return config | ||
| If the tag hierarchy setting is enabled, the tag is expanded into a | ||
| hierarchy of tags, all of which are then slugified and joined with the | ||
| configured separator. Otherwise, the tag is slugified directly. This is | ||
| necessary to keep the tag hierarchy in the slug. | ||
| Arguments: | ||
@@ -402,7 +415,14 @@ tag: The tag. | ||
| """ | ||
| slugify = self.config.tags_slugify | ||
| tags = [tag.name] | ||
| # Compute tag hierarchy, if configured | ||
| hierarchy = self.config.tags_hierarchy_separator | ||
| if self.config.tags_hierarchy: | ||
| tags = tag.name.split(hierarchy) | ||
| # Slugify tag hierarchy and join with separator | ||
| separator = self.config.tags_slugify_separator | ||
| return self.config.tags_slugify_format.format( | ||
| slug = self.config.tags_slugify( | ||
| tag.name, | ||
| self.config.tags_slugify_separator | ||
| ) | ||
| slug = hierarchy.join(slugify(name, separator) for name in tags) | ||
| ) | ||
@@ -409,0 +429,0 @@ |
@@ -84,3 +84,6 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| children = [anchors[tag] for tag in anchors if not tag.parent] | ||
| host.children[at:at + 1] = children | ||
| if listing.config.toc: | ||
| host.children[at:at + 1] = children | ||
| else: | ||
| host.children.pop(at) | ||
@@ -87,0 +90,0 @@ # Return mapping of tags to anchor links |
@@ -37,2 +37,7 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| Listing trees are a tree structure that represent the hierarchy of tags | ||
| and mappings. Each tree node is a tag, and each tag can have multiple | ||
| mappings. Additionally, each tree can have subtrees, which are typically | ||
| called nested tags. | ||
| This is an internal data structure that is used to render listings. It is | ||
@@ -39,0 +44,0 @@ also the immediate structure that is passed to the template. |
@@ -26,2 +26,3 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| from material.plugins.tags.structure.mapping import Mapping | ||
| from material.plugins.tags.structure.tag import Tag | ||
| from material.plugins.tags.structure.tag.options import TagSet | ||
@@ -129,3 +130,3 @@ from mkdocs.structure.pages import Page | ||
| for tag in self.format.validate(page.meta[tags]): | ||
| mapping.tags.add(tag) | ||
| mapping.tags.add(self._configure(tag)) | ||
@@ -148,2 +149,86 @@ # Return mapping | ||
| # ------------------------------------------------------------------------- | ||
| def _configure(self, tag: Tag) -> Tag: | ||
| """ | ||
| Configure tag. | ||
| This method is called by the mapping manager to configure a tag for the | ||
| the tag structure. Depending on the configuration, the tag is expanded | ||
| into a hierarchy of tags, and can be marked as hidden if it is a shadow | ||
| tag, hiding it from mappings and listings when rendering. | ||
| Arguments: | ||
| tag: The tag. | ||
| Returns: | ||
| The configured tag. | ||
| """ | ||
| if self.config.tags_hierarchy: | ||
| return self._configure_hierarchy(tag) | ||
| else: | ||
| return self._configure_shadow(tag, tag.name) | ||
| def _configure_hierarchy(self, tag: Tag) -> Tag: | ||
| """ | ||
| Configure hierarchical tag. | ||
| Note that shadow tags that occur as part of a tag hierarchy propagate | ||
| their hidden state to all of their children. | ||
| Arguments: | ||
| tag: The tag. | ||
| Returns: | ||
| The configured tag. | ||
| """ | ||
| separator = self.config.tags_hierarchy_separator | ||
| root, *rest = tag.name.split(separator) | ||
| # Create tag root and hierarchy | ||
| tag = self._configure_shadow(Tag(root), root) | ||
| for name in rest: | ||
| tag = self._configure_shadow(Tag( | ||
| separator.join([tag.name, name]), | ||
| parent = tag, hidden = tag.hidden | ||
| ), name) | ||
| # Return tag | ||
| return tag | ||
| def _configure_shadow(self, tag: Tag, name: str) -> Tag: | ||
| """ | ||
| Configure shadow tag. | ||
| Regardless of the configuration, tags are always marked as hidden if | ||
| they're classified as shadow tags, e.g., if their name matches the | ||
| configured shadow prefix or suffix, or if they're part of the list of | ||
| shadow tags. Whether they're displayed is decided before rendering. | ||
| The tag name must be passed separately, as it may be different from the | ||
| tag's name, e.g., when creating a tag hierarchy. In this case, the name | ||
| represents the part that was added to the tag, essentially the suffix. | ||
| The name is checked for shadow prefixes and suffixes. | ||
| Arguments: | ||
| tag: The tag. | ||
| name: The tag name. | ||
| Returns: | ||
| The configured tag. | ||
| """ | ||
| if not tag.hidden: | ||
| tag.hidden = tag in self.config.shadow_tags | ||
| # Check if tag matches shadow prefix, if defined | ||
| if not tag.hidden and self.config.shadow_tags_prefix: | ||
| tag.hidden = name.startswith(self.config.shadow_tags_prefix) | ||
| # Check if tag matches shadow suffix, if defined | ||
| if not tag.hidden and self.config.shadow_tags_suffix: | ||
| tag.hidden = name.endswith(self.config.shadow_tags_suffix) | ||
| # Return tag | ||
| return tag | ||
| # ----------------------------------------------------------------------------- | ||
@@ -150,0 +235,0 @@ # Functions |
@@ -34,2 +34,31 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| A tag. | ||
| Tags can be used to categorize pages and group them into a tag structure. A | ||
| tag is a simple string, which can be split into a hierarchy of tags by using | ||
| the character or string as defined in the `hierarchy_separator` setting in | ||
| `mkdocs.yml`. Each parent tag contains their child tags. | ||
| Example: | ||
| ```yaml | ||
| tags: | ||
| - foo/bar | ||
| - foo/baz | ||
| - qux | ||
| ``` | ||
| The tag structure for the above example would look like this: | ||
| ``` | ||
| . | ||
| ├─ foo | ||
| │ ├─ bar | ||
| │ └─ baz | ||
| └─ qux | ||
| ``` | ||
| Note that this class does not split the tag name into a hierarchy of tags | ||
| by itself, but rather provides a simple interface to iterate over the tag | ||
| and its parents. Splitting is left to the caller, in order to allow for | ||
| changing the separator in `mkdocs.yml`. | ||
| """ | ||
@@ -36,0 +65,0 @@ |
@@ -30,2 +30,7 @@ {#- | ||
| {% endif %} | ||
| {% if config.extra.alternate is iterable %} | ||
| {% for alt in config.extra.alternate %} | ||
| <link rel="alternate" href="{{ alt.link | url }}" hreflang="{{ alt.lang | d(lang.t('language')) }}"> | ||
| {% endfor %} | ||
| {% endif %} | ||
| {% if "rss" in config.plugins %} | ||
@@ -36,3 +41,3 @@ <link rel="alternate" type="application/rss+xml" title="{{ lang.t('rss.created') }}" href="{{ 'feed_rss_created.xml' | url }}"> | ||
| <link rel="icon" href="{{ config.theme.favicon | url }}"> | ||
| <meta name="generator" content="mkdocs-{{ mkdocs_version }}, mkdocs-material-9.6.23"> | ||
| <meta name="generator" content="mkdocs-{{ mkdocs_version }}, mkdocs-material-9.7.0"> | ||
| {% endblock %} | ||
@@ -49,6 +54,6 @@ {% block htmltitle %} | ||
| {% block styles %} | ||
| <link rel="stylesheet" href="{{ 'assets/stylesheets/main.84d31ad4.min.css' | url }}"> | ||
| <link rel="stylesheet" href="{{ 'assets/stylesheets/main.618322db.min.css' | url }}"> | ||
| {% if config.theme.palette %} | ||
| {% set palette = config.theme.palette %} | ||
| <link rel="stylesheet" href="{{ 'assets/stylesheets/palette.06af60db.min.css' | url }}"> | ||
| <link rel="stylesheet" href="{{ 'assets/stylesheets/palette.ab4e12ef.min.css' | url }}"> | ||
| {% endif %} | ||
@@ -81,7 +86,2 @@ {% include "partials/icons.html" %} | ||
| {% endblock %} | ||
| {% if page.meta and page.meta.meta %} | ||
| {% for tag in page.meta.meta %} | ||
| <meta {% for key, value in tag | items %} {{ key }}="{{value}}" {% endfor %}> | ||
| {% endfor %} | ||
| {% endif %} | ||
| {% block extrahead %}{% endblock %} | ||
@@ -186,2 +186,5 @@ </head> | ||
| <div class="md-content" data-md-component="content"> | ||
| {% if "navigation.path" in features %} | ||
| {% include "partials/path.html" %} | ||
| {% endif %} | ||
| <article class="md-content__inner md-typeset"> | ||
@@ -223,2 +226,3 @@ {% block content %} | ||
| {% set _ = namespace() %} | ||
| {% set _.annotate = config.extra.annotate %} | ||
| {% set _.tags = config.extra.tags %} | ||
@@ -247,3 +251,4 @@ {%- if config.extra.version -%} | ||
| }, | ||
| "search": "assets/javascripts/workers/search.973d3a69.min.js" | url, | ||
| "search": "assets/javascripts/workers/search.7a47a382.min.js" | url, | ||
| "annotate": _.annotate or none, | ||
| "tags": _.tags or none, | ||
@@ -255,3 +260,3 @@ "version": _.version or none | ||
| {% block scripts %} | ||
| <script src="{{ 'assets/javascripts/bundle.f55a23d4.min.js' | url }}"></script> | ||
| <script src="{{ 'assets/javascripts/bundle.e71a0d61.min.js' | url }}"></script> | ||
| {% for script in config.extra_javascript %} | ||
@@ -258,0 +263,0 @@ {{ script | script_tag }} |
@@ -11,2 +11,3 @@ {#- | ||
| "blog.archive": "Archiv", | ||
| "blog.authors": "Autoren", | ||
| "blog.categories": "Kategorien", | ||
@@ -13,0 +14,0 @@ "blog.categories.in": "in", |
@@ -12,2 +12,3 @@ {#- | ||
| "blog.archive": "Archive", | ||
| "blog.authors": "Authors", | ||
| "blog.categories": "Categories", | ||
@@ -14,0 +15,0 @@ "blog.categories.in": "in", |
@@ -13,2 +13,11 @@ {#- | ||
| {% endmacro %} | ||
| {% macro render_title(nav_item) %} | ||
| {% if nav_item.typeset %} | ||
| <span class="md-typeset"> | ||
| {{ nav_item.typeset.title }} | ||
| </span> | ||
| {% else %} | ||
| {{ nav_item.title }} | ||
| {% endif %} | ||
| {% endmacro %} | ||
| {% macro render_content(nav_item, ref) %} | ||
@@ -20,3 +29,3 @@ {% set ref = ref or nav_item %} | ||
| <span class="md-ellipsis"> | ||
| {{ ref.title }} | ||
| {{ render_title(ref) }} | ||
| {% if nav_item.meta and nav_item.meta.subtitle %} | ||
@@ -27,2 +36,5 @@ <br> | ||
| </span> | ||
| {% if nav_item.meta and nav_item.encrypted %} | ||
| {{ render_status(nav_item, "encrypted") }} | ||
| {% endif %} | ||
| {% if nav_item.meta and nav_item.meta.status %} | ||
@@ -119,3 +131,3 @@ {{ render_status(nav_item, nav_item.meta.status) }} | ||
| <span class="md-nav__icon md-icon"></span> | ||
| {{ nav_item.title }} | ||
| {{ render_title(nav_item) }} | ||
| </label> | ||
@@ -122,0 +134,0 @@ <ul class="md-nav__list" data-md-scrollfix> |
@@ -44,2 +44,5 @@ {#- | ||
| {% endif %} | ||
| {% if post.config.pin %} | ||
| <span class="md-pin"></span> | ||
| {% endif %} | ||
| </ul> | ||
@@ -62,3 +65,6 @@ {% if post.config.draft %} | ||
| {% endif %} | ||
| {% if post.config.pin %} | ||
| <hr> | ||
| {% endif %} | ||
| </div> | ||
| </article> |
@@ -11,2 +11,5 @@ {#- | ||
| {% set class = "md-tag" %} | ||
| {% if tag.hidden %} | ||
| {% set class = class ~ " md-tag-shadow" %} | ||
| {% endif %} | ||
| {% if config.extra.tags %} | ||
@@ -13,0 +16,0 @@ {% set class = class ~ " md-tag-icon" %} |
@@ -7,3 +7,9 @@ {#- | ||
| <span class="md-ellipsis"> | ||
| {{ toc_item.title }} | ||
| {% if toc_item.typeset %} | ||
| <span class="md-typeset"> | ||
| {{ toc_item.typeset.title }} | ||
| </span> | ||
| {% else %} | ||
| {{ toc_item.title }} | ||
| {% endif %} | ||
| </span> | ||
@@ -10,0 +16,0 @@ </a> |
+1
-1
| { | ||
| "name": "mkdocs-material", | ||
| "version": "9.6.23", | ||
| "version": "9.7.0", | ||
| "description": "Documentation that simply works", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+14
-18
| Metadata-Version: 2.4 | ||
| Name: mkdocs-material | ||
| Version: 9.6.23 | ||
| Version: 9.7.0 | ||
| Summary: Documentation that simply works | ||
@@ -22,13 +22,13 @@ Project-URL: Homepage, https://squidfunk.github.io/mkdocs-material/ | ||
| Requires-Python: >=3.8 | ||
| Requires-Dist: babel~=2.10 | ||
| Requires-Dist: backrefs~=5.7.post1 | ||
| Requires-Dist: colorama~=0.4 | ||
| Requires-Dist: jinja2~=3.1 | ||
| Requires-Dist: markdown~=3.2 | ||
| Requires-Dist: mkdocs-material-extensions~=1.3 | ||
| Requires-Dist: mkdocs~=1.6 | ||
| Requires-Dist: paginate~=0.5 | ||
| Requires-Dist: pygments~=2.16 | ||
| Requires-Dist: pymdown-extensions~=10.2 | ||
| Requires-Dist: requests~=2.26 | ||
| Requires-Dist: babel>=2.10 | ||
| Requires-Dist: backrefs>=5.7.post1 | ||
| Requires-Dist: colorama>=0.4 | ||
| Requires-Dist: jinja2>=3.1 | ||
| Requires-Dist: markdown>=3.2 | ||
| Requires-Dist: mkdocs-material-extensions>=1.3 | ||
| Requires-Dist: mkdocs>=1.6 | ||
| Requires-Dist: paginate>=0.5 | ||
| Requires-Dist: pygments>=2.16 | ||
| Requires-Dist: pymdown-extensions>=10.2 | ||
| Requires-Dist: requests>=2.26 | ||
| Provides-Extra: git | ||
@@ -76,6 +76,2 @@ Requires-Dist: mkdocs-git-committers-plugin-2<3,>=1.1; extra == 'git' | ||
| /></a> | ||
| <a href="https://github.com/sponsors/squidfunk"><img | ||
| src="https://img.shields.io/github/sponsors/squidfunk" | ||
| alt="Sponsors" | ||
| /></a> | ||
| </p> | ||
@@ -217,4 +213,4 @@ | ||
| /></a> | ||
| <a href="https://maxar.com/" target=_blank><img | ||
| src="https://raw.githubusercontent.com/squidfunk/mkdocs-material/master/.github/assets/sponsors/sponsor-maxar.png" height="58" | ||
| <a href="https://vantor.com/" target=_blank><img | ||
| src="https://raw.githubusercontent.com/squidfunk/mkdocs-material/master/.github/assets/sponsors/sponsor-vantor.png" height="58" | ||
| /></a> | ||
@@ -221,0 +217,0 @@ <a href="https://www.equipmentshare.com/" target=_blank><img |
+3
-1
@@ -75,3 +75,2 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| Issues = "https://github.com/squidfunk/mkdocs-material/issues" | ||
| Funding = "https://github.com/sponsors/squidfunk" | ||
@@ -84,6 +83,9 @@ [project.entry-points."mkdocs.plugins"] | ||
| "material/offline" = "material.plugins.offline.plugin:OfflinePlugin" | ||
| "material/optimize" = "material.plugins.optimize.plugin:OptimizePlugin" | ||
| "material/privacy" = "material.plugins.privacy.plugin:PrivacyPlugin" | ||
| "material/projects" = "material.plugins.projects.plugin:ProjectsPlugin" | ||
| "material/search" = "material.plugins.search.plugin:SearchPlugin" | ||
| "material/social" = "material.plugins.social.plugin:SocialPlugin" | ||
| "material/tags" = "material.plugins.tags.plugin:TagsPlugin" | ||
| "material/typeset" = "material.plugins.typeset.plugin:TypesetPlugin" | ||
@@ -90,0 +92,0 @@ [project.entry-points."mkdocs.themes"] |
+2
-6
@@ -31,6 +31,2 @@ <p align="center"> | ||
| /></a> | ||
| <a href="https://github.com/sponsors/squidfunk"><img | ||
| src="https://img.shields.io/github/sponsors/squidfunk" | ||
| alt="Sponsors" | ||
| /></a> | ||
| </p> | ||
@@ -172,4 +168,4 @@ | ||
| /></a> | ||
| <a href="https://maxar.com/" target=_blank><img | ||
| src="https://raw.githubusercontent.com/squidfunk/mkdocs-material/master/.github/assets/sponsors/sponsor-maxar.png" height="58" | ||
| <a href="https://vantor.com/" target=_blank><img | ||
| src="https://raw.githubusercontent.com/squidfunk/mkdocs-material/master/.github/assets/sponsors/sponsor-vantor.png" height="58" | ||
| /></a> | ||
@@ -176,0 +172,0 @@ <a href="https://www.equipmentshare.com/" target=_blank><img |
+11
-11
@@ -22,15 +22,15 @@ # Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com> | ||
| # Requirements for core | ||
| jinja2~=3.1 | ||
| markdown~=3.2 | ||
| mkdocs~=1.6 | ||
| mkdocs-material-extensions~=1.3 | ||
| pygments~=2.16 | ||
| pymdown-extensions~=10.2 | ||
| jinja2>=3.1 | ||
| markdown>=3.2 | ||
| mkdocs>=1.6 | ||
| mkdocs-material-extensions>=1.3 | ||
| pygments>=2.16 | ||
| pymdown-extensions>=10.2 | ||
| # Requirements for plugins | ||
| babel~=2.10 | ||
| colorama~=0.4 | ||
| paginate~=0.5 | ||
| backrefs~=5.7.post1 | ||
| requests~=2.26 | ||
| babel>=2.10 | ||
| colorama>=0.4 | ||
| paginate>=0.5 | ||
| backrefs>=5.7.post1 | ||
| requests>=2.26 | ||
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
| "use strict";(()=>{var xe=Object.create;var U=Object.defineProperty,ve=Object.defineProperties,Se=Object.getOwnPropertyDescriptor,Te=Object.getOwnPropertyDescriptors,Qe=Object.getOwnPropertyNames,J=Object.getOwnPropertySymbols,Ee=Object.getPrototypeOf,Z=Object.prototype.hasOwnProperty,be=Object.prototype.propertyIsEnumerable;var K=Math.pow,X=(t,e,r)=>e in t?U(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,A=(t,e)=>{for(var r in e||(e={}))Z.call(e,r)&&X(t,r,e[r]);if(J)for(var r of J(e))be.call(e,r)&&X(t,r,e[r]);return t},G=(t,e)=>ve(t,Te(e));var Le=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var we=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Qe(e))!Z.call(t,i)&&i!==r&&U(t,i,{get:()=>e[i],enumerable:!(n=Se(e,i))||n.enumerable});return t};var Pe=(t,e,r)=>(r=t!=null?xe(Ee(t)):{},we(e||!t||!t.__esModule?U(r,"default",{value:t,enumerable:!0}):r,t));var B=(t,e,r)=>new Promise((n,i)=>{var s=u=>{try{a(r.next(u))}catch(c){i(c)}},o=u=>{try{a(r.throw(u))}catch(c){i(c)}},a=u=>u.done?n(u.value):Promise.resolve(u.value).then(s,o);a((r=r.apply(t,e)).next())});var re=Le((ee,te)=>{/** | ||
| * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| * @license MIT | ||
| */(function(){var t=function(e){var r=new t.Builder;return r.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),r.searchPipeline.add(t.stemmer),e.call(r,r),r.build()};t.version="2.3.9";/*! | ||
| * lunr.utils | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.utils={},t.utils.warn=(function(e){return function(r){e.console&&console.warn&&console.warn(r)}})(this),t.utils.asString=function(e){return e==null?"":e.toString()},t.utils.clone=function(e){if(e==null)return e;for(var r=Object.create(null),n=Object.keys(e),i=0;i<n.length;i++){var s=n[i],o=e[s];if(Array.isArray(o)){r[s]=o.slice();continue}if(typeof o=="string"||typeof o=="number"||typeof o=="boolean"){r[s]=o;continue}throw new TypeError("clone is not deep and does not support nested objects")}return r},t.FieldRef=function(e,r,n){this.docRef=e,this.fieldName=r,this._stringValue=n},t.FieldRef.joiner="/",t.FieldRef.fromString=function(e){var r=e.indexOf(t.FieldRef.joiner);if(r===-1)throw"malformed field ref string";var n=e.slice(0,r),i=e.slice(r+1);return new t.FieldRef(i,n,e)},t.FieldRef.prototype.toString=function(){return this._stringValue==null&&(this._stringValue=this.fieldName+t.FieldRef.joiner+this.docRef),this._stringValue};/*! | ||
| * lunr.Set | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.Set=function(e){if(this.elements=Object.create(null),e){this.length=e.length;for(var r=0;r<this.length;r++)this.elements[e[r]]=!0}else this.length=0},t.Set.complete={intersect:function(e){return e},union:function(){return this},contains:function(){return!0}},t.Set.empty={intersect:function(){return this},union:function(e){return e},contains:function(){return!1}},t.Set.prototype.contains=function(e){return!!this.elements[e]},t.Set.prototype.intersect=function(e){var r,n,i,s=[];if(e===t.Set.complete)return this;if(e===t.Set.empty)return e;this.length<e.length?(r=this,n=e):(r=e,n=this),i=Object.keys(r.elements);for(var o=0;o<i.length;o++){var a=i[o];a in n.elements&&s.push(a)}return new t.Set(s)},t.Set.prototype.union=function(e){return e===t.Set.complete?t.Set.complete:e===t.Set.empty?this:new t.Set(Object.keys(this.elements).concat(Object.keys(e.elements)))},t.idf=function(e,r){var n=0;for(var i in e)i!="_index"&&(n+=Object.keys(e[i]).length);var s=(r-n+.5)/(n+.5);return Math.log(1+Math.abs(s))},t.Token=function(e,r){this.str=e||"",this.metadata=r||{}},t.Token.prototype.toString=function(){return this.str},t.Token.prototype.update=function(e){return this.str=e(this.str,this.metadata),this},t.Token.prototype.clone=function(e){return e=e||function(r){return r},new t.Token(e(this.str,this.metadata),this.metadata)};/*! | ||
| * lunr.tokenizer | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.tokenizer=function(e,r){if(e==null||e==null)return[];if(Array.isArray(e))return e.map(function(g){return new t.Token(t.utils.asString(g).toLowerCase(),t.utils.clone(r))});for(var n=e.toString().toLowerCase(),i=n.length,s=[],o=0,a=0;o<=i;o++){var u=n.charAt(o),c=o-a;if(u.match(t.tokenizer.separator)||o==i){if(c>0){var f=t.utils.clone(r)||{};f.position=[a,c],f.index=s.length,s.push(new t.Token(n.slice(a,o),f))}a=o+1}}return s},t.tokenizer.separator=/[\s\-]+/;/*! | ||
| * lunr.Pipeline | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions=Object.create(null),t.Pipeline.registerFunction=function(e,r){r in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+r),e.label=r,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var r=e.label&&e.label in this.registeredFunctions;r||t.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. | ||
| `,e)},t.Pipeline.load=function(e){var r=new t.Pipeline;return e.forEach(function(n){var i=t.Pipeline.registeredFunctions[n];if(i)r.add(i);else throw new Error("Cannot load unregistered function: "+n)}),r},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(r){t.Pipeline.warnIfFunctionNotRegistered(r),this._stack.push(r)},this)},t.Pipeline.prototype.after=function(e,r){t.Pipeline.warnIfFunctionNotRegistered(r);var n=this._stack.indexOf(e);if(n==-1)throw new Error("Cannot find existingFn");n=n+1,this._stack.splice(n,0,r)},t.Pipeline.prototype.before=function(e,r){t.Pipeline.warnIfFunctionNotRegistered(r);var n=this._stack.indexOf(e);if(n==-1)throw new Error("Cannot find existingFn");this._stack.splice(n,0,r)},t.Pipeline.prototype.remove=function(e){var r=this._stack.indexOf(e);r!=-1&&this._stack.splice(r,1)},t.Pipeline.prototype.run=function(e){for(var r=this._stack.length,n=0;n<r;n++){for(var i=this._stack[n],s=[],o=0;o<e.length;o++){var a=i(e[o],o,e);if(!(a==null||a===""))if(Array.isArray(a))for(var u=0;u<a.length;u++)s.push(a[u]);else s.push(a)}e=s}return e},t.Pipeline.prototype.runString=function(e,r){var n=new t.Token(e,r);return this.run([n]).map(function(i){return i.toString()})},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})};/*! | ||
| * lunr.Vector | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.Vector=function(e){this._magnitude=0,this.elements=e||[]},t.Vector.prototype.positionForIndex=function(e){if(this.elements.length==0)return 0;for(var r=0,n=this.elements.length/2,i=n-r,s=Math.floor(i/2),o=this.elements[s*2];i>1&&(o<e&&(r=s),o>e&&(n=s),o!=e);)i=n-r,s=r+Math.floor(i/2),o=this.elements[s*2];if(o==e||o>e)return s*2;if(o<e)return(s+1)*2},t.Vector.prototype.insert=function(e,r){this.upsert(e,r,function(){throw"duplicate index"})},t.Vector.prototype.upsert=function(e,r,n){this._magnitude=0;var i=this.positionForIndex(e);this.elements[i]==e?this.elements[i+1]=n(this.elements[i+1],r):this.elements.splice(i,0,e,r)},t.Vector.prototype.magnitude=function(){if(this._magnitude)return this._magnitude;for(var e=0,r=this.elements.length,n=1;n<r;n+=2){var i=this.elements[n];e+=i*i}return this._magnitude=Math.sqrt(e)},t.Vector.prototype.dot=function(e){for(var r=0,n=this.elements,i=e.elements,s=n.length,o=i.length,a=0,u=0,c=0,f=0;c<s&&f<o;)a=n[c],u=i[f],a<u?c+=2:a>u?f+=2:a==u&&(r+=n[c+1]*i[f+1],c+=2,f+=2);return r},t.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},t.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),r=1,n=0;r<this.elements.length;r+=2,n++)e[n]=this.elements[r];return e},t.Vector.prototype.toJSON=function(){return this.elements};/*! | ||
| * lunr.stemmer | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt | ||
| */t.stemmer=(function(){var e={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},r={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",s=n+"[^aeiouy]*",o=i+"[aeiou]*",a="^("+s+")?"+o+s,u="^("+s+")?"+o+s+"("+o+")?$",c="^("+s+")?"+o+s+o+s,f="^("+s+")?"+i,g=new RegExp(a),l=new RegExp(c),m=new RegExp(u),x=new RegExp(f),v=/^(.+?)(ss|i)es$/,d=/^(.+?)([^s])s$/,y=/^(.+?)eed$/,b=/^(.+?)(ed|ing)$/,E=/.$/,w=/(at|bl|iz)$/,R=new RegExp("([^aeiouylsz])\\1$"),j=new RegExp("^"+s+i+"[^aeiouwxy]$"),_=/^(.+?[^aeiou])y$/,D=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,N=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,C=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,V=/^(.+?)(s|t)(ion)$/,P=/^(.+?)e$/,z=/ll$/,$=new RegExp("^"+s+i+"[^aeiouwxy]$"),M=function(h){var S,k,L,p,T,O,F;if(h.length<3)return h;if(L=h.substr(0,1),L=="y"&&(h=L.toUpperCase()+h.substr(1)),p=v,T=d,p.test(h)?h=h.replace(p,"$1$2"):T.test(h)&&(h=h.replace(T,"$1$2")),p=y,T=b,p.test(h)){var Q=p.exec(h);p=g,p.test(Q[1])&&(p=E,h=h.replace(p,""))}else if(T.test(h)){var Q=T.exec(h);S=Q[1],T=x,T.test(S)&&(h=S,T=w,O=R,F=j,T.test(h)?h=h+"e":O.test(h)?(p=E,h=h.replace(p,"")):F.test(h)&&(h=h+"e"))}if(p=_,p.test(h)){var Q=p.exec(h);S=Q[1],h=S+"i"}if(p=D,p.test(h)){var Q=p.exec(h);S=Q[1],k=Q[2],p=g,p.test(S)&&(h=S+e[k])}if(p=N,p.test(h)){var Q=p.exec(h);S=Q[1],k=Q[2],p=g,p.test(S)&&(h=S+r[k])}if(p=C,T=V,p.test(h)){var Q=p.exec(h);S=Q[1],p=l,p.test(S)&&(h=S)}else if(T.test(h)){var Q=T.exec(h);S=Q[1]+Q[2],T=l,T.test(S)&&(h=S)}if(p=P,p.test(h)){var Q=p.exec(h);S=Q[1],p=l,T=m,O=$,(p.test(S)||T.test(S)&&!O.test(S))&&(h=S)}return p=z,T=l,p.test(h)&&T.test(h)&&(p=E,h=h.replace(p,"")),L=="y"&&(h=L.toLowerCase()+h.substr(1)),h};return function(I){return I.update(M)}})(),t.Pipeline.registerFunction(t.stemmer,"stemmer");/*! | ||
| * lunr.stopWordFilter | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.generateStopWordFilter=function(e){var r=e.reduce(function(n,i){return n[i]=i,n},{});return function(n){if(n&&r[n.toString()]!==n.toString())return n}},t.stopWordFilter=t.generateStopWordFilter(["a","able","about","across","after","all","almost","also","am","among","an","and","any","are","as","at","be","because","been","but","by","can","cannot","could","dear","did","do","does","either","else","ever","every","for","from","get","got","had","has","have","he","her","hers","him","his","how","however","i","if","in","into","is","it","its","just","least","let","like","likely","may","me","might","most","must","my","neither","no","nor","not","of","off","often","on","only","or","other","our","own","rather","said","say","says","she","should","since","so","some","than","that","the","their","them","then","there","these","they","this","tis","to","too","twas","us","wants","was","we","were","what","when","where","which","while","who","whom","why","will","with","would","yet","you","your"]),t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter");/*! | ||
| * lunr.trimmer | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.trimmer=function(e){return e.update(function(r){return r.replace(/^\W+/,"").replace(/\W+$/,"")})},t.Pipeline.registerFunction(t.trimmer,"trimmer");/*! | ||
| * lunr.TokenSet | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.TokenSet=function(){this.final=!1,this.edges={},this.id=t.TokenSet._nextId,t.TokenSet._nextId+=1},t.TokenSet._nextId=1,t.TokenSet.fromArray=function(e){for(var r=new t.TokenSet.Builder,n=0,i=e.length;n<i;n++)r.insert(e[n]);return r.finish(),r.root},t.TokenSet.fromClause=function(e){return"editDistance"in e?t.TokenSet.fromFuzzyString(e.term,e.editDistance):t.TokenSet.fromString(e.term)},t.TokenSet.fromFuzzyString=function(e,r){for(var n=new t.TokenSet,i=[{node:n,editsRemaining:r,str:e}];i.length;){var s=i.pop();if(s.str.length>0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new t.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new t.TokenSet;s.node.edges["*"]=u}if(s.str.length==0&&(u.final=!0),i.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var c=s.node.edges["*"];else{var c=new t.TokenSet;s.node.edges["*"]=c}s.str.length==1&&(c.final=!0),i.push({node:c,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var f=s.str.charAt(0),g=s.str.charAt(1),l;g in s.node.edges?l=s.node.edges[g]:(l=new t.TokenSet,s.node.edges[g]=l),s.str.length==1&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:f+s.str.slice(2)})}}}return n},t.TokenSet.fromString=function(e){for(var r=new t.TokenSet,n=r,i=0,s=e.length;i<s;i++){var o=e[i],a=i==s-1;if(o=="*")r.edges[o]=r,r.final=a;else{var u=new t.TokenSet;u.final=a,r.edges[o]=u,r=u}}return n},t.TokenSet.prototype.toArray=function(){for(var e=[],r=[{prefix:"",node:this}];r.length;){var n=r.pop(),i=Object.keys(n.node.edges),s=i.length;n.node.final&&(n.prefix.charAt(0),e.push(n.prefix));for(var o=0;o<s;o++){var a=i[o];r.push({prefix:n.prefix.concat(a),node:n.node.edges[a]})}}return e},t.TokenSet.prototype.toString=function(){if(this._str)return this._str;for(var e=this.final?"1":"0",r=Object.keys(this.edges).sort(),n=r.length,i=0;i<n;i++){var s=r[i],o=this.edges[s];e=e+s+o.id}return e},t.TokenSet.prototype.intersect=function(e){for(var r=new t.TokenSet,n=void 0,i=[{qNode:e,output:r,node:this}];i.length;){n=i.pop();for(var s=Object.keys(n.qNode.edges),o=s.length,a=Object.keys(n.node.edges),u=a.length,c=0;c<o;c++)for(var f=s[c],g=0;g<u;g++){var l=a[g];if(l==f||f=="*"){var m=n.node.edges[l],x=n.qNode.edges[f],v=m.final&&x.final,d=void 0;l in n.output.edges?(d=n.output.edges[l],d.final=d.final||v):(d=new t.TokenSet,d.final=v,n.output.edges[l]=d),i.push({qNode:x,output:d,node:m})}}}return r},t.TokenSet.Builder=function(){this.previousWord="",this.root=new t.TokenSet,this.uncheckedNodes=[],this.minimizedNodes={}},t.TokenSet.Builder.prototype.insert=function(e){var r,n=0;if(e<this.previousWord)throw new Error("Out of order word insertion");for(var i=0;i<e.length&&i<this.previousWord.length&&e[i]==this.previousWord[i];i++)n++;this.minimize(n),this.uncheckedNodes.length==0?r=this.root:r=this.uncheckedNodes[this.uncheckedNodes.length-1].child;for(var i=n;i<e.length;i++){var s=new t.TokenSet,o=e[i];r.edges[o]=s,this.uncheckedNodes.push({parent:r,char:o,child:s}),r=s}r.final=!0,this.previousWord=e},t.TokenSet.Builder.prototype.finish=function(){this.minimize(0)},t.TokenSet.Builder.prototype.minimize=function(e){for(var r=this.uncheckedNodes.length-1;r>=e;r--){var n=this.uncheckedNodes[r],i=n.child.toString();i in this.minimizedNodes?n.parent.edges[n.char]=this.minimizedNodes[i]:(n.child._str=i,this.minimizedNodes[i]=n.child),this.uncheckedNodes.pop()}};/*! | ||
| * lunr.Index | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(r){var n=new t.QueryParser(e,r);n.parse()})},t.Index.prototype.query=function(e){for(var r=new t.Query(this.fields),n=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u<this.fields.length;u++)i[this.fields[u]]=new t.Vector;e.call(r,r);for(var u=0;u<r.clauses.length;u++){var c=r.clauses[u],f=null,g=t.Set.empty;c.usePipeline?f=this.pipeline.runString(c.term,{fields:c.fields}):f=[c.term];for(var l=0;l<f.length;l++){var m=f[l];c.term=m;var x=t.TokenSet.fromClause(c),v=this.tokenSet.intersect(x).toArray();if(v.length===0&&c.presence===t.Query.presence.REQUIRED){for(var d=0;d<c.fields.length;d++){var y=c.fields[d];o[y]=t.Set.empty}break}for(var b=0;b<v.length;b++)for(var E=v[b],w=this.invertedIndex[E],R=w._index,d=0;d<c.fields.length;d++){var y=c.fields[d],j=w[y],_=Object.keys(j),D=E+"/"+y,N=new t.Set(_);if(c.presence==t.Query.presence.REQUIRED&&(g=g.union(N),o[y]===void 0&&(o[y]=t.Set.complete)),c.presence==t.Query.presence.PROHIBITED){a[y]===void 0&&(a[y]=t.Set.empty),a[y]=a[y].union(N);continue}if(i[y].upsert(R,c.boost,function(ye,me){return ye+me}),!s[D]){for(var C=0;C<_.length;C++){var V=_[C],P=new t.FieldRef(V,y),z=j[V],$;($=n[P])===void 0?n[P]=new t.MatchData(E,y,z):$.add(E,y,z)}s[D]=!0}}}if(c.presence===t.Query.presence.REQUIRED)for(var d=0;d<c.fields.length;d++){var y=c.fields[d];o[y]=o[y].intersect(g)}}for(var M=t.Set.complete,I=t.Set.empty,u=0;u<this.fields.length;u++){var y=this.fields[u];o[y]&&(M=M.intersect(o[y])),a[y]&&(I=I.union(a[y]))}var h=Object.keys(n),S=[],k=Object.create(null);if(r.isNegated()){h=Object.keys(this.fieldVectors);for(var u=0;u<h.length;u++){var P=h[u],L=t.FieldRef.fromString(P);n[P]=new t.MatchData}}for(var u=0;u<h.length;u++){var L=t.FieldRef.fromString(h[u]),p=L.docRef;if(M.contains(p)&&!I.contains(p)){var T=this.fieldVectors[L],O=i[L.fieldName].similarity(T),F;if((F=k[p])!==void 0)F.score+=O,F.matchData.combine(n[L]);else{var Q={ref:p,score:O,matchData:n[L]};k[p]=Q,S.push(Q)}}}return S.sort(function(pe,ge){return ge.score-pe.score})},t.Index.prototype.toJSON=function(){var e=Object.keys(this.invertedIndex).sort().map(function(n){return[n,this.invertedIndex[n]]},this),r=Object.keys(this.fieldVectors).map(function(n){return[n,this.fieldVectors[n].toJSON()]},this);return{version:t.version,fields:this.fields,fieldVectors:r,invertedIndex:e,pipeline:this.pipeline.toJSON()}},t.Index.load=function(e){var r={},n={},i=e.fieldVectors,s=Object.create(null),o=e.invertedIndex,a=new t.TokenSet.Builder,u=t.Pipeline.load(e.pipeline);e.version!=t.version&&t.utils.warn("Version mismatch when loading serialised index. Current version of lunr '"+t.version+"' does not match serialized index '"+e.version+"'");for(var c=0;c<i.length;c++){var f=i[c],g=f[0],l=f[1];n[g]=new t.Vector(l)}for(var c=0;c<o.length;c++){var f=o[c],m=f[0],x=f[1];a.insert(m),s[m]=x}return a.finish(),r.fields=e.fields,r.fieldVectors=n,r.invertedIndex=s,r.tokenSet=a.root,r.pipeline=u,new t.Index(r)};/*! | ||
| * lunr.Builder | ||
| * Copyright (C) 2020 Oliver Nightingale | ||
| */t.Builder=function(){this._ref="id",this._fields=Object.create(null),this._documents=Object.create(null),this.invertedIndex=Object.create(null),this.fieldTermFrequencies={},this.fieldLengths={},this.tokenizer=t.tokenizer,this.pipeline=new t.Pipeline,this.searchPipeline=new t.Pipeline,this.documentCount=0,this._b=.75,this._k1=1.2,this.termIndex=0,this.metadataWhitelist=[]},t.Builder.prototype.ref=function(e){this._ref=e},t.Builder.prototype.field=function(e,r){if(/\//.test(e))throw new RangeError("Field '"+e+"' contains illegal character '/'");this._fields[e]=r||{}},t.Builder.prototype.b=function(e){e<0?this._b=0:e>1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,r){var n=e[this._ref],i=Object.keys(this._fields);this._documents[n]=r||{},this.documentCount+=1;for(var s=0;s<i.length;s++){var o=i[s],a=this._fields[o].extractor,u=a?a(e):e[o],c=this.tokenizer(u,{fields:[o]}),f=this.pipeline.run(c),g=new t.FieldRef(n,o),l=Object.create(null);this.fieldTermFrequencies[g]=l,this.fieldLengths[g]=0,this.fieldLengths[g]+=f.length;for(var m=0;m<f.length;m++){var x=f[m];if(l[x]==null&&(l[x]=0),l[x]+=1,this.invertedIndex[x]==null){var v=Object.create(null);v._index=this.termIndex,this.termIndex+=1;for(var d=0;d<i.length;d++)v[i[d]]=Object.create(null);this.invertedIndex[x]=v}this.invertedIndex[x][o][n]==null&&(this.invertedIndex[x][o][n]=Object.create(null));for(var y=0;y<this.metadataWhitelist.length;y++){var b=this.metadataWhitelist[y],E=x.metadata[b];this.invertedIndex[x][o][n][b]==null&&(this.invertedIndex[x][o][n][b]=[]),this.invertedIndex[x][o][n][b].push(E)}}}},t.Builder.prototype.calculateAverageFieldLengths=function(){for(var e=Object.keys(this.fieldLengths),r=e.length,n={},i={},s=0;s<r;s++){var o=t.FieldRef.fromString(e[s]),a=o.fieldName;i[a]||(i[a]=0),i[a]+=1,n[a]||(n[a]=0),n[a]+=this.fieldLengths[o]}for(var u=Object.keys(this._fields),s=0;s<u.length;s++){var c=u[s];n[c]=n[c]/i[c]}this.averageFieldLength=n},t.Builder.prototype.createFieldVectors=function(){for(var e={},r=Object.keys(this.fieldTermFrequencies),n=r.length,i=Object.create(null),s=0;s<n;s++){for(var o=t.FieldRef.fromString(r[s]),a=o.fieldName,u=this.fieldLengths[o],c=new t.Vector,f=this.fieldTermFrequencies[o],g=Object.keys(f),l=g.length,m=this._fields[a].boost||1,x=this._documents[o.docRef].boost||1,v=0;v<l;v++){var d=g[v],y=f[d],b=this.invertedIndex[d]._index,E,w,R;i[d]===void 0?(E=t.idf(this.invertedIndex[d],this.documentCount),i[d]=E):E=i[d],w=E*((this._k1+1)*y)/(this._k1*(1-this._b+this._b*(u/this.averageFieldLength[a]))+y),w*=m,w*=x,R=Math.round(w*1e3)/1e3,c.insert(b,R)}e[o]=c}this.fieldVectors=e},t.Builder.prototype.createTokenSet=function(){this.tokenSet=t.TokenSet.fromArray(Object.keys(this.invertedIndex).sort())},t.Builder.prototype.build=function(){return this.calculateAverageFieldLengths(),this.createFieldVectors(),this.createTokenSet(),new t.Index({invertedIndex:this.invertedIndex,fieldVectors:this.fieldVectors,tokenSet:this.tokenSet,fields:Object.keys(this._fields),pipeline:this.searchPipeline})},t.Builder.prototype.use=function(e){var r=Array.prototype.slice.call(arguments,1);r.unshift(this),e.apply(this,r)},t.MatchData=function(e,r,n){for(var i=Object.create(null),s=Object.keys(n||{}),o=0;o<s.length;o++){var a=s[o];i[a]=n[a].slice()}this.metadata=Object.create(null),e!==void 0&&(this.metadata[e]=Object.create(null),this.metadata[e][r]=i)},t.MatchData.prototype.combine=function(e){for(var r=Object.keys(e.metadata),n=0;n<r.length;n++){var i=r[n],s=Object.keys(e.metadata[i]);this.metadata[i]==null&&(this.metadata[i]=Object.create(null));for(var o=0;o<s.length;o++){var a=s[o],u=Object.keys(e.metadata[i][a]);this.metadata[i][a]==null&&(this.metadata[i][a]=Object.create(null));for(var c=0;c<u.length;c++){var f=u[c];this.metadata[i][a][f]==null?this.metadata[i][a][f]=e.metadata[i][a][f]:this.metadata[i][a][f]=this.metadata[i][a][f].concat(e.metadata[i][a][f])}}}},t.MatchData.prototype.add=function(e,r,n){if(!(e in this.metadata)){this.metadata[e]=Object.create(null),this.metadata[e][r]=n;return}if(!(r in this.metadata[e])){this.metadata[e][r]=n;return}for(var i=Object.keys(n),s=0;s<i.length;s++){var o=i[s];o in this.metadata[e][r]?this.metadata[e][r][o]=this.metadata[e][r][o].concat(n[o]):this.metadata[e][r][o]=n[o]}},t.Query=function(e){this.clauses=[],this.allFields=e},t.Query.wildcard=new String("*"),t.Query.wildcard.NONE=0,t.Query.wildcard.LEADING=1,t.Query.wildcard.TRAILING=2,t.Query.presence={OPTIONAL:1,REQUIRED:2,PROHIBITED:3},t.Query.prototype.clause=function(e){return"fields"in e||(e.fields=this.allFields),"boost"in e||(e.boost=1),"usePipeline"in e||(e.usePipeline=!0),"wildcard"in e||(e.wildcard=t.Query.wildcard.NONE),e.wildcard&t.Query.wildcard.LEADING&&e.term.charAt(0)!=t.Query.wildcard&&(e.term="*"+e.term),e.wildcard&t.Query.wildcard.TRAILING&&e.term.slice(-1)!=t.Query.wildcard&&(e.term=""+e.term+"*"),"presence"in e||(e.presence=t.Query.presence.OPTIONAL),this.clauses.push(e),this},t.Query.prototype.isNegated=function(){for(var e=0;e<this.clauses.length;e++)if(this.clauses[e].presence!=t.Query.presence.PROHIBITED)return!1;return!0},t.Query.prototype.term=function(e,r){if(Array.isArray(e))return e.forEach(function(i){this.term(i,t.utils.clone(r))},this),this;var n=r||{};return n.term=e.toString(),this.clause(n),this},t.QueryParseError=function(e,r,n){this.name="QueryParseError",this.message=e,this.start=r,this.end=n},t.QueryParseError.prototype=new Error,t.QueryLexer=function(e){this.lexemes=[],this.str=e,this.length=e.length,this.pos=0,this.start=0,this.escapeCharPositions=[]},t.QueryLexer.prototype.run=function(){for(var e=t.QueryLexer.lexText;e;)e=e(this)},t.QueryLexer.prototype.sliceString=function(){for(var e=[],r=this.start,n=this.pos,i=0;i<this.escapeCharPositions.length;i++)n=this.escapeCharPositions[i],e.push(this.str.slice(r,n)),r=n+1;return e.push(this.str.slice(r,this.pos)),this.escapeCharPositions.length=0,e.join("")},t.QueryLexer.prototype.emit=function(e){this.lexemes.push({type:e,str:this.sliceString(),start:this.start,end:this.pos}),this.start=this.pos},t.QueryLexer.prototype.escapeCharacter=function(){this.escapeCharPositions.push(this.pos-1),this.pos+=1},t.QueryLexer.prototype.next=function(){if(this.pos>=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,r;do e=this.next(),r=e.charCodeAt(0);while(r>47&&r<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos<this.length},t.QueryLexer.EOS="EOS",t.QueryLexer.FIELD="FIELD",t.QueryLexer.TERM="TERM",t.QueryLexer.EDIT_DISTANCE="EDIT_DISTANCE",t.QueryLexer.BOOST="BOOST",t.QueryLexer.PRESENCE="PRESENCE",t.QueryLexer.lexField=function(e){return e.backup(),e.emit(t.QueryLexer.FIELD),e.ignore(),t.QueryLexer.lexText},t.QueryLexer.lexTerm=function(e){if(e.width()>1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var r=e.next();if(r==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(r.charCodeAt(0)==92){e.escapeCharacter();continue}if(r==":")return t.QueryLexer.lexField;if(r=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(r=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(r=="+"&&e.width()===1||r=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(r.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,r){this.lexer=new t.QueryLexer(e),this.query=r,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var r=e.peekLexeme();if(r!=null)switch(r.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var n="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(n+=" with value '"+r.str+"'"),new t.QueryParseError(n,r.start,r.end)}},t.QueryParser.parsePresence=function(e){var r=e.consumeLexeme();if(r!=null){switch(r.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var n="unrecognised presence operator'"+r.str+"'";throw new t.QueryParseError(n,r.start,r.end)}var i=e.peekLexeme();if(i==null){var n="expecting term or field, found nothing";throw new t.QueryParseError(n,r.start,r.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var n="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(n,i.start,i.end)}}},t.QueryParser.parseField=function(e){var r=e.consumeLexeme();if(r!=null){if(e.query.allFields.indexOf(r.str)==-1){var n=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+r.str+"', possible fields: "+n;throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.fields=[r.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,r.start,r.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var r=e.consumeLexeme();if(r!=null){e.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var n=e.peekLexeme();if(n==null){e.nextClause();return}switch(n.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+n.type+"'";throw new t.QueryParseError(i,n.start,n.end)}}},t.QueryParser.parseEditDistance=function(e){var r=e.consumeLexeme();if(r!=null){var n=parseInt(r.str,10);if(isNaN(n)){var i="edit distance must be numeric";throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.editDistance=n;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var r=e.consumeLexeme();if(r!=null){var n=parseInt(r.str,10);if(isNaN(n)){var i="boost must be numeric";throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.boost=n;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},(function(e,r){typeof define=="function"&&define.amd?define(r):typeof ee=="object"?te.exports=r():e.lunr=r()})(this,function(){return t})})()});var Y=Pe(re());function ne(t,e=document){let r=ke(t,e);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${t}" to be present`);return r}function ke(t,e=document){return e.querySelector(t)||void 0}Object.entries||(Object.entries=function(t){let e=[];for(let r of Object.keys(t))e.push([r,t[r]]);return e});Object.values||(Object.values=function(t){let e=[];for(let r of Object.keys(t))e.push(t[r]);return e});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(t,e){typeof t=="object"?(this.scrollLeft=t.left,this.scrollTop=t.top):(this.scrollLeft=t,this.scrollTop=e)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...t){let e=this.parentNode;if(e){t.length===0&&e.removeChild(this);for(let r=t.length-1;r>=0;r--){let n=t[r];typeof n=="string"?n=document.createTextNode(n):n.parentNode&&n.parentNode.removeChild(n),r?e.insertBefore(this.previousSibling,n):e.replaceChild(n,this)}}}));function ie(t){let e=new Map;for(let r of t){let[n]=r.location.split("#"),i=e.get(n);typeof i=="undefined"?e.set(n,r):(e.set(r.location,r),r.parent=i)}return e}function W(t,e,r){var s;e=new RegExp(e,"g");let n,i=0;do{n=e.exec(t);let o=(s=n==null?void 0:n.index)!=null?s:t.length;if(i<o&&r(i,o),n){let[a]=n;i=n.index+a.length,a.length===0&&(e.lastIndex=n.index+1)}}while(n)}function se(t,e){let r=0,n=0,i=0;for(let s=0;i<t.length;i++)t.charAt(i)==="<"&&i>n?e(r,1,n,n=i):t.charAt(i)===">"&&(t.charAt(n+1)==="/"?--s===0&&e(r++,2,n,i+1):t.charAt(i-1)!=="/"&&s++===0&&e(r,0,n,i+1),n=i+1);i>n&&e(r,1,n,i)}function oe(t,e,r,n=!1){return q([t],e,r,n).pop()}function q(t,e,r,n=!1){let i=[0];for(let s=1;s<e.length;s++){let o=e[s-1],a=e[s],u=o[o.length-1]>>>2&1023,c=a[0]>>>12;i.push(+(u>c)+i[i.length-1])}return t.map((s,o)=>{let a=0,u=new Map;for(let f of r.sort((g,l)=>g-l)){let g=f&1048575,l=f>>>20;if(i[l]!==o)continue;let m=u.get(l);typeof m=="undefined"&&u.set(l,m=[]),m.push(g)}if(u.size===0)return s;let c=[];for(let[f,g]of u){let l=e[f],m=l[0]>>>12,x=l[l.length-1]>>>12,v=l[l.length-1]>>>2&1023;n&&m>a&&c.push(s.slice(a,m));let d=s.slice(m,x+v);for(let y of g.sort((b,E)=>E-b)){let b=(l[y]>>>12)-m,E=(l[y]>>>2&1023)+b;d=[d.slice(0,b),"<mark>",d.slice(b,E),"</mark>",d.slice(E)].join("")}if(a=x+v,c.push(d)===2)break}return n&&a<s.length&&c.push(s.slice(a)),c.join("")})}function ae(t){let e=[];if(typeof t=="undefined")return e;let r=Array.isArray(t)?t:[t];for(let n=0;n<r.length;n++){let i=lunr.tokenizer.table,s=i.length;se(r[n],(o,a,u,c)=>{var f;switch(i[f=o+=s]||(i[f]=[]),a){case 0:case 2:i[o].push(u<<12|c-u<<2|a);break;case 1:let g=r[n].slice(u,c);W(g,lunr.tokenizer.separator,(l,m)=>{if(typeof lunr.segmenter!="undefined"){let x=g.slice(l,m);if(/^[MHIK]$/.test(lunr.segmenter.ctype_(x))){let v=lunr.segmenter.segment(x);for(let d=0,y=0;d<v.length;d++)i[o]||(i[o]=[]),i[o].push(u+l+y<<12|v[d].length<<2|a),e.push(new lunr.Token(v[d].toLowerCase(),{position:o<<20|i[o].length-1})),y+=v[d].length;return}}i[o].push(u+l<<12|m-l<<2|a),e.push(new lunr.Token(g.slice(l,m).toLowerCase(),{position:o<<20|i[o].length-1}))})}})}return e}function ue(t,e=r=>r){return t.trim().split(/"([^"]+)"/g).map((r,n)=>n&1?r.replace(/^\b|^(?![^\x00-\x7F]|$)|\s+/g," +"):r).join("").replace(/"|(?:^|\s+)[*+\-:^~]+(?=\s+|$)/g,"").split(/\s+/g).reduce((r,n)=>{let i=e(n);return[...r,...Array.isArray(i)?i:[i]]},[]).map(r=>/([~^]$)/.test(r)?`${r}1`:r).map(r=>/(^[+-]|[~^]\d+$)/.test(r)?r:`${r}*`).join(" ")}function ce(t){return ue(t,e=>{let r=[],n=new lunr.QueryLexer(e);n.run();for(let{type:i,str:s,start:o,end:a}of n.lexemes)switch(i){case"FIELD":["title","text","tags"].includes(s)||(e=[e.slice(0,a)," ",e.slice(a+1)].join(""));break;case"TERM":W(s,lunr.tokenizer.separator,(...u)=>{r.push([e.slice(0,o),s.slice(...u),e.slice(a)].join(""))})}return r})}function le(t){let e=new lunr.Query(["title","text","tags"]);new lunr.QueryParser(t,e).parse();for(let n of e.clauses)n.usePipeline=!0,n.term.startsWith("*")&&(n.wildcard=lunr.Query.wildcard.LEADING,n.term=n.term.slice(1)),n.term.endsWith("*")&&(n.wildcard=lunr.Query.wildcard.TRAILING,n.term=n.term.slice(0,-1));return e.clauses}function he(t,e){var i;let r=new Set(t),n={};for(let s=0;s<e.length;s++)for(let o of r)e[s].startsWith(o.term)&&(n[o.term]=!0,r.delete(o));for(let s of r)(i=lunr.stopWordFilter)!=null&&i.call(lunr,s.term)&&(n[s.term]=!1);return n}function fe(t,e){let r=new Set,n=new Uint16Array(t.length);for(let s=0;s<t.length;s++)for(let o=s+1;o<t.length;o++)t.slice(s,o)in e&&(n[s]=o-s);let i=[0];for(let s=i.length;s>0;){let o=i[--s];for(let u=1;u<n[o];u++)n[o+u]>n[o]-u&&(r.add(t.slice(o,o+u)),i[s++]=o+u);let a=o+n[o];n[a]&&a<t.length-1&&(i[s++]=a),r.add(t.slice(o,a))}return r.has("")?new Set([t]):r}function Oe(t){return e=>r=>{if(typeof r[e]=="undefined")return;let n=[r.location,e].join(":");return t.set(n,lunr.tokenizer.table=[]),r[e]}}function Re(t,e){let[r,n]=[new Set(t),new Set(e)];return[...new Set([...r].filter(i=>!n.has(i)))]}var H=class{constructor({config:e,docs:r,options:n}){let i=Oe(this.table=new Map);this.map=ie(r),this.options=n,this.index=lunr(function(){this.metadataWhitelist=["position"],this.b(0),e.lang.length===1&&e.lang[0]!=="en"?this.use(lunr[e.lang[0]]):e.lang.length>1&&this.use(lunr.multiLanguage(...e.lang)),this.tokenizer=ae,lunr.tokenizer.separator=new RegExp(e.separator),lunr.segmenter="TinySegmenter"in lunr?new lunr.TinySegmenter:void 0;let s=Re(["trimmer","stopWordFilter","stemmer"],e.pipeline);for(let o of e.lang.map(a=>a==="en"?lunr:lunr[a]))for(let a of s)this.pipeline.remove(o[a]),this.searchPipeline.remove(o[a]);this.ref("location"),this.field("title",{boost:1e3,extractor:i("title")}),this.field("text",{boost:1,extractor:i("text")}),this.field("tags",{boost:1e6,extractor:i("tags")});for(let o of r)this.add(o,{boost:o.boost})})}search(e){if(e=e.replace(new RegExp("\\p{sc=Han}+","gu"),s=>[...fe(s,this.index.invertedIndex)].join("* ")),e=ce(e),!e)return{items:[]};let r=le(e).filter(s=>s.presence!==lunr.Query.presence.PROHIBITED),n=this.index.search(e).reduce((s,{ref:o,score:a,matchData:u})=>{let c=this.map.get(o);if(typeof c!="undefined"){c=A({},c),c.tags&&(c.tags=[...c.tags]);let f=he(r,Object.keys(u.metadata));for(let l of this.index.fields){if(typeof c[l]=="undefined")continue;let m=[];for(let d of Object.values(u.metadata))typeof d[l]!="undefined"&&m.push(...d[l].position);if(!m.length)continue;let x=this.table.get([c.location,l].join(":")),v=Array.isArray(c[l])?q:oe;c[l]=v(c[l],x,m,l!=="text")}let g=+!c.parent+Object.values(f).filter(l=>l).length/Object.keys(f).length;s.push(G(A({},c),{score:a*(1+K(g,2)),terms:f}))}return s},[]).sort((s,o)=>o.score-s.score).reduce((s,o)=>{let a=this.map.get(o.location);if(typeof a!="undefined"){let u=a.parent?a.parent.location:a.location;s.set(u,[...s.get(u)||[],o])}return s},new Map);for(let[s,o]of n)if(!o.find(a=>a.location===s)){let a=this.map.get(s);o.push(G(A({},a),{score:0,terms:{}}))}let i;if(this.options.suggest){let s=this.index.query(o=>{for(let a of r)o.term(a.term,{fields:["title"],presence:lunr.Query.presence.REQUIRED,wildcard:lunr.Query.wildcard.TRAILING})});i=s.length?Object.keys(s[0].matchData.metadata):[]}return A({items:[...n.values()]},typeof i!="undefined"&&{suggest:i})}};var de;function Ie(t){return B(this,null,function*(){let e="../lunr";if(typeof parent!="undefined"&&"IFrameWorker"in parent){let n=ne("script[src]"),[i]=n.src.split("/worker");e=e.replace("..",i)}let r=[];for(let n of t.lang){switch(n){case"ja":r.push(`${e}/tinyseg.js`);break;case"hi":case"th":r.push(`${e}/wordcut.js`);break}n!=="en"&&r.push(`${e}/min/lunr.${n}.min.js`)}t.lang.length>1&&r.push(`${e}/min/lunr.multi.min.js`),r.length&&(yield importScripts(`${e}/min/lunr.stemmer.support.min.js`,...r))})}function Fe(t){return B(this,null,function*(){switch(t.type){case 0:return yield Ie(t.data.config),de=new H(t.data),{type:1};case 2:let e=t.data;try{return{type:3,data:de.search(e)}}catch(r){return console.warn(`Invalid query: ${e} \u2013 see https://bit.ly/2s3ChXG`),console.warn(r),{type:3,data:{items:[]}}}default:throw new TypeError("Invalid message type")}})}self.lunr=Y.default;Y.default.utils.warn=console.warn;addEventListener("message",t=>B(null,null,function*(){postMessage(yield Fe(t.data))}));})(); | ||
| //# sourceMappingURL=search.973d3a69.min.js.map | ||
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
| {"version":3,"sources":["src/templates/assets/stylesheets/main/components/_meta.scss","../../../../src/templates/assets/stylesheets/main.scss","src/templates/assets/stylesheets/main/_resets.scss","src/templates/assets/stylesheets/main/_colors.scss","src/templates/assets/stylesheets/main/_icons.scss","src/templates/assets/stylesheets/main/_typeset.scss","src/templates/assets/stylesheets/utilities/_break.scss","src/templates/assets/stylesheets/main/components/_author.scss","src/templates/assets/stylesheets/main/components/_banner.scss","src/templates/assets/stylesheets/main/components/_base.scss","src/templates/assets/stylesheets/main/components/_clipboard.scss","src/templates/assets/stylesheets/main/components/_code.scss","src/templates/assets/stylesheets/main/components/_consent.scss","src/templates/assets/stylesheets/main/components/_content.scss","src/templates/assets/stylesheets/main/components/_dialog.scss","src/templates/assets/stylesheets/main/components/_feedback.scss","src/templates/assets/stylesheets/main/components/_footer.scss","src/templates/assets/stylesheets/main/components/_form.scss","src/templates/assets/stylesheets/main/components/_header.scss","node_modules/material-design-color/material-color.scss","src/templates/assets/stylesheets/main/components/_nav.scss","src/templates/assets/stylesheets/main/components/_pagination.scss","src/templates/assets/stylesheets/main/components/_post.scss","src/templates/assets/stylesheets/main/components/_progress.scss","src/templates/assets/stylesheets/main/components/_search.scss","src/templates/assets/stylesheets/main/components/_select.scss","src/templates/assets/stylesheets/main/components/_sidebar.scss","src/templates/assets/stylesheets/main/components/_source.scss","src/templates/assets/stylesheets/main/components/_status.scss","src/templates/assets/stylesheets/main/components/_tabs.scss","src/templates/assets/stylesheets/main/components/_tag.scss","src/templates/assets/stylesheets/main/components/_tooltip.scss","src/templates/assets/stylesheets/main/components/_tooltip2.scss","src/templates/assets/stylesheets/main/components/_top.scss","src/templates/assets/stylesheets/main/components/_version.scss","src/templates/assets/stylesheets/main/extensions/markdown/_admonition.scss","src/templates/assets/stylesheets/main/extensions/markdown/_footnotes.scss","src/templates/assets/stylesheets/main/extensions/markdown/_toc.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_critic.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_details.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_emoji.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_highlight.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_keys.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss","src/templates/assets/stylesheets/main/integrations/_giscus.scss","src/templates/assets/stylesheets/main/integrations/_mermaid.scss","src/templates/assets/stylesheets/main/modifiers/_grid.scss","src/templates/assets/stylesheets/main/modifiers/_inline.scss"],"names":[],"mappings":"AA0CE,gBC6yCF,CC3zCA,KAEE,6BAAA,CAAA,0BAAA,CAAA,qBAAA,CADA,qBDzBF,CC8BA,iBAGE,kBD3BF,CC8BE,gCANF,iBAOI,yBDzBF,CACF,CC6BA,KACE,QD1BF,CC8BA,qBAIE,uCD3BF,CC+BA,EACE,aAAA,CACA,oBD5BF,CCgCA,GAME,QAAA,CALA,kBAAA,CACA,aAAA,CACA,aAAA,CAEA,gBAAA,CADA,SD3BF,CCiCA,MACE,aD9BF,CCkCA,QAEE,eD/BF,CCmCA,IACE,iBDhCF,CCoCA,MAEE,uBAAA,CADA,gBDhCF,CCqCA,MAEE,eAAA,CACA,kBDlCF,CCsCA,OAKE,gBAAA,CACA,QAAA,CAHA,mBAAA,CACA,iBAAA,CAFA,QAAA,CADA,SD9BF,CCuCA,MACE,QAAA,CACA,YDpCF,CErDA,MAIE,6BAAA,CACA,oCAAA,CACA,mCAAA,CACA,0BAAA,CACA,sCAAA,CAGA,4BAAA,CACA,2CAAA,CACA,yBAAA,CACA,qCFmDF,CE7CA,+BAIE,kBF6CF,CE1CE,oHAEE,YF4CJ,CEnCA,qCAIE,eAAA,CAGA,+BAAA,CACA,sCAAA,CACA,wCAAA,CACA,yCAAA,CACA,0BAAA,CACA,sCAAA,CACA,wCAAA,CACA,yCAAA,CAGA,0BAAA,CACA,0BAAA,CAGA,0BAAA,CACA,mCAAA,CAGA,iCAAA,CACA,kCAAA,CACA,mCAAA,CACA,mCAAA,CACA,kCAAA,CACA,iCAAA,CACA,+CAAA,CACA,6DAAA,CACA,gEAAA,CACA,4DAAA,CACA,4DAAA,CACA,6DAAA,CAGA,6CAAA,CAGA,+CAAA,CAGA,gCAAA,CACA,gCAAA,CAGA,8BAAA,CACA,kCAAA,CACA,qCAAA,CAGA,iCAAA,CAGA,kCAAA,CACA,gDAAA,CAGA,mDAAA,CACA,mDAAA,CAGA,+BAAA,CACA,0BAAA,CAGA,yBAAA,CACA,qCAAA,CACA,uCAAA,CACA,8BAAA,CACA,oCAAA,CAGA,8DAAA,CAKA,8DAAA,CAKA,0DFKF,CG9HE,aAIE,iBAAA,CAHA,aAAA,CAEA,aAAA,CADA,YHmIJ,CIxIA,KACE,kCAAA,CACA,iCAAA,CAGA,uGAAA,CAKA,mFJyIF,CInIA,iBAIE,mCAAA,CACA,6BAAA,CAFA,sCJwIF,CIlIA,aAIE,4BAAA,CADA,sCJsIF,CI7HA,MACE,wNAAA,CACA,gNAAA,CACA,iNJgIF,CIzHA,YAIE,gCAAA,CAAA,kBAAA,CAHA,eAAA,CACA,eAAA,CACA,wBJ6HF,CIxHE,aARF,YASI,gBJ2HF,CACF,CIxHE,uGAME,iBAAA,CAAA,cJ0HJ,CItHE,eAKE,uCAAA,CAHA,aAAA,CAEA,eAAA,CAHA,iBJ6HJ,CIpHE,8BAPE,eAAA,CAGA,qBJ+HJ,CI3HE,eAEE,kBAAA,CAEA,eAAA,CAHA,oBJ0HJ,CIlHE,eAEE,gBAAA,CACA,eAAA,CAEA,qBAAA,CADA,eAAA,CAHA,mBJwHJ,CIhHE,kBACE,eJkHJ,CI9GE,eAEE,eAAA,CACA,qBAAA,CAFA,YJkHJ,CI5GE,8BAKE,uCAAA,CAFA,cAAA,CACA,eAAA,CAEA,qBAAA,CAJA,eJkHJ,CI1GE,eACE,wBJ4GJ,CIzGI,oBACE,mBJ2GN,CItGE,eAGE,+DAAA,CAFA,iBAAA,CACA,cJyGJ,CIpGE,cACE,+BAAA,CACA,qBJsGJ,CInGI,mCAEE,sBJoGN,CIhGI,wCACE,+BJkGN,CI/FM,kDACE,uDJiGR,CI5FI,mBACE,kBAAA,CACA,iCJ8FN,CI1FI,4BACE,uCAAA,CACA,oBJ4FN,CIvFE,iDAIE,6BAAA,CACA,aAAA,CAFA,2BJ2FJ,CItFI,aARF,iDASI,oBJ2FJ,CACF,CIvFE,iBAIE,wCAAA,CACA,mBAAA,CACA,kCAAA,CAAA,0BAAA,CAJA,eAAA,CADA,uBAAA,CAEA,qBJ4FJ,CItFI,qCAEE,uCAAA,CADA,YJyFN,CInFE,gBAEE,iBAAA,CACA,eAAA,CAFA,iBJuFJ,CIlFI,qBAWE,kCAAA,CAAA,0BAAA,CADA,eAAA,CATA,aAAA,CAEA,QAAA,CAMA,uCAAA,CALA,aAAA,CAFA,oCAAA,CAKA,yDAAA,CACA,oBAAA,CAFA,iBAAA,CADA,iBJ0FN,CIjFM,2BACE,+CJmFR,CI/EM,wCAEE,YAAA,CADA,WJkFR,CI7EM,8CACE,oDJ+ER,CI5EQ,oDACE,0CJ8EV,CIvEE,gBAOE,4CAAA,CACA,mBAAA,CACA,mKACE,CANF,gCAAA,CAHA,oBAAA,CAEA,eAAA,CADA,uBAAA,CAIA,uBAAA,CADA,qBJ6EJ,CIlEE,iBAGE,6CAAA,CACA,kCAAA,CAAA,0BAAA,CAHA,aAAA,CACA,qBJsEJ,CIhEE,iBAGE,6DAAA,CADA,WAAA,CADA,oBJoEJ,CI9DE,kBACE,WJgEJ,CI5DE,oDAEE,qBJ8DJ,CIhEE,oDAEE,sBJ8DJ,CI1DE,iCACE,kBJ+DJ,CIhEE,iCACE,mBJ+DJ,CIhEE,iCAIE,2DJ4DJ,CIhEE,iCAIE,4DJ4DJ,CIhEE,uBAGE,uCAAA,CADA,aAAA,CAAA,cJ8DJ,CIxDE,eACE,oBJ0DJ,CItDI,qBACE,4BJwDN,CInDE,kDAGE,kBJqDJ,CIxDE,kDAGE,mBJqDJ,CIxDE,8BAEE,SJsDJ,CIlDI,0DACE,iBJqDN,CIjDI,oCACE,2BJoDN,CIjDM,0CACE,2BJoDR,CIjDQ,gDACE,2BJoDV,CIjDU,sDACE,2BJoDZ,CI5CI,0CACE,4BJ+CN,CI3CI,wDACE,kBJ+CN,CIhDI,wDACE,mBJ+CN,CIhDI,oCAEE,kBJ8CN,CI3CM,kGAEE,aJ+CR,CI3CM,0DACE,eJ8CR,CI1CM,4HAEE,kBJ6CR,CI/CM,4HAEE,mBJ6CR,CI/CM,oFACE,kBAAA,CAAA,eJ8CR,CIvCE,yBAEE,mBJyCJ,CI3CE,yBAEE,oBJyCJ,CI3CE,eACE,mBAAA,CAAA,cJ0CJ,CIrCE,kDAIE,WAAA,CADA,cJwCJ,CIhCI,4BAEE,oBJkCN,CI9BI,6BAEE,oBJgCN,CI5BI,kCACE,YJ8BN,CIzBE,mBACE,iBAAA,CAGA,eAAA,CADA,cAAA,CAEA,iBAAA,CAHA,iBJ8BJ,CIxBI,uBACE,aAAA,CACA,aJ0BN,CIrBE,uBAGE,iBAAA,CADA,eAAA,CADA,eJyBJ,CInBE,mBACE,cJqBJ,CIjBE,+BAME,2CAAA,CACA,iDAAA,CACA,mBAAA,CAPA,oBAAA,CAGA,gBAAA,CAFA,cAAA,CACA,aAAA,CAEA,iBJsBJ,CIhBI,aAXF,+BAYI,aJmBJ,CACF,CIdI,iCACE,gBJgBN,CITM,8FACE,YJWR,CIPM,4FACE,eJSR,CIJI,8FACE,eJMN,CIHM,kHACE,gBJKR,CIAI,kCAGE,eAAA,CAFA,cAAA,CACA,sBAAA,CAEA,kBJEN,CIEI,kCAGE,qDAAA,CAFA,sBAAA,CACA,kBJCN,CIII,wCACE,iCJFN,CIKM,8CACE,qDAAA,CACA,sDJHR,CIQI,iCACE,iBJNN,CIWE,wCACE,cJTJ,CIYI,wDAIE,gBJJN,CIAI,wDAIE,iBJJN,CIAI,8CAME,UAAA,CALA,oBAAA,CAEA,YAAA,CAIA,oDAAA,CAAA,4CAAA,CACA,6BAAA,CAAA,qBAAA,CACA,yBAAA,CAAA,iBAAA,CACA,iCAAA,CALA,0BAAA,CAHA,WJFN,CIcI,oDACE,oDJZN,CIgBI,mEACE,kDAAA,CACA,yDAAA,CAAA,iDJdN,CIkBI,oEACE,kDAAA,CACA,0DAAA,CAAA,kDJhBN,CIqBE,wBACE,iBAAA,CACA,eAAA,CACA,iBJnBJ,CIuBE,mBACE,oBAAA,CAEA,kBAAA,CADA,eJpBJ,CIwBI,aANF,mBAOI,aJrBJ,CACF,CIwBI,8BACE,aAAA,CAEA,QAAA,CACA,eAAA,CAFA,UJpBN,CKtWI,0CDyYF,uBACE,iBJ/BF,CIkCE,4BACE,eJhCJ,CACF,CMriBE,uBAOE,kBAAA,CALA,aAAA,CACA,aAAA,CAEA,aAAA,CACA,eAAA,CALA,iBAAA,CAOA,sCACE,CALF,YN2iBJ,CMliBI,2BACE,aNoiBN,CMhiBI,6BAME,+CAAA,CAFA,yCAAA,CAHA,eAAA,CACA,eAAA,CACA,kBAAA,CAEA,iBNmiBN,CM9hBI,6BAEE,aAAA,CADA,YNiiBN,CM3hBE,wBACE,kBN6hBJ,CM1hBI,4BAIE,kBAAA,CAHA,mCAAA,CAIA,uBN0hBN,CMthBI,4DAEE,oBAAA,CADA,SNyhBN,CMrhBM,oEACE,mBNuhBR,COhlBA,WAGE,0CAAA,CADA,+BAAA,CADA,aPqlBF,COhlBE,aANF,WAOI,YPmlBF,CACF,COhlBE,oBAEE,2CAAA,CADA,gCPmlBJ,CO9kBE,kBAGE,eAAA,CADA,iBAAA,CADA,ePklBJ,CO5kBE,6BACE,WPilBJ,COllBE,6BACE,UPilBJ,COllBE,mBAEE,aAAA,CACA,cAAA,CACA,uBP8kBJ,CO3kBI,0BACE,YP6kBN,COzkBI,yBACE,UP2kBN,CQhnBA,KASE,cAAA,CARA,WAAA,CACA,iBRonBF,CKhdI,oCGtKJ,KAaI,gBR6mBF,CACF,CKrdI,oCGtKJ,KAkBI,cR6mBF,CACF,CQxmBA,KASE,2CAAA,CAPA,YAAA,CACA,qBAAA,CAKA,eAAA,CAHA,eAAA,CAJA,iBAAA,CAGA,UR8mBF,CQtmBE,aAZF,KAaI,aRymBF,CACF,CKtdI,0CGhJF,yBAII,cRsmBJ,CACF,CQ7lBA,SAEE,gBAAA,CAAA,iBAAA,CADA,eRimBF,CQ5lBA,cACE,YAAA,CAEA,qBAAA,CADA,WRgmBF,CQ5lBE,aANF,cAOI,aR+lBF,CACF,CQ3lBA,SACE,WR8lBF,CQ3lBE,gBACE,YAAA,CACA,WAAA,CACA,iBR6lBJ,CQxlBA,aACE,eAAA,CACA,sBR2lBF,CQllBA,WACE,YRqlBF,CQhlBA,WAGE,QAAA,CACA,SAAA,CAHA,iBAAA,CACA,ORqlBF,CQhlBE,uCACE,aRklBJ,CQ9kBE,+BAEE,uCAAA,CADA,kBRilBJ,CQ3kBA,SASE,2CAAA,CACA,mBAAA,CAFA,gCAAA,CADA,gBAAA,CADA,YAAA,CAMA,SAAA,CADA,uCAAA,CANA,mBAAA,CAJA,cAAA,CAYA,2BAAA,CATA,URqlBF,CQzkBE,eAEE,SAAA,CAIA,uBAAA,CAHA,oEACE,CAHF,UR8kBJ,CQhkBA,MACE,WRmkBF,CS5tBA,MACE,6PT8tBF,CSxtBA,cASE,mBAAA,CAFA,0CAAA,CACA,cAAA,CAFA,YAAA,CAIA,uCAAA,CACA,oBAAA,CAVA,iBAAA,CAEA,UAAA,CADA,QAAA,CAUA,qBAAA,CAPA,WAAA,CADA,STmuBF,CSxtBE,aAfF,cAgBI,YT2tBF,CACF,CSxtBE,kCAEE,uCAAA,CADA,YT2tBJ,CSttBE,qBACE,uCTwtBJ,CSptBE,wCACE,+BTstBJ,CSjtBE,oBAME,6BAAA,CADA,UAAA,CAJA,aAAA,CAEA,cAAA,CACA,aAAA,CAGA,2CAAA,CAAA,mCAAA,CAEA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CARA,aT2tBJ,CS/sBE,sBACE,cTitBJ,CS9sBI,2BACE,2CTgtBN,CS1sBI,kEAEE,uDAAA,CADA,+BT6sBN,CU/wBE,8BACE,YVkxBJ,CWvxBA,mBACE,GACE,SAAA,CACA,0BX0xBF,CWvxBA,GACE,SAAA,CACA,uBXyxBF,CACF,CWrxBA,mBACE,GACE,SXuxBF,CWpxBA,GACE,SXsxBF,CACF,CW3wBE,qBASE,2BAAA,CAFA,mCAAA,CAAA,2BAAA,CADA,0BAAA,CADA,WAAA,CAGA,SAAA,CAPA,cAAA,CACA,KAAA,CAEA,UAAA,CADA,SXmxBJ,CWzwBE,mBAcE,mDAAA,CANA,2CAAA,CACA,QAAA,CACA,mBAAA,CARA,QAAA,CASA,kDACE,CAPF,eAAA,CAEA,aAAA,CADA,SAAA,CALA,cAAA,CAGA,UAAA,CADA,SXoxBJ,CWrwBE,kBACE,aXuwBJ,CWnwBE,sBACE,YAAA,CACA,YXqwBJ,CWlwBI,oCACE,aXowBN,CW/vBE,sBACE,mBXiwBJ,CW9vBI,6CACE,cXgwBN,CK1pBI,0CMvGA,6CAKI,aAAA,CAEA,gBAAA,CACA,iBAAA,CAFA,UXkwBN,CACF,CW3vBE,kBACE,cX6vBJ,CY91BA,YACE,WAAA,CAIA,WZ81BF,CY31BE,mBAEE,qBAAA,CADA,iBZ81BJ,CKjsBI,sCOtJE,4EACE,kBZ01BN,CYt1BI,0JACE,mBZw1BN,CYz1BI,8EACE,kBZw1BN,CACF,CYn1BI,0BAGE,UAAA,CAFA,aAAA,CACA,YZs1BN,CYj1BI,+BACE,eZm1BN,CY70BE,8BACE,WZk1BJ,CYn1BE,8BACE,UZk1BJ,CYn1BE,8BAIE,iBZ+0BJ,CYn1BE,8BAIE,kBZ+0BJ,CYn1BE,oBAGE,cAAA,CADA,SZi1BJ,CY50BI,aAPF,oBAQI,YZ+0BJ,CACF,CY50BI,gCACE,yCZ80BN,CY10BI,wBACE,cAAA,CACA,kBZ40BN,CYz0BM,kCACE,oBZ20BR,Ca54BA,qBAEE,Wb05BF,Ca55BA,qBAEE,Ub05BF,Ca55BA,WAQE,2CAAA,CACA,mBAAA,CANA,YAAA,CAOA,8BAAA,CALA,iBAAA,CAMA,SAAA,CALA,mBAAA,CACA,mBAAA,CANA,cAAA,CAcA,0BAAA,CAHA,wCACE,CATF,Sbw5BF,Ca14BE,aAlBF,WAmBI,Yb64BF,CACF,Ca14BE,mBAEE,SAAA,CADA,mBAAA,CAKA,uBAAA,CAHA,kEb64BJ,Cat4BE,kBAEE,gCAAA,CADA,eby4BJ,Cc36BA,aACE,gBAAA,CACA,iBd86BF,Cc36BE,sBAGE,WAAA,CADA,QAAA,CADA,Sd+6BJ,Ccz6BE,oBAEE,eAAA,CADA,ed46BJ,Ccv6BE,oBACE,iBdy6BJ,Ccr6BE,mBAEE,YAAA,CACA,cAAA,CACA,6BAAA,CAHA,iBd06BJ,Ccp6BI,iDACE,yCds6BN,Ccl6BI,6BACE,iBdo6BN,Cc/5BE,mBAGE,uCAAA,CACA,cAAA,CAHA,aAAA,CACA,cAAA,CAGA,sBdi6BJ,Cc95BI,gDACE,+Bdg6BN,Cc55BI,4BACE,0CAAA,CACA,mBd85BN,Ccz5BE,mBAEE,SAAA,CADA,iBAAA,CAKA,2BAAA,CAHA,8Dd45BJ,Cct5BI,qBAEE,aAAA,CADA,edy5BN,Ccp5BI,6BACE,SAAA,CACA,uBds5BN,Ccj5BE,aAnFF,aAoFI,Ydo5BF,CACF,Cez+BA,WAEE,0CAAA,CADA,+Bf6+BF,Cez+BE,aALF,WAMI,Yf4+BF,CACF,Cez+BE,kBACE,6BAAA,CAEA,aAAA,CADA,af4+BJ,Cex+BI,gCACE,Yf0+BN,Cer+BE,iBAOE,eAAA,CANA,YAAA,CAKA,cAAA,CAGA,mBAAA,CAAA,eAAA,CADA,cAAA,CAGA,uCAAA,CADA,eAAA,CAEA,uBfm+BJ,Ceh+BI,8CACE,Ufk+BN,Ce99BI,+BACE,oBfg+BN,CKl1BI,0CUvIE,uBACE,af49BN,Cez9BM,yCACE,Yf29BR,CACF,Cet9BI,iCACE,gBfy9BN,Ce19BI,iCACE,iBfy9BN,Ce19BI,uBAEE,gBfw9BN,Cer9BM,iCACE,efu9BR,Cej9BE,kBACE,WAAA,CAIA,eAAA,CADA,mBAAA,CAFA,6BAAA,CACA,cAAA,CAGA,kBfm9BJ,Ce/8BE,mBAEE,YAAA,CADA,afk9BJ,Ce78BE,sBACE,gBAAA,CACA,Uf+8BJ,Ce18BA,gBACE,gDf68BF,Ce18BE,uBACE,YAAA,CACA,cAAA,CACA,6BAAA,CACA,af48BJ,Cex8BE,kCACE,sCf08BJ,Cev8BI,gFACE,+Bfy8BN,Cej8BA,cAKE,wCAAA,CADA,gBAAA,CADA,iBAAA,CADA,eAAA,CADA,Ufw8BF,CK55BI,mCU7CJ,cASI,Ufo8BF,CACF,Ceh8BE,yBACE,sCfk8BJ,Ce37BA,WACE,mBAAA,CACA,SAAA,CAEA,cAAA,CADA,qBf+7BF,CK36BI,mCUvBJ,WAQI,ef87BF,CACF,Ce37BE,iBACE,oBAAA,CAEA,aAAA,CACA,iBAAA,CAFA,Yf+7BJ,Ce17BI,wBACE,ef47BN,Cex7BI,qBAGE,iBAAA,CAFA,gBAAA,CACA,mBf27BN,CgBjmCE,uBAME,kBAAA,CACA,mBAAA,CAHA,gCAAA,CACA,cAAA,CAJA,oBAAA,CAEA,eAAA,CADA,kBAAA,CAMA,gEhBomCJ,CgB9lCI,gCAEE,2CAAA,CACA,uCAAA,CAFA,gChBkmCN,CgB5lCI,0DAEE,0CAAA,CACA,sCAAA,CAFA,+BhBgmCN,CgBzlCE,gCAKE,4BhB8lCJ,CgBnmCE,gEAME,6BhB6lCJ,CgBnmCE,gCAME,4BhB6lCJ,CgBnmCE,sBAIE,6DAAA,CAGA,8BAAA,CAJA,eAAA,CAFA,aAAA,CACA,eAAA,CAMA,sChB2lCJ,CgBtlCI,wDACE,6CAAA,CACA,8BhBwlCN,CgBplCI,+BACE,UhBslCN,CiBzoCA,WAOE,2CAAA,CAGA,8CACE,CALF,gCAAA,CADA,aAAA,CAHA,MAAA,CADA,eAAA,CACA,OAAA,CACA,KAAA,CACA,SjBgpCF,CiBroCE,aAfF,WAgBI,YjBwoCF,CACF,CiBroCE,mBAIE,2BAAA,CAHA,iEjBwoCJ,CiBjoCE,mBACE,kDACE,CAEF,kEjBioCJ,CiB3nCE,kBAEE,kBAAA,CADA,YAAA,CAEA,ejB6nCJ,CiBznCE,mBAKE,kBAAA,CAEA,cAAA,CAHA,YAAA,CAIA,uCAAA,CALA,aAAA,CAFA,iBAAA,CAQA,uBAAA,CAHA,qBAAA,CAJA,SjBkoCJ,CiBxnCI,yBACE,UjB0nCN,CiBtnCI,iCACE,oBjBwnCN,CiBpnCI,uCAEE,uCAAA,CADA,YjBunCN,CiBlnCI,2BAEE,YAAA,CADA,ajBqnCN,CKvgCI,0CY/GA,2BAMI,YjBonCN,CACF,CiBjnCM,8DAIE,iBAAA,CAHA,aAAA,CAEA,aAAA,CADA,UjBqnCR,CKriCI,mCYzEA,iCAII,YjB8mCN,CACF,CiB3mCM,wCACE,YjB6mCR,CiBzmCM,+CACE,oBjB2mCR,CKhjCI,sCYtDA,iCAII,YjBsmCN,CACF,CiBjmCE,kBAEE,YAAA,CACA,cAAA,CAFA,iBAAA,CAIA,8DACE,CAFF,kBjBomCJ,CiB9lCI,oCAGE,SAAA,CADA,mBAAA,CAKA,6BAAA,CAHA,8DACE,CAJF,UjBomCN,CiB3lCM,8CACE,8BjB6lCR,CiBxlCI,8BACE,ejB0lCN,CiBrlCE,4BAGE,gBAAA,CAAA,kBjBylCJ,CiB5lCE,4BAGE,iBAAA,CAAA,iBjBylCJ,CiB5lCE,kBACE,WAAA,CAGA,eAAA,CAFA,aAAA,CAGA,kBjBulCJ,CiBplCI,4CAGE,SAAA,CADA,mBAAA,CAKA,8BAAA,CAHA,8DACE,CAJF,UjB0lCN,CiBjlCM,sDACE,6BjBmlCR,CiB/kCM,8DAGE,SAAA,CADA,mBAAA,CAKA,uBAAA,CAHA,8DACE,CAJF,SjBqlCR,CiB1kCI,uCAGE,WAAA,CAFA,iBAAA,CACA,UjB6kCN,CiBvkCE,mBACE,YAAA,CACA,aAAA,CACA,cAAA,CAEA,+CACE,CAFF,kBjB0kCJ,CiBpkCI,8DACE,WAAA,CACA,SAAA,CACA,oCjBskCN,CiB7jCI,yBACE,QjB+jCN,CiB1jCE,mBACE,YjB4jCJ,CKxnCI,mCY2DF,6BAQI,gBjB4jCJ,CiBpkCA,6BAQI,iBjB4jCJ,CiBpkCA,mBAKI,aAAA,CAEA,iBAAA,CADA,ajB8jCJ,CACF,CKhoCI,sCY2DF,6BAaI,kBjB4jCJ,CiBzkCA,6BAaI,mBjB4jCJ,CACF,CD3yCA,SAGE,uCAAA,CAFA,eAAA,CACA,eC+yCF,CD3yCE,eACE,mBAAA,CACA,cAAA,CAGA,eAAA,CADA,QAAA,CADA,SC+yCJ,CDzyCE,sCAEE,WAAA,CADA,iBAAA,CAAA,kBC4yCJ,CDvyCE,eACE,+BCyyCJ,CDtyCI,0CACE,+BCwyCN,CDlyCA,UAKE,wBmBaa,CnBZb,oBAAA,CAFA,UAAA,CAHA,oBAAA,CAEA,eAAA,CADA,0BAAA,CAAA,2BCyyCF,CmB30CA,MACE,uMAAA,CACA,sLAAA,CACA,iNnB80CF,CmBx0CA,QACE,eAAA,CACA,enB20CF,CmBx0CE,eAKE,uCAAA,CAJA,aAAA,CAGA,eAAA,CADA,eAAA,CADA,eAAA,CAIA,sBnB00CJ,CmBv0CI,+BACE,YnBy0CN,CmBt0CM,mCAEE,WAAA,CADA,UnBy0CR,CmBj0CQ,sFAME,iBAAA,CALA,aAAA,CAGA,aAAA,CADA,cAAA,CAEA,kBAAA,CAHA,UnBu0CV,CmB5zCE,cAGE,eAAA,CADA,QAAA,CADA,SnBg0CJ,CmB1zCE,cAGE,sBAAA,CAFA,YAAA,CACA,SAAA,CAEA,iBAAA,CACA,uBAAA,CACA,sBnB4zCJ,CmBzzCI,sBACE,uCnB2zCN,CmBpzCM,6EAEE,+BnBszCR,CmBjzCI,2BAIE,iBnBgzCN,CmB5yCI,4CACE,gBnB8yCN,CmB/yCI,4CACE,iBnB8yCN,CmB1yCI,kBAME,iBAAA,CAFA,aAAA,CACA,YAAA,CAFA,iBnB6yCN,CmBtyCI,sGACE,+BAAA,CACA,cnBwyCN,CmBpyCI,4BACE,uCAAA,CACA,oBnBsyCN,CmBlyCI,0CACE,YnBoyCN,CmBjyCM,yDAIE,6BAAA,CAHA,aAAA,CAEA,WAAA,CAEA,qCAAA,CAAA,6BAAA,CAHA,UnBsyCR,CmB/xCM,kDACE,YnBiyCR,CmB3xCE,iCACE,YnB6xCJ,CmB1xCI,6CACE,WAAA,CAGA,WnB0xCN,CmBrxCE,cACE,anBuxCJ,CmBnxCE,gBACE,YnBqxCJ,CKtvCI,0CcxBA,0CASE,2CAAA,CAHA,YAAA,CACA,qBAAA,CACA,WAAA,CALA,MAAA,CADA,iBAAA,CACA,OAAA,CACA,KAAA,CACA,SnBoxCJ,CmBzwCI,+DACE,eAAA,CACA,enB2wCN,CmBvwCI,gCAQE,qDAAA,CAHA,uCAAA,CAEA,cAAA,CALA,aAAA,CAEA,kBAAA,CADA,wBAAA,CAFA,iBAAA,CAKA,kBnB2wCN,CmBtwCM,wDAEE,UnB6wCR,CmB/wCM,wDAEE,WnB6wCR,CmB/wCM,8CAIE,aAAA,CAEA,aAAA,CACA,YAAA,CANA,iBAAA,CAEA,SAAA,CAEA,YnB0wCR,CmBrwCQ,oDAKE,6BAAA,CADA,UAAA,CAHA,aAAA,CAEA,WAAA,CAGA,2CAAA,CAAA,mCAAA,CAEA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CAPA,UnB8wCV,CmBlwCM,8CAIE,2CAAA,CACA,gEACE,CALF,eAAA,CAEA,4BAAA,CADA,kBnBuwCR,CmBhwCQ,2DACE,YnBkwCV,CmB7vCM,8CAGE,2CAAA,CADA,gCAAA,CADA,enBiwCR,CmB3vCM,yCAIE,aAAA,CAFA,UAAA,CAIA,YAAA,CADA,aAAA,CAJA,iBAAA,CACA,WAAA,CACA,SnBgwCR,CmBxvCI,+BACE,MnB0vCN,CmBtvCI,+BACE,4DnBwvCN,CmBrvCM,qDACE,+BnBuvCR,CmBpvCQ,sHACE,+BnBsvCV,CmBhvCI,+BAEE,YAAA,CADA,mBnBmvCN,CmB/uCM,mCACE,enBivCR,CmB7uCM,6CACE,SnB+uCR,CmB3uCM,uDAGE,mBnB8uCR,CmBjvCM,uDAGE,kBnB8uCR,CmBjvCM,6CAIE,gBAAA,CAFA,aAAA,CADA,YnBgvCR,CmB1uCQ,mDAKE,6BAAA,CADA,UAAA,CAHA,aAAA,CAEA,WAAA,CAGA,2CAAA,CAAA,mCAAA,CAEA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CAPA,UnBmvCV,CmBnuCM,+CACE,mBnBquCR,CmB7tCM,4CAEE,wBAAA,CADA,enBguCR,CmB5tCQ,oEACE,mBnB8tCV,CmB/tCQ,oEACE,oBnB8tCV,CmB1tCQ,4EACE,iBnB4tCV,CmB7tCQ,4EACE,kBnB4tCV,CmBxtCQ,oFACE,mBnB0tCV,CmB3tCQ,oFACE,oBnB0tCV,CmBttCQ,4FACE,mBnBwtCV,CmBztCQ,4FACE,oBnBwtCV,CmBjtCE,mBACE,wBnBmtCJ,CmB/sCE,wBACE,YAAA,CACA,SAAA,CAIA,0BAAA,CAHA,oEnBktCJ,CmB5sCI,kCACE,2BnB8sCN,CmBzsCE,gCACE,SAAA,CAIA,uBAAA,CAHA,qEnB4sCJ,CmBtsCI,8CAEE,kCAAA,CAAA,0BnBusCN,CACF,CKz4CI,0Cc0MA,0CACE,YnBksCJ,CmB/rCI,yDACE,UnBisCN,CmB7rCI,wDACE,YnB+rCN,CmB3rCI,kDACE,YnB6rCN,CmBxrCE,gBAIE,iDAAA,CADA,gCAAA,CAFA,aAAA,CACA,enB4rCJ,CACF,CKt8CM,+DcmRF,6CACE,YnBsrCJ,CmBnrCI,4DACE,UnBqrCN,CmBjrCI,2DACE,YnBmrCN,CmB/qCI,qDACE,YnBirCN,CACF,CK97CI,mCc7JJ,QAgbI,oBnB+qCF,CmBzqCI,kCAME,qCAAA,CACA,qDAAA,CANA,eAAA,CACA,KAAA,CAGA,SnB2qCN,CmBtqCM,6CACE,uBnBwqCR,CmBpqCM,gDACE,YnBsqCR,CmBjqCI,2CACE,kBnBoqCN,CmBrqCI,2CACE,mBnBoqCN,CmBrqCI,iCAEE,oBnBmqCN,CmB5pCI,yDACE,kBnB8pCN,CmB/pCI,yDACE,iBnB8pCN,CACF,CKv9CI,sCc7JJ,QA4dI,oBAAA,CACA,oDnB4pCF,CmBtpCI,gCAME,qCAAA,CACA,qDAAA,CANA,eAAA,CACA,KAAA,CAGA,SnBwpCN,CmBnpCM,8CACE,uBnBqpCR,CmBjpCM,8CACE,YnBmpCR,CmB9oCI,yCACE,kBnBipCN,CmBlpCI,yCACE,mBnBipCN,CmBlpCI,+BAEE,oBnBgpCN,CmBzoCI,uDACE,kBnB2oCN,CmB5oCI,uDACE,iBnB2oCN,CmBtoCE,wBACE,YAAA,CAGA,oCAAA,CAEA,SAAA,CACA,6FACE,CAHF,mBnBwoCJ,CmBhoCI,sCACE,enBkoCN,CmB7nCE,iFACE,oCAAA,CAEA,SAAA,CACA,4FACE,CAHF,kBnBioCJ,CmBxnCE,iDACE,enB0nCJ,CmBtnCE,6CACE,YnBwnCJ,CmBpnCE,uBACE,aAAA,CACA,enBsnCJ,CmBnnCI,kCACE,enBqnCN,CmBjnCI,qCACE,enBmnCN,CmBhnCM,0CACE,uCnBknCR,CmB9mCM,6DACE,mBnBgnCR,CmB5mCM,yFAEE,YnB8mCR,CmBzmCI,yCAEE,kBnB6mCN,CmB/mCI,yCAEE,mBnB6mCN,CmB/mCI,+BACE,aAAA,CAGA,SAAA,CADA,kBnB4mCN,CmBxmCM,2DACE,SnB0mCR,CmBpmCE,cAGE,kBAAA,CADA,YAAA,CAEA,gCAAA,CAHA,WnBymCJ,CmBnmCI,oBACE,uDnBqmCN,CmBjmCI,oBAME,6BAAA,CACA,kBAAA,CAFA,UAAA,CAJA,oBAAA,CAEA,WAAA,CAKA,2CAAA,CAAA,mCAAA,CAEA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CACA,yBAAA,CARA,qBAAA,CAFA,UnB6mCN,CmBhmCM,8BACE,wBnBkmCR,CmB9lCM,kKAEE,uBnB+lCR,CmBjlCI,2EACE,YnBslCN,CmBnlCM,oDACE,anBqlCR,CmBllCQ,kEAKE,qCAAA,CACA,qDAAA,CAFA,YAAA,CAHA,eAAA,CACA,KAAA,CACA,SnBulCV,CmBjlCU,0FACE,mBnBmlCZ,CmB9kCQ,0EACE,QnBglCV,CmB3kCM,sFACE,kBnB6kCR,CmB9kCM,sFACE,mBnB6kCR,CmBzkCM,kDACE,uCnB2kCR,CmBrkCI,2CACE,oCAAA,CAEA,SAAA,CADA,kBnBwkCN,CmB/jCI,qFAIE,mDnBkkCN,CmBtkCI,qFAIE,oDnBkkCN,CmBtkCI,2EACE,aAAA,CACA,oBAAA,CAGA,SAAA,CAFA,kBnBmkCN,CmB9jCM,yFAEE,gBAAA,CADA,gBnBikCR,CmB5jCM,0FACE,YnB8jCR,CACF,CoBvxDA,eAKE,eAAA,CACA,eAAA,CAJA,SpB8xDF,CoBvxDE,gCANA,kBAAA,CAFA,YAAA,CAGA,sBpBqyDF,CoBhyDE,iBAOE,mBAAA,CAFA,aAAA,CADA,gBAAA,CAEA,iBpB0xDJ,CoBrxDE,wBAEE,qDAAA,CADA,uCpBwxDJ,CoBnxDE,qBACE,6CpBqxDJ,CoBhxDI,sDAEE,uDAAA,CADA,+BpBmxDN,CoB/wDM,8DACE,+BpBixDR,CoB5wDI,mCACE,uCAAA,CACA,oBpB8wDN,CoB1wDI,yBAKE,iBAAA,CADA,yCAAA,CAHA,aAAA,CAEA,eAAA,CADA,YpB+wDN,CqB/zDE,eAGE,+DAAA,CADA,oBAAA,CADA,qBrBo0DJ,CK/oDI,0CgBtLF,eAOI,YrBk0DJ,CACF,CqB5zDM,6BACE,oBrB8zDR,CqBxzDE,kBACE,YAAA,CACA,qBAAA,CACA,SAAA,CACA,qBrB0zDJ,CqBnzDI,0BACE,sBrBqzDN,CqBlzDM,gEACE,+BrBozDR,CqB9yDE,gBAEE,uCAAA,CADA,erBizDJ,CqB5yDE,kBACE,oBrB8yDJ,CqB3yDI,mCAGE,kBAAA,CAFA,YAAA,CACA,SAAA,CAEA,iBrB6yDN,CqBzyDI,oCAIE,kBAAA,CAHA,mBAAA,CACA,kBAAA,CACA,SAAA,CAGA,QAAA,CADA,iBrB4yDN,CqBvyDI,0DACE,kBrByyDN,CqB1yDI,0DACE,iBrByyDN,CqBryDI,iDACE,uBAAA,CAEA,YrBsyDN,CqBjyDE,4BACE,YrBmyDJ,CqB5xDA,YAGE,kBAAA,CAFA,YAAA,CAIA,eAAA,CAHA,SAAA,CAIA,eAAA,CAFA,UrBiyDF,CqB5xDE,yBACE,WrB8xDJ,CqBvxDA,kBACE,YrB0xDF,CKltDI,0CgBzEJ,kBAKI,wBrB0xDF,CACF,CqBvxDE,qCACE,WAAA,CACA,WrByxDJ,CK9uDI,sCgB7CF,+CAMI,kBrByxDJ,CqB/xDA,+CAMI,mBrByxDJ,CACF,CKhuDI,0CgBpDJ,6BAMI,SAAA,CAFA,eAAA,CACA,UrBsxDF,CqBnxDE,qDACE,gBrBqxDJ,CqBlxDE,gDACE,SrBoxDJ,CqBjxDE,4CACE,iBAAA,CAAA,kBrBmxDJ,CqBhxDE,2CAEE,WAAA,CADA,crBmxDJ,CqB/wDE,2CACE,mBAAA,CACA,cAAA,CACA,SAAA,CACA,oBAAA,CAAA,iBrBixDJ,CqB9wDE,2CACE,SrBgxDJ,CqB7wDE,qCAEE,WAAA,CACA,eAAA,CAFA,erBixDJ,CACF,CsB57DA,MACE,qBAAA,CACA,yBtB+7DF,CsBz7DA,aAME,qCAAA,CADA,cAAA,CAEA,0FACE,CAPF,cAAA,CACA,KAAA,CAaA,mDAAA,CACA,qBAAA,CAJA,wFACE,CATF,UAAA,CADA,StBm8DF,CuB98DA,MACE,mfvBi9DF,CuB38DA,WACE,iBvB88DF,CKhzDI,mCkB/JJ,WAKI,evB88DF,CACF,CuB38DE,kBACE,YvB68DJ,CuBz8DE,oBAEE,SAAA,CADA,SvB48DJ,CKzyDI,0CkBpKF,8BAOI,YvBo9DJ,CuB39DA,8BAOI,avBo9DJ,CuB39DA,oBAaI,2CAAA,CACA,kBAAA,CAJA,WAAA,CACA,eAAA,CACA,mBAAA,CANA,iBAAA,CAEA,SAAA,CAUA,uBAAA,CAHA,4CACE,CAPF,UvBk9DJ,CuBt8DI,+DACE,SAAA,CACA,oCvBw8DN,CACF,CK/0DI,mCkBjJF,8BAgCI,MvB28DJ,CuB3+DA,8BAgCI,OvB28DJ,CuB3+DA,oBAqCI,0BAAA,CADA,cAAA,CADA,QAAA,CAJA,cAAA,CAEA,KAAA,CAKA,sDACE,CALF,OvBy8DJ,CuB/7DI,+DAME,YAAA,CACA,SAAA,CACA,4CACE,CARF,UvBo8DN,CACF,CK90DI,0CkBxGA,+DAII,mBvBs7DN,CACF,CK53DM,+DkB/DF,+DASI,mBvBs7DN,CACF,CKj4DM,+DkB/DF,+DAcI,mBvBs7DN,CACF,CuBj7DE,kBAEE,kCAAA,CAAA,0BvBk7DJ,CKh2DI,0CkBpFF,4BAOI,MvB07DJ,CuBj8DA,4BAOI,OvB07DJ,CuBj8DA,kBAWI,QAAA,CAEA,SAAA,CADA,eAAA,CANA,cAAA,CAEA,KAAA,CAWA,wBAAA,CALA,qGACE,CALF,OAAA,CADA,SvBw7DJ,CuB36DI,4BACE,yBvB66DN,CuBz6DI,6DAEE,WAAA,CACA,SAAA,CAMA,uBAAA,CALA,sGACE,CAJF,UvB+6DN,CACF,CK34DI,mCkBjEF,4BA2CI,WvBy6DJ,CuBp9DA,4BA2CI,UvBy6DJ,CuBp9DA,kBA6CI,eAAA,CAHA,iBAAA,CAIA,8CAAA,CAFA,avBw6DJ,CACF,CK16DM,+DkBOF,6DAII,avBm6DN,CACF,CKz5DI,sCkBfA,6DASI,avBm6DN,CACF,CuB95DE,iBAIE,2CAAA,CACA,0BAAA,CAFA,aAAA,CAFA,iBAAA,CAKA,2CACE,CALF,SvBo6DJ,CKt6DI,mCkBAF,iBAaI,0BAAA,CACA,mBAAA,CAFA,avBg6DJ,CuB35DI,uBACE,0BvB65DN,CACF,CuBz5DI,4DAEE,2CAAA,CACA,6BAAA,CACA,8BAAA,CAHA,gCvB85DN,CuBt5DE,4BAKE,mBAAA,CAAA,oBvB25DJ,CuBh6DE,4BAKE,mBAAA,CAAA,oBvB25DJ,CuBh6DE,kBAQE,gBAAA,CAFA,eAAA,CAFA,WAAA,CAHA,iBAAA,CAMA,sBAAA,CAJA,UAAA,CADA,SvB85DJ,CuBr5DI,+BACE,qBvBu5DN,CuBn5DI,kEAEE,uCvBo5DN,CuBh5DI,6BACE,YvBk5DN,CKt7DI,0CkBaF,kBA8BI,eAAA,CADA,aAAA,CADA,UvBm5DJ,CACF,CKh9DI,mCkBgCF,4BAmCI,mBvBm5DJ,CuBt7DA,4BAmCI,oBvBm5DJ,CuBt7DA,kBAqCI,aAAA,CADA,evBk5DJ,CuB94DI,+BACE,uCvBg5DN,CuB54DI,mCACE,gCvB84DN,CuB14DI,6DACE,kBvB44DN,CuBz4DM,8EACE,uCvB24DR,CuBv4DM,0EACE,WvBy4DR,CACF,CuBn4DE,iBAIE,cAAA,CAHA,oBAAA,CAEA,aAAA,CAEA,kCACE,CAJF,YvBw4DJ,CuBh4DI,uBACE,UvBk4DN,CuB93DI,yCAEE,UvBk4DN,CuBp4DI,yCAEE,WvBk4DN,CuBp4DI,+BACE,iBAAA,CAEA,SAAA,CACA,SvBg4DN,CuB73DM,6CACE,oBvB+3DR,CKt+DI,0CkB+FA,yCAaI,UvB+3DN,CuB54DE,yCAaI,WvB+3DN,CuB54DE,+BAcI,SvB83DN,CuB33DM,+CACE,YvB63DR,CACF,CKlgEI,mCkBkHA,+BAwBI,mBvB43DN,CuBz3DM,8CACE,YvB23DR,CACF,CuBr3DE,8BAEE,WvB03DJ,CuB53DE,8BAEE,UvB03DJ,CuB53DE,oBAKE,mBAAA,CAJA,iBAAA,CAEA,SAAA,CACA,SvBw3DJ,CK9/DI,0CkBkIF,8BASI,WvBw3DJ,CuBj4DA,8BASI,UvBw3DJ,CuBj4DA,oBAUI,SvBu3DJ,CACF,CuBp3DI,uCACE,iBvB03DN,CuB33DI,uCACE,kBvB03DN,CuB33DI,6BAEE,uCAAA,CACA,SAAA,CAIA,oBAAA,CAHA,+DvBu3DN,CuBj3DM,iDAEE,uCAAA,CADA,YvBo3DR,CuB/2DM,gGAGE,SAAA,CADA,mBAAA,CAEA,kBvBg3DR,CuB72DQ,sGACE,UvB+2DV,CuBx2DE,8BAOE,mBAAA,CAAA,oBvB+2DJ,CuBt3DE,8BAOE,mBAAA,CAAA,oBvB+2DJ,CuBt3DE,oBAIE,kBAAA,CAKA,yCAAA,CANA,YAAA,CAKA,eAAA,CAFA,WAAA,CAKA,SAAA,CAVA,iBAAA,CACA,KAAA,CAUA,uBAAA,CAFA,kBAAA,CALA,UvBi3DJ,CKxjEI,mCkBkMF,8BAgBI,mBvB22DJ,CuB33DA,8BAgBI,oBvB22DJ,CuB33DA,oBAiBI,evB02DJ,CACF,CuBv2DI,+DACE,SAAA,CACA,0BvBy2DN,CuBp2DE,6BAKE,+BvBu2DJ,CuB52DE,0DAME,gCvBs2DJ,CuB52DE,6BAME,+BvBs2DJ,CuB52DE,mBAIE,eAAA,CAHA,iBAAA,CAEA,UAAA,CADA,SvB02DJ,CKvjEI,0CkB2MF,mBAWI,QAAA,CADA,UvBu2DJ,CACF,CKhlEI,mCkB8NF,mBAiBI,SAAA,CADA,UAAA,CAEA,sBvBs2DJ,CuBn2DI,8DACE,8BAAA,CACA,SvBq2DN,CACF,CuBh2DE,uBASE,kCAAA,CAAA,0BAAA,CAFA,2CAAA,CANA,WAAA,CACA,eAAA,CAIA,kBvBi2DJ,CuB31DI,iEAZF,uBAaI,uBvB81DJ,CACF,CK7nEM,+DkBiRJ,uBAkBI,avB81DJ,CACF,CK5mEI,sCkB2PF,uBAuBI,avB81DJ,CACF,CKjnEI,mCkB2PF,uBA4BI,YAAA,CACA,yDAAA,CACA,oBvB81DJ,CuB31DI,kEACE,evB61DN,CuBz1DI,6BACE,+CvB21DN,CuBv1DI,0CAEE,YAAA,CADA,WvB01DN,CuBr1DI,gDACE,oDvBu1DN,CuBp1DM,sDACE,0CvBs1DR,CACF,CuB/0DA,kBACE,gCAAA,CACA,qBvBk1DF,CuB/0DE,wBAME,qDAAA,CAFA,uCAAA,CAFA,gBAAA,CACA,kBAAA,CAFA,eAAA,CAIA,uBvBk1DJ,CKrpEI,mCkB8TF,kCAUI,mBvBi1DJ,CuB31DA,kCAUI,oBvBi1DJ,CACF,CuB70DE,wBAGE,eAAA,CADA,QAAA,CADA,SAAA,CAIA,wBAAA,CAAA,gBvB80DJ,CuB10DE,wBACE,yDvB40DJ,CuBz0DI,oCACE,evB20DN,CuBt0DE,wBACE,aAAA,CAEA,YAAA,CADA,uBAAA,CAEA,gCvBw0DJ,CuBr0DI,4DACE,uDvBu0DN,CuBn0DI,gDACE,mBvBq0DN,CuBh0DE,gCAKE,cAAA,CADA,aAAA,CAGA,YAAA,CANA,eAAA,CAKA,uBAAA,CAJA,KAAA,CACA,SvBs0DJ,CuB/zDI,wCACE,YvBi0DN,CuB5zDI,wDACE,YvB8zDN,CuB1zDI,oCAGE,+BAAA,CADA,gBAAA,CADA,mBAAA,CAGA,2CvB4zDN,CKvsEI,mCkBuYA,8CAUI,mBvB0zDN,CuBp0DE,8CAUI,oBvB0zDN,CACF,CuBtzDI,oFAEE,uDAAA,CADA,+BvByzDN,CuBnzDE,sCACE,2CvBqzDJ,CuBhzDE,2BAGE,eAAA,CADA,eAAA,CADA,iBvBozDJ,CKxtEI,mCkBmaF,qCAOI,mBvBkzDJ,CuBzzDA,qCAOI,oBvBkzDJ,CACF,CuB9yDE,kCAEE,MvBozDJ,CuBtzDE,kCAEE,OvBozDJ,CuBtzDE,wBAME,uCAAA,CAFA,aAAA,CACA,YAAA,CAJA,iBAAA,CAEA,YvBmzDJ,CKltEI,0CkB4ZF,wBAUI,YvBgzDJ,CACF,CuB7yDI,8BAKE,6BAAA,CADA,UAAA,CAHA,oBAAA,CAEA,WAAA,CAGA,+CAAA,CAAA,uCAAA,CAEA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CAPA,UvBszDN,CuB5yDM,wCACE,oBvB8yDR,CuBxyDE,8BAGE,uCAAA,CAFA,gBAAA,CACA,evB2yDJ,CuBvyDI,iCAKE,gCAAA,CAHA,eAAA,CACA,eAAA,CACA,eAAA,CAHA,evB6yDN,CuBtyDM,sCACE,oBvBwyDR,CuBnyDI,iCAKE,gCAAA,CAHA,gBAAA,CACA,eAAA,CACA,eAAA,CAHA,avByyDN,CuBlyDM,sCACE,oBvBoyDR,CuB9xDE,yBAKE,gCAAA,CAJA,aAAA,CAEA,gBAAA,CACA,iBAAA,CAFA,avBmyDJ,CuB5xDE,uBAGE,wBAAA,CAFA,+BAAA,CACA,yBvB+xDJ,CwBn8EA,WACE,iBAAA,CACA,SxBs8EF,CwBn8EE,kBAOE,2CAAA,CACA,mBAAA,CACA,8BAAA,CAHA,gCAAA,CAHA,QAAA,CAEA,gBAAA,CADA,YAAA,CAMA,SAAA,CATA,iBAAA,CACA,sBAAA,CAaA,mCAAA,CAJA,oExBs8EJ,CwB/7EI,6EACE,gBAAA,CACA,SAAA,CAKA,+BAAA,CAJA,8ExBk8EN,CwB17EI,wBAWE,+BAAA,CAAA,8CAAA,CAFA,6BAAA,CAAA,8BAAA,CACA,YAAA,CAFA,UAAA,CAHA,QAAA,CAFA,QAAA,CAIA,kBAAA,CADA,iBAAA,CALA,iBAAA,CACA,KAAA,CAEA,OxBm8EN,CwBv7EE,iBAOE,mBAAA,CAFA,eAAA,CACA,oBAAA,CAHA,QAAA,CAFA,kBAAA,CAGA,aAAA,CAFA,SxB87EJ,CwBr7EE,iBACE,kBxBu7EJ,CwBn7EE,2BAGE,kBAAA,CAAA,oBxBy7EJ,CwB57EE,2BAGE,mBAAA,CAAA,mBxBy7EJ,CwB57EE,iBAIE,cAAA,CAHA,aAAA,CAKA,YAAA,CADA,uBAAA,CAEA,2CACE,CANF,UxB07EJ,CwBh7EI,8CACE,+BxBk7EN,CwB96EI,uBACE,qDxBg7EN,CyBpgFA,YAIE,qBAAA,CADA,aAAA,CAGA,gBAAA,CALA,eAAA,CACA,UAAA,CAGA,azBwgFF,CyBpgFE,aATF,YAUI,YzBugFF,CACF,CKz1EI,0CoB3KF,+BAKI,azB4gFJ,CyBjhFA,+BAKI,czB4gFJ,CyBjhFA,qBAWI,2CAAA,CAHA,aAAA,CAEA,WAAA,CANA,cAAA,CAEA,KAAA,CASA,uBAAA,CAHA,iEACE,CAJF,aAAA,CAFA,SzB0gFJ,CyB//EI,mEACE,8BAAA,CACA,6BzBigFN,CyB9/EM,6EACE,8BzBggFR,CyB3/EI,6CAEE,QAAA,CAAA,MAAA,CACA,QAAA,CACA,eAAA,CAHA,iBAAA,CACA,OAAA,CAGA,qBAAA,CAHA,KzBggFN,CACF,CKx4EI,sCoBtKJ,YAuDI,QzB2/EF,CyBx/EE,mBACE,WzB0/EJ,CyBt/EE,6CACE,UzBw/EJ,CACF,CyBp/EE,uBACE,YAAA,CACA,OzBs/EJ,CKv5EI,mCoBjGF,uBAMI,QzBs/EJ,CyBn/EI,8BACE,WzBq/EN,CyBj/EI,qCACE,azBm/EN,CyB/+EI,+CACE,kBzBi/EN,CACF,CyB5+EE,wBAKE,kCAAA,CAAA,0BAAA,CAJA,cAAA,CACA,eAAA,CACA,yDzB++EJ,CK36EI,mCoBvEF,wBASI,uBAAA,CAKA,oBzBy+EJ,CACF,CyBt+EI,2CAEE,YAAA,CADA,WzBy+EN,CyBp+EI,mEACE,+CzBs+EN,CyBn+EM,qHACE,oDzBq+ER,CyBl+EQ,iIACE,0CzBo+EV,CyBr9EE,wCAGE,wBACE,qBzBq9EJ,CyBj9EE,6BACE,kCzBm9EJ,CyBp9EE,6BACE,iCzBm9EJ,CACF,CKn7EI,0CoBxBF,YAME,0BAAA,CADA,QAAA,CAEA,SAAA,CANA,cAAA,CACA,KAAA,CAMA,sDACE,CALF,OAAA,CADA,SzBo9EF,CyBz8EE,4CAEE,WAAA,CACA,SAAA,CACA,4CACE,CAJF,UzB88EJ,CACF,C0B/nFA,iBACE,GACE,Q1BioFF,C0B9nFA,GACE,a1BgoFF,CACF,C0B5nFA,gBACE,GACE,SAAA,CACA,0B1B8nFF,C0B3nFA,IACE,S1B6nFF,C0B1nFA,GACE,SAAA,CACA,uB1B4nFF,CACF,C0BpnFA,MACE,2eAAA,CACA,+fAAA,CACA,0lBAAA,CACA,kf1BsnFF,C0BhnFA,WAOE,kCAAA,CAAA,0BAAA,CANA,aAAA,CACA,gBAAA,CACA,eAAA,CAEA,uCAAA,CAGA,uBAAA,CAJA,kB1BsnFF,C0B/mFE,iBACE,U1BinFJ,C0B7mFE,iBACE,oBAAA,CAEA,aAAA,CACA,qBAAA,CAFA,U1BinFJ,C0B5mFI,+BACE,iB1B+mFN,C0BhnFI,+BACE,kB1B+mFN,C0BhnFI,qBAEE,gB1B8mFN,C0B1mFI,kDACE,iB1B6mFN,C0B9mFI,kDACE,kB1B6mFN,C0B9mFI,kDAEE,iB1B4mFN,C0B9mFI,kDAEE,kB1B4mFN,C0BvmFE,iCAGE,iB1B4mFJ,C0B/mFE,iCAGE,kB1B4mFJ,C0B/mFE,uBACE,oBAAA,CACA,6BAAA,CAEA,eAAA,CACA,sBAAA,CACA,qB1BymFJ,C0BrmFE,kBACE,YAAA,CAMA,gBAAA,CALA,SAAA,CAMA,oBAAA,CAHA,gBAAA,CAIA,WAAA,CAHA,eAAA,CAFA,SAAA,CADA,U1B6mFJ,C0BpmFI,iDACE,4B1BsmFN,C0BjmFE,iBACE,eAAA,CACA,sB1BmmFJ,C0BhmFI,gDACE,2B1BkmFN,C0B9lFI,kCAIE,kB1BsmFN,C0B1mFI,kCAIE,iB1BsmFN,C0B1mFI,wBAOE,6BAAA,CADA,UAAA,CALA,oBAAA,CAEA,YAAA,CAMA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CALA,uBAAA,CAHA,W1BwmFN,C0B5lFI,iCACE,a1B8lFN,C0B1lFI,iCACE,gDAAA,CAAA,wC1B4lFN,C0BxlFI,+BACE,8CAAA,CAAA,sC1B0lFN,C0BtlFI,+BACE,8CAAA,CAAA,sC1BwlFN,C0BplFI,sCACE,qDAAA,CAAA,6C1BslFN,C0BhlFA,gBACE,Y1BmlFF,C0BhlFE,gCAIE,kB1BolFJ,C0BxlFE,gCAIE,iB1BolFJ,C0BxlFE,sBAGE,kBAAA,CAGA,uCAAA,CALA,mBAAA,CAIA,gBAAA,CAHA,S1BslFJ,C0B/kFI,+BACE,aAAA,CACA,oB1BilFN,C0B7kFI,2CACE,U1BglFN,C0BjlFI,2CACE,W1BglFN,C0BjlFI,iCAEE,kB1B+kFN,C0B3kFI,0BACE,W1B6kFN,C2BpwFA,MACE,iSAAA,CACA,4UAAA,CACA,+NAAA,CACA,gZ3BuwFF,C2B9vFE,iBAME,kDAAA,CADA,UAAA,CAJA,oBAAA,CAEA,cAAA,CAIA,mCAAA,CAAA,2BAAA,CAEA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CANA,0BAAA,CAFA,a3BywFJ,C2B7vFE,uBACE,6B3B+vFJ,C2B3vFE,sBACE,wCAAA,CAAA,gC3B6vFJ,C2BzvFE,6BACE,+CAAA,CAAA,uC3B2vFJ,C2BvvFE,4BACE,8CAAA,CAAA,sC3ByvFJ,C4BpyFA,SASE,2CAAA,CADA,gCAAA,CAJA,aAAA,CAGA,eAAA,CADA,aAAA,CADA,UAAA,CAFA,S5B2yFF,C4BlyFE,aAZF,SAaI,Y5BqyFF,CACF,CK1nFI,0CuBzLJ,SAkBI,Y5BqyFF,CACF,C4BlyFE,iBACE,mB5BoyFJ,C4BhyFE,yBAIE,iB5BuyFJ,C4B3yFE,yBAIE,kB5BuyFJ,C4B3yFE,eAQE,eAAA,CAPA,YAAA,CAMA,eAAA,CAJA,QAAA,CAEA,aAAA,CAHA,SAAA,CAWA,oBAAA,CAPA,kB5BqyFJ,C4B3xFI,kCACE,Y5B6xFN,C4BxxFE,eACE,aAAA,CACA,kBAAA,CAAA,mB5B0xFJ,C4BvxFI,sCACE,aAAA,CACA,S5ByxFN,C4BnxFE,eAOE,kCAAA,CAAA,0BAAA,CANA,YAAA,CAEA,eAAA,CADA,gBAAA,CAMA,UAAA,CAJA,uCAAA,CACA,oBAAA,CAIA,8D5BoxFJ,C4B/wFI,0CACE,aAAA,CACA,S5BixFN,C4B7wFI,6BAEE,kB5BgxFN,C4BlxFI,6BAEE,iB5BgxFN,C4BlxFI,mBAGE,iBAAA,CAFA,Y5BixFN,C4B1wFM,2CACE,qB5B4wFR,C4B7wFM,2CACE,qB5B+wFR,C4BhxFM,2CACE,qB5BkxFR,C4BnxFM,2CACE,qB5BqxFR,C4BtxFM,2CACE,oB5BwxFR,C4BzxFM,2CACE,qB5B2xFR,C4B5xFM,2CACE,qB5B8xFR,C4B/xFM,2CACE,qB5BiyFR,C4BlyFM,4CACE,qB5BoyFR,C4BryFM,4CACE,oB5BuyFR,C4BxyFM,4CACE,qB5B0yFR,C4B3yFM,4CACE,qB5B6yFR,C4B9yFM,4CACE,qB5BgzFR,C4BjzFM,4CACE,qB5BmzFR,C4BpzFM,4CACE,oB5BszFR,C4BhzFI,gCACE,SAAA,CAIA,yBAAA,CAHA,wC5BmzFN,C6Bt5FA,MACE,mS7By5FF,C6Bh5FE,mCACE,mBAAA,CACA,cAAA,CACA,QAAA,CAEA,mBAAA,CADA,kB7Bo5FJ,C6B/4FE,oBAGE,kBAAA,CAOA,+CAAA,CACA,oBAAA,CAVA,mBAAA,CAIA,gBAAA,CACA,0BAAA,CACA,eAAA,CALA,QAAA,CAOA,qBAAA,CADA,eAAA,CAJA,wB7Bw5FJ,C6B94FI,0BAGE,uCAAA,CAFA,aAAA,CACA,YAAA,CAEA,6C7Bg5FN,C6B34FM,gEAEE,0CAAA,CADA,+B7B84FR,C6Bx4FI,yBACE,uB7B04FN,C6Bl4FI,gCAME,oDAAA,CADA,UAAA,CAJA,oBAAA,CAEA,YAAA,CAIA,qCAAA,CAAA,6BAAA,CAEA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CACA,iCAAA,CAPA,0BAAA,CAFA,W7B64FN,C6Bh4FI,wFACE,0C7Bk4FN,C8B58FA,iBACE,GACE,oB9B+8FF,C8B58FA,IACE,kB9B88FF,C8B38FA,GACE,oB9B68FF,CACF,C8Br8FA,MACE,yNAAA,CACA,sP9Bw8FF,C8Bj8FA,YA6BE,kCAAA,CAAA,0BAAA,CAVA,2CAAA,CACA,mBAAA,CACA,8BAAA,CAHA,gCAAA,CADA,sCAAA,CAdA,+IACE,CAYF,8BAAA,CAMA,SAAA,CArBA,iBAAA,CACA,uBAAA,CAyBA,4BAAA,CAJA,uDACE,CATF,6BAAA,CADA,S9Bq8FF,C8Bn7FE,oBAEE,SAAA,CAKA,uBAAA,CAJA,2EACE,CAHF,S9Bw7FJ,C8B96FE,oBAEE,eAAA,CACA,wBAAA,CAAA,gBAAA,CAFA,U9Bk7FJ,C8B76FI,6CACE,qC9B+6FN,C8B36FI,uCAEE,eAAA,CADA,mB9B86FN,C8Bx6FI,6BACE,Y9B06FN,C8Br6FE,8CACE,sC9Bu6FJ,C8Bn6FE,mBAEE,gBAAA,CADA,a9Bs6FJ,C8Bl6FI,2CACE,Y9Bo6FN,C8Bh6FI,0CACE,e9Bk6FN,C8B15FA,eACE,iBAAA,CACA,eAAA,CAIA,YAAA,CAHA,kBAAA,CAEA,0BAAA,CADA,kB9B+5FF,C8B15FE,yBACE,a9B45FJ,C8Bx5FE,oBACE,sCAAA,CACA,iB9B05FJ,C8Bt5FE,6BACE,oBAAA,CAGA,gB9Bs5FJ,C8Bl5FE,sBAYE,mBAAA,CANA,cAAA,CAHA,oBAAA,CACA,gBAAA,CAAA,iBAAA,CAIA,YAAA,CAGA,eAAA,CAVA,iBAAA,CAMA,wBAAA,CAAA,gBAAA,CAFA,uBAAA,CAHA,S9B45FJ,C8B94FI,qCACE,uB9Bg5FN,C8B54FI,cArBF,sBAsBI,W9B+4FJ,C8B54FI,wCACE,2B9B84FN,C8B14FI,6BAOE,qCAAA,CACA,+CAAA,CAAA,uC9B+4FN,C8Br4FI,yDAZE,UAAA,CADA,YAAA,CAKA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CAVA,iBAAA,CACA,SAAA,CAEA,WAAA,CADA,U9Bm6FN,C8Bp5FI,4BAOE,oDAAA,CACA,4CAAA,CAAA,oCAAA,CAQA,uBAAA,CAJA,+C9Bw4FN,C8Bj4FM,gDACE,uB9Bm4FR,C8B/3FM,mFACE,0C9Bi4FR,CACF,C8B53FI,0CAGE,2BAAA,CADA,uBAAA,CADA,S9Bg4FN,C8B13FI,8CACE,oB9B43FN,C8Bz3FM,aAJF,8CASI,8CAAA,CACA,iBAAA,CAHA,gCAAA,CADA,eAAA,CADA,cAAA,CAGA,kB9B83FN,C8Bz3FM,oDACE,mC9B23FR,CACF,C8B/2FE,gCAME,wBAAA,CADA,yB9B+2FJ,C8B32FI,mCACE,iB9B62FN,C8B12FM,oDAEE,a9By3FR,C8B33FM,oDAEE,c9By3FR,C8B33FM,0CAcE,8CAAA,CACA,iBAAA,CALA,gCAAA,CAEA,2BAAA,CACA,4BAAA,CANA,iBAAA,CACA,eAAA,CAHA,UAAA,CAIA,gBAAA,CALA,aAAA,CAEA,cAAA,CALA,iBAAA,CAUA,iBAAA,CARA,S9Bw3FR,C+B5oGA,MACE,wBAAA,CACA,wB/B+oGF,C+BzoGA,aA+BE,kCAAA,CAAA,0BAAA,CAjBA,gCAAA,CADA,sCAAA,CAGA,SAAA,CADA,mBAAA,CAdA,iBAAA,CAGA,wDACE,CAgBF,4BAAA,CAGA,uEACE,CARF,uDACE,CANF,UAAA,CADA,S/B6oGF,C+BtnGE,oBAuBE,8CAAA,CAAA,+CAAA,CADA,UAAA,CADA,aAAA,CAfA,gJACE,CANF,iBAAA,CAmBA,S/B0mGJ,C+BnmGE,yBAGE,kEAAA,CAFA,gDAAA,CACA,6C/BsmGJ,C+BjmGE,4BAGE,qEAAA,CADA,8CAAA,CADA,6C/BqmGJ,C+B/lGE,qBAEE,SAAA,CAKA,uBAAA,CAJA,wEACE,CAHF,S/BomGJ,C+B1lGE,oBAqBE,uBAAA,CAEA,2CAAA,CACA,mBAAA,CACA,8BAAA,CAnBA,0FACE,CAaF,eAAA,CADA,8BAAA,CAlBA,iBAAA,CAqBA,oB/B+kGJ,C+BzkGI,uCAEE,YAAA,CADA,W/B4kGN,C+BvkGI,6CACE,oD/BykGN,C+BtkGM,mDACE,0C/BwkGR,C+BhkGI,mCAwBE,eAAA,CACA,eAAA,CAxBA,oIACE,CAgBF,sCACE,CAIF,mBAAA,CAKA,wBAAA,CAAA,gBAAA,CAbA,iB/B0jGN,C+BziGI,4CACE,Y/B2iGN,C+BviGI,2CACE,e/ByiGN,CgC5tGA,kBAME,ehCwuGF,CgC9uGA,kBAME,gBhCwuGF,CgC9uGA,QAUE,2CAAA,CACA,oBAAA,CAEA,8BAAA,CALA,uCAAA,CACA,cAAA,CALA,aAAA,CAGA,eAAA,CAKA,YAAA,CAPA,mBAAA,CAJA,cAAA,CACA,UAAA,CAiBA,yBAAA,CALA,mGACE,CAZF,ShC2uGF,CgCxtGE,aAtBF,QAuBI,YhC2tGF,CACF,CgCxtGE,kBACE,wBhC0tGJ,CgCttGE,gBAEE,SAAA,CADA,mBAAA,CAGA,+BAAA,CADA,uBhCytGJ,CgCrtGI,0BACE,8BhCutGN,CgCltGE,4BAEE,0CAAA,CADA,+BhCqtGJ,CgChtGE,YACE,oBAAA,CACA,oBhCktGJ,CiCvwGA,oBACE,GACE,mBjC0wGF,CACF,CiClwGA,MACE,qfjCowGF,CiC9vGA,YACE,aAAA,CAEA,eAAA,CADA,ajCkwGF,CiC9vGE,+BAOE,kBAAA,CAAA,kBjC+vGJ,CiCtwGE,+BAOE,iBAAA,CAAA,mBjC+vGJ,CiCtwGE,qBAQE,aAAA,CACA,cAAA,CACA,YAAA,CATA,iBAAA,CAKA,UjCgwGJ,CiCzvGI,qCAIE,iBjCiwGN,CiCrwGI,qCAIE,kBjCiwGN,CiCrwGI,2BAME,6BAAA,CADA,UAAA,CAJA,oBAAA,CAEA,YAAA,CAIA,yCAAA,CAAA,iCAAA,CAEA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CARA,WjCmwGN,CiCtvGE,mBACE,iBAAA,CACA,UjCwvGJ,CiCpvGE,kBAWE,2CAAA,CACA,mBAAA,CACA,8BAAA,CALA,gCAAA,CACA,oBAAA,CAHA,kBAAA,CAFA,YAAA,CAUA,SAAA,CAPA,aAAA,CAFA,SAAA,CAJA,iBAAA,CASA,4BAAA,CARA,UAAA,CAaA,+CACE,CAbF,SjCkwGJ,CiCjvGI,+EACE,gBAAA,CACA,SAAA,CACA,sCjCmvGN,CiC7uGI,qCAEE,oCACE,gCjC8uGN,CiC1uGI,2CACE,cjC4uGN,CACF,CiCvuGE,kBACE,kBjCyuGJ,CiCruGE,4BAGE,kBAAA,CAAA,oBjC4uGJ,CiC/uGE,4BAGE,mBAAA,CAAA,mBjC4uGJ,CiC/uGE,kBAKE,cAAA,CAJA,aAAA,CAMA,YAAA,CADA,uBAAA,CAEA,2CACE,CALF,kBAAA,CAFA,UjC6uGJ,CiCluGI,gDACE,+BjCouGN,CiChuGI,wBACE,qDjCkuGN,CkCx0GA,MAEI,6VAAA,CAAA,uWAAA,CAAA,qPAAA,CAAA,2xBAAA,CAAA,qMAAA,CAAA,+aAAA,CAAA,2LAAA,CAAA,yPAAA,CAAA,2TAAA,CAAA,oaAAA,CAAA,2SAAA,CAAA,2LlCi2GJ,CkCr1GE,4CAME,8CAAA,CACA,4BAAA,CACA,mBAAA,CACA,8BAAA,CAJA,mCAAA,CAJA,iBAAA,CAGA,gBAAA,CADA,iBAAA,CADA,eAAA,CASA,uBAAA,CADA,2BlCy1GJ,CkCr1GI,aAdF,4CAeI,elCw1GJ,CACF,CkCr1GI,sEACE,gClCu1GN,CkCl1GI,gDACE,qBlCo1GN,CkCh1GI,gIAEE,iBAAA,CADA,clCm1GN,CkC90GI,4FACE,iBlCg1GN,CkC50GI,kFACE,elC80GN,CkC10GI,0FACE,YlC40GN,CkCx0GI,8EACE,mBlC00GN,CkCr0GE,sEAGE,iBAAA,CAAA,mBlC+0GJ,CkCl1GE,sEAGE,kBAAA,CAAA,kBlC+0GJ,CkCl1GE,sEASE,uBlCy0GJ,CkCl1GE,sEASE,wBlCy0GJ,CkCl1GE,sEAUE,4BlCw0GJ,CkCl1GE,4IAWE,6BlCu0GJ,CkCl1GE,sEAWE,4BlCu0GJ,CkCl1GE,kDAOE,0BAAA,CACA,WAAA,CAFA,eAAA,CADA,eAAA,CAHA,oBAAA,CAAA,iBAAA,CADA,iBlCi1GJ,CkCp0GI,kFACE,elCs0GN,CkCl0GI,oFAEE,UlC60GN,CkC/0GI,oFAEE,WlC60GN,CkC/0GI,gEAOE,wBhBiIU,CgBlIV,UAAA,CADA,WAAA,CAGA,kDAAA,CAAA,0CAAA,CAEA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CAVA,iBAAA,CAEA,UAAA,CACA,UlC20GN,CkCh0GI,4DACE,4DlCk0GN,CkCpzGE,sDACE,oBlCuzGJ,CkCpzGI,gFACE,gClCszGN,CkCjzGE,8DACE,0BlCozGJ,CkCjzGI,4EACE,wBAlBG,CAmBH,kDAAA,CAAA,0ClCmzGN,CkC/yGI,0EACE,alCizGN,CkCt0GE,8DACE,oBlCy0GJ,CkCt0GI,wFACE,gClCw0GN,CkCn0GE,sEACE,0BlCs0GJ,CkCn0GI,oFACE,wBAlBG,CAmBH,sDAAA,CAAA,8ClCq0GN,CkCj0GI,kFACE,alCm0GN,CkCx1GE,sDACE,oBlC21GJ,CkCx1GI,gFACE,gClC01GN,CkCr1GE,8DACE,0BlCw1GJ,CkCr1GI,4EACE,wBAlBG,CAmBH,kDAAA,CAAA,0ClCu1GN,CkCn1GI,0EACE,alCq1GN,CkC12GE,oDACE,oBlC62GJ,CkC12GI,8EACE,gClC42GN,CkCv2GE,4DACE,0BlC02GJ,CkCv2GI,0EACE,wBAlBG,CAmBH,iDAAA,CAAA,yClCy2GN,CkCr2GI,wEACE,alCu2GN,CkC53GE,4DACE,oBlC+3GJ,CkC53GI,sFACE,gClC83GN,CkCz3GE,oEACE,0BlC43GJ,CkCz3GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClC23GN,CkCv3GI,gFACE,alCy3GN,CkC94GE,8DACE,oBlCi5GJ,CkC94GI,wFACE,gClCg5GN,CkC34GE,sEACE,0BlC84GJ,CkC34GI,oFACE,wBAlBG,CAmBH,sDAAA,CAAA,8ClC64GN,CkCz4GI,kFACE,alC24GN,CkCh6GE,4DACE,oBlCm6GJ,CkCh6GI,sFACE,gClCk6GN,CkC75GE,oEACE,0BlCg6GJ,CkC75GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClC+5GN,CkC35GI,gFACE,alC65GN,CkCl7GE,4DACE,oBlCq7GJ,CkCl7GI,sFACE,gClCo7GN,CkC/6GE,oEACE,0BlCk7GJ,CkC/6GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClCi7GN,CkC76GI,gFACE,alC+6GN,CkCp8GE,0DACE,oBlCu8GJ,CkCp8GI,oFACE,gClCs8GN,CkCj8GE,kEACE,0BlCo8GJ,CkCj8GI,gFACE,wBAlBG,CAmBH,oDAAA,CAAA,4ClCm8GN,CkC/7GI,8EACE,alCi8GN,CkCt9GE,oDACE,oBlCy9GJ,CkCt9GI,8EACE,gClCw9GN,CkCn9GE,4DACE,0BlCs9GJ,CkCn9GI,0EACE,wBAlBG,CAmBH,iDAAA,CAAA,yClCq9GN,CkCj9GI,wEACE,alCm9GN,CkCx+GE,4DACE,oBlC2+GJ,CkCx+GI,sFACE,gClC0+GN,CkCr+GE,oEACE,0BlCw+GJ,CkCr+GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClCu+GN,CkCn+GI,gFACE,alCq+GN,CkC1/GE,wDACE,oBlC6/GJ,CkC1/GI,kFACE,gClC4/GN,CkCv/GE,gEACE,0BlC0/GJ,CkCv/GI,8EACE,wBAlBG,CAmBH,mDAAA,CAAA,2ClCy/GN,CkCr/GI,4EACE,alCu/GN,CmC3pHA,MACE,qMnC8pHF,CmCrpHE,sBAEE,uCAAA,CADA,gBnCypHJ,CmCrpHI,mCACE,anCupHN,CmCxpHI,mCACE,cnCupHN,CmCnpHM,4BACE,sBnCqpHR,CmClpHQ,mCACE,gCnCopHV,CmChpHQ,2DACE,SAAA,CAEA,uBAAA,CADA,enCmpHV,CmC9oHQ,yGACE,SAAA,CACA,uBnCgpHV,CmC5oHQ,yCACE,YnC8oHV,CmCvoHE,0BACE,eAAA,CACA,enCyoHJ,CmCtoHI,+BACE,oBnCwoHN,CmCnoHE,gDACE,YnCqoHJ,CmCjoHE,8BAIE,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","file":"main.css"} |
| @media screen{[data-md-color-scheme=slate]{--md-default-fg-color:hsla(var(--md-hue),15%,90%,0.82);--md-default-fg-color--light:hsla(var(--md-hue),15%,90%,0.56);--md-default-fg-color--lighter:hsla(var(--md-hue),15%,90%,0.32);--md-default-fg-color--lightest:hsla(var(--md-hue),15%,90%,0.12);--md-default-bg-color:hsla(var(--md-hue),15%,14%,1);--md-default-bg-color--light:hsla(var(--md-hue),15%,14%,0.54);--md-default-bg-color--lighter:hsla(var(--md-hue),15%,14%,0.26);--md-default-bg-color--lightest:hsla(var(--md-hue),15%,14%,0.07);--md-code-fg-color:hsla(var(--md-hue),18%,86%,0.82);--md-code-bg-color:hsla(var(--md-hue),15%,18%,1);--md-code-hl-color:#2977ff;--md-code-hl-color--light:#2977ff1a;--md-code-hl-number-color:#e6695b;--md-code-hl-special-color:#f06090;--md-code-hl-function-color:#c973d9;--md-code-hl-constant-color:#9383e2;--md-code-hl-keyword-color:#6791e0;--md-code-hl-string-color:#2fb170;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-kbd-color:hsla(var(--md-hue),15%,90%,0.12);--md-typeset-kbd-accent-color:hsla(var(--md-hue),15%,90%,0.2);--md-typeset-kbd-border-color:hsla(var(--md-hue),15%,14%,1);--md-typeset-mark-color:#4287ff4d;--md-typeset-table-color:hsla(var(--md-hue),15%,95%,0.12);--md-typeset-table-color--light:hsla(var(--md-hue),15%,95%,0.035);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-footer-bg-color:hsla(var(--md-hue),15%,10%,0.87);--md-footer-bg-color--dark:hsla(var(--md-hue),15%,8%,1);--md-shadow-z1:0 0.2rem 0.5rem #0000000d,0 0 0.05rem #0000001a;--md-shadow-z2:0 0.2rem 0.5rem #00000040,0 0 0.05rem #00000040;--md-shadow-z3:0 0.2rem 0.5rem #0006,0 0 0.05rem #00000059;color-scheme:dark}[data-md-color-scheme=slate] img[src$="#gh-light-mode-only"],[data-md-color-scheme=slate] img[src$="#only-light"]{display:none}[data-md-color-scheme=slate][data-md-color-primary=pink]{--md-typeset-a-color:#ed5487}[data-md-color-scheme=slate][data-md-color-primary=purple]{--md-typeset-a-color:#c46fd3}[data-md-color-scheme=slate][data-md-color-primary=deep-purple]{--md-typeset-a-color:#a47bea}[data-md-color-scheme=slate][data-md-color-primary=indigo]{--md-typeset-a-color:#5488e8}[data-md-color-scheme=slate][data-md-color-primary=teal]{--md-typeset-a-color:#00ccb8}[data-md-color-scheme=slate][data-md-color-primary=green]{--md-typeset-a-color:#71c174}[data-md-color-scheme=slate][data-md-color-primary=deep-orange]{--md-typeset-a-color:#ff764d}[data-md-color-scheme=slate][data-md-color-primary=brown]{--md-typeset-a-color:#c1775c}[data-md-color-scheme=slate][data-md-color-primary=black],[data-md-color-scheme=slate][data-md-color-primary=blue-grey],[data-md-color-scheme=slate][data-md-color-primary=grey],[data-md-color-scheme=slate][data-md-color-primary=white]{--md-typeset-a-color:#5e8bde}[data-md-color-switching] *,[data-md-color-switching] :after,[data-md-color-switching] :before{transition-duration:0ms!important}}[data-md-color-accent=red]{--md-accent-fg-color:#ff1947;--md-accent-fg-color--transparent:#ff19471a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=pink]{--md-accent-fg-color:#f50056;--md-accent-fg-color--transparent:#f500561a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=purple]{--md-accent-fg-color:#df41fb;--md-accent-fg-color--transparent:#df41fb1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=deep-purple]{--md-accent-fg-color:#7c4dff;--md-accent-fg-color--transparent:#7c4dff1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=indigo]{--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:#526cfe1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=blue]{--md-accent-fg-color:#4287ff;--md-accent-fg-color--transparent:#4287ff1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=light-blue]{--md-accent-fg-color:#0091eb;--md-accent-fg-color--transparent:#0091eb1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=cyan]{--md-accent-fg-color:#00bad6;--md-accent-fg-color--transparent:#00bad61a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=teal]{--md-accent-fg-color:#00bda4;--md-accent-fg-color--transparent:#00bda41a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=green]{--md-accent-fg-color:#00c753;--md-accent-fg-color--transparent:#00c7531a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=light-green]{--md-accent-fg-color:#63de17;--md-accent-fg-color--transparent:#63de171a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=lime]{--md-accent-fg-color:#b0eb00;--md-accent-fg-color--transparent:#b0eb001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=yellow]{--md-accent-fg-color:#ffd500;--md-accent-fg-color--transparent:#ffd5001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=amber]{--md-accent-fg-color:#fa0;--md-accent-fg-color--transparent:#ffaa001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=orange]{--md-accent-fg-color:#ff9100;--md-accent-fg-color--transparent:#ff91001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=deep-orange]{--md-accent-fg-color:#ff6e42;--md-accent-fg-color--transparent:#ff6e421a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-primary=red]{--md-primary-fg-color:#ef5552;--md-primary-fg-color--light:#e57171;--md-primary-fg-color--dark:#e53734;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=pink]{--md-primary-fg-color:#e92063;--md-primary-fg-color--light:#ec417a;--md-primary-fg-color--dark:#c3185d;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=purple]{--md-primary-fg-color:#ab47bd;--md-primary-fg-color--light:#bb69c9;--md-primary-fg-color--dark:#8c24a8;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=deep-purple]{--md-primary-fg-color:#7e56c2;--md-primary-fg-color--light:#9574cd;--md-primary-fg-color--dark:#673ab6;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=indigo]{--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=blue]{--md-primary-fg-color:#2094f3;--md-primary-fg-color--light:#42a5f5;--md-primary-fg-color--dark:#1975d2;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=light-blue]{--md-primary-fg-color:#02a6f2;--md-primary-fg-color--light:#28b5f6;--md-primary-fg-color--dark:#0287cf;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=cyan]{--md-primary-fg-color:#00bdd6;--md-primary-fg-color--light:#25c5da;--md-primary-fg-color--dark:#0097a8;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=teal]{--md-primary-fg-color:#009485;--md-primary-fg-color--light:#26a699;--md-primary-fg-color--dark:#007a6c;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=green]{--md-primary-fg-color:#4cae4f;--md-primary-fg-color--light:#68bb6c;--md-primary-fg-color--dark:#398e3d;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=light-green]{--md-primary-fg-color:#8bc34b;--md-primary-fg-color--light:#9ccc66;--md-primary-fg-color--dark:#689f38;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=lime]{--md-primary-fg-color:#cbdc38;--md-primary-fg-color--light:#d3e156;--md-primary-fg-color--dark:#b0b52c;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=yellow]{--md-primary-fg-color:#ffec3d;--md-primary-fg-color--light:#ffee57;--md-primary-fg-color--dark:#fbc02d;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=amber]{--md-primary-fg-color:#ffc105;--md-primary-fg-color--light:#ffc929;--md-primary-fg-color--dark:#ffa200;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=orange]{--md-primary-fg-color:#ffa724;--md-primary-fg-color--light:#ffa724;--md-primary-fg-color--dark:#fa8900;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=deep-orange]{--md-primary-fg-color:#ff6e42;--md-primary-fg-color--light:#ff8a66;--md-primary-fg-color--dark:#f4511f;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=brown]{--md-primary-fg-color:#795649;--md-primary-fg-color--light:#8d6e62;--md-primary-fg-color--dark:#5d4037;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=grey]{--md-primary-fg-color:#757575;--md-primary-fg-color--light:#9e9e9e;--md-primary-fg-color--dark:#616161;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-typeset-a-color:#4051b5}[data-md-color-primary=blue-grey]{--md-primary-fg-color:#546d78;--md-primary-fg-color--light:#607c8a;--md-primary-fg-color--dark:#455a63;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-typeset-a-color:#4051b5}[data-md-color-primary=light-green]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#72ad2e}[data-md-color-primary=lime]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#8b990a}[data-md-color-primary=yellow]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#b8a500}[data-md-color-primary=amber]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#d19d00}[data-md-color-primary=orange]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#e68a00}[data-md-color-primary=white]{--md-primary-fg-color:hsla(var(--md-hue),0%,100%,1);--md-primary-fg-color--light:hsla(var(--md-hue),0%,100%,0.7);--md-primary-fg-color--dark:hsla(var(--md-hue),0%,0%,0.07);--md-primary-bg-color:hsla(var(--md-hue),0%,0%,0.87);--md-primary-bg-color--light:hsla(var(--md-hue),0%,0%,0.54);--md-typeset-a-color:#4051b5}[data-md-color-primary=white] .md-button{color:var(--md-typeset-a-color)}[data-md-color-primary=white] .md-button--primary{background-color:var(--md-typeset-a-color);border-color:var(--md-typeset-a-color);color:hsla(var(--md-hue),0%,100%,1)}@media screen and (min-width:60em){[data-md-color-primary=white] .md-search__form{background-color:hsla(var(--md-hue),0%,0%,.07)}[data-md-color-primary=white] .md-search__form:hover{background-color:hsla(var(--md-hue),0%,0%,.32)}[data-md-color-primary=white] .md-search__input+.md-search__icon{color:hsla(var(--md-hue),0%,0%,.87)}}@media screen and (min-width:76.25em){[data-md-color-primary=white] .md-tabs{border-bottom:.05rem solid #00000012}}[data-md-color-primary=black]{--md-primary-fg-color:hsla(var(--md-hue),15%,9%,1);--md-primary-fg-color--light:hsla(var(--md-hue),15%,9%,0.54);--md-primary-fg-color--dark:hsla(var(--md-hue),15%,9%,1);--md-primary-bg-color:hsla(var(--md-hue),15%,100%,1);--md-primary-bg-color--light:hsla(var(--md-hue),15%,100%,0.7);--md-typeset-a-color:#4051b5}[data-md-color-primary=black] .md-button{color:var(--md-typeset-a-color)}[data-md-color-primary=black] .md-button--primary{background-color:var(--md-typeset-a-color);border-color:var(--md-typeset-a-color);color:hsla(var(--md-hue),0%,100%,1)}[data-md-color-primary=black] .md-header{background-color:hsla(var(--md-hue),15%,9%,1)}@media screen and (max-width:59.984375em){[data-md-color-primary=black] .md-nav__source{background-color:hsla(var(--md-hue),15%,11%,.87)}}@media screen and (max-width:76.234375em){html [data-md-color-primary=black] .md-nav--primary .md-nav__title[for=__drawer]{background-color:hsla(var(--md-hue),15%,9%,1)}}@media screen and (min-width:76.25em){[data-md-color-primary=black] .md-tabs{background-color:hsla(var(--md-hue),15%,9%,1)}} |
| {"version":3,"sources":["src/templates/assets/stylesheets/palette/_scheme.scss","../../../../src/templates/assets/stylesheets/palette.scss","src/templates/assets/stylesheets/palette/_accent.scss","src/templates/assets/stylesheets/palette/_primary.scss","src/templates/assets/stylesheets/utilities/_break.scss"],"names":[],"mappings":"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","file":"palette.css"} |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
12983339
1.82%14543
0.17%17694
18.22%