diff --git a/.github/assets/sponsors/hummingbot.png b/.github/assets/sponsors/hummingbot.png new file mode 100644 index 000000000..283987a02 Binary files /dev/null and b/.github/assets/sponsors/hummingbot.png differ diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 712e732f1..9e13024d2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,7 +52,7 @@ jobs: run: npm install - name: Check project - run: npm run lint + run: npm run check - name: Build project run: | diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 26e6c601e..7f9bf175b 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -46,7 +46,8 @@ jobs: pip install . pip install \ mkdocs-minify-plugin>=0.3 \ - mkdocs-redirects>=1.0 + mkdocs-redirects>=1.0 \ + mkdocs-rss-plugin>=0.17 - name: Install Insiders build if: github.event.repository.fork == false diff --git a/CHANGELOG b/CHANGELOG index 068d678ca..1872aec72 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,67 @@ +mkdocs-material-7.3.0 (2021-09-23) + + * Added support for sticky navigation tabs + * Added support for section index pages + * Added support for removing generator notice + +mkdocs-material-7.2.8 (2021-09-20) + + * Fixed #3039: Search modal overlays menu on mobile (7.2.7 regression) + +mkdocs-material-7.2.7+insiders-3.0.1 (2021-09-19) + + * Added support for using literal h1-6 tags for search plugin + * Fixed search plugin breaking on void elements without slashes + * Fixed search plugin filtering link contents from headlines + * Fixed search plugin handling of multiple h1 headlines + * Fixed search plugin handling of missing h1 headlines + +mkdocs-material-7.2.7 (2021-09-19) + + * Updated Serbian and Serbo-Croatian translations + * Improved appearance of outline on details + * Fixed #2934: Scrollbar when header is hidden on some mobile browsers + * Fixed #3032: Anchor in details doesn't open on load (7.0.0 regression) + * Fixed back-to-top button being focusable when invisible + * Fixed broken admonition icons (removed in upstream) + +mkdocs-material-7.2.6+insiders-3.0.0 (2021-09-13) + + * Rewrite of MkDocs' search plugin + * Added support for rich search previews + * Added support for tokenizer with lookahead + * Improved search indexing performance (twice as fast) + * Improved search highlighting + +mkdocs-material-7.2.6+insiders-2.13.3 (2021-09-01) + + * Added support for disabling social card generation + +mkdocs-material-7.2.6 (2021-09-01) + + * Fixed rendering of blockquote elements (7.0.0 regression) + * Fixed #2973: Custom search worker setting ignored + +mkdocs-material-7.2.5+insiders-2.13.2 (2021-08-25) + + * Fixed #2965: Social plugin error when primary color is not defined + +mkdocs-material-7.2.5 (2021-08-25) + + * Updated Portuguese translations + * Fixed execution of RxJS teardown logic (7.2.3 regression) + * Fixed #2970: Search results show escaped characters (7.2.2 regression) + +mkdocs-material-7.2.4+insiders-2.13.1 (2021-08-22) + + * Fixed #2948: Social cards are not cached + * Fixed #2953: Mermaid.js diagrams can't be centered anymore + +mkdocs-material-7.2.4 (2021-08-11) + + * Fixed #2926: Version selector not working (7.2.3 regression) + * Fixed #2929: Missing CSS class for banner (consistency with Insiders) + mkdocs-material-7.2.3 (2021-08-09) * Slight facelift of data tables, now closer to Material Design diff --git a/Dockerfile b/Dockerfile index 78d98a8d2..3b07cb1a7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,33 +40,40 @@ COPY setup.py setup.py # Perform build and cleanup artifacts and caches RUN \ - apk upgrade --update-cache -a && \ + apk upgrade --update-cache -a \ +&& \ apk add --no-cache \ git \ git-fast-import \ openssh \ - && apk add --no-cache --virtual .build gcc musl-dev \ - && pip install --no-cache-dir . \ - && \ - if [ "${WITH_PLUGINS}" = "true" ]; then \ - pip install --no-cache-dir \ - "mkdocs-minify-plugin>=0.3" \ - "mkdocs-redirects>=1.0"; \ - fi \ - && apk del .build gcc musl-dev \ - && \ - for theme in mkdocs readthedocs; do \ - rm -rf ${PACKAGES}/mkdocs/themes/$theme; \ - ln -s \ - ${PACKAGES}/material \ - ${PACKAGES}/mkdocs/themes/$theme; \ - done \ - && rm -rf /tmp/* /root/.cache \ - && \ - find ${PACKAGES} \ - -type f \ - -path "*/__pycache__/*" \ - -exec rm -f {} \; +&& \ + apk add --no-cache --virtual .build \ + gcc \ + musl-dev \ +&& \ + pip install --no-cache-dir . \ +&& \ + if [ "${WITH_PLUGINS}" = "true" ]; then \ + pip install --no-cache-dir \ + "mkdocs-minify-plugin>=0.3" \ + "mkdocs-redirects>=1.0"; \ + fi \ +&& \ + apk del .build \ +&& \ + for theme in mkdocs readthedocs; do \ + rm -rf ${PACKAGES}/mkdocs/themes/$theme; \ + ln -s \ + ${PACKAGES}/material \ + ${PACKAGES}/mkdocs/themes/$theme; \ + done \ +&& \ + rm -rf /tmp/* /root/.cache \ +&& \ + find ${PACKAGES} \ + -type f \ + -path "*/__pycache__/*" \ + -exec rm -f {} \; # Set working directory WORKDIR /docs diff --git a/README.md b/README.md index dc48e392b..65269b1e7 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,6 @@ src="https://img.shields.io/docker/pulls/squidfunk/mkdocs-material" alt="Docker Pulls" /> -

@@ -53,13 +50,30 @@

- A demo is worth a thousand words — check it out at + Check out the demo – squidfunk.github.io/mkdocs-material.

+

+

 

+

Special thanks to our premium sponsors:

+

 

+

+       +    + +

+

 

+ ## Features * **It's just Markdown ...** — write your technical documentation in Markdown – @@ -124,24 +138,15 @@ For other installation methods, configuration options, and a demo, visit [1]: https://squidfunk.github.io/mkdocs-material/ -## Premium Sponsors - -

- - -

- ## Trusted by ... -### ... leading companies +### ... leading organizations -[Atlassian](https://atlassian-labs.github.io/data-center-helm-charts/), +[Atlassian](https://atlassian.github.io/data-center-helm-charts/), [AWS](https://aws.github.io/copilot-cli/), [Binance](https://docs.binance.org/), +[Bloomberg](https://bloomberg.github.io/selekt/), +[CERN](http://abpcomputing.web.cern.ch/), [Datadog](https://datadoghq.dev/integrations-core/), [Google](https://google.github.io/accompanist/), [ING](https://ing-bank.github.io/baker/), @@ -157,6 +162,7 @@ For other installation methods, configuration options, and a demo, visit ### ... and successful Open Source projects +[Arduino](https://arduino.github.io/arduino-cli/), [AutoKeras](https://autokeras.com/), [BFE](https://www.bfe-networks.net/), [Crystal](https://crystal-lang.org/reference/), diff --git a/docs/blog/2021/search-better-faster-smaller.md b/docs/blog/2021/search-better-faster-smaller.md new file mode 100644 index 000000000..50b473517 --- /dev/null +++ b/docs/blog/2021/search-better-faster-smaller.md @@ -0,0 +1,649 @@ +--- +template: overrides/main.html +description: > + How we rebuilt client-side search, delivering a better user experience while + making it faster and smaller at the same time +disqus: mkdocs-material +search: + exclude: true +--- + +# Search: better, faster, smaller + +__This is the story of how we managed to completely rebuild client-side search, +delivering a significantly better user experience while making it faster and +smaller at the same time.__ + + + + [1]: https://avatars.githubusercontent.com/u/932156 + +--- + +The [search][2] of Material for MkDocs is by far one of its best and most-loved +assets: [multilingual][3], [offline-capable][4], and most importantly: _all +client-side_. It provides a solution to empower the users of your documentation +to find what they're searching for instantly without the headache of managing +additional servers. However, even though several iterations have been made, +there's still some room for improvement, which is why we rebuilt the search +plugin and integration from the ground up. This article shines some light on the +internals of the new search, why it's much more powerful than the previous +version, and what's about to come. + +_The next section discusses the architecture and issues of the current search +implementation. If you immediately want to learn what's new, skip to the +[section just after that][5]._ + + [2]: ../../setup/setting-up-site-search.md + [3]: ../../setup/setting-up-site-search.md#lang + [4]: ../../setup/setting-up-site-search.md#offline-search + [5]: #whats-new + +## Architecture + +Material for MkDocs uses [lunr][6] together with [lunr-languages][7] to +implement its client-side search capabilities. When a documentation page is +loaded and JavaScript is available, the search index as generated by the +[built-in search plugin][8] during the build process is requested from the +server: + +``` ts +const index$ = document.forms.namedItem("search") + ? __search?.index || requestJSON( + new URL("search/search_index.json", config.base) + ) + : NEVER +``` + + [6]: https://lunrjs.com + [7]: https://github.com/MihaiValentin/lunr-languages + [8]: ../../setup/setting-up-site-search.md#built-in-search + +### Search index + +The search index includes a stripped-down version of all pages. Let's take a +look at an example to understand precisely what the search index contains from +the original Markdown file: + +??? example "Expand to inspect example" + + === "`docs/page.md`" + + ```` markdown + # Example + + ## Text + + It's very easy to make some words **bold** and other words *italic* + with Markdown. You can even add [links](#), or even `code`: + + ``` + if (isAwesome) { + return true + } + ``` + + ## Lists + + Sometimes you want numbered lists: + + 1. One + 2. Two + 3. Three + + Sometimes you want bullet points: + + * Start a line with a star + * Profit! + ```` + + === "`search_index.json`" + + ``` json + { + "config": { + "indexing": "full", + "lang": [ + "en" + ], + "min_search_length": 3, + "prebuild_index": false, + "separator": "[\\s\\-]+" + }, + "docs": [ + { + "location": "page/", + "title": "Example", + "text": "Example Text It's very easy to make some words bold and other words italic with Markdown. You can even add links , or even code : if (isAwesome) { return true } Lists Sometimes you want numbered lists: One Two Three Sometimes you want bullet points: Start a line with a star Profit!" + }, + { + "location": "page/#example", + "title": "Example", + "text": "" + }, + { + "location": "page/#text", + "title": "Text", + "text": "It's very easy to make some words bold and other words italic with Markdown. You can even add links , or even code : if (isAwesome) { return true }" + }, + { + "location": "page/#lists", + "title": "Lists", + "text": "Sometimes you want numbered lists: One Two Three Sometimes you want bullet points: Start a line with a star Profit!" + } + ] + } + ``` + +If we inspect the search index, we immediately see several problems: + + 1. __All content is included twice__: the search index contains one entry + with the entire contents of the page, and one entry for each section of + the page, i.e., each block preceded by a headline or subheadline. This + significantly contributes to the size of the search index. + + 2. __All structure is lost__: when the search index is built, all structural + information like HTML tags and attributes are stripped from the content. + While this approach works well for paragraphs and inline formatting, it + might be problematic for lists and code blocks. An excerpt: + + ``` + … links , or even code : if (isAwesome) { … } Lists Sometimes you want … + ``` + + - __Context__: for an untrained eye, the result can look like gibberish, as + it's not immediately apparent what classifies as text and what as code. + Furthermore, it's not clear that `Lists` is a headline as it's merged + with the code block before and the paragraph after it. + + - __Punctuation__: inline elements like links that are immediately followed + by punctuation are separated by whitespace (see `,` and `:` in the + excerpt). This is because all extracted text is joined with a whitespace + character during the construction of the search index. + +It's not difficult to see that it can be quite challenging to implement a good +search experience for theme authors, which is why Material for MkDocs (up to +now) did some [monkey patching][9] to be able to render slightly more +meaningful search previews. + +### Search worker + +The actual search functionality is implemented as part of a web worker[^1], +which creates and manages the [lunr][6] search index. When search is +initialized, the following steps are taken: + + [^1]: + Prior to [version 5.0][10], search was carried out in the main thread which + locked up the browser, rendering it unusable. This problem was first + reported in #904 and, after some back and forth, fixed and released in + version 5.0. + +1. __Linking sections with pages__: The search index is parsed, and each section + is linked to its parent page. The parent page itself is _not indexed_, as it + would lead to duplicate results, so only the sections remain. Linking is + necessary, as search results are grouped by page. + +2. __Tokenization__: The `title` and `text` values of each section are split + into tokens by using the [separator][11] as configured in `mkdocs.yml`. + Tokenization itself is carried out by [lunr's default tokenizer][12], which + doesn't allow for lookahead or separators spanning multiple characters. + + > Why is this important and a big deal? We will see later how much more we + > can achieve with a tokenizer that is capable of separating strings with + > lookahead. + +3. __Indexing__: As a final step, each section is indexed. When querying the + index, if a search query includes one of the tokens as returned by step 2., + the section is considered to be part of the search result and passed to the + main thread. + +Now, that's basically how the search worker operates. Sure, there's a little +more magic involved, e.g., search results are [post-processed][13] and +[rescored][14] to account for some shortcomings of [lunr][6], but in general, +this is how data gets into and out of the index. + + [9]: https://github.com/squidfunk/mkdocs-material/blob/ec7ccd2b2d15dd033740f388912f7be7738feec2/src/assets/javascripts/integrations/search/document/index.ts#L68-L71 + [10]: https://squidfunk.github.io/mkdocs-material/upgrading/#upgrading-from-4x-to-5x + [11]: ../../setup/setting-up-site-search.md#separator + [12]: https://github.com/olivernn/lunr.js/blob/aa5a878f62a6bba1e8e5b95714899e17e8150b38/lunr.js#L413-L456 + [13]: https://github.com/squidfunk/mkdocs-material/blob/ec7ccd2b2d15dd033740f388912f7be7738feec2/src/assets/javascripts/integrations/search/_/index.ts#L249-L272 + [14]: https://github.com/squidfunk/mkdocs-material/blob/ec7ccd2b2d15dd033740f388912f7be7738feec2/src/assets/javascripts/integrations/search/_/index.ts#L274-L275 + +### Search previews + +Users should be able to quickly scan and evaluate the relevance of a search +result in the given context, which is why a concise summary with highlighted +occurrences of the search terms found is an essential part of a great search +experience. + +This is where the current search preview generation falls short, as some of the +search previews appear not to include any occurrence of any of the search +terms. This was due to the fact that search previews were [truncated after a +maximum of 320 characters][15], as can be seen here: + +
+ +![Search previews][16] + +
+ +The first two results look like they're not relevant, as they don't seem to +include the query string the user just searched for. Yet, they are. + +
+
+ +A better solution to this problem has been on the roadmap for a very, very long +time, but in order to solve this once and for all, several factors need to be +carefully considered: + +1. __Word boundaries__: some themes[^2] for static site generators generate + search previews by expanding the text left and right next to an occurrence, + stopping at a whitespace character when enough words have been consumed. A + preview might look like this: + + ``` + … channels, e.g., or which can be configured via mkdocs.yml … + ``` + + While this may work for languages that use whitespace as a separator + between words, it breaks down for languages like Japanese or Chinese[^3], + as they have non-whitespace word boundaries and use dedicated segmenters to + split strings into tokens. + + [^2]: + At the time of writing, [Just the Docs][17] and [Docusaurus][18] use this + method for generating search previews. Note that the latter also integrates + with Algolia, which is a fully managed server-based solution. + + [^3]: + China and Japan are both within the top 5 countries of origin of users of + Material for MkDocs. + + [15]: https://github.com/squidfunk/mkdocs-material/blob/master/src/assets/javascripts/templates/search/index.tsx#L90 + [16]: search-better-faster-smaller/search-preview.png + [17]: https://pmarsceill.github.io/just-the-docs/ + [18]: https://github.com/lelouch77/docusaurus-lunr-search + +2. __Context-awareness__: Although whitespace doesn't work for all languages, + one could argue that it could be a good enough solution. Unfortunately, this + is not necessarily true for code blocks, as the removal of whitespace might + change meaning in some languages. + +3. __Structure__: Preserving structural information is not a must, but + apparently beneficial to build more meaningful search previews which allow + for a quick evaluation of relevance. If a word occurrence is part of a code + block, it should be rendered as a code block. + +## What's new? + +After we built a solid understanding of the problem space and before we dive +into the internals of our new search implementation to see which of the +problems it already solves, a quick overview of what features and improvements +it brings: + +- __Better__: support for [rich search previews][19], preserving the structural + information of code blocks, inline code, and lists, so they are rendered + as-is, as well as [lookahead tokenization][20], + [more accurate highlighting][21], and improved stability of typeahead. Also, + a [slightly better UX][22]. +- __Faster__ and __smaller__: significant decrease in search index size of up + to 48% due to improved extraction and construction techniques, resulting in a + search experience that is up to 95% faster, which is particularly helpful for + large documentation projects. + +_Note that our new search implementation is currently 'Insiders only', which +means that it is reserved for sponsors because it's those sponsors that make +features like this possible._ + +[:octicons-heart-fill-24:{ .mdx-heart }   I want to become a sponsor](../../insiders/index.md){ .md-button .md-button--primary } + + [19]: #rich-search-previews + [20]: #tokenizer-lookahead + [21]: #accurate-highlighting + [22]: #user-interface + +### Rich search previews + +As we rebuilt the search plugin from scratch, we reworked the construction of +the search index to preserve the structural information of code blocks, inline +code, as well as unordered and ordered lists. Using the example from the +[search index][23] section, here's how it looks: + +=== "Now" + + ![Search preview now][24] + +=== "Before" + + ![Search preview before][25] + +Now, __code blocks are first-class citizens of search previews__, and even +inline code formatting is preserved. Let's take a look at the new structure of +the search index to understand why: + +??? example "Expand to inspect search index" + + === "Now" + + ``` json + { + ... + "docs": [ + { + "location": "page/", + "title": "Example", + "text": "" + }, + { + "location": "page/#text", + "title": "Text", + "text": "

It's very easy to make some words bold and other words italic with Markdown. You can even add links, or even code:

if (isAwesome){\n  return true\n}\n
" + }, + { + "location": "page/#lists", + "title": "Lists", + "text": "

Sometimes you want numbered lists:

  1. One
  2. Two
  3. Three

Sometimes you want bullet points:

" + } + ] + } + ``` + + === "Before" + + ``` json + { + ... + "docs": [ + { + "location": "page/", + "title": "Example", + "text": "Example Text It's very easy to make some words bold and other words italic with Markdown. You can even add links , or even code : if (isAwesome) { return true } Lists Sometimes you want numbered lists: One Two Three Sometimes you want bullet points: Start a line with a star Profit!" + }, + { + "location": "page/#example", + "title": "Example", + "text": "" + }, + { + "location": "page/#text", + "title": "Text", + "text": "It's very easy to make some words bold and other words italic with Markdown. You can even add links , or even code : if (isAwesome) { return true }" + }, + { + "location": "page/#lists", + "title": "Lists", + "text": "Sometimes you want numbered lists: One Two Three Sometimes you want bullet points: Start a line with a star Profit!" + } + ] + } + ``` + +If we inspect the search index again, we can see how the situation improved: + +1. __Content is included only once__: the search index does not include the + content of the page twice, as only the sections of a page are part of the + search index. This leads to a significant reduction in size, fewer bytes to + transfer, and a smaller search index. + +2. __Some structure is preserved__: each section of the search index includes a + small subset of HTML to provide the necessary structure to allow for more + sophisticated search previews. Revisiting our example from before, let's + look at an excerpt: + + === "Now" + + ``` html + … links, or even code:

if (isAwesome){ … }\n
+ ``` + + === "Before" + + ``` + … links , or even code : if (isAwesome) { … } + ``` + + The punctuation issue is gone, as no additional whitespace is inserted, and + the preserved markup yields additional context to make scanning search + results more effective. + +On to the next step in the process: __tokenization__. + + [23]: #search-index + [24]: search-better-faster-smaller/search-preview-now.png + [25]: search-better-faster-smaller/search-preview-before.png + +### Tokenizer lookahead + +The [default tokenizer][12] of [lunr][6] uses a regular expression to split a +given string by matching each character against the [separator][11] as defined +in `mkdocs.yml`. This doesn't allow for more complex separators based on +lookahead or multiple characters. + +Fortunately, __our new search implementation provides an advanced tokenizer__ +that doesn't have these shortcomings and supports more complex regular +expressions. As a result, Material for MkDocs just changed its own separator +configuration to the following value: + +``` +[\s\-,:!=\[\]()"/]+|(?!\b)(?=[A-Z][a-z])|\.(?!\d)|&[lg]t; +``` + +While the first part up to the first `|` contains a list of single control +characters at which the string should be split, the following three sections +explain the remainder of the regular expression.[^4] + + [^4]: + As a fun fact: the [separator default value][26] of the search plugin being + `[\s\-]+` always has been kind of irritating, as it suggests that multiple + characters can be considered being a separator. However, the `+` is + completely irrelevant, as regular expression groups involving multiple + characters were never supported by [lunr's default tokenizer][12]. + + [26]: https://www.mkdocs.org/user-guide/configuration/#separator + +#### Case changes + +Many programming languages use `PascalCase` or `camelCase` naming conventions. +When a user searches for the term `case`, it's quite natural to expect for +`PascalCase` and `camelCase` to show up. By adding the following match group to +the separator, this can now be achieved with ease: + +``` +(?!\b)(?=[A-Z][a-z]) +``` + +This regular expression is a combination of a negative lookahead (`\b`, i.e., +not a word boundary) and a positive lookahead (`[A-Z][a-z]`, i.e., an uppercase +character followed by a lowercase character), and has the following behavior: + +- `PascalCase` :octicons-arrow-right-24: `Pascal`, `Case` +- `camelCase` :octicons-arrow-right-24: `camel`, `Case` +- `UPPERCASE` :octicons-arrow-right-24: `UPPERCASE` + +Searching for [:octicons-search-24: searchHighlight][27] now brings up the +section discussing the `search.highlight` feature flag, which also demonstrates +that this even works for search queries now![^5] + + [^5]: + Previously, the search query was not correctly tokenized due to the way + [lunr][6] treats wildcards, as it disables the pipeline for search terms + that contain wildcards. In order to provide a good typeahead experience, + Material for MkDocs adds wildcards to the end of each search term not + explicitly preceded with `+` or `-`, effectively disabling tokenization. + + [27]: ?q=searchHighlight + +#### Version numbers + +Indexing version numbers is another problem that can be solved with a small +lookahead. Usually, `.` should be considered a separator to split words like +`search.highlight`. However, splitting version numbers at `.` will make them +undiscoverable. Thus, the following expression: + +``` +\.(?!\d) +``` + +This regular expression matches a `.` only if not immediately followed by a +digit `\d`, which leaves version numbers discoverable. Searching for +[:octicons-search-24: 7.2.6][28] brings up the [7.2.6][29] release notes. + + [28]: ?q=7.2.6 + [29]: ../../changelog.md#726-_-september-1-2021 + +#### HTML/XML tags + +If your documentation includes HTML/XML code examples, you may want to allow +users to find specific tag names. Unfortunately, the `<` and `>` control +characters are encoded in code blocks as `<` and `>`. Now, adding the +following expression to the separator allows for just that: + +``` +&[lg]t; +``` + +Searching for [:octicons-search-24: custom search worker script][30] brings up +the section on [custom search][31] and matches the `script` tag among the other +search terms discovered. + +--- + +_We've only just begun to scratch the surface of the new possibilities +tokenizer lookahead brings. If you found other useful expressions, you're +invited to share them in the comment section._ + + [30]: ?q=custom+search+worker+script + [31]: ../../setup/setting-up-site-search.md#custom-search + +### Accurate highlighting + +Highlighting is the last step in the process of search and involves the +highlighting of all search term occurrences in a given search result. For a +long time, highlighting was implemented through dynamically generated +[regular expressions][32].[^6] + +This approach has some problems with non-whitespace languages like Japanese or +Chinese[^3] since it only works if the highlighted term is at a word boundary. +However, Asian languages are tokenized using a [dedicated segmenter][33], which +cannot be modeled with regular expressions. + + [^6]: + Using the separator as defined in `mkdocs.yml`, a regular expression was + constructed that was trying to mimic the tokenizer. As an example, the + search query `search highlight` was transformed into the rather cumbersome + regular expression `(^|)(search|highlight)`, which only matches + at word boundaries. + +Now, as a direct result of the [new tokenization approach][34], __our new +search implementation uses token positions for highlighting__, making it +exactly as powerful as tokenization: + +1. __Word boundaries__: as the new highlighter uses token positions, word + boundaries are equal to token boundaries. This means that more complex cases + of tokenization (e.g., [case changes][35], [version numbers][36], [HTML/XML + tags][37]), are now all highlighted accurately. + +2. __Context-awareness__: as the new search index preserves some of the + structural information of the original document, the content of a section is + now divided into separate content blocks – paragraphs, code blocks, and + lists. + + Now, only the content blocks that actually contain occurrences of one of + the search terms are considered for inclusion into the search preview. If a + term only occurs in a code block, it's the code block that gets rendered, + see, for example, the results of [:octicons-search-24: twitter][38]. + + [32]: https://github.com/squidfunk/mkdocs-material/blob/ec7ccd2b2d15dd033740f388912f7be7738feec2/src/assets/javascripts/integrations/search/highlighter/index.ts#L61-L91 + [33]: http://chasen.org/~taku/software/TinySegmenter/ + [34]: #tokenizer-lookahead + [35]: #case-changes + [36]: #version-numbers + [37]: #htmlxml-tags + [38]: ?q=twitter + +### Benchmarks + +We conducted two benchmarks – one with the documentation of Material for MkDocs +itself, and one with a very massive corpus of Markdown files with more than +800,000 words – a size most documentation projects will likely never +reach: + +
+ +| | Before | Now | Relative | +| ----------------------- | -------: | -------------: | -----------: | +| __Material for MkDocs__ | | | | +| Index size | 573 kB | __335 kB__ | __–42%__ | +| Index size (`gzip`) | 105 kB | __78 kB__ | __–27%__ | +| Indexing time[^7] | 265 ms | __177 ms__ | __–34%__ | +| __KJV Markdown[^8]__ | | | | +| Index size | 8.2 MB | __4.4 MB__ | __–47%__ | +| Index size (`gzip`) | 2.3 MB | __1.2 MB__ | __–48%__ | +| Indexing time | 2,700 ms | __1,390 ms__ | __–48%__ | + +
+

Benchmark results

+
+ +
+ + [^7]: + Smallest value of ten distinct runs. + + [^8]: + We agnostically use [KJV Markdown][39] as a tool for testing to learn how + Material for MkDocs behaves on large corpora, as it's a very large set of + Markdown files with over 800k words. + +The results show that indexing time, which is the time that it takes to set up +the search when the page is loaded, has dropped by up to 48%, which means __the +new search is up to 95% faster__. This is a significant improvement, +particularly relevant for large documentation projects. + +While 1,3s still may sound like a long time, using the new client-side search +together with [instant loading][40] only creates the search index on the initial +page load. When navigating, the search index is preserved across pages, so the +cost does only have to be paid once. + + [39]: https://github.com/arleym/kjv-markdown + [40]: ../../setup/setting-up-navigation.md#instant-loading + +### User interface + +Additionally, some small improvements have been made, most prominently the +__more results on this page__ button, which now sticks to the top of the search +result list when open. This enables the user to jump out of the list more +quickly. + +## What's next? + +Our new search implementation is a big improvement to Material for MkDocs. It +solves some long-standing issues which needed to be tackled for years. Yet, +it's only the start of a search experience that is going to get better and +better. Next up: + +- __Context-aware search summarization__: currently, the first two matching + content blocks are rendered as a search preview. With the new tokenization + technique, we laid the groundwork for more sophisticated shortening and + summarization methods, which we're tackling next. + +- __User interface improvements__: as we now gained full control over the + search plugin, we can now add meaningful metadata to provide more context and + a better experience. We'll explore some of those paths in the future. + +If you've made it this far, thank you for your time and interest in Material +for MkDocs! This is the first blog article that I decided to write after a +short [Twitter survey][41] made me to. You're invited to [leave a comment][42] +to share your experiences with the new search implementation. + + [41]: https://twitter.com/squidfunk/status/1434477478823743488 + [42]: #__comments diff --git a/docs/blog/2021/search-better-faster-smaller/search-preview-before.png b/docs/blog/2021/search-better-faster-smaller/search-preview-before.png new file mode 100644 index 000000000..7f6fcc8bd Binary files /dev/null and b/docs/blog/2021/search-better-faster-smaller/search-preview-before.png differ diff --git a/docs/blog/2021/search-better-faster-smaller/search-preview-now.png b/docs/blog/2021/search-better-faster-smaller/search-preview-now.png new file mode 100644 index 000000000..093516624 Binary files /dev/null and b/docs/blog/2021/search-better-faster-smaller/search-preview-now.png differ diff --git a/docs/blog/2021/search-better-faster-smaller/search-preview.png b/docs/blog/2021/search-better-faster-smaller/search-preview.png new file mode 100644 index 000000000..7ca97e1cf Binary files /dev/null and b/docs/blog/2021/search-better-faster-smaller/search-preview.png differ diff --git a/docs/blog/index.md b/docs/blog/index.md new file mode 100644 index 000000000..5ff813ee6 --- /dev/null +++ b/docs/blog/index.md @@ -0,0 +1,27 @@ +--- +template: overrides/main.html +search: + exclude: true +--- + +# Blog + +

Search: better, faster, smaller

+ +__This is the story of how we managed to completely rebuild client-side search, +delivering a significantly better user experience while making it faster and +smaller at the same time.__ + +The search of Material for MkDocs is by far one of its best and most-loved +assets: multilingual, offline-capable, and most importantly: _all client-side_. +It provides a solution to empower the users of your documentation to find what +they're searching for instantly without the headache of managing additional +servers. However, even though several iterations have been made, there's still +some room for improvement, which is why we rebuilt the search plugin and +integration from the ground up. This article shines some light on the internals +of the new search, why it's much more powerful than the previous version, and +what's about to come. + +[Continue reading :octicons-arrow-right-24:][1]{ .md-button } + + [1]: 2021/search-better-faster-smaller.md diff --git a/docs/changelog.md b/docs/changelog.md index 8611f8c19..d456ee427 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,6 +6,41 @@ template: overrides/main.html ## Material for MkDocs +### 7.3.0 _ September 23, 2021 + +- Added support for sticky navigation tabs +- Added support for section index pages +- Added support for removing generator notice + +### 7.2.8 _ September 20, 2021 + +- Fixed #3039: Search modal overlays menu on mobile (7.2.7 regression) + +### 7.2.7 _ September 19, 2021 + +- Updated Serbian and Serbo-Croatian translations +- Improved appearance of outline on details +- Fixed #2934: Scrollbar when header is hidden on some mobile browsers +- Fixed #3032: Anchor in details doesn't open on load (7.0.0 regression) +- Fixed back-to-top button being focusable when invisible +- Fixed broken admonition icons (removed in upstream) + +### 7.2.6 _ September 1, 2021 + +- Fixed rendering of `blockquote` elements (7.0.0 regression) +- Fixed #2973: Custom search worker setting ignored + +### 7.2.5 _ August 25, 2021 + +- Updated Portuguese translations +- Fixed execution of RxJS teardown logic (7.2.3 regression) +- Fixed #2970: Search results show escaped characters (7.2.2 regression) + +### 7.2.4 _ August 11, 2021 + +- Fixed #2926: Version selector not working (7.2.3 regression) +- Fixed #2929: Missing CSS class for banner (consistency with Insiders) + ### 7.2.3 _ August 9, 2021 - Slight facelift of data tables, now closer to Material Design diff --git a/docs/getting-started.md b/docs/getting-started.md index d062563df..8ca7810be 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -79,9 +79,22 @@ The following plugins are bundled with the Docker image: The new image can be used exactly like the official image. +!!! info ":material-apple: Apple Silicon (M1) and :fontawesome-brands-raspberry-pi: Raspberry Pi" + + The official Docker image is only available for `linux/amd64`. We recommend + the [third-party image][11] by @afritzler if you want to run Material for + MkDocs via Docker on `arm64` or `armv7`, as it is automatically built on + every release: + + ``` + docker pull ghcr.io/afritzler/mkdocs-material + ``` + + [11]: https://github.com/afritzler/mkdocs-material + ### with git -Material for MkDocs can be directly used from [GitHub][11] by cloning the +Material for MkDocs can be directly used from [GitHub][12] by cloning the repository into a subfolder of your project root which might be useful if you want to use the very latest version: @@ -96,4 +109,4 @@ from `git`, you must install all required dependencies yourself: pip install -r mkdocs-material/requirements.txt ``` - [11]: https://github.com/squidfunk/mkdocs-material + [12]: https://github.com/squidfunk/mkdocs-material diff --git a/docs/insiders/changelog.md b/docs/insiders/changelog.md index 3498fb326..3e5159444 100644 --- a/docs/insiders/changelog.md +++ b/docs/insiders/changelog.md @@ -6,6 +6,35 @@ template: overrides/main.html ## Material for MkDocs Insiders +### 3.0.1 _ September 19, 2021 + +- Added support for using literal `h1-6` tags for search plugin +- Fixed search plugin breaking on void elements without slashes +- Fixed search plugin filtering link contents from headlines +- Fixed search plugin handling of multiple `h1` headlines +- Fixed search plugin handling of missing `h1` headlines + +### 3.0.0 _ September 13, 2021 + +- Rewrite of MkDocs' search plugin +- Added support for rich search previews +- Added support for tokenizer with lookahead +- Improved search indexing performance (twice as fast) +- Improved search highlighting + +### 2.13.3 _ September 1, 2021 + +- Added support for disabling social card generation + +### 2.13.2 _ August 25, 2021 + +- Fixed #2965: Social plugin error when primary color is not defined + +### 2.13.1 _ August 21, 2021 + +- Fixed #2948: Social cards are not cached +- Fixed #2953: Mermaid.js diagrams can't be centered anymore + ### 2.13.0 _ August 7, 2021 - Added support for custom colors in social cards diff --git a/docs/insiders/index.md b/docs/insiders/index.md index 805782d4b..5c4e0e6d9 100644 --- a/docs/insiders/index.md +++ b/docs/insiders/index.md @@ -98,6 +98,26 @@ You can cancel your sponsorship anytime.[^4] [:octicons-heart-fill-24:{ .mdx-heart }   Join our awesome sponsors][5]{ .md-button .md-button--primary .mdx-sponsorship-button } +
+ +
+ +**Special thanks** to our **premium sponsors**: + +[![Hummingbot]](https://hummingbot.io/){ target=_blank } +      +[![Basler]](https://docs.baslerweb.com/){ target=_blank } +   +[![Cirrus CI]](https://cirrus-ci.org/){ target=_blank } + +
+ + [Hummingbot]: https://raw.githubusercontent.com/squidfunk/mkdocs-material/master/.github/assets/sponsors/hummingbot.png + [Basler]: https://raw.githubusercontent.com/squidfunk/mkdocs-material/master/.github/assets/sponsors/basler.png + [Cirrus CI]: https://raw.githubusercontent.com/squidfunk/mkdocs-material/master/.github/assets/sponsors/cirrus-ci.svg + +
+ {% endif %} - Made with - - Material for MkDocs - + {% if not config.extra.generator == false %} + Made with + + Material for MkDocs + + {% endif %} {{ extracopyright }} {% include "partials/social.html" %} diff --git a/material/partials/header.html b/material/partials/header.html index 65db9e3cb..4872af867 100644 --- a/material/partials/header.html +++ b/material/partials/header.html @@ -1,7 +1,11 @@ {#- This file was automatically generated - do not edit -#} -
+{% set class = "md-header" %} +{% if "navigation.tabs.sticky" in features %} + {% set class = class ~ " md-header--lifted" %} +{% endif %} +
+ {% endif %} diff --git a/mkdocs.yml b/mkdocs.yml index 7c4508e7e..7e9711113 100755 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -54,7 +54,7 @@ theme: - content.tabs.link # - header.autohide # - navigation.expand - # - navigation.indexes + - navigation.indexes # - navigation.instant - navigation.sections - navigation.tabs @@ -213,7 +213,11 @@ nav: - Meta tags: reference/meta-tags.md - Variables: reference/variables.md - Insiders: - - Sponsorship: insiders/index.md + - insiders/index.md - Getting started: - Installation: insiders/getting-started.md - Changelog: insiders/changelog.md + - Blog: + - blog/index.md + - 2021: + - blog/2021/search-better-faster-smaller.md diff --git a/package-lock.json b/package-lock.json index 8f8dc2b17..46a719210 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "mkdocs-material", - "version": "7.2.3", + "version": "7.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -331,12 +331,12 @@ } }, "@es-joy/jsdoccomment": { - "version": "0.10.7", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.10.7.tgz", - "integrity": "sha512-aNKZEoMESDzOBjKxCWrFuG50mcpMeKVBnBNko4+IZZ5t9zXYs8GT1KB0ZaOq1YUsKumDRc6YII/TQm309MJ0KQ==", + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.10.8.tgz", + "integrity": "sha512-3P1JiGL4xaR9PoTKUHa2N/LKwa2/eUdRqGwijMWWgBqbFEqJUVpmaOi2TcjcemrsRMgFLBzQCK4ToPhrSVDiFQ==", "dev": true, "requires": { - "comment-parser": "1.2.3", + "comment-parser": "1.2.4", "esquery": "^1.4.0", "jsdoc-type-pratt-parser": "1.1.1" } @@ -388,6 +388,12 @@ "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==", "dev": true }, + "@gar/promisify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz", + "integrity": "sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==", + "dev": true + }, "@humanwhocodes/config-array": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", @@ -406,9 +412,9 @@ "dev": true }, "@mdi/svg": { - "version": "5.9.55", - "resolved": "https://registry.npmjs.org/@mdi/svg/-/svg-5.9.55.tgz", - "integrity": "sha512-gO0ZpKIeCn9vFg46QduK9MM+n1fuCNwSdcdlBTtbafnnuvwLveK2uj+byhdLtg/8VJGXDhp+DJ35QUMbeWeULA==", + "version": "6.1.95", + "resolved": "https://registry.npmjs.org/@mdi/svg/-/svg-6.1.95.tgz", + "integrity": "sha512-X3qD1LWJotRUMnkjuAdAJxb1fqRsa+uWh3UugEM2b66mj7NSC6jhn97W8b/UW/ukhDvdP/jRPrqz+2mJf2jN9w==", "dev": true }, "@mrmlnc/readdir-enhanced": { @@ -447,6 +453,27 @@ "fastq": "^1.6.0" } }, + "@npmcli/fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.0.0.tgz", + "integrity": "sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ==", + "dev": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "@npmcli/git": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", @@ -524,22 +551,21 @@ } }, "@npmcli/run-script": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.5.tgz", - "integrity": "sha512-NQspusBCpTjNwNRFMtz2C5MxoxyzlbuJ4YEhxAKrIonTiirKDtatsZictx9RgamQIx6+QuHMNmPl0wQdoESs9A==", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.6.tgz", + "integrity": "sha512-e42bVZnC6VluBZBAFEr3YrdqSspG3bgilyg4nSLBJ7TRGNCzxHa92XAHxQBLYg0BmgwO4b2mf3h/l5EkEWRn3g==", "dev": true, "requires": { "@npmcli/node-gyp": "^1.0.2", "@npmcli/promise-spawn": "^1.3.2", - "infer-owner": "^1.0.4", "node-gyp": "^7.1.0", "read-package-json-fast": "^2.0.1" } }, "@primer/octicons": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-15.0.0.tgz", - "integrity": "sha512-yRHyp70HLWIETjtTQm+8D+iYG4lUOgmT2eT/wOKXWyYjXXI1fStdewMuuVC0avWncDv46bISY5VRFfa48NzMZw==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-15.2.0.tgz", + "integrity": "sha512-4cHZzcZ3F/HQNL4EKSaFyVsW7XtITiJkTeB1JDDmRuP/XobyWyF9gWxuV9c+byUa8dOB5KNQn37iRvNrIehPUQ==", "dev": true, "requires": { "object-assign": "^4.1.1" @@ -673,6 +699,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "@types/lunr": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz", @@ -701,9 +733,9 @@ "dev": true }, "@types/node": { - "version": "16.4.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.13.tgz", - "integrity": "sha512-bLL69sKtd25w7p1nvg9pigE4gtKVpGTPojBFLMkGHXuUgap2sLqQt2qUnqmVCDfzGUL0DRNZP+1prIZJbMeAXg==", + "version": "16.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.1.tgz", + "integrity": "sha512-4/Z9DMPKFexZj/Gn3LylFgamNKHm4K3QDi0gz9B26Uk0c8izYf97B5fxfpspMNkWlFupblKM/nV8+NA9Ffvr+w==", "dev": true }, "@types/normalize-package-data": { @@ -775,13 +807,13 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.1.tgz", - "integrity": "sha512-AHqIU+SqZZgBEiWOrtN94ldR3ZUABV5dUG94j8Nms9rQnHFc8fvDOue/58K4CFz6r8OtDDc35Pw9NQPWo0Ayrw==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.31.2.tgz", + "integrity": "sha512-w63SCQ4bIwWN/+3FxzpnWrDjQRXVEGiTt9tJTRptRXeFvdZc/wLiz3FQUwNQ2CVoRGI6KUWMNUj/pk63noUfcA==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.29.1", - "@typescript-eslint/scope-manager": "4.29.1", + "@typescript-eslint/experimental-utils": "4.31.2", + "@typescript-eslint/scope-manager": "4.31.2", "debug": "^4.3.1", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.1.0", @@ -801,55 +833,55 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.1.tgz", - "integrity": "sha512-kl6QG6qpzZthfd2bzPNSJB2YcZpNOrP6r9jueXupcZHnL74WiuSjaft7WSu17J9+ae9zTlk0KJMXPUj0daBxMw==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.31.2.tgz", + "integrity": "sha512-3tm2T4nyA970yQ6R3JZV9l0yilE2FedYg8dcXrTar34zC9r6JB7WyBQbpIVongKPlhEMjhQ01qkwrzWy38Bk1Q==", "dev": true, "requires": { "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.29.1", - "@typescript-eslint/types": "4.29.1", - "@typescript-eslint/typescript-estree": "4.29.1", + "@typescript-eslint/scope-manager": "4.31.2", + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/typescript-estree": "4.31.2", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.29.1.tgz", - "integrity": "sha512-3fL5iN20hzX3Q4OkG7QEPFjZV2qsVGiDhEwwh+EkmE/w7oteiOvUNzmpu5eSwGJX/anCryONltJ3WDmAzAoCMg==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.31.2.tgz", + "integrity": "sha512-EcdO0E7M/sv23S/rLvenHkb58l3XhuSZzKf6DBvLgHqOYdL6YFMYVtreGFWirxaU2mS1GYDby3Lyxco7X5+Vjw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.29.1", - "@typescript-eslint/types": "4.29.1", - "@typescript-eslint/typescript-estree": "4.29.1", + "@typescript-eslint/scope-manager": "4.31.2", + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/typescript-estree": "4.31.2", "debug": "^4.3.1" } }, "@typescript-eslint/scope-manager": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.29.1.tgz", - "integrity": "sha512-Hzv/uZOa9zrD/W5mftZa54Jd5Fed3tL6b4HeaOpwVSabJK8CJ+2MkDasnX/XK4rqP5ZTWngK1ZDeCi6EnxPQ7A==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.31.2.tgz", + "integrity": "sha512-2JGwudpFoR/3Czq6mPpE8zBPYdHWFGL6lUNIGolbKQeSNv4EAiHaR5GVDQaLA0FwgcdcMtRk+SBJbFGL7+La5w==", "dev": true, "requires": { - "@typescript-eslint/types": "4.29.1", - "@typescript-eslint/visitor-keys": "4.29.1" + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/visitor-keys": "4.31.2" } }, "@typescript-eslint/types": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.29.1.tgz", - "integrity": "sha512-Jj2yu78IRfw4nlaLtKjVaGaxh/6FhofmQ/j8v3NXmAiKafbIqtAPnKYrf0sbGjKdj0hS316J8WhnGnErbJ4RCA==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.31.2.tgz", + "integrity": "sha512-kWiTTBCTKEdBGrZKwFvOlGNcAsKGJSBc8xLvSjSppFO88AqGxGNYtF36EuEYG6XZ9vT0xX8RNiHbQUKglbSi1w==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.1.tgz", - "integrity": "sha512-lIkkrR9E4lwZkzPiRDNq0xdC3f2iVCUjw/7WPJ4S2Sl6C3nRWkeE1YXCQ0+KsiaQRbpY16jNaokdWnm9aUIsfw==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.31.2.tgz", + "integrity": "sha512-ieBq8U9at6PvaC7/Z6oe8D3czeW5d//Fo1xkF/s9394VR0bg/UaMYPdARiWyKX+lLEjY3w/FNZJxitMsiWv+wA==", "dev": true, "requires": { - "@typescript-eslint/types": "4.29.1", - "@typescript-eslint/visitor-keys": "4.29.1", + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/visitor-keys": "4.31.2", "debug": "^4.3.1", "globby": "^11.0.3", "is-glob": "^4.0.1", @@ -869,12 +901,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.1.tgz", - "integrity": "sha512-zLqtjMoXvgdZY/PG6gqA73V8BjqPs4af1v2kiiETBObp+uC6gRYnJLmJHxC0QyUrrHDLJPIWNYxoBV3wbcRlag==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.31.2.tgz", + "integrity": "sha512-PrBId7EQq2Nibns7dd/ch6S6/M4/iwLM9McbgeEbCXfxdwRUNxJ4UNreJ6Gh3fI2GNKNrWnQxKL7oCPmngKBug==", "dev": true, "requires": { - "@typescript-eslint/types": "4.29.1", + "@typescript-eslint/types": "4.31.2", "eslint-visitor-keys": "^2.0.0" } }, @@ -1046,9 +1078,9 @@ "dev": true }, "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", "dev": true, "requires": { "delegates": "^1.0.0", @@ -1208,23 +1240,67 @@ "dev": true }, "autoprefixer": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.3.1.tgz", - "integrity": "sha512-L8AmtKzdiRyYg7BUXJTzigmhbQRCXFKz6SA1Lqo0+AR2FBbQ4aTAPFSDlOutnFkjhiz8my4agGXog1xlMjPJ6A==", + "version": "10.3.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.3.6.tgz", + "integrity": "sha512-3bDjTfF0MfZntwVCSd18XAT2Zndufh3Mep+mafbzdIQEeWbncVRUVDjH8/EPANV9Hq40seJ24QcYAyhUsFz7gQ==", "dev": true, "requires": { - "browserslist": "^4.16.6", - "caniuse-lite": "^1.0.30001243", - "colorette": "^1.2.2", + "browserslist": "^4.17.1", + "caniuse-lite": "^1.0.30001260", "fraction.js": "^4.1.1", + "nanocolors": "^0.2.8", "normalize-range": "^0.1.2", "postcss-value-parser": "^4.1.0" }, "dependencies": { + "browserslist": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.1.tgz", + "integrity": "sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001259", + "electron-to-chromium": "^1.3.846", + "escalade": "^3.1.1", + "nanocolors": "^0.1.5", + "node-releases": "^1.1.76" + }, + "dependencies": { + "nanocolors": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.12.tgz", + "integrity": "sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ==", + "dev": true + } + } + }, "caniuse-lite": { - "version": "1.0.30001245", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001245.tgz", - "integrity": "sha512-768fM9j1PKXpOCKws6eTo3RHmvTUsG9UrpT4WoREFeZgJBTi4/X9g565azS/rVUGtqb8nt7FjLeF5u4kukERnA==", + "version": "1.0.30001260", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001260.tgz", + "integrity": "sha512-Fhjc/k8725ItmrvW5QomzxLeojewxvqiYCKeFcfFEhut28IVLdpHU19dneOmltZQIE5HNbawj1HYD+1f2bM1Dg==", + "dev": true, + "requires": { + "nanocolors": "^0.1.0" + }, + "dependencies": { + "nanocolors": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.12.tgz", + "integrity": "sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ==", + "dev": true + } + } + }, + "electron-to-chromium": { + "version": "1.3.850", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.850.tgz", + "integrity": "sha512-ZzkDcdzePeF4dhoGZQT77V2CyJOpwfTZEOg4h0x6R/jQhGt/rIRpbRyVreWLtD7B/WsVxo91URm2WxMKR9JQZA==", + "dev": true + }, + "node-releases": { + "version": "1.1.76", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", + "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==", "dev": true } } @@ -1449,11 +1525,12 @@ "dev": true }, "cacache": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz", - "integrity": "sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", "dev": true, "requires": { + "@npmcli/fs": "^1.0.0", "@npmcli/move-file": "^1.0.1", "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -1853,9 +1930,9 @@ "dev": true }, "colord": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.4.0.tgz", - "integrity": "sha512-2306/NeTDOykDwvFQK0ctnP+9I5KQdqVm+IJAM6MsAr4vvy1llAdJyax4YmZoqTxdJ/lvRBwR8MqyJi/tupBAw==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.7.0.tgz", + "integrity": "sha512-pZJBqsHz+pYyw3zpX6ZRXWoCHM1/cvFikY9TV8G3zcejCaKE0lhankoj8iScyrrePA8C7yJ5FStfA9zbcOnw7Q==", "dev": true }, "colorette": { @@ -1886,9 +1963,9 @@ "dev": true }, "comment-parser": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.2.3.tgz", - "integrity": "sha512-vnqDwBSXSsdAkGS5NjwMIPelE47q+UkEgWKHvCDNhVIIaQSUFY6sNnEYGzdoPGMdpV+7KR3ZkRd7oyWIjtuvJg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.2.4.tgz", + "integrity": "sha512-pm0b+qv+CkWNriSTMsfnjChF9kH0kxz55y44Wo5le9qLxMj5xDQAaEd9ZN1ovSuk9CsrncWaFwgpOMg7ClJwkw==", "dev": true }, "component-emitter": { @@ -1939,9 +2016,9 @@ "dev": true }, "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, "cosmiconfig": { @@ -2069,21 +2146,21 @@ "dev": true }, "cssnano": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.7.tgz", - "integrity": "sha512-7C0tbb298hef3rq+TtBbMuezBQ9VrFtrQEsPNuBKNVgWny/67vdRsnq8EoNu7TRjAHURgYvWlRIpCUmcMZkRzw==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.8.tgz", + "integrity": "sha512-Lda7geZU0Yu+RZi2SGpjYuQz4HI4/1Y+BhdD0jL7NXAQ5larCzVn+PUGuZbDMYz904AXXCOgO5L1teSvgu7aFg==", "dev": true, "requires": { - "cssnano-preset-default": "^5.1.3", + "cssnano-preset-default": "^5.1.4", "is-resolvable": "^1.1.0", "lilconfig": "^2.0.3", "yaml": "^1.10.2" } }, "cssnano-preset-default": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.3.tgz", - "integrity": "sha512-qo9tX+t4yAAZ/yagVV3b+QBKeLklQbmgR3wI7mccrDcR+bEk9iHgZN1E7doX68y9ThznLya3RDmR+nc7l6/2WQ==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.4.tgz", + "integrity": "sha512-sPpQNDQBI3R/QsYxQvfB4mXeEcWuw0wGtKtmS5eg8wudyStYMgKOQT39G07EbW1LB56AOYrinRS9f0ig4Y3MhQ==", "dev": true, "requires": { "css-declaration-sorter": "^6.0.3", @@ -2098,7 +2175,7 @@ "postcss-merge-longhand": "^5.0.2", "postcss-merge-rules": "^5.0.2", "postcss-minify-font-values": "^5.0.1", - "postcss-minify-gradients": "^5.0.1", + "postcss-minify-gradients": "^5.0.2", "postcss-minify-params": "^5.0.1", "postcss-minify-selectors": "^5.1.0", "postcss-normalize-charset": "^5.0.1", @@ -2501,10 +2578,140 @@ } }, "esbuild": { - "version": "0.12.19", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.19.tgz", - "integrity": "sha512-5NuT1G6THW7l3fsSCDkcPepn24R0XtyPjKoqKHD8LfhqMXzCdz0mrS9HgO6hIhzVT7zt0T+JGbzCqF5AH8hS9w==", - "dev": true + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.2.tgz", + "integrity": "sha512-/tpIqo45hyRREGqh7hsIut8GwY1X2n9IhKbIwRIXUO6IohzG3/RarSGX7dT2eNvYzIbQmelpX+ZyuIphE5u+Bw==", + "dev": true, + "requires": { + "esbuild-android-arm64": "0.13.2", + "esbuild-darwin-64": "0.13.2", + "esbuild-darwin-arm64": "0.13.2", + "esbuild-freebsd-64": "0.13.2", + "esbuild-freebsd-arm64": "0.13.2", + "esbuild-linux-32": "0.13.2", + "esbuild-linux-64": "0.13.2", + "esbuild-linux-arm": "0.13.2", + "esbuild-linux-arm64": "0.13.2", + "esbuild-linux-mips64le": "0.13.2", + "esbuild-linux-ppc64le": "0.13.2", + "esbuild-openbsd-64": "0.13.2", + "esbuild-sunos-64": "0.13.2", + "esbuild-windows-32": "0.13.2", + "esbuild-windows-64": "0.13.2", + "esbuild-windows-arm64": "0.13.2" + } + }, + "esbuild-android-arm64": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.2.tgz", + "integrity": "sha512-Eh2paXUWYqf5JgikdkC0LnhtjSC8tGAz/L2kJRlMC0o3DzOBIxcmT2fdzBerdhW4roY0bOExfcO6deI1qsxI/A==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.2.tgz", + "integrity": "sha512-jqp6uXHIIAWZ8kxRqFjxyMmIE1cuSbINellwwigOgk44eLg74ls82oqjY72MbDAowPivQkOU/fF7tsyaGQf5Zg==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.2.tgz", + "integrity": "sha512-bD4oAyPZdzOWEA/JoX0sAitOhjJRwhomhWMeyRyowtlVQhQleG2ijRUKTvkq4CAvSobrW5EnZxjvHNKJ5L7zJg==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.2.tgz", + "integrity": "sha512-fFJ0yc3lZyfwca+F5OPN/s+izozWryUQpN8aUMIdUkOa7UKX0h3xXrKnkDgdOo8vy3d1A6zHH0/4f2VJfEzLCg==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.2.tgz", + "integrity": "sha512-DWBZauEfjmqdfWxIacI+KBEim3ulOjtvK+WVm1bX67XlfyUVIkD915OIfT2EBhQUWmv+Z0tZZwskSMNj5DKojw==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.2.tgz", + "integrity": "sha512-Gt2rNqqRCRh1QQC2d83KP0iWIXWQYpns7l2+41a1n0PQxXkQ5AarpjjL9mUzdXtcZauNXbUvWwBKAuBTCW+XQg==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.2.tgz", + "integrity": "sha512-yT0D5Xly8oGHuqq975k1XUyULHzk3fN/ZlTY+awlU+nCFsYPZ43NE5msGpxlNQu8i6KOXQEke5GXN3y5d+Zd4g==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.2.tgz", + "integrity": "sha512-KXeyotqj9jbvCjbSpwnxDE8E8jKoBgrgbJpOvvY5Zz7Pp2fAwu/94vWQtE/jPEJndY4C4MSs+ryJLFWzmLOa4w==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.2.tgz", + "integrity": "sha512-qwXL+3NDCWiC8RMKBBETpuOWdC+pUAUS+pwg9jJmapYblLdVKkyRtwF/ogj06TdYs6riSSNikW8HK/Xs0HHbbQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.2.tgz", + "integrity": "sha512-sx8eheRX2XC2ppNAsbQm8/VUcU8XPYGpJK0BEyRefqHONz6u5Ib2guUdOz2Wh4YlbA7oOd482lHjprXSTwUcrQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.2.tgz", + "integrity": "sha512-y8iZ3qy2TIAKKsZ6xSopCztHOtGW9oiYDl22vQ0UIoVWjnfRKrbSzX7Y2F94y32hSvRWle6OhAIC+UpS5nQmIA==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.2.tgz", + "integrity": "sha512-g6AYrjBeV9OK624bw0KQ1TjHJQSW+X1Yicyd1NvDWqSFpMqKAjw7EUX4tA87mOFqv8BflPGr4f43ySgNvSVzIw==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.2.tgz", + "integrity": "sha512-hIXvFIyrqwFd6v62XSra0ctCUXDS9Tu5D6QYbvnbhEoBmvD/TmEJRYRH48/+xmRifKJLzu6aegcrjAsDmaww7g==", + "dev": true, + "optional": true + }, + "esbuild-windows-32": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.2.tgz", + "integrity": "sha512-Y767LG0NFkw0sPoDVOTKC5gaj4vURjjWfSCCDV5awpXXxBKOF2zsIp3aia4KvVoivoSSeRPk3emDd0OPHuPrKg==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.2.tgz", + "integrity": "sha512-01b59kVJUMasctn6lzswC0drchr7zO75QtF22o5w0nlOw0Zorw0loY/8i5choFuWc30gXJId9qBSc1zPvt7uEw==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.2.tgz", + "integrity": "sha512-HxyY604ytmh8NkPYyS1TdIB/bFS7DWd1hP90e8Ovo/elEdN5I13h0tyIatDYZkXKS0Ztk+9T/3h6K0fI1a/4tQ==", + "dev": true, + "optional": true }, "escalade": { "version": "3.1.1", @@ -2674,9 +2881,9 @@ } }, "eslint-import-resolver-node": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.5.tgz", - "integrity": "sha512-XMoPKjSpXbkeJ7ZZ9icLnJMTY5Mc1kZbCakHquaFsXPpyWOwK0TK6CODO+0ca54UoM9LKOxyUNnoVZRl8TeaAg==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", "dev": true, "requires": { "debug": "^3.2.7", @@ -2726,26 +2933,26 @@ } }, "eslint-plugin-import": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.0.tgz", - "integrity": "sha512-Kc6xqT9hiYi2cgybOc0I2vC9OgAYga5o/rAFinam/yF/t5uBqxQbauNPMC6fgb640T/89P0gFoO27FOilJ/Cqg==", + "version": "2.24.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz", + "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", "dev": true, "requires": { "array-includes": "^3.1.3", "array.prototype.flat": "^1.2.4", "debug": "^2.6.9", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.5", + "eslint-import-resolver-node": "^0.3.6", "eslint-module-utils": "^2.6.2", "find-up": "^2.0.0", "has": "^1.0.3", - "is-core-module": "^2.4.0", + "is-core-module": "^2.6.0", "minimatch": "^3.0.4", - "object.values": "^1.1.3", + "object.values": "^1.1.4", "pkg-up": "^2.0.0", "read-pkg-up": "^3.0.0", "resolve": "^1.20.0", - "tsconfig-paths": "^3.9.0" + "tsconfig-paths": "^3.11.0" }, "dependencies": { "debug": { @@ -2767,9 +2974,9 @@ } }, "is-core-module": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz", - "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", + "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", "dev": true, "requires": { "has": "^1.0.3" @@ -2784,13 +2991,13 @@ } }, "eslint-plugin-jsdoc": { - "version": "36.0.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-36.0.6.tgz", - "integrity": "sha512-vOm27rI2SMfi1bOAYmzzGkanMCD/boquKwvN5ECi8EF9ASsXJwlnCzYtiOYpsDpbC2+6JXEHAmWMkqYNA3BWRw==", + "version": "36.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-36.1.0.tgz", + "integrity": "sha512-Qpied2AJCQcScxfzTObLKRiP5QgLXjMU/ITjBagEV5p2Q/HpumD1EQtazdRYdjDSwPmXhwOl2yquwOGQ4HOJNw==", "dev": true, "requires": { - "@es-joy/jsdoccomment": "0.10.7", - "comment-parser": "1.2.3", + "@es-joy/jsdoccomment": "0.10.8", + "comment-parser": "1.2.4", "debug": "^4.3.2", "esquery": "^1.4.0", "jsdoc-type-pratt-parser": "^1.1.1", @@ -3740,30 +3947,12 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", - "dev": true - }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, - "hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", - "dev": true - }, - "hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", - "dev": true - }, "html-minifier": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", @@ -4103,28 +4292,6 @@ "ci-info": "^2.0.0" } }, - "is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "dev": true, - "requires": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" - }, - "dependencies": { - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true - } - } - }, "is-core-module": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", @@ -4498,12 +4665,12 @@ "dev": true }, "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "^1.2.0" } }, "jsonlines": { @@ -4788,9 +4955,9 @@ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" }, "lunr-languages": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/lunr-languages/-/lunr-languages-1.8.0.tgz", - "integrity": "sha512-oUwn4suj9A12nSs0ZBbu5GlqR0jzUunaTiDG0wS4gXTVBT5UjEwqm+O5U9sinwmC8kJtZAQSnxlzP/xu/mQxCA==" + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/lunr-languages/-/lunr-languages-1.9.0.tgz", + "integrity": "sha512-Be5vFuc8NAheOIjviCRms3ZqFFBlzns3u9DXpPSZvALetgnydAN0poV71pVLFn0keYy/s4VblMMkqewTLe+KPg==" }, "make-dir": { "version": "3.1.0", @@ -4816,9 +4983,9 @@ "dev": true }, "make-fetch-happen": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.0.4.tgz", - "integrity": "sha512-sQWNKMYqSmbAGXqJg2jZ+PmHh5JAybvwu0xM8mZR/bsTjGiTASj3ldXJV7KFHy1k/IJIBkjxQFoWIVsv9+PQMg==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", "dev": true, "requires": { "agentkeepalive": "^4.1.3", @@ -4835,7 +5002,7 @@ "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.2", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^5.0.0", + "socks-proxy-agent": "^6.0.0", "ssri": "^8.0.0" } }, @@ -5229,9 +5396,9 @@ } }, "minipass-fetch": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.3.4.tgz", - "integrity": "sha512-TielGogIzbUEtd1LsjZFs47RWuHHfhl6TiCx1InVxApBAmQ8bL0dL5ilkLGcRvuyW/A9nE+Lvn855Ewz8S0PnQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", "dev": true, "requires": { "encoding": "^0.1.12", @@ -5329,12 +5496,21 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "nanocolors": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.2.8.tgz", + "integrity": "sha512-lFtwopLYUwJ/w+VcBib+Ti1J+USzc/m2XN9LCCl/AhibkzYlXk6FHeqkJqBWRd70RpjArGWYZQjwa6UgwYycBA==", "dev": true }, + "nanoid": { + "version": "3.1.26", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.26.tgz", + "integrity": "sha512-f2KGmBOi1LcVIg7mz8tBR5Ma1cu+gnMk3dS13Ig5gAUbp4Ia5Zpci6KvNgy4nJMN9jR1ELw/YU7X3dRzE18aQQ==", + "dev": true, + "requires": { + "nanocolors": "^0.2.6" + } + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -5474,12 +5650,12 @@ } }, "npm-check-updates": { - "version": "11.8.3", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-11.8.3.tgz", - "integrity": "sha512-NslIB6Af7GagVrN+bvBkObLyawIZfOnDnl8n9MHE+dFt0aChRYtvR6T2BLJKzOPIepCLmmh0NRR/qha0ExAELQ==", + "version": "11.8.5", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-11.8.5.tgz", + "integrity": "sha512-IYSHjlWe8UEugDy7X0qjBeJwcni4DlcWdBK4QQEbwgkNlEDlXyd4yQJYWFumKaJzrp/n5/EcvaboXsBD1Er/pw==", "dev": true, "requires": { - "chalk": "^4.1.1", + "chalk": "^4.1.2", "cint": "^8.2.1", "cli-table": "^0.3.6", "commander": "^6.2.1", @@ -5495,7 +5671,7 @@ "lodash": "^4.17.21", "minimatch": "^3.0.4", "p-map": "^4.0.0", - "pacote": "^11.3.4", + "pacote": "^11.3.5", "parse-github-url": "^1.0.2", "progress": "^2.0.3", "prompts": "^2.4.1", @@ -5782,9 +5958,9 @@ } }, "nth-check": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", - "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", "dev": true, "requires": { "boolbase": "^1.0.0" @@ -6184,9 +6360,9 @@ "dev": true }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-type": { @@ -6244,13 +6420,13 @@ "dev": true }, "postcss": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", - "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", + "version": "8.3.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.8.tgz", + "integrity": "sha512-GT5bTjjZnwDifajzczOC+r3FI3Cu+PgPvrsjhQdRqa2kTJ4968/X9CUce9xttIB0xOs5c6xf0TCWZo/y9lF6bA==", "dev": true, "requires": { - "colorette": "^1.2.2", - "nanoid": "^3.1.23", + "nanocolors": "^0.2.2", + "nanoid": "^3.1.25", "source-map-js": "^0.6.2" } }, @@ -6407,15 +6583,6 @@ "domelementtype": "^2.0.1", "domhandler": "^4.0.0" } - }, - "nth-check": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", - "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", - "dev": true, - "requires": { - "boolbase": "^1.0.0" - } } } }, @@ -6636,13 +6803,13 @@ } }, "postcss-minify-gradients": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.1.tgz", - "integrity": "sha512-odOwBFAIn2wIv+XYRpoN2hUV3pPQlgbJ10XeXPq8UY2N+9ZG42xu45lTn/g9zZ+d70NKSQD6EOi6UiCMu3FN7g==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.2.tgz", + "integrity": "sha512-7Do9JP+wqSD6Prittitt2zDLrfzP9pqKs2EcLX7HJYxsxCOwrrcLt4x/ctQTsiOw+/8HYotAoqNkrzItL19SdQ==", "dev": true, "requires": { + "colord": "^2.6", "cssnano-utils": "^2.0.1", - "is-color-stop": "^1.1.0", "postcss-value-parser": "^4.1.0" } }, @@ -7408,18 +7575,6 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, - "rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", - "dev": true - }, - "rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", - "dev": true - }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -7475,9 +7630,9 @@ "dev": true }, "sass": { - "version": "1.37.5", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.37.5.tgz", - "integrity": "sha512-Cx3ewxz9QB/ErnVIiWg2cH0kiYZ0FPvheDTVC6BsiEGBTZKKZJ1Gq5Kq6jy3PKtL6+EJ8NIoaBW/RSd2R6cZOA==", + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.42.1.tgz", + "integrity": "sha512-/zvGoN8B7dspKc5mC6HlaygyCBRvnyzzgD5khiaCfglWztY99cYoiTUksVx11NlnemrcfH5CEaCpsUKoW0cQqg==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0" @@ -7626,9 +7781,9 @@ } }, "smart-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true }, "snapdragon": { @@ -7776,14 +7931,14 @@ } }, "socks-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", - "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.0.0.tgz", + "integrity": "sha512-FIgZbQWlnjVEQvMkylz64/rUggGtrKstPnx8OZyYFG0tAFR8CSBtpXxSwbFLHyeXFn/cunFL7MpuSOvDSOPo9g==", "dev": true, "requires": { "agent-base": "^6.0.2", - "debug": "4", - "socks": "^2.3.3" + "debug": "^4.3.1", + "socks": "^2.6.1" } }, "source-map": { @@ -9056,9 +9211,9 @@ } }, "stylelint-scss": { - "version": "3.20.1", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.20.1.tgz", - "integrity": "sha512-OTd55O1TTAC5nGKkVmUDLpz53LlK39R3MImv1CfuvsK7/qugktqiZAeQLuuC4UBhzxCnsc7fp9u/gfRZwFAIkA==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.21.0.tgz", + "integrity": "sha512-CMI2wSHL+XVlNExpauy/+DbUcB/oUZLARDtMIXkpV/5yd8nthzylYd1cdHeDMJVBXeYHldsnebUX6MoV5zPW4A==", "dev": true, "requires": { "lodash": "^4.17.15", @@ -9130,9 +9285,9 @@ } }, "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -9218,9 +9373,9 @@ } }, "tar": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.2.tgz", - "integrity": "sha512-EwKEgqJ7nJoS+s8QfLYVGMDmAsj+StbI2AM/RTHeUSsOw6Z8bwNBRv5z3CY0m7laC5qUAqruLX5AhMuc5deY3Q==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "requires": { "chownr": "^2.0.0", @@ -9360,9 +9515,9 @@ "dev": true }, "ts-node": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.0.tgz", - "integrity": "sha512-FstYHtQz6isj8rBtYMN4bZdnXN1vq4HCbqn9vdNQcInRqtB86PePJQIxE6es0PhxKWhj2PHuwbG40H+bxkZPmg==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", + "integrity": "sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.6.1", @@ -9388,12 +9543,13 @@ } }, "tsconfig-paths": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz", - "integrity": "sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz", + "integrity": "sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA==", "dev": true, "requires": { - "json5": "^2.2.0", + "@types/json5": "^0.0.29", + "json5": "^1.0.1", "minimist": "^1.2.0", "strip-bom": "^3.0.0" } @@ -9453,9 +9609,9 @@ } }, "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true }, "uglify-js": { @@ -9817,6 +9973,14 @@ "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + } } }, "vfile": { diff --git a/package.json b/package.json index 67ed56018..072be3e39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mkdocs-material", - "version": "7.2.3", + "version": "7.3.0", "description": "A Material Design theme for MkDocs", "keywords": [ "mkdocs", @@ -27,9 +27,11 @@ "build": "rimraf material && ts-node -T tools/build --optimize", "build:dirty": "ts-node -T tools/build --dirty", "clean": "rimraf material", - "lint": "run-p lint:*", - "lint:scss": "stylelint \"src/assets/**/*.scss\"", - "lint:ts": "eslint --cache \"src/**/*.ts\"", + "check": "run-p check:*", + "check:build": "tsc --noEmit", + "check:style": "run-p check:style:*", + "check:style:scss": "stylelint \"src/**/*.scss\"", + "check:style:ts": "eslint --cache \"src/**/*.ts\"", "start": "ts-node -T tools/build --verbose --dirty --watch", "upgrade": "run-s upgrade:*", "upgrade:bump": "ncu --upgrade --filterVersion \"/^\\^/\"", @@ -41,53 +43,53 @@ "focus-visible": "^5.2.0", "fuzzaldrin-plus": "^0.6.0", "lunr": "^2.3.9", - "lunr-languages": "^1.8.0", + "lunr-languages": "^1.9.0", "rxjs": "^7.3.0" }, "devDependencies": { "@fortawesome/fontawesome-free": "^5.15.4", - "@mdi/svg": "^5.9.55", - "@primer/octicons": "^15.0.0", + "@mdi/svg": "^6.1.95", + "@primer/octicons": "^15.2.0", "@types/clipboard": "^2.0.7", "@types/escape-html": "1.0.1", "@types/fuzzaldrin-plus": "^0.6.2", "@types/html-minifier": "^4.0.1", "@types/lunr": "^2.3.4", - "@types/node": "^16.4.13", + "@types/node": "^16.10.1", "@types/resize-observer-browser": "^0.1.6", "@types/sass": "^1.16.1", - "@typescript-eslint/eslint-plugin": "^4.29.1", - "@typescript-eslint/parser": "^4.29.1", - "autoprefixer": "^10.3.1", + "@typescript-eslint/eslint-plugin": "^4.31.2", + "@typescript-eslint/parser": "^4.31.2", + "autoprefixer": "^10.3.6", "chokidar": "^3.5.2", - "cssnano": "^5.0.7", - "esbuild": "^0.12.19", + "cssnano": "^5.0.8", + "esbuild": "^0.13.2", "eslint": "^7.32.0", "eslint-plugin-eslint-comments": "^3.2.0", - "eslint-plugin-import": "^2.24.0", - "eslint-plugin-jsdoc": "^36.0.6", + "eslint-plugin-import": "^2.24.2", + "eslint-plugin-jsdoc": "^36.1.0", "eslint-plugin-no-null": "^1.0.2", "github-types": "^1.0.0", "gitlab": "^14.2.2", "html-minifier": "^4.0.0", "material-design-color": "^2.3.2", "material-shadows": "^3.0.1", - "npm-check-updates": "^11.8.3", + "npm-check-updates": "^11.8.5", "npm-run-all": "^4.1.5", - "postcss": "^8.3.6", + "postcss": "^8.3.8", "postcss-inline-svg": "^5.0.0", "preact": "^10.5.14", "rimraf": "^3.0.2", - "sass": "^1.37.5", + "sass": "^1.42.1", "stylelint": "^13.13.1", "stylelint-config-rational-order": "^0.1.2", "stylelint-config-recommended": "^5.0.0", "stylelint-config-standard": "^22.0.0", - "stylelint-scss": "^3.20.1", - "svgo": "^2.3.1", + "stylelint-scss": "^3.21.0", + "svgo": "2.3.1", "tiny-glob": "^0.2.9", - "ts-node": "^10.2.0", - "typescript": "^4.3.5" + "ts-node": "^10.2.1", + "typescript": "^4.4.3" }, "engines": { "node": ">= 14" diff --git a/src/assets/javascripts/_/index.ts b/src/assets/javascripts/_/index.ts index 8e8712a2b..b00cda9ce 100644 --- a/src/assets/javascripts/_/index.ts +++ b/src/assets/javascripts/_/index.ts @@ -33,8 +33,10 @@ export type Flag = | "header.autohide" /* Hide header */ | "navigation.expand" /* Automatic expansion */ | "navigation.instant" /* Instant loading */ - | "navigation.sections" /* Sections navigation */ + | "navigation.indexes" /* Section pages */ + | "navigation.sections" /* Section navigation */ | "navigation.tabs" /* Tabs navigation */ + | "navigation.tabs.sticky" /* Tabs navigation (sticky) */ | "navigation.top" /* Back-to-top button */ | "search.highlight" /* Search highlighting */ | "search.share" /* Search sharing */ @@ -96,7 +98,7 @@ export interface Config { */ const script = getElementOrThrow("#__config") const config: Config = JSON.parse(script.textContent!) -config.base = new URL(config.base, getLocation()).toString() +config.base = `${new URL(config.base, getLocation())}` /* ---------------------------------------------------------------------------- * Functions diff --git a/src/assets/javascripts/browser/element/_/index.ts b/src/assets/javascripts/browser/element/_/index.ts index 48b3f805d..5fc98e69b 100644 --- a/src/assets/javascripts/browser/element/_/index.ts +++ b/src/assets/javascripts/browser/element/_/index.ts @@ -36,7 +36,7 @@ */ export function getElement( selector: T, node?: ParentNode -): HTMLElementTagNameMap[T] +): HTMLElementTagNameMap[T] | undefined export function getElement( selector: string, node?: ParentNode @@ -74,6 +74,8 @@ export function getElementOrThrow( throw new ReferenceError( `Missing element: expected "${selector}" to be present` ) + + /* Return element */ return el } @@ -114,21 +116,6 @@ export function getElements( /* ------------------------------------------------------------------------- */ -/** - * Create an element - * - * @template T - Tag name type - * - * @param tagName - Tag name - * - * @returns Element - */ -export function createElement( - tagName: T -): HTMLElementTagNameMap[T] { - return document.createElement(tagName) -} - /** * Replace an element with the given list of nodes * diff --git a/src/assets/javascripts/browser/location/hash/index.ts b/src/assets/javascripts/browser/location/hash/index.ts index 3e0212684..0118f243d 100644 --- a/src/assets/javascripts/browser/location/hash/index.ts +++ b/src/assets/javascripts/browser/location/hash/index.ts @@ -20,10 +20,16 @@ * IN THE SOFTWARE. */ -import { Observable, fromEvent, of } from "rxjs" -import { filter, map, share, startWith, switchMap } from "rxjs/operators" +import { Observable, fromEvent } from "rxjs" +import { + filter, + map, + shareReplay, + startWith +} from "rxjs/operators" -import { createElement, getElement } from "~/browser" +import { getElement } from "~/browser" +import { h } from "~/utilities" /* ---------------------------------------------------------------------------- * Functions @@ -49,8 +55,7 @@ export function getLocationHash(): string { * @param hash - Location hash */ export function setLocationHash(hash: string): void { - const el = createElement("a") - el.href = hash + const el = h("a", { href: hash }) el.addEventListener("click", ev => ev.stopPropagation()) el.click() } @@ -68,7 +73,7 @@ export function watchLocationHash(): Observable { map(getLocationHash), startWith(getLocationHash()), filter(hash => hash.length > 0), - share() + shareReplay(1) ) } @@ -80,6 +85,7 @@ export function watchLocationHash(): Observable { export function watchLocationTarget(): Observable { return watchLocationHash() .pipe( - switchMap(id => of(getElement(`[id="${id}"]`)!)) + map(id => getElement(`[id="${id}"]`)!), + filter(el => typeof el !== "undefined") ) } diff --git a/src/assets/javascripts/components/_/index.ts b/src/assets/javascripts/components/_/index.ts index bb79dc394..375372fc2 100644 --- a/src/assets/javascripts/components/_/index.ts +++ b/src/assets/javascripts/components/_/index.ts @@ -27,7 +27,7 @@ import { getElementOrThrow, getElements } from "~/browser" * ------------------------------------------------------------------------- */ /** - * Component + * Component type */ export type ComponentType = | "announce" /* Announcement bar */ @@ -52,7 +52,7 @@ export type ComponentType = | "top" /* Back-to-top button */ /** - * A component + * Component * * @template T - Component type * @template U - Reference type diff --git a/src/assets/javascripts/components/content/code/index.ts b/src/assets/javascripts/components/content/code/index.ts index e01963071..142eb520d 100644 --- a/src/assets/javascripts/components/content/code/index.ts +++ b/src/assets/javascripts/components/content/code/index.ts @@ -31,6 +31,7 @@ import { } from "rxjs" import { distinctUntilKeyChanged, + finalize, map, switchMap, tap, @@ -175,7 +176,8 @@ export function mountCodeBlock( /* Create and return component */ return watchCodeBlock(el, options) .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), map(state => ({ ref: el, ...state })) ) } diff --git a/src/assets/javascripts/components/content/details/index.ts b/src/assets/javascripts/components/content/details/index.ts index 5032ceaac..94fc6b021 100644 --- a/src/assets/javascripts/components/content/details/index.ts +++ b/src/assets/javascripts/components/content/details/index.ts @@ -23,6 +23,7 @@ import { Observable, Subject } from "rxjs" import { filter, + finalize, map, mapTo, mergeWith, @@ -38,7 +39,9 @@ import { Component } from "../../_" /** * Details */ -export interface Details {} +export interface Details { + scroll?: boolean /* Scroll into view */ +} /* ---------------------------------------------------------------------------- * Helper types @@ -79,8 +82,8 @@ export function watchDetails( .pipe( map(target => target.closest("details:not([open])")!), filter(details => el === details), - mergeWith(print$), - mapTo(el) + mapTo({ scroll: true }), + mergeWith(print$.pipe(mapTo({}))) ) } @@ -99,15 +102,17 @@ export function mountDetails( el: HTMLDetailsElement, options: MountOptions ): Observable> { const internal$ = new Subject
() - internal$.subscribe(() => { + internal$.subscribe(({ scroll }) => { el.setAttribute("open", "") - el.scrollIntoView() + if (scroll) + el.scrollIntoView() }) /* Create and return component */ return watchDetails(el, options) .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), mapTo({ ref: el }) ) } diff --git a/src/assets/javascripts/components/content/table/index.ts b/src/assets/javascripts/components/content/table/index.ts index 6b0f53d35..0066f3380 100644 --- a/src/assets/javascripts/components/content/table/index.ts +++ b/src/assets/javascripts/components/content/table/index.ts @@ -22,8 +22,9 @@ import { Observable, of } from "rxjs" -import { createElement, replaceElement } from "~/browser" +import { replaceElement } from "~/browser" import { renderTable } from "~/templates" +import { h } from "~/utilities" import { Component } from "../../_" @@ -43,7 +44,7 @@ export interface DataTable {} /** * Sentinel for replacement */ -const sentinel = createElement("table") +const sentinel = h("table") /* ---------------------------------------------------------------------------- * Functions diff --git a/src/assets/javascripts/components/dialog/index.ts b/src/assets/javascripts/components/dialog/index.ts index 52b2f5f18..bad326144 100644 --- a/src/assets/javascripts/components/dialog/index.ts +++ b/src/assets/javascripts/components/dialog/index.ts @@ -29,6 +29,7 @@ import { } from "rxjs" import { delay, + finalize, map, observeOn, switchMap, @@ -131,7 +132,8 @@ export function mountDialog( /* Create and return component */ return watchDialog(el, options) .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), map(state => ({ ref: el, ...state })) ) } diff --git a/src/assets/javascripts/components/header/title/index.ts b/src/assets/javascripts/components/header/title/index.ts index 471a3ea75..982836021 100644 --- a/src/assets/javascripts/components/header/title/index.ts +++ b/src/assets/javascripts/components/header/title/index.ts @@ -28,6 +28,7 @@ import { } from "rxjs" import { distinctUntilKeyChanged, + finalize, map, observeOn, tap @@ -139,7 +140,8 @@ export function mountHeaderTitle( /* Create and return component */ return watchHeaderTitle(headline, options) .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), map(state => ({ ref: el, ...state })) ) } diff --git a/src/assets/javascripts/components/palette/index.ts b/src/assets/javascripts/components/palette/index.ts index 93ff47acd..100377f90 100644 --- a/src/assets/javascripts/components/palette/index.ts +++ b/src/assets/javascripts/components/palette/index.ts @@ -27,6 +27,7 @@ import { of } from "rxjs" import { + finalize, map, mapTo, mergeMap, @@ -140,7 +141,8 @@ export function mountPalette( const inputs = getElements("input", el) return watchPalette(inputs) .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), map(state => ({ ref: el, ...state })) ) } diff --git a/src/assets/javascripts/components/search/_/index.ts b/src/assets/javascripts/components/search/_/index.ts index 923c74242..43d93836d 100644 --- a/src/assets/javascripts/components/search/_/index.ts +++ b/src/assets/javascripts/components/search/_/index.ts @@ -95,7 +95,8 @@ export function mountSearch( ): Observable> { const config = configuration() try { - const worker = setupSearchWorker(config.search, index$) + const url = __search?.worker || config.search + const worker = setupSearchWorker(url, index$) /* Retrieve query and result components */ const query = getComponentElement("search-query", el) @@ -212,7 +213,7 @@ export function mountSearch( /* Search sharing */ ...getComponentElements("search-share", el) - .map(child => mountSearchShare(child, { query$ })), + .map(child => mountSearchShare(child, { query$ })), /* Search suggestions */ ...getComponentElements("search-suggest", el) diff --git a/src/assets/javascripts/components/search/highlight/index.ts b/src/assets/javascripts/components/search/highlight/index.ts index 151fe2321..b54deea83 100644 --- a/src/assets/javascripts/components/search/highlight/index.ts +++ b/src/assets/javascripts/components/search/highlight/index.ts @@ -83,7 +83,7 @@ export function mountSearchHiglight( ) ]) .pipe( - map(([index, url]) => setupSearchHighlighter(index.config)( + map(([index, url]) => setupSearchHighlighter(index.config, true)( url.searchParams.get("h")! )), map(fn => { diff --git a/src/assets/javascripts/components/search/query/index.ts b/src/assets/javascripts/components/search/query/index.ts index 3fa0c86d9..0241983d8 100644 --- a/src/assets/javascripts/components/search/query/index.ts +++ b/src/assets/javascripts/components/search/query/index.ts @@ -32,6 +32,7 @@ import { distinctUntilChanged, distinctUntilKeyChanged, filter, + finalize, map, take, takeLast, @@ -172,7 +173,8 @@ export function mountSearchQuery( /* Create and return component */ return watchSearchQuery(el, { tx$, rx$ }) .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), map(state => ({ ref: el, ...state })) ) } diff --git a/src/assets/javascripts/components/search/result/index.ts b/src/assets/javascripts/components/search/result/index.ts index 053866984..4b91e3aa3 100644 --- a/src/assets/javascripts/components/search/result/index.ts +++ b/src/assets/javascripts/components/search/result/index.ts @@ -30,6 +30,7 @@ import { import { bufferCount, filter, + finalize, map, observeOn, switchMap, @@ -152,7 +153,8 @@ export function mountSearchResult( /* Create and return component */ return result$ .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), map(state => ({ ref: el, ...state })) ) } diff --git a/src/assets/javascripts/components/search/share/index.ts b/src/assets/javascripts/components/search/share/index.ts index b408cb13b..67056b77c 100644 --- a/src/assets/javascripts/components/search/share/index.ts +++ b/src/assets/javascripts/components/search/share/index.ts @@ -25,7 +25,11 @@ import { Subject, fromEvent } from "rxjs" -import { map, tap } from "rxjs/operators" +import { + finalize, + map, + tap +} from "rxjs/operators" import { getLocation } from "~/browser" @@ -112,7 +116,8 @@ export function mountSearchShare( /* Create and return component */ return watchSearchShare(el, options) .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), map(state => ({ ref: el, ...state })) ) } diff --git a/src/assets/javascripts/components/search/suggest/index.ts b/src/assets/javascripts/components/search/suggest/index.ts index 070f98b28..b743e5479 100644 --- a/src/assets/javascripts/components/search/suggest/index.ts +++ b/src/assets/javascripts/components/search/suggest/index.ts @@ -30,6 +30,7 @@ import { combineLatestWith, distinctUntilChanged, filter, + finalize, map, observeOn, tap @@ -144,7 +145,8 @@ export function mountSearchSuggest( /* Create and return component */ return result$ .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), map(() => ({ ref: el })) ) } diff --git a/src/assets/javascripts/components/sidebar/index.ts b/src/assets/javascripts/components/sidebar/index.ts index 355a9638c..5be71632c 100644 --- a/src/assets/javascripts/components/sidebar/index.ts +++ b/src/assets/javascripts/components/sidebar/index.ts @@ -28,6 +28,7 @@ import { } from "rxjs" import { distinctUntilChanged, + finalize, map, observeOn, tap, @@ -157,7 +158,8 @@ export function mountSidebar( /* Create and return component */ return watchSidebar(el, options) .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), map(state => ({ ref: el, ...state })) ) } diff --git a/src/assets/javascripts/components/source/_/index.ts b/src/assets/javascripts/components/source/_/index.ts index ce70d1c77..d99549f03 100644 --- a/src/assets/javascripts/components/source/_/index.ts +++ b/src/assets/javascripts/components/source/_/index.ts @@ -24,6 +24,7 @@ import { NEVER, Observable, Subject, defer, of } from "rxjs" import { catchError, filter, + finalize, map, shareReplay, tap @@ -117,7 +118,8 @@ export function mountSource( /* Create and return component */ return watchSource(el) .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), map(state => ({ ref: el, ...state })) ) } diff --git a/src/assets/javascripts/components/tabs/index.ts b/src/assets/javascripts/components/tabs/index.ts index 6f39e612d..d925e12e6 100644 --- a/src/assets/javascripts/components/tabs/index.ts +++ b/src/assets/javascripts/components/tabs/index.ts @@ -20,15 +20,22 @@ * IN THE SOFTWARE. */ -import { Observable, Subject, animationFrameScheduler } from "rxjs" +import { + Observable, + Subject, + animationFrameScheduler, + of +} from "rxjs" import { distinctUntilKeyChanged, + finalize, map, observeOn, switchMap, tap } from "rxjs/operators" +import { feature } from "~/_" import { resetTabsState, setTabsState } from "~/actions" import { Viewport, @@ -133,9 +140,14 @@ export function mountTabs( }) /* Create and return component */ - return watchTabs(el, options) + return ( + feature("navigation.tabs.sticky") + ? of({ hidden: false }) + : watchTabs(el, options) + ) .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), map(state => ({ ref: el, ...state })) ) } diff --git a/src/assets/javascripts/components/toc/index.ts b/src/assets/javascripts/components/toc/index.ts index 031bbb813..6969d70e8 100644 --- a/src/assets/javascripts/components/toc/index.ts +++ b/src/assets/javascripts/components/toc/index.ts @@ -30,6 +30,7 @@ import { bufferCount, distinctUntilChanged, distinctUntilKeyChanged, + finalize, map, observeOn, scan, @@ -267,7 +268,8 @@ export function mountTableOfContents( const anchors = getElements("[href^=\\#]", el) return watchTableOfContents(anchors, options) .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), map(state => ({ ref: el, ...state })) ) } diff --git a/src/assets/javascripts/components/top/index.ts b/src/assets/javascripts/components/top/index.ts index 1aad9615e..9b317f04f 100644 --- a/src/assets/javascripts/components/top/index.ts +++ b/src/assets/javascripts/components/top/index.ts @@ -30,6 +30,7 @@ import { bufferCount, distinctUntilChanged, distinctUntilKeyChanged, + finalize, map, observeOn, tap, @@ -39,8 +40,10 @@ import { import { resetBackToTopOffset, resetBackToTopState, + resetFocusable, setBackToTopOffset, - setBackToTopState + setBackToTopState, + setFocusable } from "~/actions" import { Viewport, setElementFocus } from "~/browser" @@ -155,8 +158,10 @@ export function mountBackToTop( if (hidden) { setBackToTopState(el, "hidden") setElementFocus(el, false) + setFocusable(el, -1) } else { resetBackToTopState(el) + resetFocusable(el) } }, @@ -164,13 +169,15 @@ export function mountBackToTop( complete() { resetBackToTopOffset(el) resetBackToTopState(el) + resetFocusable(el) } }) /* Create and return component */ return watchBackToTop(el, { viewport$, header$, main$ }) .pipe( - tap(internal$), + tap(state => internal$.next(state)), + finalize(() => internal$.complete()), map(state => ({ ref: el, ...state })) ) } diff --git a/src/assets/javascripts/integrations/instant/index.ts b/src/assets/javascripts/integrations/instant/index.ts index e492047a3..63b81ee4a 100644 --- a/src/assets/javascripts/integrations/instant/index.ts +++ b/src/assets/javascripts/integrations/instant/index.ts @@ -45,11 +45,10 @@ import { switchMap } from "rxjs/operators" -import { configuration } from "~/_" +import { configuration, feature } from "~/_" import { Viewport, ViewportOffset, - createElement, getElement, getElements, replaceElement, @@ -60,6 +59,7 @@ import { setViewportOffset } from "~/browser" import { getComponentElement } from "~/components" +import { h } from "~/utilities" /* ---------------------------------------------------------------------------- * Types @@ -283,7 +283,10 @@ export function setupInstantLoading( "[data-md-component=container]", "[data-md-component=header-topic]", "[data-md-component=logo], .md-logo", // compat - "[data-md-component=skip]" + "[data-md-component=skip]", + ...feature("navigation.tabs.sticky") + ? ["[data-md-component=tabs]"] + : [] ]) { const source = getElement(selector) const target = getElement(selector, replacement) @@ -303,7 +306,7 @@ export function setupInstantLoading( map(() => getComponentElement("container")), switchMap(el => of(...getElements("script", el))), concatMap(el => { - const script = createElement("script") + const script = h("script") if (el.src) { for (const name of el.getAttributeNames()) script.setAttribute(name, el.getAttribute(name)!) diff --git a/src/assets/javascripts/integrations/search/_/index.ts b/src/assets/javascripts/integrations/search/_/index.ts index ac9628abf..d3ed21085 100644 --- a/src/assets/javascripts/integrations/search/_/index.ts +++ b/src/assets/javascripts/integrations/search/_/index.ts @@ -167,7 +167,7 @@ export class Search { /* Set up document map and highlighter factory */ this.documents = setupSearchDocumentMap(docs) - this.highlight = setupSearchHighlighter(config) + this.highlight = setupSearchHighlighter(config, false) /* Set separator for tokenizer */ lunr.tokenizer.separator = new RegExp(config.separator) diff --git a/src/assets/javascripts/integrations/search/highlighter/index.ts b/src/assets/javascripts/integrations/search/highlighter/index.ts index 8d45f15aa..5d13e1980 100644 --- a/src/assets/javascripts/integrations/search/highlighter/index.ts +++ b/src/assets/javascripts/integrations/search/highlighter/index.ts @@ -54,11 +54,12 @@ export type SearchHighlightFactoryFn = (query: string) => SearchHighlightFn * Create a search highlighter * * @param config - Search index configuration + * @param escape - Whether to escape HTML * * @returns Search highlight factory function */ export function setupSearchHighlighter( - config: SearchIndexConfig + config: SearchIndexConfig, escape: boolean ): SearchHighlightFactoryFn { const separator = new RegExp(config.separator, "img") const highlight = (_: unknown, data: string, term: string) => { @@ -79,8 +80,12 @@ export function setupSearchHighlighter( })`, "img") /* Highlight string value */ - return value => escapeHTML(value) - .replace(match, highlight) - .replace(/<\/mark>(\s+)]*>/img, "$1") + return value => ( + escape + ? escapeHTML(value) + : value + ) + .replace(match, highlight) + .replace(/<\/mark>(\s+)]*>/img, "$1") } } diff --git a/src/assets/javascripts/integrations/search/worker/message/index.ts b/src/assets/javascripts/integrations/search/worker/message/index.ts index 3cc688255..2f6a38668 100644 --- a/src/assets/javascripts/integrations/search/worker/message/index.ts +++ b/src/assets/javascripts/integrations/search/worker/message/index.ts @@ -39,7 +39,7 @@ export const enum SearchMessageType { /* ------------------------------------------------------------------------- */ /** - * A message containing the data necessary to setup the search index + * Message containing the data necessary to setup the search index */ export interface SearchSetupMessage { type: SearchMessageType.SETUP /* Message type */ @@ -47,14 +47,14 @@ export interface SearchSetupMessage { } /** - * A message indicating the search index is ready + * Message indicating the search index is ready */ export interface SearchReadyMessage { type: SearchMessageType.READY /* Message type */ } /** - * A message containing a search query + * Message containing a search query */ export interface SearchQueryMessage { type: SearchMessageType.QUERY /* Message type */ @@ -62,7 +62,7 @@ export interface SearchQueryMessage { } /** - * A message containing results for a search query + * Message containing results for a search query */ export interface SearchResultMessage { type: SearchMessageType.RESULT /* Message type */ @@ -72,7 +72,7 @@ export interface SearchResultMessage { /* ------------------------------------------------------------------------- */ /** - * A message exchanged with the search worker + * Message exchanged with the search worker */ export type SearchMessage = | SearchSetupMessage diff --git a/src/assets/javascripts/integrations/version/index.ts b/src/assets/javascripts/integrations/version/index.ts index b05dc0a2f..4be76ee89 100644 --- a/src/assets/javascripts/integrations/version/index.ts +++ b/src/assets/javascripts/integrations/version/index.ts @@ -33,7 +33,7 @@ import { Version, renderVersionSelector } from "~/templates" */ export function setupVersionSelector(): void { const config = configuration() - requestJSON(new URL("versions.json", config.base)) + requestJSON(new URL("../versions.json", config.base)) .subscribe(versions => { const topic = getElementOrThrow(".md-header__topic") topic.appendChild(renderVersionSelector(versions)) diff --git a/src/assets/javascripts/templates/version/index.tsx b/src/assets/javascripts/templates/version/index.tsx index 4b56de01a..484f72dda 100644 --- a/src/assets/javascripts/templates/version/index.tsx +++ b/src/assets/javascripts/templates/version/index.tsx @@ -51,7 +51,7 @@ function renderVersion(version: Version): HTMLElement { const config = configuration() /* Ensure trailing slash, see https://bit.ly/3rL5u3f */ - const url = new URL(`${version.version}/`, config.base) + const url = new URL(`../${version.version}/`, config.base) return (
  • diff --git a/src/assets/javascripts/utilities/h/.eslintrc b/src/assets/javascripts/utilities/h/.eslintrc index 242e7556b..d79b45b0a 100644 --- a/src/assets/javascripts/utilities/h/.eslintrc +++ b/src/assets/javascripts/utilities/h/.eslintrc @@ -1,6 +1,7 @@ { "rules": { "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-namespace": "off" + "@typescript-eslint/no-namespace": "off", + "jsdoc/require-jsdoc": "off" } } diff --git a/src/assets/javascripts/utilities/h/index.ts b/src/assets/javascripts/utilities/h/index.ts index 481315a87..3c6dbabca 100644 --- a/src/assets/javascripts/utilities/h/index.ts +++ b/src/assets/javascripts/utilities/h/index.ts @@ -77,15 +77,25 @@ function appendChild(el: HTMLElement, child: Child | Child[]): void { /** * JSX factory * + * @template T - Element type + * * @param tag - HTML tag * @param attributes - HTML attributes * @param children - Child elements * * @returns Element */ -export function h( - tag: string, attributes: Attributes | null, ...children: Child[] -): HTMLElement { +export function h( + tag: T, attributes?: Attributes | null, ...children: Child[] +): HTMLElementTagNameMap[T] + +export function h( + tag: string, attributes?: Attributes | null, ...children: Child[] +): T + +export function h( + tag: string, attributes?: Attributes | null, ...children: Child[] +): T { const el = document.createElement(tag) /* Set attributes, if any */ @@ -101,7 +111,7 @@ export function h( appendChild(el, child) /* Return element */ - return el + return el as T } /* ---------------------------------------------------------------------------- diff --git a/src/assets/stylesheets/main/_typeset.scss b/src/assets/stylesheets/main/_typeset.scss index 4e0d8f618..5ee204a6f 100644 --- a/src/assets/stylesheets/main/_typeset.scss +++ b/src/assets/stylesheets/main/_typeset.scss @@ -166,7 +166,7 @@ kbd { color: var(--md-accent-fg-color); } - // Text link on keyboard focus + // Show outline for keyboard devices &.focus-visible { outline-color: var(--md-accent-fg-color); outline-offset: px2rem(4px); @@ -351,7 +351,6 @@ kbd { // Blockquotes, possibly nested blockquote { - display: flow-root; padding-left: px2rem(12px); color: var(--md-default-fg-color--light); border-left: px2rem(4px) solid var(--md-default-fg-color--lighter); diff --git a/src/assets/stylesheets/main/extensions/markdown/_admonition.scss b/src/assets/stylesheets/main/extensions/markdown/_admonition.scss index bd4085bec..b707db33f 100644 --- a/src/assets/stylesheets/main/extensions/markdown/_admonition.scss +++ b/src/assets/stylesheets/main/extensions/markdown/_admonition.scss @@ -27,14 +27,14 @@ /// Admonition flavours $admonitions: ( note: pencil $clr-blue-a200, - abstract summary tldr: text-subject $clr-light-blue-a400, + abstract summary tldr: clipboard-text $clr-light-blue-a400, info todo: information $clr-cyan-a700, tip hint important: fire $clr-teal-a700, - success check done: check-circle $clr-green-a700, + success check done: check-bold $clr-green-a700, question help faq: help-circle $clr-light-green-a700, warning caution attention: alert $clr-orange-a400, - failure fail missing: close-circle $clr-red-a200, - danger error: flash-circle $clr-red-a400, + failure fail missing: close-thick $clr-red-a200, + danger error: lightning-bolt $clr-red-a400, bug: bug $clr-pink-a400, example: format-list-numbered $clr-deep-purple-a200, quote cite: format-quote-close $clr-grey diff --git a/src/assets/stylesheets/main/extensions/markdown/_toc.scss b/src/assets/stylesheets/main/extensions/markdown/_toc.scss index 486647fcd..78db25c37 100644 --- a/src/assets/stylesheets/main/extensions/markdown/_toc.scss +++ b/src/assets/stylesheets/main/extensions/markdown/_toc.scss @@ -72,6 +72,15 @@ // it properly, so we settle with a cross-browser anchor correction solution. :target { scroll-margin-top: px2rem(48px + 24px); + + // [screen +]: Sticky navigation tabs + @include break-from-device(screen) { + + // Adjust scroll offset for sticky navigation tabs + .md-header--lifted ~ .md-container & { + scroll-margin-top: px2rem(96px + 24px); + } + } } // Adjust scroll offset for headlines of level 1-3 @@ -87,6 +96,21 @@ padding-top: px2rem(48px + 24px - 4px); content: ""; } + + // [screen +]: Sticky navigation tabs + @include break-from-device(screen) { + + // Adjust scroll offset for sticky navigation tabs + .md-header--lifted ~ .md-container & { + scroll-margin-top: initial; + + // Anchor correction hack + &::before { + margin-top: -1 * px2rem(96px + 24px - 4px); + padding-top: px2rem(96px + 24px - 4px); + } + } + } } // Adjust scroll offset for headlines of level 4 @@ -100,6 +124,21 @@ padding-top: px2rem(48px + 24px - 3px); content: ""; } + + // [screen +]: Sticky navigation tabs + @include break-from-device(screen) { + + // Adjust scroll offset for sticky navigation tabs + .md-header--lifted ~ .md-container & { + scroll-margin-top: initial; + + // Anchor correction hack + &::before { + margin-top: -1 * px2rem(96px + 24px - 3px); + padding-top: px2rem(96px + 24px - 3px); + } + } + } } // Adjust scroll offset for headlines of level 5-6 @@ -114,5 +153,20 @@ padding-top: px2rem(48px + 24px); content: ""; } + + // [screen +]: Sticky navigation tabs + @include break-from-device(screen) { + + // Adjust scroll offset for sticky navigation tabs + .md-header--lifted ~ .md-container & { + scroll-margin-top: initial; + + // Anchor correction hack + &::before { + margin-top: -1 * px2rem(96px + 24px); + padding-top: px2rem(96px + 24px); + } + } + } } } diff --git a/src/assets/stylesheets/main/extensions/pymdownx/_details.scss b/src/assets/stylesheets/main/extensions/pymdownx/_details.scss index 425485dd2..c26e08b4a 100644 --- a/src/assets/stylesheets/main/extensions/pymdownx/_details.scss +++ b/src/assets/stylesheets/main/extensions/pymdownx/_details.scss @@ -84,6 +84,12 @@ padding: px2rem(8px) px2rem(44px) px2rem(8px) px2rem(36px); } + // Show outline for keyboard devices + &.focus-visible { + outline-color: var(--md-accent-fg-color); + outline-offset: px2rem(4px); + } + // Hide outline for pointer devices &:not(.focus-visible) { outline: none; diff --git a/src/assets/stylesheets/main/layout/_base.scss b/src/assets/stylesheets/main/layout/_base.scss index fdfdad3a7..7863fec2a 100644 --- a/src/assets/stylesheets/main/layout/_base.scss +++ b/src/assets/stylesheets/main/layout/_base.scss @@ -140,7 +140,7 @@ body { display: block; } - // Show outline for pointer devices + // Show outline for keyboard devices &.focus-visible + label { outline-style: auto; outline-color: var(--md-accent-fg-color); diff --git a/src/assets/stylesheets/main/layout/_nav.scss b/src/assets/stylesheets/main/layout/_nav.scss index 47a6422e2..6a8886a9c 100644 --- a/src/assets/stylesheets/main/layout/_nav.scss +++ b/src/assets/stylesheets/main/layout/_nav.scss @@ -125,7 +125,7 @@ color: var(--md-accent-fg-color); } - // Navigation link on keyboard focus + // Show outline for keyboard devices &.focus-visible { outline-color: var(--md-accent-fg-color); outline-offset: px2rem(4px); diff --git a/src/assets/stylesheets/main/layout/_search.scss b/src/assets/stylesheets/main/layout/_search.scss index c4c185c85..cf35874a5 100644 --- a/src/assets/stylesheets/main/layout/_search.scss +++ b/src/assets/stylesheets/main/layout/_search.scss @@ -53,7 +53,7 @@ // [tablet portrait -]: Search modal @include break-to-device(tablet portrait) { position: absolute; - top: px2rem(4px); + top: px2rem(-20px); left: px2rem(-44px); width: px2rem(40px); height: px2rem(40px); @@ -146,41 +146,37 @@ @include break-to-device(tablet portrait) { position: fixed; top: 0; - left: 100%; + left: 0; z-index: 2; - width: 100%; - height: 100%; + width: 0; + height: 0; + overflow: hidden; transform: translateX(5%); opacity: 0; transition: - right 0ms 300ms, - left 0ms 300ms, + width 0ms 300ms, + height 0ms 300ms, transform 150ms 150ms cubic-bezier(0.4, 0, 0.2, 1), opacity 150ms 150ms; + // Adjust for right-to-left languages + [dir="rtl"] & { + right: 0; + left: initial; + transform: translateX(-5%); + } + // Adjust appearance when search is active [data-md-toggle="search"]:checked ~ .md-header & { - left: 0; + width: 100%; + height: 100%; transform: translateX(0); opacity: 1; transition: - right 0ms 0ms, - left 0ms 0ms, + width 0ms 0ms, + height 0ms 0ms, transform 150ms 150ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms 150ms; - - // Adjust for right-to-left languages - [dir="rtl"] & { - right: 0; - left: initial; - } - } - - // Adjust for right-to-left languages - html [dir="rtl"] & { - right: 100%; - left: initial; - transform: translateX(-5%); } } diff --git a/src/base.html b/src/base.html index b68786932..c68ed07b2 100644 --- a/src/base.html +++ b/src/base.html @@ -26,7 +26,7 @@ - + {% block site_meta %} @@ -214,8 +214,8 @@
    {% if self.announce() %} -
    diff --git a/src/partials/header.html b/src/partials/header.html index 5b76e336b..f5cf20608 100644 --- a/src/partials/header.html +++ b/src/partials/header.html @@ -20,8 +20,14 @@ IN THE SOFTWARE. --> + +{% set class = "md-header" %} +{% if "navigation.tabs.sticky" in features %} + {% set class = class ~ " md-header--lifted" %} +{% endif %} + -
    +
    + + + {% if "navigation.tabs.sticky" in features %} + {% if "navigation.tabs" in features %} + {% include "partials/tabs.html" %} + {% endif %} + {% endif %}
    diff --git a/src/partials/languages/pt.html b/src/partials/languages/pt.html index 6b96d30cd..9aca375c1 100644 --- a/src/partials/languages/pt.html +++ b/src/partials/languages/pt.html @@ -28,18 +28,32 @@ "edit.link.title": "Editar esta página", "footer.previous": "Anterior", "footer.next": "Próximo", + "footer.title": "Rodapé", + "header.title": "Cabeçalho", "meta.comments": "Comentários", "meta.source": "Fonte", + "nav.title": "Navegação", "search.config.lang": "pt", "search.placeholder": "Buscar", + "search.share": "Compartilhar", + "search.reset": "Limpar", + "search.result.initializer": "Inicializando a pesquisa", "search.result.placeholder": "Digite para iniciar a busca", "search.result.none": "Nenhum resultado encontrado", "search.result.one": "1 resultado encontrado", "search.result.other": "# resultados encontrados", + "search.result.more.one": "Mais 1 nesta página", + "search.result.more.other": "Mais # nesta página", + "search.result.term.missing": "Ausente", + "search.title": "Pesquisar", + "select.language.title": "Selecione o idioma", + "select.version.title": "Selecione a versão", "skip.link.title": "Ir para o conteúdo", "source.link.title": "Ir ao repositório", "source.revision.date": "Última atualização", "source.file.date.updated": "Última atualização", "source.file.date.created": "Criada", - "toc.title": "Índice" + "tabs.title": "Abas", + "toc.title": "Índice", + "top.title": "Voltar ao topo" }[key] }}{% endmacro %} diff --git a/src/partials/languages/sh.html b/src/partials/languages/sh.html index 2f64e5064..f0f7efa89 100644 --- a/src/partials/languages/sh.html +++ b/src/partials/languages/sh.html @@ -25,19 +25,34 @@ "language": "sh", "clipboard.copy": "Kopiraj u klipbord", "clipboard.copied": "Iskopirano u klipbord", - "edit.link.title": "Uredi stranicu", + "edit.link.title": "Ažuriraj stranicu", "footer.previous": "Prethodno", "footer.next": "Sledeće", + "footer.title": "Podnožje", + "header.title": "Zaglavlje", "meta.comments": "Komentari", "meta.source": "Izvor", + "nav.title": "Navigacija", "search.placeholder": "Pretraga", + "search.share": "Deljenje", + "search.reset": "Očisti", + "search.result.initializer": "Inicijalizujem pretragu", "search.result.placeholder": "Unesite pojam pretrage", "search.result.none": "Ništa nije pronađeno", "search.result.one": "1 rezultat pretrage", "search.result.other": "# rezultata pretrage", + "search.result.more.one": "još 1 na ovoj strani", + "search.result.more.other": "još # na ovoj strani", + "search.result.term.missing": "Nedostaje", + "search.title": "Pretraga", + "select.language.title": "Izaberi jezik", + "select.version.title": "Izaberi verziju", "skip.link.title": "Idi na tekst", "source.link.title": "Idi u repozitorijum", - "source.file.date.updated": "Ажуриран", - "source.file.date.created": "Створено", + "source.revision.date": "Ažuriran", + "source.file.date.updated": "Ažuriran", + "source.file.date.created": "Kreiran", + "tabs.title": "Tabovi", "toc.title": "Sadržaj" + "top.title": "Nazad na vrh" }[key] }}{% endmacro %} diff --git a/src/partials/languages/sr.html b/src/partials/languages/sr.html index 776eede6d..877b7bff0 100644 --- a/src/partials/languages/sr.html +++ b/src/partials/languages/sr.html @@ -20,26 +20,38 @@ IN THE SOFTWARE. --> - -{% macro t(key) %}{{ { - "language": "sr", +% macro t(key) %}{{ { + "language": "sh", "clipboard.copy": "Копирај у клипборд", "clipboard.copied": "Ископирано у клипборд", - "edit.link.title": "Уреди страницу", + "edit.link.title": "Ажурирај страницу", "footer.previous": "Претходно", "footer.next": "Следеће", + "footer.title": "Подножје", + "header.title": "Заглавље", "meta.comments": "Коментари", "meta.source": "Извор", - "search.config.lang": "ru", + "nav.title": "Навигација", "search.placeholder": "Претрага", + "search.share": "Дељење", + "search.reset": "Очисти", + "search.result.initializer": "Иницијализујем претрагу", "search.result.placeholder": "Унесите појам претраге", - "search.result.none": "Нису пронађени документи", + "search.result.none": "Ништа није пронађено", "search.result.one": "1 резултат претраге", "search.result.other": "# резултата претраге", + "search.result.more.one": "још 1 на овој страни", + "search.result.more.other": "још # на овој страни", + "search.result.term.missing": "Недостаје", + "search.title": "Претрага", + "select.language.title": "Изабери језик", + "select.version.title": "Изабери верзију", "skip.link.title": "Иди на текст", "source.link.title": "Иди у репозиторијум", - "source.revision.date": "Последња исправка", - "source.file.date.updated": "Последња исправка", - "source.file.date.created": "Створено", + "source.revision.date": "Ажуриран", + "source.file.date.updated": "Ажуриран", + "source.file.date.created": "Креиран", + "tabs.title": "Табови", "toc.title": "Садржај" + "top.title": "Назад на врх" }[key] }}{% endmacro %} diff --git a/src/partials/languages/zh-Hant.html b/src/partials/languages/zh-Hant.html index 55cd35362..0efe77d41 100644 --- a/src/partials/languages/zh-Hant.html +++ b/src/partials/languages/zh-Hant.html @@ -40,7 +40,7 @@ "search.result.one": "找到 1 个符合條件的結果", "search.result.other": "# 個符合條件的結果", "skip.link.title": "跳轉至", - "source.link.title": "前往 GitHub 倉庫", + "source.link.title": "前往倉庫", "source.revision.date": "最後更新", "source.file.date.updated": "最後更新", "source.file.date.created": "建立日期", diff --git a/src/partials/languages/zh.html b/src/partials/languages/zh.html index 76a64b43f..eeebacf69 100644 --- a/src/partials/languages/zh.html +++ b/src/partials/languages/zh.html @@ -40,7 +40,7 @@ "search.result.one": "找到 1 个符合条件的结果", "search.result.other": "# 个符合条件的结果", "skip.link.title": "跳转至", - "source.link.title": "前往 GitHub 仓库", + "source.link.title": "前往仓库", "source.revision.date": "最后更新", "source.file.date.updated": "最后更新", "source.file.date.created": "创建日期", diff --git a/src/partials/nav-item.html b/src/partials/nav-item.html index 26a13d7b1..a96d78557 100644 --- a/src/partials/nav-item.html +++ b/src/partials/nav-item.html @@ -63,11 +63,36 @@ /> {% endif %} - - + + {% set indexes = [] %} + {% if "navigation.indexes" in features %} + {% for item in nav_item.children %} + {% if item.is_index and not index is defined %} + {% set _ = indexes.append(item) %} + {% endif %} + {% endfor %} + {% endif %} + + + {% if not indexes %} + + + + {% else %} + {% set index = indexes | first %} + {% set class = "md-nav__link--active" if index == page %} + + {% endif %} + + diff --git a/tsconfig.json b/tsconfig.json index b61bc5e54..cb1a19991 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,6 @@ "jsxFactory": "h", "lib": [ "dom", - "es2017", "esnext", "webworker" ], @@ -25,6 +24,7 @@ }, "removeComments": false, "sourceMap": true, + "skipLibCheck": true, "strictBindCallApply": true, "strictFunctionTypes": true, "strictNullChecks": true, diff --git a/typings/_/index.d.ts b/typings/_/index.d.ts index 635c86ed5..799c3cb15 100644 --- a/typings/_/index.d.ts +++ b/typings/_/index.d.ts @@ -39,6 +39,7 @@ import { export interface GlobalSearchConfig { transform?: SearchTransformFn /* Transformation function */ index?: Promise /* Alternate index */ + worker?: string /* Alternate worker URL */ } /* ------------------------------------------------------------------------- */ @@ -53,7 +54,7 @@ declare global { /** * Global function to prefix storage items */ - function __prefix(key: string): string + function __prefix(key: string): string /** * Google Analytics