Add AI documentation skills

This commit is contained in:
2026-04-25 09:08:55 -05:00
parent 100a4d5419
commit 9d15ab56c6
255 changed files with 68574 additions and 0 deletions

View File

@@ -0,0 +1 @@
www.mkdocs.org

View File

@@ -0,0 +1 @@
--8<-- "CONTRIBUTING.md"

View File

@@ -0,0 +1,37 @@
# License
The legal stuff.
---
## Included projects
Themes used under license from the ReadTheDocs projects.
* ReadTheDocs theme - [View license](https://github.com/snide/sphinx_rtd_theme/blob/master/LICENSE).
Many thanks to the authors and contributors of those wonderful projects.
## MkDocs License (BSD)
Copyright © 2014, Tom Christie. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer. Redistributions in binary form must
reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
div.col-md-9 h1:first-of-type {
text-align: center;
font-size: 60px;
font-weight: 300;
}
div.col-md-9>p:first-of-type {
text-align: center;
}
div.col-md-9 p.admonition-title:first-of-type {
text-align: left;
}
div.col-md-9 h1:first-of-type .headerlink {
display: none;
}
div.admonition.block>.admonition-title {
display: none;
}
.admonition.new, details.new {
color: var(--bs-success-text-emphasis);
background-color: var(--bs-success-bg-subtle);
border-color: var(--bs-success-border-subtle);
}
.admonition.example, details.example {
color: var(--bs-info-text-emphasis);
background-color: var(--bs-info-bg-subtle);
border-color: var(--bs-info-border-subtle);
}
/* Definition List styles */
dd {
padding-left: 20px;
}
.card-body svg {
width: 100%;
padding: 0 50px;
height: auto;
}
/* Homepage */
body.homepage>div.container>div.row>div.col-md-3 {
display: none;
}
body.homepage>div.container>div.row>div.col-md-9 {
margin-left: 0;
flex: 0 0 100%;
max-width: 100%;
}
/* mkdocstrings */
.doc-object {
padding-left: 10px;
border-left: 4px solid var(--bs-light-border-subtle);
}
.doc-contents .field-body p:first-of-type {
display: inline;
}
.doc-label-class-attribute,
.doc-label-instance-attribute {
display: none;
}
h2.doc-heading {
font-size: 1.5rem;
}
h3.doc-heading {
font-size: 1.4rem;
}
h4.doc-heading {
font-size: 1.3rem;
}
h5.doc-heading {
font-size: 1.2rem;
}
.doc-contents {
padding-left: 0;
}

View File

@@ -0,0 +1,18 @@
# Developer Guide
Extending MkDocs
---
The MkDocs Developer Guide provides documentation for developers of third
party themes and plugins. Please see the [Contributing Guide] for information
on contributing to MkDocs itself. You can jump directly to a page listed
below, or use the *next* and *previous* buttons in the navigation bar at the
top of the page to move through the documentation in order.
- [Themes](themes.md)
- [Translations](translations.md)
- [Plugins](plugins.md)
- [API Reference](api.md)
[Contributing Guide]: ../about/contributing.md

View File

@@ -0,0 +1,24 @@
# API reference
NOTE: The main entry point to the API is through [Events](plugins.md#events) that are received by plugins. These events' descriptions link back to this page.
::: mkdocs.structure.files.Files
options:
show_root_heading: true
::: mkdocs.structure.files.File
options:
show_root_heading: true
::: mkdocs.config.base.Config
options:
show_root_heading: true
::: mkdocs.utils.templates.TemplateContext
options:
show_root_heading: true
show_if_no_docstring: true
::: mkdocs.livereload.LiveReloadServer
options:
show_root_heading: true

View File

@@ -0,0 +1,566 @@
# MkDocs Plugins
A Guide to installing, using and creating MkDocs Plugins
---
## Installing Plugins
Before a plugin can be used, it must be installed on the system. If you are
using a plugin which comes with MkDocs, then it was installed when you installed
MkDocs. However, to install third party plugins, you need to determine the
appropriate package name and install it using `pip`:
```bash
pip install mkdocs-foo-plugin
```
WARNING: Installing an MkDocs plugin means installing a Python package and executing any code that the author has put in there. So, exercise the usual caution; there's no attempt at sandboxing.
Once a plugin has been successfully installed, it is ready to use. It just needs
to be [enabled](#using-plugins) in the configuration file. The [Catalog]
repository has a large ranked list of plugins that you can install and use.
## Using Plugins
The [`plugins`][config] configuration option should contain a list of plugins to
use when building the site. Each "plugin" must be a string name assigned to the
plugin (see the documentation for a given plugin to determine its "name"). A
plugin listed here must already be [installed](#installing-plugins).
```yaml
plugins:
- search
```
Some plugins may provide configuration options of their own. If you would like
to set any configuration options, then you can nest a key/value mapping
(`option_name: option value`) of any options that a given plugin supports. Note
that a colon (`:`) must follow the plugin name and then on a new line the option
name and value must be indented and separated by a colon. If you would like to
define multiple options for a single plugin, each option must be defined on a
separate line.
```yaml
plugins:
- search:
lang: en
foo: bar
```
For information regarding the configuration options available for a given plugin,
see that plugin's documentation.
For a list of default plugins and how to override them, see the
[configuration][config] documentation.
## Developing Plugins
Like MkDocs, plugins must be written in Python. It is generally expected that
each plugin would be distributed as a separate Python module, although it is
possible to define multiple plugins in the same module. At a minimum, a MkDocs
Plugin must consist of a [BasePlugin] subclass and an [entry point] which
points to it.
### BasePlugin
A subclass of `mkdocs.plugins.BasePlugin` should define the behavior of the plugin.
The class generally consists of actions to perform on specific events in the build
process as well as a configuration scheme for the plugin.
All `BasePlugin` subclasses contain the following attributes:
#### config_scheme
A tuple of configuration validation instances. Each item must consist of a
two item tuple in which the first item is the string name of the
configuration option and the second item is an instance of
`mkdocs.config.config_options.BaseConfigOption` or any of its subclasses.
For example, the following `config_scheme` defines three configuration options: `foo`, which accepts a string; `bar`, which accepts an integer; and `baz`, which accepts a boolean value.
```python
class MyPlugin(mkdocs.plugins.BasePlugin):
config_scheme = (
('foo', mkdocs.config.config_options.Type(str, default='a default value')),
('bar', mkdocs.config.config_options.Type(int, default=0)),
('baz', mkdocs.config.config_options.Type(bool, default=True))
)
```
> NEW: **New in version 1.4.**
>
> ##### Subclassing `Config` to specify the config schema
>
> To get type safety benefits, if you're targeting only MkDocs 1.4+, define the config schema as a class instead:
>
> ```python
> class MyPluginConfig(mkdocs.config.base.Config):
> foo = mkdocs.config.config_options.Type(str, default='a default value')
> bar = mkdocs.config.config_options.Type(int, default=0)
> baz = mkdocs.config.config_options.Type(bool, default=True)
>
> class MyPlugin(mkdocs.plugins.BasePlugin[MyPluginConfig]):
> ...
> ```
##### Examples of config definitions
>! EXAMPLE:
>
> ```python
> from mkdocs.config import base, config_options as c
>
> class _ValidationOptions(base.Config):
> enabled = c.Type(bool, default=True)
> verbose = c.Type(bool, default=False)
> skip_checks = c.ListOfItems(c.Choice(('foo', 'bar', 'baz')), default=[])
>
> class MyPluginConfig(base.Config):
> definition_file = c.File(exists=True) # required
> checksum_file = c.Optional(c.File(exists=True)) # can be None but must exist if specified
> validation = c.SubConfig(_ValidationOptions)
> ```
>
> From the user's point of view `SubConfig` is similar to `Type(dict)`, it's just that it also retains full ability for validation: you define all valid keys and what each value should adhere to.
>
> And `ListOfItems` is similar to `Type(list)`, but again, we define the constraint that each value must adhere to.
>
> This accepts a config as follows:
>
> ```yaml
> my_plugin:
> definition_file: configs/test.ini # relative to mkdocs.yml
> validation:
> enabled: !ENV [CI, false]
> verbose: true
> skip_checks:
> - foo
> - baz
> ```
<!-- -->
>? EXAMPLE:
>
> ```python
> import numbers
> from mkdocs.config import base, config_options as c
>
> class _Rectangle(base.Config):
> width = c.Type(numbers.Real) # required
> height = c.Type(numbers.Real) # required
>
> class MyPluginConfig(base.Config):
> add_rectangles = c.ListOfItems(c.SubConfig(_Rectangle)) # required
> ```
>
> In this example we define a list of complex items, and that's achieved by passing a concrete `SubConfig` to `ListOfItems`.
>
> This accepts a config as follows:
>
> ```yaml
> my_plugin:
> add_rectangles:
> - width: 5
> height: 7
> - width: 12
> height: 2
> ```
When the user's configuration is loaded, the above scheme will be used to
validate the configuration and fill in any defaults for settings not
provided by the user. The validation classes may be any of the classes
provided in `mkdocs.config.config_options` or a third party subclass defined
in the plugin.
Any settings provided by the user which fail validation or are not defined
in the `config_scheme` will raise a `mkdocs.config.base.ValidationError`.
#### config
A dictionary of configuration options for the plugin, which is populated by
the `load_config` method after configuration validation has completed. Use
this attribute to access options provided by the user.
```python
def on_pre_build(self, config, **kwargs):
if self.config['baz']:
# implement "baz" functionality here...
```
> NEW: **New in version 1.4.**
>
> ##### Safe attribute-based access
>
> To get type safety benefits, if you're targeting only MkDocs 1.4+, access options as attributes instead:
>
> ```python
> def on_pre_build(self, config, **kwargs):
> if self.config.baz:
> print(self.config.bar ** 2) # OK, `int ** 2` is valid.
> ```
All `BasePlugin` subclasses contain the following method(s):
#### load_config(options)
Loads configuration from a dictionary of options. Returns a tuple of
`(errors, warnings)`. This method is called by MkDocs during configuration
validation and should not need to be called by the plugin.
#### on_&lt;event_name&gt;()
Optional methods which define the behavior for specific [events]. The plugin
should define its behavior within these methods. Replace `<event_name>` with
the actual name of the event. For example, the `pre_build` event would be
defined in the `on_pre_build` method.
Most events accept one positional argument and various keyword arguments. It
is generally expected that the positional argument would be modified (or
replaced) by the plugin and returned. If nothing is returned (the method
returns `None`), then the original, unmodified object is used. The keyword
arguments are simply provided to give context and/or supply data which may
be used to determine how the positional argument should be modified. It is
good practice to accept keyword arguments as `**kwargs`. In the event that
additional keywords are provided to an event in a future version of MkDocs,
there will be no need to alter your plugin.
For example, the following event would add an additional static_template to
the theme config:
```python
class MyPlugin(BasePlugin):
def on_config(self, config, **kwargs):
config['theme'].static_templates.add('my_template.html')
return config
```
> NEW: **New in version 1.4.**
>
> To get type safety benefits, if you're targeting only MkDocs 1.4+, access config options as attributes instead:
>
> ```python
> def on_config(self, config: MkDocsConfig):
> config.theme.static_templates.add('my_template.html')
> return config
> ```
### Events
There are three kinds of events: [Global Events], [Page Events] and
[Template Events].
<details class="card">
<summary>
See a diagram with relations between all the plugin events
</summary>
<div class="card-body">
<ul>
<li>The events themselves are shown in yellow, with their parameters.
<li>Arrows show the flow of arguments and outputs of each event.
Sometimes they're omitted.
<li>The events are chronologically ordered from top to bottom.
<li>Dotted lines appear at splits from global events to per-page events.
<li>Click the events' titles to jump to their description.
</ul>
--8<-- "docs/img/plugin-events.svg"
</div>
</details>
<br>
#### One-time Events
One-time events run once per `mkdocs` invocation. The only case where these tangibly differ from [global events](#global-events) is for `mkdocs serve`: global events, unlike these, will run multiple times -- once per *build*.
##### on_startup
::: mkdocs.plugins.BasePlugin.on_startup
options:
show_root_heading: false
show_root_toc_entry: false
##### on_shutdown
::: mkdocs.plugins.BasePlugin.on_shutdown
options:
show_root_heading: false
show_root_toc_entry: false
##### on_serve
::: mkdocs.plugins.BasePlugin.on_serve
options:
show_root_heading: false
show_root_toc_entry: false
#### Global Events
Global events are called once per build at either the beginning or end of the
build process. Any changes made in these events will have a global effect on the
entire site.
##### on_config
::: mkdocs.plugins.BasePlugin.on_config
options:
show_root_heading: false
show_root_toc_entry: false
##### on_pre_build
::: mkdocs.plugins.BasePlugin.on_pre_build
options:
show_root_heading: false
show_root_toc_entry: false
##### on_files
::: mkdocs.plugins.BasePlugin.on_files
options:
show_root_heading: false
show_root_toc_entry: false
##### on_nav
::: mkdocs.plugins.BasePlugin.on_nav
options:
show_root_heading: false
show_root_toc_entry: false
##### on_env
::: mkdocs.plugins.BasePlugin.on_env
options:
show_root_heading: false
show_root_toc_entry: false
##### on_post_build
::: mkdocs.plugins.BasePlugin.on_post_build
options:
show_root_heading: false
show_root_toc_entry: false
##### on_build_error
::: mkdocs.plugins.BasePlugin.on_build_error
options:
show_root_heading: false
show_root_toc_entry: false
#### Template Events
Template events are called once for each non-page template. Each template event
will be called for each template defined in the [extra_templates] config setting
as well as any [static_templates] defined in the theme. All template events are
called after the [env] event and before any [page events].
##### on_pre_template
::: mkdocs.plugins.BasePlugin.on_pre_template
options:
show_root_heading: false
show_root_toc_entry: false
##### on_template_context
::: mkdocs.plugins.BasePlugin.on_template_context
options:
show_root_heading: false
show_root_toc_entry: false
##### on_post_template
::: mkdocs.plugins.BasePlugin.on_post_template
options:
show_root_heading: false
show_root_toc_entry: false
#### Page Events
Page events are called once for each Markdown page included in the site. All
page events are called after the [post_template] event and before the
[post_build] event.
##### on_pre_page
::: mkdocs.plugins.BasePlugin.on_pre_page
options:
show_root_heading: false
show_root_toc_entry: false
##### on_page_read_source
::: mkdocs.plugins.BasePlugin.on_page_read_source
options:
show_root_heading: false
show_root_toc_entry: false
##### on_page_markdown
::: mkdocs.plugins.BasePlugin.on_page_markdown
options:
show_root_heading: false
show_root_toc_entry: false
##### on_page_content
::: mkdocs.plugins.BasePlugin.on_page_content
options:
show_root_heading: false
show_root_toc_entry: false
##### on_page_context
::: mkdocs.plugins.BasePlugin.on_page_context
options:
show_root_heading: false
show_root_toc_entry: false
##### on_post_page
::: mkdocs.plugins.BasePlugin.on_post_page
options:
show_root_heading: false
show_root_toc_entry: false
### Event Priorities
For each event type, corresponding methods of plugins are called in the order that the plugins appear in the `plugins` [config][].
Since MkDocs 1.4, plugins can choose to set a priority value for their events. Events with higher priority are called first. Events without a chosen priority get a default of 0. Events that have the same priority are ordered as they appear in the config.
#### ::: mkdocs.plugins.event_priority
> NEW: **New in version 1.6**
>
> There may also arise a need to register a handler for the same event at multiple different priorities.
>
> `CombinedEvent` makes this possible.
>
> #### ::: mkdocs.plugins.CombinedEvent
### Handling Errors
MkDocs defines four error types:
#### ::: mkdocs.exceptions.MkDocsException
#### ::: mkdocs.exceptions.ConfigurationError
#### ::: mkdocs.exceptions.BuildError
#### ::: mkdocs.exceptions.PluginError
Unexpected and uncaught exceptions will interrupt the build process and produce
typical Python tracebacks, which are useful for debugging your code. However,
users generally find tracebacks overwhelming and often miss the helpful error
message. Therefore, MkDocs will catch any of the errors listed above, retrieve
the error message, and exit immediately with only the helpful message displayed
to the user.
Therefore, you might want to catch any exceptions within your plugin and raise a
`PluginError`, passing in your own custom-crafted message, so that the build
process is aborted with a helpful message.
The [on_build_error] event will be triggered for any exception.
For example:
```python
from mkdocs.exceptions import PluginError
from mkdocs.plugins import BasePlugin
class MyPlugin(BasePlugin):
def on_post_page(self, output, page, config, **kwargs):
try:
# some code that could throw a KeyError
...
except KeyError as error:
raise PluginError(f"Failed to find the item by key: '{error}'")
def on_build_error(self, error, **kwargs):
# some code to clean things up
...
```
### Logging in plugins
To ensure that your plugins' log messages adhere with MkDocs' formatting and `--verbose`/`--debug` flags, please write the logs to a logger under the `mkdocs.plugins.` namespace.
> EXAMPLE:
>
> ```python
> import logging
>
> log = logging.getLogger(f"mkdocs.plugins.{__name__}")
>
> log.warning("File '%s' not found. Breaks the build if --strict is passed", my_file_name)
> log.info("Shown normally")
> log.debug("Shown only with `--verbose`")
>
> if log.getEffectiveLevel() <= logging.DEBUG:
> log.debug("Very expensive calculation only for debugging: %s", get_my_diagnostics())
> ```
`log.error()` is another logging level that is differentiated by its look, but in all other ways it functions the same as `warning`, so it's strange to use it. If your plugin encounters an actual error, it is best to just interrupt the build by raising [`mkdocs.exceptions.PluginError`][] (which will also log an ERROR message).
<!-- -->
> NEW: **New in version 1.5**
>
> MkDocs now provides a `get_plugin_logger()` convenience function that returns a logger like the above that is also prefixed with the plugin's name.
>
> #### ::: mkdocs.plugins.get_plugin_logger
### Entry Point
Plugins need to be packaged as Python libraries (distributed on PyPI separate
from MkDocs) and each must register as a Plugin via a setuptools `entry_points`.
Add the following to your `setup.py` script:
```python
entry_points={
'mkdocs.plugins': [
'pluginname = path.to.some_plugin:SomePluginClass',
]
}
```
The `pluginname` would be the name used by users (in the config file) and
`path.to.some_plugin:SomePluginClass` would be the importable plugin itself
(`from path.to.some_plugin import SomePluginClass`) where `SomePluginClass` is a
subclass of [BasePlugin] which defines the plugin behavior. Naturally, multiple
Plugin classes could exist in the same module. Simply define each as a separate
entry point.
```python
entry_points={
'mkdocs.plugins': [
'featureA = path.to.my_plugins:PluginA',
'featureB = path.to.my_plugins:PluginB'
]
}
```
Note that registering a plugin does not activate it. The user still needs to
tell MkDocs to use it via the config.
### Publishing a Plugin
You should publish a package on [PyPI], then add it to the [Catalog] for discoverability. Plugins are strongly recommended to have a unique plugin name (entry point name) according to the catalog.
[BasePlugin]:#baseplugin
[config]: ../user-guide/configuration.md#plugins
[entry point]: #entry-point
[env]: #on_env
[events]: #events
[extra_templates]: ../user-guide/configuration.md#extra_templates
[Global Events]: #global-events
[Page Events]: #page-events
[post_build]: #on_post_build
[post_template]: #on_post_template
[static_templates]: ../user-guide/configuration.md#static_templates
[Template Events]: #template-events
[catalog]: https://github.com/mkdocs/catalog
[on_build_error]: #on_build_error
[PyPI]: https://pypi.org/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,221 @@
# Translations
Theme localization guide.
---
The [built-in themes] that are included with MkDocs provide support for
translations. This is a guide for translators, which documents the process for
contributing new translations and/or updating existing translations. For
guidance on modifying the existing themes, see the [Contributing Guide][update
themes]. To enable a specific translation see the documentation about the
specific theme you are using in the [User Guide][built-in themes]. For
translations of third-party themes, please see the documentation for those
themes. For a third-party theme to make use of MkDocs' translation tools and
methods, that theme must be properly [configured] to make use of those tools.
NOTE:
Translations only apply to text contained within a theme's template, such
as "next" and "previous" links. The Markdown content of a page is not
translated. If you wish to create multilingual documentation, you need to
combine theme localization with a third-party
internationalization/localization plugin.
[built-in themes]: ../user-guide/choosing-your-theme.md
[update themes]: ../about/contributing.md#submitting-changes-to-the-builtin-themes
[configured]: themes.md#supporting-theme-localizationtranslation
## Localization tooling prerequisites
Theme localization makes use of the [babel][babel] project for generation and
compilation of localization files. You will need to be working from the
git working tree on your local machine to make use of the translation commands.
See the [Contributing Guide] for direction on how to [Install for Development]
and [Submit a Pull Request]. The instructions in this document assume that you
are working from a properly configured development environment.
Make sure translation requirements are installed in your environment:
```bash
pip install 'mkdocs[i18n]'
```
[babel]: https://babel.pocoo.org/en/latest/cmdline.html
[Contributing Guide]: ../about/contributing.md
[Install for Development]: ../about/contributing.md#installing-for-development
[Submit a Pull Request]: ../about/contributing.md#submitting-pull-requests
## Adding language translations to themes
If your favorite language locale is not yet supported on one (or both) of the
built-in themes (`mkdocs` and `readthedocs`), you can easily contribute a
translation by following the steps below.
Here is a quick summary of what you'll need to do:
1. [Fork and clone the MkDocs repository](#fork-and-clone-the-mkdocs-repository) and then [install MkDocs for development](../about/contributing.md#installing-for-development) for adding and testing translations.
2. [Initialize new localization catalogs](#initializing-the-localization-catalogs) for your language (if a translation for your locale already exists, follow the instructions for [updating theme localization files](#updating-the-translation-catalogs) instead).
3. [Add a translation](#translating-the-mkdocs-themes) for every text placeholder in the localized catalogs.
4. [Locally serve and test](#testing-theme-translations) the translated themes for your language.
5. [Update the documentation](#updating-theme-documentation) about supported translations for each translated theme.
6. [Contribute your translation](#contributing-translations) through a Pull Request.
NOTE:
Translation locales are usually identified using the [ISO-639-1] (2-letter)
language codes. While territory/region/county codes are also supported,
location specific translations should only be added after the general
language translation has been completed and the regional dialect requires
use of a term which differs from the general language translation.
[ISO-639-1]: https://en.wikipedia.org/wiki/ISO_639-1
### Fork and clone the MkDocs repository
In the following steps you'll work with a fork of the MkDocs repository. Follow
the instructions for [forking and cloning the MkDocs
repository](../about/contributing.md#installing-for-development).
To test the translations you also need to [install MkDocs for
development](../about/contributing.md#installing-for-development) from your fork.
### Initializing the localization catalogs
The templates for each theme contain text placeholders that have been extracted
into a Portable Object Template (`messages.pot`) file, which is present in each
theme's folder.
Initializing a catalog consists of running a command which will create a
directory structure for your desired language and prepare a Portable Object
(`messages.po`) file derived from the `pot` file of the theme.
Use the `init_catalog` command on each theme's directory and provide the appropriate language code (`-l <language>`).
The language code is almost always just two lowercase letters, such as `sv`, but in some cases it needs to be further disambiguated.
See:
* [Already translated languages for built-in themes](../user-guide/choosing-your-theme.md#mkdocs-locale)
* [ISO 639 Language List](https://www.localeplanet.com/icu/iso639.html)
* [Language subtag registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry)
In particular, the way to know that the `pt` language should be disambiguated as `pt_PT` and `pt_BR` is that the *Language subtag registry* page contains `pt-` if you search for it. Whereas `sv` should remain just `sv`, because that page does *not* contain `sv-`.
So, if we pick `es` (Spanish) as our example language code, to add a translation for it to both built-in themes, run these commands:
```bash
pybabel init --input-file mkdocs/themes/mkdocs/messages.pot --output-dir mkdocs/themes/mkdocs/locales -l es
pybabel init --input-file mkdocs/themes/readthedocs/messages.pot --output-dir mkdocs/themes/readthedocs/locales -l es
```
The above command will create a file structure as follows:
```text
mkdocs/themes/mkdocs/locales
├── es
│   └── LC_MESSAGES
│   └── messages.po
```
You can now move on to the next step and [add a
translation](#translating-the-mkdocs-themes) for every text placeholder in the
localized catalog.
## Updating a theme translation
If a theme's `messages.pot` template file has been [updated][update themes]
since the `messages.po` was last updated for your locale, follow the steps
below to update the theme's `messages.po` file:
1. [Update the theme's translation catalog](#updating-the-translation-catalogs) to refresh the translatable text placeholders of each theme.
2. [Translate](#translating-the-mkdocs-themes) the newly added translatable text placeholders on every `messages.po` catalog file language you can.
3. [Locally serve and test](#testing-theme-translations) the translated themes for your language.
4. [Contribute your translation](#contributing-translations) through a Pull Request.
### Updating the translation catalogs
This step should be completed after a theme template have been [updated][update
themes] for each language that you are comfortable contributing a translation
for.
To update the `fr` translation catalog of both built-in themes, use the following commands:
```bash
pybabel update --ignore-obsolete --input-file mkdocs/themes/mkdocs/messages.pot --output-dir mkdocs/themes/mkdocs/locales -l fr
pybabel update --ignore-obsolete --input-file mkdocs/themes/readthedocs/messages.pot --output-dir mkdocs/themes/readthedocs/locales -l fr
```
You can now move on to the next step and [add a translation] for every updated
text placeholder in the localized catalog.
[add a translation]: #translating-the-mkdocs-themes
### Translating the MkDocs themes
Now that your localized `messages.po` files are ready, all you need to do is
add a translation in each `msgstr` item for each `msgid` item in the file.
```text
msgid "Next"
msgstr "Siguiente"
```
WARNING:
Do not modify the `msgid` as it is common to all translations. Just add
its translation in the `msgstr` item.
Once you have finished translating all of the terms listed in the `po` file,
you'll want to [test your localized theme](#testing-theme-translations).
### Testing theme translations
To test a theme with translations, you need to first compile the `messages.po`
files of your theme into `messages.mo` files. The following commands will compile
the `es` translation for both built-in themes:
```bash
pybabel compile --statistics --directory mkdocs/themes/mkdocs/locales -l es
pybabel compile --statistics --directory mkdocs/themes/readthedocs/locales -l es
```
The above command results in the following file structure:
```text
mkdocs/themes/mkdocs/locales
├── es
│   └── LC_MESSAGES
│   ├── messages.mo
│   └── messages.po
```
Note that the compiled `messages.mo` file was generated based on the
`messages.po` file that you just edited.
Then modify the `mkdocs.yml` file at the root of the project to test the new
and/or updated locale:
```yaml
theme:
name: mkdocs
locale: es
```
Finally, run `mkdocs serve` to check out your new localized version of the theme.
> NOTE:
> The build and release process takes care of compiling and distributing
> all locales to end users so you only have to worry about contributing the
> actual text translation `messages.po` files (the rest is ignored by git).
>
> After you have finished testing your work, be sure to undo the change to
> the `locale` setting in the `mkdocs.yml` file before submitting your
> changes.
## Updating theme documentation
The page [Choosing your theme](../user-guide/choosing-your-theme.md) updates by itself with all available locale options.
## Contributing translations
It is now time for you to [contribute](../about/contributing.md) your nice work
to the project. Thank you!

View File

@@ -0,0 +1,213 @@
# Getting Started with MkDocs
An introductory tutorial!
---
## Installation
To install MkDocs, run the following command from the command line:
```bash
pip install mkdocs
```
For more details, see the [Installation Guide].
## Creating a new project
Getting started is super easy. To create a new project, run the following
command from the command line:
```bash
mkdocs new my-project
cd my-project
```
Take a moment to review the initial project that has been created for you.
![The initial MkDocs layout](img/initial-layout.png)
There's a single configuration file named `mkdocs.yml`, and a folder named
`docs` that will contain your documentation source files (`docs` is
the default value for the [docs_dir] configuration setting). Right now the `docs`
folder just contains a single documentation page, named `index.md`.
MkDocs comes with a built-in dev-server that lets you preview your documentation
as you work on it. Make sure you're in the same directory as the `mkdocs.yml`
configuration file, and then start the server by running the `mkdocs serve`
command:
```console
$ mkdocs serve
INFO - Building documentation...
INFO - Cleaning site directory
INFO - Documentation built in 0.22 seconds
INFO - [15:50:43] Watching paths for changes: 'docs', 'mkdocs.yml'
INFO - [15:50:43] Serving on http://127.0.0.1:8000/
```
Open up <http://127.0.0.1:8000/> in your browser, and you'll see the default
home page being displayed:
![The MkDocs live server](img/screenshot.png)
The dev-server also supports auto-reloading, and will rebuild your documentation
whenever anything in the configuration file, documentation directory, or theme
directory changes.
Open the `docs/index.md` document in your text editor of choice, change the
initial heading to `MkLorum`, and save your changes. Your browser will
auto-reload and you should see your updated documentation immediately.
Now try editing the configuration file: `mkdocs.yml`. Change the
[`site_name`][site_name] setting to `MkLorum` and save the file.
```yaml
site_name: MkLorum
```
Your browser should immediately reload, and you'll see your new site name take
effect.
![The site_name setting](img/site-name.png)
NOTE:
The [`site_name`][site_name] configuration
option is the only required option in your configuration file.
## Adding pages
Now add a second page to your documentation:
```bash
curl 'https://jaspervdj.be/lorem-markdownum/markdown.txt' > docs/about.md
```
As our documentation site will include some navigation headers, you may want to
edit the configuration file and add some information about the order, title, and
nesting of each page in the navigation header by adding a [`nav`][nav]
setting:
```yaml
site_name: MkLorum
nav:
- Home: index.md
- About: about.md
```
Save your changes and you'll now see a navigation bar with `Home` and `About`
items on the left as well as `Search`, `Previous`, and `Next` items on the
right.
![Screenshot](img/multipage.png)
Try the menu items and navigate back and forth between pages. Then click on
`Search`. A search dialog will appear, allowing you to search for any text on
any page. Notice that the search results include every occurrence of the search
term on the site and links directly to the section of the page in which the
search term appears. You get all of that with no effort or configuration on your
part!
![Screenshot](img/search.png)
## Theming our documentation
Now change the configuration file to alter how the documentation is displayed by
changing the theme. Edit the `mkdocs.yml` file and add a [`theme`][theme] setting:
```yaml
site_name: MkLorum
nav:
- Home: index.md
- About: about.md
theme: readthedocs
```
Save your changes, and you'll see the ReadTheDocs theme being used.
![Screenshot](img/readthedocs.png)
## Changing the Favicon Icon
By default, MkDocs uses the [MkDocs favicon] icon. To use a different icon, create
an `img` subdirectory in the `docs` directory and copy your custom `favicon.ico`
file to that directory. MkDocs will automatically detect and use that file as your
favicon icon.
[MkDocs favicon]: img/favicon.ico
## Building the site
That's looking good. You're ready to deploy the first pass of your `MkLorum`
documentation. First build the documentation:
```bash
mkdocs build
```
This will create a new directory, named `site`. Take a look inside the
directory:
```console
$ ls site
about fonts index.html license search.html
css img js mkdocs sitemap.xml
```
Notice that your source documentation has been output as two HTML files named
`index.html` and `about/index.html`. You also have various other media that's
been copied into the `site` directory as part of the documentation theme. You
even have a `sitemap.xml` file and `mkdocs/search_index.json`.
If you're using source code control such as `git` you probably don't want to
check your documentation builds into the repository. Add a line containing
`site/` to your `.gitignore` file.
```bash
echo "site/" >> .gitignore
```
If you're using another source code control tool you'll want to check its
documentation on how to ignore specific directories.
## Other Commands and Options
There are various other commands and options available. For a complete list of
commands, use the `--help` flag:
```bash
mkdocs --help
```
To view a list of options available on a given command, use the `--help` flag
with that command. For example, to get a list of all options available for the
`build` command run the following:
```bash
mkdocs build --help
```
## Deploying
The documentation site that you just built only uses static files so you'll be
able to host it from pretty much anywhere. Simply upload the contents of the
entire `site` directory to wherever you're hosting your website from and
you're done. For specific instructions on a number of common hosts, see the
[Deploying your Docs][deploy] page.
## Getting help
See the [User Guide] for more complete documentation of all of MkDocs' features.
To get help with MkDocs, please use the [GitHub discussions] or [GitHub issues].
[Installation Guide]: user-guide/installation.md
[docs_dir]: user-guide/configuration.md#docs_dir
[deploy]: user-guide/deploying-your-docs.md
[nav]: user-guide/configuration.md#nav
[GitHub discussions]: https://github.com/mkdocs/mkdocs/discussions
[GitHub issues]: https://github.com/mkdocs/mkdocs/issues
[site_name]: user-guide/configuration.md#site_name
[theme]: user-guide/configuration.md#theme
[User Guide]: user-guide/README.md

View File

@@ -0,0 +1,36 @@
from __future__ import annotations
import re
from pathlib import Path
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.structure.nav import Page
def _get_language_of_translation_file(path: Path) -> str:
with path.open(encoding='utf-8') as f:
translation_line = f.readline()
m = re.search('^# (.+) translations ', translation_line)
assert m
return m[1]
def on_page_markdown(markdown: str, page: Page, config: MkDocsConfig, **kwargs) -> str | None:
if page.file.src_uri == 'user-guide/choosing-your-theme.md':
here = Path(config.config_file_path).parent
def replacement(m: re.Match) -> str:
lines = []
for d in sorted(here.glob(m[2])):
lang = _get_language_of_translation_file(Path(d, 'LC_MESSAGES', 'messages.po'))
lines.append(f'{m[1]}`{d.name}`: {lang}')
return '\n'.join(lines)
return re.sub(
r'^( *\* )\(see the list of existing directories `(.+)`\)$',
replacement,
markdown,
flags=re.MULTILINE,
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,171 @@
# Run this to re-generate 'plugin-events.svg'.
# Requires `pip install graphviz`.
import contextlib
import pathlib
import re
from graphviz import Digraph
graph = Digraph("MkDocs", format="svg")
graph.attr(compound="true", bgcolor="transparent")
graph.graph_attr.update(fontname="inherit", tooltip=" ")
graph.node_attr.update(fontname="inherit", tooltip=" ", style="filled")
graph.edge_attr.update(fontname="inherit", tooltip=" ")
def strip_suffix(name):
return re.sub(r"_.$", "", name)
subgraph_to_first_node = {}
subgraph_to_last_node = {}
def node(g, name, **kwargs):
if "_point" in name:
kwargs.setdefault("shape", "point")
else:
kwargs.setdefault("fillcolor", "#77ff7788")
kwargs.setdefault("color", "#00000099")
kwargs.setdefault("label", strip_suffix(name))
g.node(name, **kwargs)
subgraph_to_first_node.setdefault(g.name, name)
subgraph_to_last_node[g.name] = name
def edge(g, a, b, dashed=False, **kwargs):
if kwargs.get("style") == "dashed":
kwargs.setdefault("penwidth", "1.5")
if a in subgraph_to_last_node:
kwargs.setdefault("ltail", a)
a = subgraph_to_last_node[a]
if b in subgraph_to_first_node:
kwargs.setdefault("lhead", b)
b = subgraph_to_first_node[b]
if a.startswith(("on_", "placeholder_")):
a += ":s"
else:
node(g, a.split(":")[0])
if b.startswith(("on_", "placeholder_")):
b += ":n"
else:
node(g, b.split(":")[0])
g.edge(a, b, **kwargs)
def ensure_order(a, b):
edge(graph, a, b, style="invis")
@contextlib.contextmanager
def cluster(g, name, **kwargs):
assert name.startswith("cluster_")
kwargs.setdefault("label", strip_suffix(name)[len("cluster_") :])
kwargs.setdefault("bgcolor", "#dddddd55")
kwargs.setdefault("pencolor", "#00000066")
with g.subgraph(name=name) as c:
c.attr(**kwargs)
yield c
def event(g, name, parameters):
with cluster(
g, f"cluster_{name}", href=f"#{name}", bgcolor="#ffff3388", pencolor="#00000088"
) as c:
label = "|".join(f"<{p}>{p}" for p in parameters.split())
node(c, name, shape="record" if parameters else "point", label=label, fillcolor="#ffffff55")
def placeholder_cluster(g, name):
with cluster(g, name) as c:
node(c, f"placeholder_{name}", label="...", fillcolor="transparent", color="transparent")
event(graph, "on_startup", "command dirty")
with cluster(graph, "cluster_build", bgcolor="#dddddd11") as g:
event(g, "on_config", "config")
event(g, "on_pre_build", "config")
event(g, "on_files", "files config")
event(g, "on_nav", "nav config files")
edge(g, "load_config", "on_config:config")
edge(g, "on_config:config", "on_pre_build:config")
edge(g, "on_config:config", "get_files")
edge(g, "get_files", "on_files:files")
edge(g, "on_files:files", "get_nav")
edge(g, "get_nav", "on_nav:nav")
edge(g, "on_files:files", "on_nav:files")
with cluster(g, "cluster_populate_page") as c:
event(c, "on_pre_page", "page config files")
event(c, "on_page_read_source", "page config")
event(c, "on_page_markdown", "markdown page config files")
event(c, "on_page_content", "html page config files")
edge(c, "on_pre_page:page", "on_page_read_source:page", style="dashed")
edge(c, "cluster_on_page_read_source", "on_page_markdown:markdown", style="dashed")
edge(c, "on_page_markdown:markdown", "render_p", style="dashed")
edge(c, "render_p", "on_page_content:html", style="dashed")
edge(g, "on_nav:files", "pages_point_a", arrowhead="none")
edge(g, "pages_point_a", "on_pre_page:page", style="dashed")
edge(g, "pages_point_a", "cluster_populate_page")
for i in 2, 3:
placeholder_cluster(g, f"cluster_populate_page_{i}")
edge(g, "pages_point_a", f"cluster_populate_page_{i}", style="dashed")
edge(g, f"cluster_populate_page_{i}", "pages_point_b", style="dashed")
event(g, "on_env", "env config files")
edge(g, "on_page_content:html", "pages_point_b", style="dashed")
edge(g, "pages_point_b", "on_env:files")
edge(g, "pages_point_b", "pages_point_c", arrowhead="none")
edge(g, "pages_point_c", "on_page_context:page", style="dashed")
with cluster(g, "cluster_build_page") as c:
event(c, "on_page_context", "context page config nav")
event(c, "on_post_page", "output page config")
edge(c, "get_context", "on_page_context:context")
edge(c, "on_page_context:context", "render")
edge(c, "get_template", "render")
edge(c, "render", "on_post_page:output")
edge(c, "on_post_page:output", "write_file")
edge(g, "on_nav:nav", "cluster_build_page")
edge(g, "on_env:env", "cluster_build_page")
for i in 2, 3:
placeholder_cluster(g, f"cluster_build_page_{i}")
edge(g, "pages_point_c", f"cluster_build_page_{i}", style="dashed")
event(g, "on_post_build", "config")
event(graph, "on_serve", "server config")
event(graph, "on_shutdown", "")
ensure_order("on_startup", "cluster_build")
ensure_order("on_pre_build", "on_files")
ensure_order("on_nav", "cluster_populate_page")
ensure_order("cluster_populate_page_2", "cluster_populate_page_3")
ensure_order("on_page_content", "on_env")
ensure_order("pages_point_c", "cluster_build_page")
ensure_order("cluster_build_page_2", "cluster_build_page_3")
ensure_order("cluster_build_page", "on_post_build")
ensure_order("on_post_build", "on_serve")
ensure_order("on_serve", "on_shutdown")
data = graph.pipe()
data = data[data.index(b"<svg ") :]
pathlib.Path(__file__).with_suffix(".svg").write_bytes(data)

View File

@@ -0,0 +1,780 @@
<svg width="527pt" height="1786pt"
viewBox="0.00 0.00 527.00 1785.80" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1781.8)">
<title>MkDocs</title>
<g id="a_graph0"><a xlink:title=" ">
</a>
</g>
<g id="clust1" class="cluster">
<title>cluster_on_startup</title>
<g id="a_clust1"><a xlink:href="#on_startup" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="199,-1693.8 199,-1769.8 341,-1769.8 341,-1693.8 199,-1693.8"/>
<text text-anchor="middle" x="270" y="-1754.6" font-family="inherit" font-size="14.00">on_startup</text>
</a>
</g>
</g>
<g id="clust2" class="cluster">
<title>cluster_build</title>
<g id="a_clust2"><a xlink:title=" ">
<polygon fill="#dddddd" fill-opacity="0.066667" stroke="#000000" stroke-opacity="0.400000" points="8,-142.6 8,-1685.8 511,-1685.8 511,-142.6 8,-142.6"/>
<text text-anchor="middle" x="259.5" y="-1670.6" font-family="inherit" font-size="14.00">build</text>
</a>
</g>
</g>
<g id="clust3" class="cluster">
<title>cluster_on_config</title>
<g id="a_clust3"><a xlink:href="#on_config" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="230,-1534.8 230,-1610.8 310,-1610.8 310,-1534.8 230,-1534.8"/>
<text text-anchor="middle" x="270" y="-1595.6" font-family="inherit" font-size="14.00">on_config</text>
</a>
</g>
</g>
<g id="clust4" class="cluster">
<title>cluster_on_pre_build</title>
<g id="a_clust4"><a xlink:href="#on_pre_build" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="161,-1450.8 161,-1526.8 261,-1526.8 261,-1450.8 161,-1450.8"/>
<text text-anchor="middle" x="211" y="-1511.6" font-family="inherit" font-size="14.00">on_pre_build</text>
</a>
</g>
</g>
<g id="clust5" class="cluster">
<title>cluster_on_files</title>
<g id="a_clust5"><a xlink:href="#on_files" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="226,-1366.8 226,-1442.8 342,-1442.8 342,-1366.8 226,-1366.8"/>
<text text-anchor="middle" x="284" y="-1427.6" font-family="inherit" font-size="14.00">on_files</text>
</a>
</g>
</g>
<g id="clust6" class="cluster">
<title>cluster_on_nav</title>
<g id="a_clust6"><a xlink:href="#on_nav" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="181,-1218.8 181,-1294.8 335,-1294.8 335,-1218.8 181,-1218.8"/>
<text text-anchor="middle" x="258" y="-1279.6" font-family="inherit" font-size="14.00">on_nav</text>
</a>
</g>
</g>
<g id="clust7" class="cluster">
<title>cluster_populate_page</title>
<g id="a_clust7"><a xlink:title=" ">
<polygon fill="#dddddd" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.400000" points="22,-729.2 22,-1179.2 284,-1179.2 284,-729.2 22,-729.2"/>
<text text-anchor="middle" x="153" y="-1164" font-family="inherit" font-size="14.00">populate_page</text>
</a>
</g>
</g>
<g id="clust8" class="cluster">
<title>cluster_on_pre_page</title>
<g id="a_clust8"><a xlink:href="#on_pre_page" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="80,-1072.2 80,-1148.2 244,-1148.2 244,-1072.2 80,-1072.2"/>
<text text-anchor="middle" x="162" y="-1133" font-family="inherit" font-size="14.00">on_pre_page</text>
</a>
</g>
</g>
<g id="clust9" class="cluster">
<title>cluster_on_page_read_source</title>
<g id="a_clust9"><a xlink:href="#on_page_read_source" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="46,-988.2 46,-1064.2 201,-1064.2 201,-988.2 46,-988.2"/>
<text text-anchor="middle" x="123.5" y="-1049" font-family="inherit" font-size="14.00">on_page_read_source</text>
</a>
</g>
</g>
<g id="clust10" class="cluster">
<title>cluster_on_page_markdown</title>
<g id="a_clust10"><a xlink:href="#on_page_markdown" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="30,-904.2 30,-980.2 276,-980.2 276,-904.2 30,-904.2"/>
<text text-anchor="middle" x="153" y="-965" font-family="inherit" font-size="14.00">on_page_markdown</text>
</a>
</g>
</g>
<g id="clust11" class="cluster">
<title>cluster_on_page_content</title>
<g id="a_clust11"><a xlink:href="#on_page_content" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="66,-737.2 66,-813.2 276,-813.2 276,-737.2 66,-737.2"/>
<text text-anchor="middle" x="171" y="-798" font-family="inherit" font-size="14.00">on_page_content</text>
</a>
</g>
</g>
<g id="clust14" class="cluster">
<title>cluster_on_env</title>
<g id="a_clust14"><a xlink:href="#on_env" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="202,-621.6 202,-697.6 356,-697.6 356,-621.6 202,-621.6"/>
<text text-anchor="middle" x="279" y="-682.4" font-family="inherit" font-size="14.00">on_env</text>
</a>
</g>
</g>
<g id="clust12" class="cluster">
<title>cluster_populate_page_2</title>
<g id="a_clust12"><a xlink:title=" ">
<polygon fill="#dddddd" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.400000" points="292,-821.2 292,-896.2 404,-896.2 404,-821.2 292,-821.2"/>
<text text-anchor="middle" x="348" y="-881" font-family="inherit" font-size="14.00">populate_page</text>
</a>
</g>
</g>
<g id="clust13" class="cluster">
<title>cluster_populate_page_3</title>
<g id="a_clust13"><a xlink:title=" ">
<polygon fill="#dddddd" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.400000" points="312,-737.7 312,-812.7 424,-812.7 424,-737.7 312,-737.7"/>
<text text-anchor="middle" x="368" y="-797.5" font-family="inherit" font-size="14.00">populate_page</text>
</a>
</g>
</g>
<g id="clust15" class="cluster">
<title>cluster_build_page</title>
<g id="a_clust15"><a xlink:title=" ">
<polygon fill="#dddddd" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.400000" points="16,-234.6 16,-613.6 398,-613.6 398,-234.6 16,-234.6"/>
<text text-anchor="middle" x="207" y="-598.4" font-family="inherit" font-size="14.00">build_page</text>
</a>
</g>
</g>
<g id="clust16" class="cluster">
<title>cluster_on_page_context</title>
<g id="a_clust16"><a xlink:href="#on_page_context" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="166,-454.6 166,-530.6 390,-530.6 390,-454.6 166,-454.6"/>
<text text-anchor="middle" x="278" y="-515.4" font-family="inherit" font-size="14.00">on_page_context</text>
</a>
</g>
</g>
<g id="clust17" class="cluster">
<title>cluster_on_post_page</title>
<g id="a_clust17"><a xlink:href="#on_post_page" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="109,-306.6 109,-382.6 291,-382.6 291,-306.6 109,-306.6"/>
<text text-anchor="middle" x="200" y="-367.4" font-family="inherit" font-size="14.00">on_post_page</text>
</a>
</g>
</g>
<g id="clust18" class="cluster">
<title>cluster_build_page_2</title>
<g id="a_clust18"><a xlink:title=" ">
<polygon fill="#dddddd" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.400000" points="406,-538.6 406,-613.6 493,-613.6 493,-538.6 406,-538.6"/>
<text text-anchor="middle" x="449.5" y="-598.4" font-family="inherit" font-size="14.00">build_page</text>
</a>
</g>
</g>
<g id="clust19" class="cluster">
<title>cluster_build_page_3</title>
<g id="a_clust19"><a xlink:title=" ">
<polygon fill="#dddddd" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.400000" points="411,-455.1 411,-530.1 498,-530.1 498,-455.1 411,-455.1"/>
<text text-anchor="middle" x="454.5" y="-514.9" font-family="inherit" font-size="14.00">build_page</text>
</a>
</g>
</g>
<g id="clust20" class="cluster">
<title>cluster_on_post_build</title>
<g id="a_clust20"><a xlink:href="#on_post_build" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="95,-150.6 95,-226.6 202,-226.6 202,-150.6 95,-150.6"/>
<text text-anchor="middle" x="148.5" y="-211.4" font-family="inherit" font-size="14.00">on_post_build</text>
</a>
</g>
</g>
<g id="clust21" class="cluster">
<title>cluster_on_serve</title>
<g id="a_clust21"><a xlink:href="#on_serve" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="84,-58.6 84,-134.6 212,-134.6 212,-58.6 84,-58.6"/>
<text text-anchor="middle" x="148" y="-119.4" font-family="inherit" font-size="14.00">on_serve</text>
</a>
</g>
</g>
<g id="clust22" class="cluster">
<title>cluster_on_shutdown</title>
<g id="a_clust22"><a xlink:href="#on_shutdown" xlink:title=" ">
<polygon fill="#ffff33" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.533333" points="102,-8 102,-50.6 206,-50.6 206,-8 102,-8"/>
<text text-anchor="middle" x="154" y="-35.4" font-family="inherit" font-size="14.00">on_shutdown</text>
</a>
</g>
</g>
<!-- on_startup -->
<g id="node1" class="node">
<title>on_startup</title>
<g id="a_node1"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="207,-1702.3 207,-1738.3 333,-1738.3 333,-1702.3 207,-1702.3"/>
<text text-anchor="middle" x="246.5" y="-1716.6" font-family="inherit" font-size="14.00">command</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="286,-1702.3 286,-1738.3"/>
<text text-anchor="middle" x="309.5" y="-1716.6" font-family="inherit" font-size="14.00">dirty</text>
</a>
</g>
</g>
<!-- load_config -->
<g id="node6" class="node">
<title>load_config</title>
<g id="a_node6"><a xlink:title=" ">
<ellipse fill="#77ff77" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.600000" cx="270" cy="-1636.8" rx="59.29" ry="18"/>
<text text-anchor="middle" x="270" y="-1633.1" font-family="inherit" font-size="14.00">load_config</text>
</a>
</g>
</g>
<!-- on_startup&#45;&gt;load_config -->
<!-- on_config -->
<g id="node2" class="node">
<title>on_config</title>
<g id="a_node2"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="242,-1543.3 242,-1579.3 298,-1579.3 298,-1543.3 242,-1543.3"/>
<text text-anchor="middle" x="270" y="-1557.6" font-family="inherit" font-size="14.00">config</text>
</a>
</g>
</g>
<!-- on_pre_build -->
<g id="node3" class="node">
<title>on_pre_build</title>
<g id="a_node3"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="196,-1459.3 196,-1495.3 252,-1495.3 252,-1459.3 196,-1459.3"/>
<text text-anchor="middle" x="224" y="-1473.6" font-family="inherit" font-size="14.00">config</text>
</a>
</g>
</g>
<!-- on_config&#45;&gt;on_pre_build -->
<g id="edge2" class="edge">
<title>on_config:s&#45;&gt;on_pre_build:n</title>
<g id="a_edge2"><a xlink:title=" ">
<path fill="none" stroke="black" d="M270,-1542.3C270,-1536.71 242.48,-1516.57 230.01,-1504.39"/>
<polygon fill="black" stroke="black" points="232.77,-1502.24 224,-1496.3 227.16,-1506.41 232.77,-1502.24"/>
</a>
</g>
</g>
<!-- get_files -->
<g id="node7" class="node">
<title>get_files</title>
<g id="a_node7"><a xlink:title=" ">
<ellipse fill="#77ff77" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.600000" cx="316" cy="-1477.3" rx="46.29" ry="18"/>
<text text-anchor="middle" x="316" y="-1473.6" font-family="inherit" font-size="14.00">get_files</text>
</a>
</g>
</g>
<!-- on_config&#45;&gt;get_files -->
<g id="edge3" class="edge">
<title>on_config:s&#45;&gt;get_files</title>
<g id="a_edge3"><a xlink:title=" ">
<path fill="none" stroke="black" d="M270,-1542.3C270,-1526.62 279.21,-1512.16 289.39,-1500.97"/>
<polygon fill="black" stroke="black" points="291.96,-1503.34 296.49,-1493.76 286.98,-1498.43 291.96,-1503.34"/>
</a>
</g>
</g>
<!-- on_files -->
<g id="node4" class="node">
<title>on_files</title>
<g id="a_node4"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="234.5,-1375.3 234.5,-1411.3 333.5,-1411.3 333.5,-1375.3 234.5,-1375.3"/>
<text text-anchor="middle" x="256" y="-1389.6" font-family="inherit" font-size="14.00">files</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="277.5,-1375.3 277.5,-1411.3"/>
<text text-anchor="middle" x="305.5" y="-1389.6" font-family="inherit" font-size="14.00">config</text>
</a>
</g>
</g>
<!-- on_pre_build&#45;&gt;on_files -->
<!-- on_nav -->
<g id="node5" class="node">
<title>on_nav</title>
<g id="a_node5"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="189.5,-1227.3 189.5,-1263.3 326.5,-1263.3 326.5,-1227.3 189.5,-1227.3"/>
<text text-anchor="middle" x="208.5" y="-1241.6" font-family="inherit" font-size="14.00">nav</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="227.5,-1227.3 227.5,-1263.3"/>
<text text-anchor="middle" x="255.5" y="-1241.6" font-family="inherit" font-size="14.00">config</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="283.5,-1227.3 283.5,-1263.3"/>
<text text-anchor="middle" x="305" y="-1241.6" font-family="inherit" font-size="14.00">files</text>
</a>
</g>
</g>
<!-- on_files&#45;&gt;on_nav -->
<g id="edge7" class="edge">
<title>on_files:s&#45;&gt;on_nav:n</title>
<g id="a_edge7"><a xlink:title=" ">
<path fill="none" stroke="black" d="M256,-1374.3C256,-1352.14 280.73,-1358.43 291,-1338.8 304.91,-1312.21 305.15,-1300.61 305.03,-1274.59"/>
<polygon fill="black" stroke="black" points="308.53,-1274.29 305,-1264.3 301.53,-1274.31 308.53,-1274.29"/>
</a>
</g>
</g>
<!-- get_nav -->
<g id="node8" class="node">
<title>get_nav</title>
<g id="a_node8"><a xlink:title=" ">
<ellipse fill="#77ff77" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.600000" cx="216" cy="-1320.8" rx="43.59" ry="18"/>
<text text-anchor="middle" x="216" y="-1317.1" font-family="inherit" font-size="14.00">get_nav</text>
</a>
</g>
</g>
<!-- on_files&#45;&gt;get_nav -->
<g id="edge5" class="edge">
<title>on_files:s&#45;&gt;get_nav</title>
<g id="a_edge5"><a xlink:title=" ">
<path fill="none" stroke="black" d="M256,-1374.3C256,-1363.19 250.26,-1352.96 243.12,-1344.54"/>
<polygon fill="black" stroke="black" points="245.43,-1341.88 236,-1337.05 240.36,-1346.71 245.43,-1341.88"/>
</a>
</g>
</g>
<!-- render_p -->
<g id="node13" class="node">
<title>render_p</title>
<g id="a_node13"><a xlink:title=" ">
<ellipse fill="#77ff77" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.600000" cx="88" cy="-847.2" rx="38.19" ry="18"/>
<text text-anchor="middle" x="88" y="-843.5" font-family="inherit" font-size="14.00">render</text>
</a>
</g>
</g>
<!-- on_nav&#45;&gt;render_p -->
<!-- pages_point_a -->
<g id="node14" class="node">
<title>pages_point_a</title>
<g id="a_node14"><a xlink:title=" ">
<ellipse fill="black" stroke="black" cx="305" cy="-1189" rx="1.8" ry="1.8"/>
</a>
</g>
</g>
<!-- on_nav&#45;&gt;pages_point_a -->
<g id="edge12" class="edge">
<title>on_nav:s&#45;&gt;pages_point_a</title>
<g id="a_edge12"><a xlink:title=" ">
<path fill="none" stroke="black" d="M305,-1226.3C305,-1212.18 305,-1194.98 305,-1190.9"/>
</a>
</g>
</g>
<!-- get_context -->
<g id="node22" class="node">
<title>get_context</title>
<g id="a_node22"><a xlink:title=" ">
<ellipse fill="#77ff77" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.600000" cx="229" cy="-564.6" rx="61.19" ry="18"/>
<text text-anchor="middle" x="229" y="-560.9" font-family="inherit" font-size="14.00">get_context</text>
</a>
</g>
</g>
<!-- on_nav&#45;&gt;get_context -->
<g id="edge28" class="edge">
<title>on_nav:s&#45;&gt;get_context</title>
<g id="a_edge28"><a xlink:title=" ">
<path fill="none" stroke="black" d="M208,-1226.3C208,-1176.78 394.87,-1227.61 428,-1190.8 455.13,-1160.66 433,-1140.25 433,-1099.7 433,-1099.7 433,-1099.7 433,-706.4 433,-656.67 403.87,-645.02 360,-621.6 337.19,-609.42 324.79,-620.79 303.65,-616.51"/>
<polygon fill="black" stroke="black" points="304.58,-613.13 294,-613.6 302.57,-619.84 304.58,-613.13"/>
</a>
</g>
</g>
<!-- load_config&#45;&gt;on_config -->
<g id="edge1" class="edge">
<title>load_config&#45;&gt;on_config:n</title>
<g id="a_edge1"><a xlink:title=" ">
<path fill="none" stroke="black" d="M270,-1618.8C270,-1610.64 270,-1600.5 270,-1590.53"/>
<polygon fill="black" stroke="black" points="273.5,-1590.3 270,-1580.3 266.5,-1590.3 273.5,-1590.3"/>
</a>
</g>
</g>
<!-- get_files&#45;&gt;on_files -->
<g id="edge4" class="edge">
<title>get_files&#45;&gt;on_files:n</title>
<g id="a_edge4"><a xlink:title=" ">
<path fill="none" stroke="black" d="M290.55,-1462.16C276.88,-1452.84 261.92,-1439.27 257.38,-1422.52"/>
<polygon fill="black" stroke="black" points="260.81,-1421.74 256,-1412.3 253.87,-1422.68 260.81,-1421.74"/>
</a>
</g>
</g>
<!-- get_nav&#45;&gt;on_nav -->
<g id="edge6" class="edge">
<title>get_nav&#45;&gt;on_nav:n</title>
<g id="a_edge6"><a xlink:title=" ">
<path fill="none" stroke="black" d="M212.19,-1302.4C210.66,-1294.31 209.09,-1284.32 208.39,-1274.45"/>
<polygon fill="black" stroke="black" points="211.88,-1274.16 208,-1264.3 204.88,-1274.43 211.88,-1274.16"/>
</a>
</g>
</g>
<!-- on_pre_page -->
<g id="node9" class="node">
<title>on_pre_page</title>
<g id="a_node9"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="88.5,-1080.7 88.5,-1116.7 235.5,-1116.7 235.5,-1080.7 88.5,-1080.7"/>
<text text-anchor="middle" x="112.5" y="-1095" font-family="inherit" font-size="14.00">page</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="136.5,-1080.7 136.5,-1116.7"/>
<text text-anchor="middle" x="164.5" y="-1095" font-family="inherit" font-size="14.00">config</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="192.5,-1080.7 192.5,-1116.7"/>
<text text-anchor="middle" x="214" y="-1095" font-family="inherit" font-size="14.00">files</text>
</a>
</g>
</g>
<!-- on_page_read_source -->
<g id="node10" class="node">
<title>on_page_read_source</title>
<g id="a_node10"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="72,-996.7 72,-1032.7 176,-1032.7 176,-996.7 72,-996.7"/>
<text text-anchor="middle" x="96" y="-1011" font-family="inherit" font-size="14.00">page</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="120,-996.7 120,-1032.7"/>
<text text-anchor="middle" x="148" y="-1011" font-family="inherit" font-size="14.00">config</text>
</a>
</g>
</g>
<!-- on_pre_page&#45;&gt;on_page_read_source -->
<g id="edge8" class="edge">
<title>on_pre_page:s&#45;&gt;on_page_read_source:n</title>
<g id="a_edge8"><a xlink:title=" ">
<path fill="none" stroke="black" stroke-width="1.5" stroke-dasharray="5,2" d="M112,-1079.7C112,-1061.94 101.23,-1056.93 97.36,-1043.75"/>
<polygon fill="black" stroke="black" stroke-width="1.5" points="100.81,-1043.14 96,-1033.7 93.88,-1044.08 100.81,-1043.14"/>
</a>
</g>
</g>
<!-- on_page_markdown -->
<g id="node11" class="node">
<title>on_page_markdown</title>
<g id="a_node11"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="37.5,-912.7 37.5,-948.7 268.5,-948.7 268.5,-912.7 37.5,-912.7"/>
<text text-anchor="middle" x="79.5" y="-927" font-family="inherit" font-size="14.00">markdown</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="121.5,-912.7 121.5,-948.7"/>
<text text-anchor="middle" x="145.5" y="-927" font-family="inherit" font-size="14.00">page</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="169.5,-912.7 169.5,-948.7"/>
<text text-anchor="middle" x="197.5" y="-927" font-family="inherit" font-size="14.00">config</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="225.5,-912.7 225.5,-948.7"/>
<text text-anchor="middle" x="247" y="-927" font-family="inherit" font-size="14.00">files</text>
</a>
</g>
</g>
<!-- on_page_read_source&#45;&gt;on_page_markdown -->
<g id="edge9" class="edge">
<title>on_page_read_source:s&#45;&gt;on_page_markdown:n</title>
<g id="a_edge9"><a xlink:title=" ">
<path fill="none" stroke="black" stroke-width="1.5" stroke-dasharray="5,2" d="M122.73,-988.2C116.55,-971.96 89.67,-974.03 81.42,-959.63"/>
<polygon fill="black" stroke="black" stroke-width="1.5" points="84.77,-958.59 79,-949.7 77.97,-960.24 84.77,-958.59"/>
</a>
</g>
</g>
<!-- on_page_markdown&#45;&gt;render_p -->
<g id="edge10" class="edge">
<title>on_page_markdown:s&#45;&gt;render_p</title>
<g id="a_edge10"><a xlink:title=" ">
<path fill="none" stroke="black" stroke-width="1.5" stroke-dasharray="5,2" d="M79,-911.7C79,-899.72 80.59,-886.59 82.43,-875.4"/>
<polygon fill="black" stroke="black" stroke-width="1.5" points="85.91,-875.82 84.24,-865.36 79.02,-874.58 85.91,-875.82"/>
</a>
</g>
</g>
<!-- on_page_content -->
<g id="node12" class="node">
<title>on_page_content</title>
<g id="a_node12"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="74,-745.7 74,-781.7 268,-781.7 268,-745.7 74,-745.7"/>
<text text-anchor="middle" x="97.5" y="-760" font-family="inherit" font-size="14.00">html</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="121,-745.7 121,-781.7"/>
<text text-anchor="middle" x="145" y="-760" font-family="inherit" font-size="14.00">page</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="169,-745.7 169,-781.7"/>
<text text-anchor="middle" x="197" y="-760" font-family="inherit" font-size="14.00">config</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="225,-745.7 225,-781.7"/>
<text text-anchor="middle" x="246.5" y="-760" font-family="inherit" font-size="14.00">files</text>
</a>
</g>
</g>
<!-- pages_point_b -->
<g id="node16" class="node">
<title>pages_point_b</title>
<g id="a_node16"><a xlink:title=" ">
<ellipse fill="black" stroke="black" cx="326" cy="-707.4" rx="1.8" ry="1.8"/>
</a>
</g>
</g>
<!-- on_page_content&#45;&gt;pages_point_b -->
<g id="edge19" class="edge">
<title>on_page_content:s&#45;&gt;pages_point_b</title>
<g id="a_edge19"><a xlink:title=" ">
<path fill="none" stroke="black" stroke-width="1.5" stroke-dasharray="5,2" d="M97,-744.7C97,-722.45 267.86,-711.55 314.41,-709"/>
<polygon fill="black" stroke="black" stroke-width="1.5" points="314.73,-712.49 324.54,-708.47 314.37,-705.5 314.73,-712.49"/>
</a>
</g>
</g>
<!-- on_env -->
<g id="node18" class="node">
<title>on_env</title>
<g id="a_node18"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="210.5,-630.1 210.5,-666.1 347.5,-666.1 347.5,-630.1 210.5,-630.1"/>
<text text-anchor="middle" x="229.5" y="-644.4" font-family="inherit" font-size="14.00">env</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="248.5,-630.1 248.5,-666.1"/>
<text text-anchor="middle" x="276.5" y="-644.4" font-family="inherit" font-size="14.00">config</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="304.5,-630.1 304.5,-666.1"/>
<text text-anchor="middle" x="326" y="-644.4" font-family="inherit" font-size="14.00">files</text>
</a>
</g>
</g>
<!-- on_page_content&#45;&gt;on_env -->
<!-- render_p&#45;&gt;on_page_content -->
<g id="edge11" class="edge">
<title>render_p&#45;&gt;on_page_content:n</title>
<g id="a_edge11"><a xlink:title=" ">
<path fill="none" stroke="black" stroke-width="1.5" stroke-dasharray="5,2" d="M91.76,-829.04C93.71,-819 95.85,-805.8 96.66,-792.91"/>
<polygon fill="black" stroke="black" stroke-width="1.5" points="100.16,-792.81 97,-782.7 93.17,-792.58 100.16,-792.81"/>
</a>
</g>
</g>
<!-- pages_point_a&#45;&gt;on_pre_page -->
<g id="edge13" class="edge">
<title>pages_point_a&#45;&gt;on_pre_page:n</title>
<g id="a_edge13"><a xlink:title=" ">
<path fill="none" stroke="black" stroke-width="1.5" stroke-dasharray="5,2" d="M303.35,-1187.97C295.87,-1187.77 264.43,-1186.52 240,-1179.2 183.09,-1162.14 119.89,-1178.63 112.68,-1127.97"/>
<polygon fill="black" stroke="black" stroke-width="1.5" points="116.15,-1127.45 112,-1117.7 109.17,-1127.91 116.15,-1127.45"/>
</a>
</g>
</g>
<!-- pages_point_a&#45;&gt;render_p -->
<g id="edge14" class="edge">
<title>pages_point_a&#45;&gt;render_p</title>
<g id="a_edge14"><a xlink:title=" ">
<path fill="none" stroke="black" d="M304.65,-1187.19C304,-1185.67 302.63,-1182.23 302,-1179.2 290.63,-1124.31 314.02,-979.63 288.52,-918.91"/>
<polygon fill="black" stroke="black" points="291.62,-917.28 284,-909.91 285.36,-920.42 291.62,-917.28"/>
</a>
</g>
</g>
<!-- placeholder_cluster_populate_page_2 -->
<g id="node15" class="node">
<title>placeholder_cluster_populate_page_2</title>
<g id="a_node15"><a xlink:title=" ">
<ellipse fill="none" stroke="none" cx="327" cy="-847.2" rx="27" ry="18"/>
<text text-anchor="middle" x="327" y="-843.5" font-family="inherit" font-size="14.00">...</text>
</a>
</g>
</g>
<!-- pages_point_a&#45;&gt;placeholder_cluster_populate_page_2 -->
<g id="edge15" class="edge">
<title>pages_point_a&#45;&gt;placeholder_cluster_populate_page_2:n</title>
<g id="a_edge15"><a xlink:title=" ">
<path fill="none" stroke="black" stroke-width="1.5" stroke-dasharray="5,2" d="M305.1,-1187.2C306.53,-1175.36 323.3,-1034.9 326.48,-906.23"/>
<polygon fill="black" stroke="black" stroke-width="1.5" points="329.98,-906.27 326.7,-896.2 322.98,-906.12 329.98,-906.27"/>
</a>
</g>
</g>
<!-- placeholder_cluster_populate_page_3 -->
<g id="node17" class="node">
<title>placeholder_cluster_populate_page_3</title>
<g id="a_node17"><a xlink:title=" ">
<ellipse fill="none" stroke="none" cx="349" cy="-763.7" rx="27" ry="18"/>
<text text-anchor="middle" x="349" y="-760" font-family="inherit" font-size="14.00">...</text>
</a>
</g>
</g>
<!-- pages_point_a&#45;&gt;placeholder_cluster_populate_page_3 -->
<g id="edge17" class="edge">
<title>pages_point_a&#45;&gt;placeholder_cluster_populate_page_3:n</title>
<g id="a_edge17"><a xlink:title=" ">
<path fill="none" stroke="black" stroke-width="1.5" stroke-dasharray="5,2" d="M306.14,-1187.29C308.27,-1185.92 312.92,-1182.74 316,-1179.2 371.44,-1115.44 413,-1100.19 413,-1015.7 413,-1015.7 413,-1015.7 413,-929.7 413,-881.43 431.96,-863.11 408,-821.2 407.81,-820.86 407.61,-820.54 407.41,-820.22"/>
<polygon fill="black" stroke="black" stroke-width="1.5" points="409.89,-817.74 400.58,-812.7 404.71,-822.45 409.89,-817.74"/>
</a>
</g>
</g>
<!-- placeholder_cluster_populate_page_2&#45;&gt;pages_point_b -->
<g id="edge16" class="edge">
<title>placeholder_cluster_populate_page_2:s&#45;&gt;pages_point_b</title>
<g id="a_edge16"><a xlink:title=" ">
<path fill="none" stroke="black" stroke-width="1.5" stroke-dasharray="5,2" d="M322.03,-821.2C317.43,-819.39 310.89,-819.17 308,-813.2 291.72,-779.6 294.96,-764.18 308,-729.2 309.91,-724.06 313.73,-719.35 317.36,-715.72"/>
<polygon fill="black" stroke="black" stroke-width="1.5" points="319.79,-718.24 325.08,-709.06 315.22,-712.94 319.79,-718.24"/>
</a>
</g>
</g>
<!-- placeholder_cluster_populate_page_2&#45;&gt;placeholder_cluster_populate_page_3 -->
<!-- pages_point_b&#45;&gt;on_env -->
<g id="edge20" class="edge">
<title>pages_point_b&#45;&gt;on_env:n</title>
<g id="a_edge20"><a xlink:title=" ">
<path fill="none" stroke="black" d="M326,-705.43C326,-701.98 326,-689.85 326,-677.43"/>
<polygon fill="black" stroke="black" points="329.5,-677.1 326,-667.1 322.5,-677.1 329.5,-677.1"/>
</a>
</g>
</g>
<!-- pages_point_c -->
<g id="node19" class="node">
<title>pages_point_c</title>
<g id="a_node19"><a xlink:title=" ">
<ellipse fill="black" stroke="black" cx="454" cy="-648.1" rx="1.8" ry="1.8"/>
</a>
</g>
</g>
<!-- pages_point_b&#45;&gt;pages_point_c -->
<g id="edge21" class="edge">
<title>pages_point_b&#45;&gt;pages_point_c</title>
<g id="a_edge21"><a xlink:title=" ">
<path fill="none" stroke="black" d="M327.33,-706.17C331.95,-705.33 347.7,-702.3 360,-697.6 399.8,-682.4 444.66,-654.92 452.73,-649.9"/>
</a>
</g>
</g>
<!-- placeholder_cluster_populate_page_3&#45;&gt;pages_point_b -->
<g id="edge18" class="edge">
<title>placeholder_cluster_populate_page_3:s&#45;&gt;pages_point_b</title>
<g id="a_edge18"><a xlink:title=" ">
<path fill="none" stroke="black" stroke-width="1.5" stroke-dasharray="5,2" d="M348.02,-737.7C345.7,-729.17 339.67,-721.46 334.5,-716.09"/>
<polygon fill="black" stroke="black" stroke-width="1.5" points="336.72,-713.38 326.99,-709.18 331.97,-718.53 336.72,-713.38"/>
</a>
</g>
</g>
<!-- on_env&#45;&gt;get_context -->
<g id="edge29" class="edge">
<title>on_env:s&#45;&gt;get_context</title>
<g id="a_edge29"><a xlink:title=" ">
<path fill="none" stroke="black" d="M229,-629.1C229,-627.37 229,-625.62 229,-623.85"/>
<polygon fill="black" stroke="black" points="232.5,-623.6 229,-613.6 225.5,-623.6 232.5,-623.6"/>
</a>
</g>
</g>
<!-- on_page_context -->
<g id="node20" class="node">
<title>on_page_context</title>
<g id="a_node20"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="174.5,-463.1 174.5,-499.1 381.5,-499.1 381.5,-463.1 174.5,-463.1"/>
<text text-anchor="middle" x="207" y="-477.4" font-family="inherit" font-size="14.00">context</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="239.5,-463.1 239.5,-499.1"/>
<text text-anchor="middle" x="263.5" y="-477.4" font-family="inherit" font-size="14.00">page</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="287.5,-463.1 287.5,-499.1"/>
<text text-anchor="middle" x="315.5" y="-477.4" font-family="inherit" font-size="14.00">config</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="343.5,-463.1 343.5,-499.1"/>
<text text-anchor="middle" x="362.5" y="-477.4" font-family="inherit" font-size="14.00">nav</text>
</a>
</g>
</g>
<!-- pages_point_c&#45;&gt;on_page_context -->
<g id="edge22" class="edge">
<title>pages_point_c&#45;&gt;on_page_context:n</title>
<g id="a_edge22"><a xlink:title=" ">
<path fill="none" stroke="black" stroke-width="1.5" stroke-dasharray="5,2" d="M453.63,-645.9C452.27,-641.79 447.22,-628.11 438,-621.6 424.61,-612.15 415.2,-623.31 402,-613.6 371.05,-590.83 387.99,-563.81 359,-538.6 327.29,-511.03 273.34,-540.1 264.3,-510.2"/>
<polygon fill="black" stroke="black" stroke-width="1.5" points="267.75,-509.57 263,-500.1 260.81,-510.47 267.75,-509.57"/>
</a>
</g>
</g>
<!-- pages_point_c&#45;&gt;get_context -->
<!-- placeholder_cluster_build_page_2 -->
<g id="node26" class="node">
<title>placeholder_cluster_build_page_2</title>
<g id="a_node26"><a xlink:title=" ">
<ellipse fill="none" stroke="none" cx="455" cy="-564.6" rx="27" ry="18"/>
<text text-anchor="middle" x="455" y="-560.9" font-family="inherit" font-size="14.00">...</text>
</a>
</g>
</g>
<!-- pages_point_c&#45;&gt;placeholder_cluster_build_page_2 -->
<g id="edge30" class="edge">
<title>pages_point_c&#45;&gt;placeholder_cluster_build_page_2:n</title>
<g id="a_edge30"><a xlink:title=" ">
<path fill="none" stroke="black" stroke-width="1.5" stroke-dasharray="5,2" d="M454.02,-646.2C454.1,-643.5 454.33,-634.89 454.54,-623.84"/>
<polygon fill="black" stroke="black" stroke-width="1.5" points="458.05,-623.66 454.72,-613.6 451.05,-623.53 458.05,-623.66"/>
</a>
</g>
</g>
<!-- placeholder_cluster_build_page_3 -->
<g id="node27" class="node">
<title>placeholder_cluster_build_page_3</title>
<g id="a_node27"><a xlink:title=" ">
<ellipse fill="none" stroke="none" cx="459" cy="-481.1" rx="27" ry="18"/>
<text text-anchor="middle" x="459" y="-477.4" font-family="inherit" font-size="14.00">...</text>
</a>
</g>
</g>
<!-- pages_point_c&#45;&gt;placeholder_cluster_build_page_3 -->
<g id="edge31" class="edge">
<title>pages_point_c&#45;&gt;placeholder_cluster_build_page_3:n</title>
<g id="a_edge31"><a xlink:title=" ">
<path fill="none" stroke="black" stroke-width="1.5" stroke-dasharray="5,2" d="M455.41,-646.55C461.75,-644.01 487.69,-632.65 497,-613.6 511.63,-583.65 509.66,-569.44 497,-538.6 496.96,-538.49 496.91,-538.38 496.86,-538.27"/>
<polygon fill="black" stroke="black" stroke-width="1.5" points="499.88,-536.48 491.42,-530.1 494.05,-540.36 499.88,-536.48"/>
</a>
</g>
</g>
<!-- render -->
<g id="node23" class="node">
<title>render</title>
<g id="a_node23"><a xlink:title=" ">
<ellipse fill="#77ff77" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.600000" cx="148" cy="-408.6" rx="38.19" ry="18"/>
<text text-anchor="middle" x="148" y="-404.9" font-family="inherit" font-size="14.00">render</text>
</a>
</g>
</g>
<!-- on_page_context&#45;&gt;render -->
<g id="edge24" class="edge">
<title>on_page_context:s&#45;&gt;render</title>
<g id="a_edge24"><a xlink:title=" ">
<path fill="none" stroke="black" d="M207,-462.1C207,-447.17 196.33,-435.45 184.06,-426.9"/>
<polygon fill="black" stroke="black" points="185.76,-423.83 175.43,-421.49 182.04,-429.76 185.76,-423.83"/>
</a>
</g>
</g>
<!-- on_post_page -->
<g id="node21" class="node">
<title>on_post_page</title>
<g id="a_node21"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="117,-315.1 117,-351.1 283,-351.1 283,-315.1 117,-315.1"/>
<text text-anchor="middle" x="148" y="-329.4" font-family="inherit" font-size="14.00">output</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="179,-315.1 179,-351.1"/>
<text text-anchor="middle" x="203" y="-329.4" font-family="inherit" font-size="14.00">page</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="227,-315.1 227,-351.1"/>
<text text-anchor="middle" x="255" y="-329.4" font-family="inherit" font-size="14.00">config</text>
</a>
</g>
</g>
<!-- write_file -->
<g id="node25" class="node">
<title>write_file</title>
<g id="a_node25"><a xlink:title=" ">
<ellipse fill="#77ff77" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.600000" cx="148" cy="-260.6" rx="50.09" ry="18"/>
<text text-anchor="middle" x="148" y="-256.9" font-family="inherit" font-size="14.00">write_file</text>
</a>
</g>
</g>
<!-- on_post_page&#45;&gt;write_file -->
<g id="edge27" class="edge">
<title>on_post_page:s&#45;&gt;write_file</title>
<g id="a_edge27"><a xlink:title=" ">
<path fill="none" stroke="black" d="M148,-314.1C148,-305.86 148,-296.9 148,-288.74"/>
<polygon fill="black" stroke="black" points="151.5,-288.62 148,-278.62 144.5,-288.62 151.5,-288.62"/>
</a>
</g>
</g>
<!-- get_context&#45;&gt;on_page_context -->
<g id="edge23" class="edge">
<title>get_context&#45;&gt;on_page_context:n</title>
<g id="a_edge23"><a xlink:title=" ">
<path fill="none" stroke="black" d="M219.67,-546.63C214.89,-536.68 209.68,-523.49 207.76,-510.21"/>
<polygon fill="black" stroke="black" points="211.24,-509.81 207,-500.1 204.26,-510.34 211.24,-509.81"/>
</a>
</g>
</g>
<!-- render&#45;&gt;on_post_page -->
<g id="edge26" class="edge">
<title>render&#45;&gt;on_post_page:n</title>
<g id="a_edge26"><a xlink:title=" ">
<path fill="none" stroke="black" d="M148,-390.6C148,-382.44 148,-372.3 148,-362.33"/>
<polygon fill="black" stroke="black" points="151.5,-362.1 148,-352.1 144.5,-362.1 151.5,-362.1"/>
</a>
</g>
</g>
<!-- get_template -->
<g id="node24" class="node">
<title>get_template</title>
<g id="a_node24"><a xlink:title=" ">
<ellipse fill="#77ff77" fill-opacity="0.533333" stroke="#000000" stroke-opacity="0.600000" cx="90" cy="-481.1" rx="66.09" ry="18"/>
<text text-anchor="middle" x="90" y="-477.4" font-family="inherit" font-size="14.00">get_template</text>
</a>
</g>
</g>
<!-- get_template&#45;&gt;render -->
<g id="edge25" class="edge">
<title>get_template&#45;&gt;render</title>
<g id="a_edge25"><a xlink:title=" ">
<path fill="none" stroke="black" d="M103.75,-463.39C111.07,-454.49 120.22,-443.37 128.29,-433.56"/>
<polygon fill="black" stroke="black" points="131.1,-435.65 134.75,-425.7 125.7,-431.2 131.1,-435.65"/>
</a>
</g>
</g>
<!-- on_post_build -->
<g id="node28" class="node">
<title>on_post_build</title>
<g id="a_node28"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="120,-159.1 120,-195.1 176,-195.1 176,-159.1 120,-159.1"/>
<text text-anchor="middle" x="148" y="-173.4" font-family="inherit" font-size="14.00">config</text>
</a>
</g>
</g>
<!-- write_file&#45;&gt;on_post_build -->
<!-- placeholder_cluster_build_page_2&#45;&gt;placeholder_cluster_build_page_3 -->
<!-- on_serve -->
<g id="node29" class="node">
<title>on_serve</title>
<g id="a_node29"><a xlink:title=" ">
<polygon fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" points="92,-67.1 92,-103.1 204,-103.1 204,-67.1 92,-67.1"/>
<text text-anchor="middle" x="120" y="-81.4" font-family="inherit" font-size="14.00">server</text>
<polyline fill="none" stroke="#000000" stroke-opacity="0.600000" points="148,-67.1 148,-103.1"/>
<text text-anchor="middle" x="176" y="-81.4" font-family="inherit" font-size="14.00">config</text>
</a>
</g>
</g>
<!-- on_post_build&#45;&gt;on_serve -->
<!-- on_shutdown -->
<g id="node30" class="node">
<title>on_shutdown</title>
<g id="a_node30"><a xlink:title=" ">
<ellipse fill="#ffffff" fill-opacity="0.333333" stroke="#000000" stroke-opacity="0.600000" cx="148" cy="-17.8" rx="1.8" ry="1.8"/>
</a>
</g>
</g>
<!-- on_serve&#45;&gt;on_shutdown -->
</g>
</svg>

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,89 @@
# MkDocs
Project documentation with&nbsp;Markdown.
---
MkDocs is a **fast**, **simple** and **downright gorgeous** static site
generator that's geared towards building project documentation. Documentation
source files are written in Markdown, and configured with a single YAML
configuration file. Start by reading the [introductory tutorial], then check the
[User Guide] for more information.
[introductory tutorial]: getting-started.md
[User Guide]: user-guide/README.md
<div class="text-center">
<a href="getting-started/" class="btn btn-primary" role="button">Getting Started</a>
<a href="user-guide/" class="btn btn-primary" role="button">User Guide</a>
</div>
<div class="pt-2 pb-4 px-4 my-4 bg-body-tertiary rounded-3">
<h2 class="display-4 text-center">Features</h2>
<div class="row">
<div class="col-sm-6">
<div class="card mb-4">
<div class="card-body">
<h3 class="card-title">Great themes available</h3>
<p class="card-text">
There's a stack of good looking <a href="user-guide/choosing-your-theme">themes</a> available for MkDocs.
Choose between the built in themes:
<a href="user-guide/choosing-your-theme/#mkdocs">mkdocs</a> and
<a href="user-guide/choosing-your-theme/#readthedocs">readthedocs</a>,
select one of the third-party themes
(on the <a href="https://github.com/mkdocs/mkdocs/wiki/MkDocs-Themes">MkDocs Themes</a> wiki page
as well as the <a href="https://github.com/mkdocs/catalog#-theming">MkDocs Catalog</a>),
or <a href="dev-guide/themes/">build your own</a>.
</p>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="card mb-4">
<div class="card-body">
<h3 class="card-title">Easy to customize</h3>
<p class="card-text">
Get your project documentation looking just the way you want it by
<a href="user-guide/customizing-your-theme/">customizing your
theme</a> and/or installing some <a
href="user-guide/configuration/#plugins">plugins</a>. Modify
Markdown's behavior with <a
href="user-guide/configuration/#markdown_extensions">Markdown
extensions</a>. Many <a
href="user-guide/configuration/">configuration options</a> are
available.
</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="card">
<div class="card-body">
<h3 class="card-title">Preview your site as you work</h3>
<p class="card-text">
The built-in dev-server allows you to preview your documentation
as you're writing it. It will even auto-reload and refresh your
browser whenever you save your changes.
</p>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="card">
<div class="card-body">
<h3 class="card-title">Host anywhere</h3>
<p class="card-text">
MkDocs builds completely static HTML sites that you can host on
GitHub Pages, Amazon S3, or <a
href="user-guide/deploying-your-docs/">anywhere</a> else you
choose.
</p>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,21 @@
# User Guide
Building Documentation with MkDocs
---
The MkDocs User Guide provides documentation for users of MkDocs. See
[Getting Started] for an introductory tutorial. You can jump directly to a
page listed below, or use the *next* and *previous* buttons in the navigation
bar at the top of the page to move through the documentation in order.
- [Installation](installation.md)
- [Writing Your Docs](writing-your-docs.md)
- [Choosing Your Theme](choosing-your-theme.md)
- [Customizing Your Theme](customizing-your-theme.md)
- [Localizing Your Theme](localizing-your-theme.md)
- [Configuration](configuration.md)
- [Command Line Interface](cli.md)
- [Deploying Your Docs](deploying-your-docs.md)
[Getting Started]: ../getting-started.md

View File

@@ -0,0 +1,229 @@
# Choosing your Theme
Selecting and configuring a theme.
---
MkDocs includes two built-in themes ([mkdocs](#mkdocs) and
[readthedocs](#readthedocs)), as documented below. However, many [third party
themes] are available to choose from as well.
To choose a theme, set the [theme] configuration option in your `mkdocs.yml`
config file.
```yaml
theme:
name: readthedocs
```
## mkdocs
The default theme, which was built as a custom [Bootstrap] theme, supports almost
every feature of MkDocs.
<div id="mkdocs-theme-images" class="carousel slide carousel-fade" data-bs-ride="carousel">
<div class="carousel-inner">
<div class="carousel-item active">
<img src="../../img/mkdocs_theme_light_mode.png" class="d-block w-100" alt="MkDocs theme in light mode">
</div>
<div class="carousel-item">
<img src="../../img/mkdocs_theme_dark_mode.png" class="d-block w-100" alt="MkDocs theme in dark mode">
</div>
</div>
</div>
In addition to the default [theme configuration options][theme], the `mkdocs` theme
supports the following options:
* **`color_mode`**: Set the default color mode for the theme to one of `light`,
`dark`, or `auto`. The `auto` mode will switch to `light` or `dark` based on
the system configuration of the user's device. Default: `light`.
* **`user_color_mode_toggle`**: Enable a toggle menu in the navigation bar
which allows users to select their preferred `color_mode` (light, dark, auto)
from within the browser and save their preference for future page loads. The
default selection of the toggle menu on first page load is the value set to
`color_mode`. Default: `false`.
![color mode toggle menu](../img/color_mode_toggle_menu.png)
* **`nav_style`**: Adjust the visual style of the top navigation bar. Set to
one of `primary`, `dark` or `light`. Default: `primary`. This option is
independent of the `color_mode` option and must be defined separately.
* **`highlightjs`**: Enables highlighting of source code in code blocks using
the [highlight.js] JavaScript library. Default: `True`.
* **`hljs_style`**: The highlight.js library provides many different [styles]
(color variations) for highlighting source code in code blocks. Set this to
the name of the desired style when in `light` mode. Default: `github`.
* **`hljs_style_dark`**: Set this to the name of the desired highlight.js
style when in `dark` mode. Default: `github_dark`.
* **`hljs_languages`**: By default, highlight.js only supports 23 common
languages. List additional languages here to include support for them.
```yaml
theme:
name: mkdocs
highlightjs: true
hljs_languages:
- yaml
- rust
```
* **`analytics`**: Defines configuration options for an analytics service.
Currently, only Google Analytics v4 is supported via the `gtag` option.
* **`gtag`**: To enable Google Analytics, set to a Google Analytics v4
tracking ID, which uses the `G-` format. See Google's documentation to
[Set up Analytics for a website and/or app (GA4)][setup-GA4] or to
[Upgrade to a Google Analytics 4 property][upgrade-GA4].
```yaml
theme:
name: mkdocs
analytics:
gtag: G-ABC123
```
When set to the default (`null`) Google Analytics is disabled for the
site.
* **`shortcuts`**: Defines keyboard shortcut keys.
```yaml
theme:
name: mkdocs
shortcuts:
help: 191 # ?
next: 78 # n
previous: 80 # p
search: 83 # s
```
All values must be numeric key codes. It is best to use keys that are
available on all keyboards. You may use <https://keycode.info/> to determine
the key code for a given key.
* **`help`**: Display a help modal that lists the keyboard shortcuts.
Default: `191` (&quest;)
* **`next`**: Navigate to the "next" page. Default: `78` (n)
* **`previous`**: Navigate to the "previous" page. Default: `80` (p)
* **`search`**: Display the search modal. Default: `83` (s)
* **`navigation_depth`**: The maximum depth of the navigation tree in the
sidebar. Default: `2`.
* **`locale`**{ #mkdocs-locale }: The locale (language/location) used to
build the theme. If your locale is not yet supported, it will fall back
to the default.
The following locales are supported by this theme:
* `en`: English (default)
* (see the list of existing directories `mkdocs/themes/mkdocs/locales/*/`)
See the guide on [localizing your theme] for more information.
## readthedocs
A clone of the default theme used by the [Read the Docs] service, which offers
the same restricted feature set as its parent theme. Like its parent theme, only
two levels of navigation are supported.
![ReadTheDocs](../img/readthedocs.png)
In addition to the default [theme configuration options][theme], the `readthedocs`
theme supports the following options:
* **`highlightjs`**: Enables highlighting of source code in code blocks using
the [highlight.js] JavaScript library. Default: `True`.
* **`hljs_languages`**: By default, highlight.js only supports 23 common
languages. List additional languages here to include support for them.
```yaml
theme:
name: readthedocs
highlightjs: true
hljs_languages:
- yaml
- rust
```
* **`analytics`**: Defines configuration options for an analytics service.
* **`gtag`**: To enable Google Analytics, set to a Google Analytics v4
tracking ID, which uses the `G-` format. See Google's documentation to
[Set up Analytics for a website and/or app (GA4)][setup-GA4] or to
[Upgrade to a Google Analytics 4 property][upgrade-GA4].
```yaml
theme:
name: readthedocs
analytics:
gtag: G-ABC123
```
When set to the default (`null`) Google Analytics is disabled for the
* **`anonymize_ip`**: To enable anonymous IP address for Google Analytics,
set this to `True`. Default: `False`.
* **`include_homepage_in_sidebar`**: Lists the homepage in the sidebar menu. As
MkDocs requires that the homepage be listed in the `nav` configuration
option, this setting allows the homepage to be included or excluded from
the sidebar. Note that the site name/logo always links to the homepage.
Default: `True`.
* **`prev_next_buttons_location`**: One of `bottom`, `top`, `both` , or `none`.
Displays the “Next” and “Previous” buttons accordingly. Default: `bottom`.
* **`navigation_depth`**: The maximum depth of the navigation tree in the
sidebar. Default: `4`.
* **`collapse_navigation`**: Only include the page section headers in the
sidebar for the current page. Default: `True`.
* **`titles_only`**: Only include page titles in the sidebar, excluding all
section headers for all pages. Default: `False`.
* **`sticky_navigation`**: If True, causes the sidebar to scroll with the main
page content as you scroll the page. Default: `True`.
* **`locale`**{ #readthedocs-locale }: The locale (language/location) used to
build the theme. If your locale is not yet supported, it will fall back
to the default.
The following locales are supported by this theme:
* `en`: English (default)
* (see the list of existing directories `mkdocs/themes/readthedocs/locales/*/`)
See the guide on [localizing your theme] for more information.
* **`logo`**: To set a logo on your project instead of the plain text
`site_name`, set this variable to be the location of your image. Default: `null`.
## Third Party Themes
A list of third party themes can be found at the [community wiki] page and [the ranked catalog][catalog]. If you have created your own, please add them there.
WARNING: Installing an MkDocs theme means installing a Python package and executing any code that the author has put in there. So, exercise the usual caution; there's no attempt at sandboxing.
[third party themes]: #third-party-themes
[theme]: configuration.md#theme
[Bootstrap]: https://getbootstrap.com/
[highlight.js]: https://highlightjs.org/
[styles]: https://highlightjs.org/static/demo/
[setup-GA4]: https://support.google.com/analytics/answer/9304153?hl=en&ref_topic=9303319
[upgrade-GA4]: https://support.google.com/analytics/answer/9744165?hl=en&ref_topic=9303319
[Read the Docs]: https://readthedocs.org/
[community wiki]: https://github.com/mkdocs/mkdocs/wiki/MkDocs-Themes
[catalog]: https://github.com/mkdocs/catalog#-theming
[localizing your theme]: localizing-your-theme.md

View File

@@ -0,0 +1,8 @@
# Command Line Interface
::: mkdocs-click
:module: mkdocs.__main__
:command: cli
:prog_name: mkdocs
:style: table
:list_subcommands: true

View File

@@ -0,0 +1,226 @@
# Customizing Your Theme
Altering a theme to suit your needs.
---
If you would like to make a few tweaks to an existing theme, there is no need
to create your own theme from scratch. For minor tweaks which only require
some CSS and/or JavaScript, you can [use the docs_dir](#using-the-docs_dir).
However, for more complex customizations, including overriding templates, you
will need to [use the theme custom_dir](#using-the-theme-custom_dir) setting.
## Using the docs_dir
The [extra_css] and [extra_javascript] configuration options can be used to
make tweaks and customizations to existing themes. To use these, you simply
need to include either CSS or JavaScript files within your [documentation
directory].
For example, to change the color of the headers in your documentation, create
a file called (for example) `style.css` and place it next to the documentation Markdown. In
that file add the following CSS.
```css
h1 {
color: red;
}
```
Then you need to add it to `mkdocs.yml`:
```yaml
extra_css:
- style.css
```
After making these changes, they should be visible when you run
`mkdocs serve` - if you already had this running, you should see that the CSS
changes were automatically picked up and the documentation will be updated.
NOTE:
Any extra CSS or JavaScript files will be added to the generated HTML
document after the page content. If you desire to include a JavaScript
library, you may have better success including the library by using the
theme [custom_dir].
## Using the theme custom_dir
The [`theme.custom_dir`][custom_dir] configuration option can be used to point
to a directory of files which override the files in a parent theme. The parent
theme would be the theme defined in the [`theme.name`][name] configuration
option. Any file in the `custom_dir` with the same name as a file in the
parent theme will replace the file of the same name in the parent theme. Any
additional files in the `custom_dir` will be added to the parent theme. The
contents of the `custom_dir` should mirror the directory structure of the
parent theme. You may include templates, JavaScript files, CSS files, images,
fonts, or any other media included in a theme.
NOTE:
For this to work, the `theme.name` setting must be set to a known
installed theme. If the `name` setting is instead set to `null` (or not
defined), then there is no theme to override and the contents of the
`custom_dir` must be a complete, standalone theme. See the [Theme
Developer Guide][custom theme] for more information.
For example, the [mkdocs] theme ([browse source]), contains the following
directory structure (in part):
```text
- css\
- fonts\
- img\
- favicon.ico
- grid.png
- js\
- 404.html
- base.html
- content.html
- nav-sub.html
- nav.html
- toc.html
```
To override any of the files contained in that theme, create a new directory
next to your `docs_dir`:
```bash
mkdir custom_theme
```
And then point your `mkdocs.yml` configuration file at the new directory:
```yaml
theme:
name: mkdocs
custom_dir: custom_theme/
```
To override the 404 error page ("file not found"), add a new template file named
`404.html` to the `custom_theme` directory. For information on what can be
included in a template, review the [Theme Developer Guide][custom theme].
To override the favicon, you can add a new icon file at
`custom_theme/img/favicon.ico`.
To include a JavaScript library, copy the library to the `custom_theme/js/`
directory.
Your directory structure should now look like this:
```text
- docs/
- index.html
- custom_theme/
- img/
- favicon.ico
- js/
- somelib.js
- 404.html
- config.yml
```
NOTE:
Any files included in the parent theme (defined in `name`) but not
included in the `custom_dir` will still be utilized. The `custom_dir` will
only override/replace files in the parent theme. If you want to remove
files, or build a theme from scratch, then you should review the [Theme
Developer Guide][custom theme].
### Overriding Template Blocks
The built-in themes implement many of their parts inside template blocks which
can be individually overridden in the `main.html` template. Simply create a
`main.html` template file in your `custom_dir` and define replacement blocks
within that file. Just make sure that the `main.html` extends `base.html`. For
example, to alter the title of the MkDocs theme, your replacement `main.html`
template would contain the following:
```django
{% extends "base.html" %}
{% block htmltitle %}
<title>Custom title goes here</title>
{% endblock %}
```
In the above example, the `htmltitle` block defined in your custom `main.html` file
will be used in place of the default `htmltitle` block defined in the parent theme.
You may re-define as many blocks as you desire, as long as those blocks are
defined in the parent. For example, you could replace the Google Analytics
script with one for a different service or replace the search feature with your
own. You will need to consult the parent theme you are using to determine what
blocks are available to override. The MkDocs and ReadTheDocs themes provide the
following blocks:
* `site_meta`: Contains meta tags in the document head.
* `htmltitle`: Contains the page title in the document head.
* `styles`: Contains the link tags for stylesheets.
* `libs`: Contains the JavaScript libraries (jQuery, etc) included in the page header.
* `scripts`: Contains JavaScript scripts which should execute after a page loads.
* `analytics`: Contains the analytics script.
* `extrahead`: An empty block in the `<head>` to insert custom tags/scripts/etc.
* `site_name`: Contains the site name in the navigation bar.
* `site_nav`: Contains the site navigation in the navigation bar.
* `search_button`: Contains the search box in the navigation bar.
* `next_prev`: Contains the next and previous buttons in the navigation bar.
* `repo`: Contains the repository link in the navigation bar.
* `content`: Contains the page content and table of contents for the page.
* `footer`: Contains the page footer.
You may need to view the source template files to ensure your modifications will
work with the structure of the site. See [Template Variables] for a list of
variables you can use within your custom blocks. For a more complete
explanation of blocks, consult the [Jinja documentation].
### Combining the custom_dir and Template Blocks
Adding a JavaScript library to the `custom_dir` will make it available, but
won't include it in the pages generated by MkDocs. Therefore, a link needs to
be added to the library from the HTML.
Starting the with directory structure above (truncated):
```text
- docs/
- custom_theme/
- js/
- somelib.js
- config.yml
```
A link to the `custom_theme/js/somelib.js` file needs to be added to the
template. As `somelib.js` is a JavaScript library, it would logically go in the
`libs` block. However, a new `libs` block that only includes the new script will
replace the block defined in the parent template and any links to libraries in
the parent template will be removed. To avoid breaking the template, a
[super block] can be used with a call to `super` from within the block:
```django
{% extends "base.html" %}
{% block libs %}
{{ super() }}
<script src="{{ base_url }}/js/somelib.js"></script>
{% endblock %}
```
Note that the [base_url] template variable was used to ensure that the link is
always relative to the current page.
Now the generated pages will include links to the template provided libraries as
well as the library included in the `custom_dir`. The same would be required for
any additional CSS files included in the `custom_dir`.
[custom theme]: ../dev-guide/themes.md
[extra_css]: ./configuration.md#extra_css
[extra_javascript]: ./configuration.md#extra_javascript
[documentation directory]: ./configuration.md#docs_dir
[custom_dir]: ./configuration.md#custom_dir
[name]: ./configuration.md#name
[mkdocs]: ./choosing-your-theme.md#mkdocs
[browse source]: https://github.com/mkdocs/mkdocs/tree/master/mkdocs/themes/mkdocs
[Template Variables]: ../dev-guide/themes.md#template-variables
[Jinja documentation]: https://jinja.palletsprojects.com/en/latest/templates/#template-inheritance
[super block]: https://jinja.palletsprojects.com/en/latest/templates/#super-blocks
[base_url]: ../dev-guide/themes.md#base_url

View File

@@ -0,0 +1,223 @@
# Deploying your docs
A basic guide to deploying your docs to various hosting providers
---
## GitHub Pages
If you host the source code for a project on [GitHub], you can easily use
[GitHub Pages] to host the documentation for your project. There are two basic
[types of GitHub Pages sites]: Project Pages sites, and User and Organization
Pages sites. They are nearly identical but have some important differences,
which require a different work flow when deploying.
### Project Pages
Project Pages sites are simpler as the site files get deployed to a branch
within the project repository (`gh-pages` by default). After you `checkout` the
primary working branch (usually `master`) of the git repository where you
maintain the source documentation for your project, run the following command:
```sh
mkdocs gh-deploy
```
That's it! Behind the scenes, MkDocs will build your docs and use the
[ghp-import] tool to commit them to the `gh-pages` branch and push the
`gh-pages` branch to GitHub.
Use `mkdocs gh-deploy --help` to get a full list of options available for the
`gh-deploy` command.
Be aware that you will not be able to review the built site before it is pushed
to GitHub. Therefore, you may want to verify any changes you make to the docs
beforehand by using the `build` or `serve` commands and reviewing the built
files locally.
WARNING:
You should never edit files in your pages repository by hand if you're using
the `gh-deploy` command because you will lose your work the next time you
run the command.
WARNING:
If there are untracked files or uncommitted work in the local repository where
`mkdocs gh-deploy` is run, these will be included in the pages that are deployed.
### Organization and User Pages
User and Organization Pages sites are not tied to a specific project, and the
site files are deployed to the `master` branch in a dedicated repository named
with the GitHub account name. Therefore, you need working copies of two
repositories on our local system. For example, consider the following file
structure:
```text
my-project/
mkdocs.yml
docs/
orgname.github.io/
```
After making and verifying updates to your project you need to change
directories to the `orgname.github.io` repository and call the
`mkdocs gh-deploy` command from there:
```sh
cd ../orgname.github.io/
mkdocs gh-deploy --config-file ../my-project/mkdocs.yml --remote-branch master
```
Note that you need to explicitly point to the `mkdocs.yml` configuration file as
it is no longer in the current working directory. You also need to inform the
deploy script to commit to the `master` branch. You may override the default
with the [remote_branch] configuration setting, but if you forget to change
directories before running the deploy script, it will commit to the `master`
branch of your project, which you probably don't want.
### Custom Domains
GitHub Pages includes support for using a [Custom Domain] for your site. In
addition to the steps documented by GitHub, you need to take one additional step
so that MkDocs will work with your custom domain. You need to add a `CNAME` file
to the root of your [docs_dir]. The file must contain a single bare domain or
subdomain on a single line (see MkDocs' own [CNAME file] as an example). You may
create the file manually, or use GitHub's web interface to set up the custom
domain (under Settings / Custom Domain). If you use the web interface, GitHub
will create the `CNAME` file for you and save it to the root of your "pages"
branch. So that the file does not get removed the next time you deploy, you need
to copy the file to your `docs_dir`. With the file properly included in your
`docs_dir`, MkDocs will include the file in your built site and push it to your
"pages" branch each time you run the `gh-deploy` command.
If you are having problems getting a custom domain to work, see GitHub's
documentation on [Troubleshooting custom domains].
[GitHub]: https://github.com/
[GitHub Pages]: https://pages.github.com/
[types of GitHub Pages sites]: https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages#types-of-github-pages-sites
[ghp-import]: https://github.com/davisp/ghp-import
[remote_branch]: ./configuration.md#remote_branch
[Custom Domain]: https://help.github.com/articles/adding-or-removing-a-custom-domain-for-your-github-pages-site
[docs_dir]: ./configuration.md#docs_dir
[CNAME file]: https://github.com/mkdocs/mkdocs/blob/master/docs/CNAME
[Troubleshooting custom domains]: https://help.github.com/articles/troubleshooting-custom-domains/
## Read the Docs
[Read the Docs][rtd] offers free documentation hosting. You can import your docs
using the Git version control system. Read the Docs supports MkDocs out-of-the-box.
Follow the [instructions] on their site to arrange the files in your repository properly,
create an account and point it at your publicly hosted repository. If properly
configured, your documentation will update each time you push commits to your
public repository.
[rtd]: https://readthedocs.org/
[instructions]: https://docs.readthedocs.io/en/stable/intro/getting-started-with-mkdocs.html
## Other Providers
Any hosting provider which can serve static files can be used to serve
documentation generated by MkDocs. While it would be impossible to document how
to upload the docs to every hosting provider out there, the following guidelines
should provide some general assistance.
When you build your site (using the `mkdocs build` command), all of the files
are written to the directory assigned to the [site_dir] configuration option
(defaults to `"site"`) in your `mkdocs.yaml` config file. Generally, you will
simply need to copy the contents of that directory to the root directory of your
hosting provider's server. Depending on your hosting provider's setup, you may
need to use a graphical or command line [ftp], [ssh] or [scp] client to transfer
the files.
For example, a typical set of commands from the command line might look
something like this:
```sh
mkdocs build
scp -r ./site user@host:/path/to/server/root
```
Of course, you will need to replace `user` with the username you have with your
hosting provider and `host` with the appropriate domain name. Additionally, you
will need to adjust the `/path/to/server/root` to match the configuration of
your hosts' file system.
[ftp]: https://en.wikipedia.org/wiki/File_Transfer_Protocol
[ssh]: https://en.wikipedia.org/wiki/Secure_Shell
[scp]: https://en.wikipedia.org/wiki/Secure_copy
See your host's documentation for specifics. You will likely want to search
their documentation for "ftp" or "uploading site".
## Local Files
Rather than hosting your documentation on a server, you may instead distribute
the files directly, which can then be viewed in a browser using the `file://`
scheme.
Note that, due to the security settings of all modern browsers, some things
will not work the same and some features may not work at all. In fact, a few
settings will need to be customized in very specific ways.
- [site_url]:
The `site_url` must be set to an empty string, which instructs MkDocs to
build your site so that it will work with the `file://` scheme.
```yaml
site_url: ""
```
- [use_directory_urls]:
Set `use_directory_urls` to `false`. Otherwise, internal links between
pages will not work properly.
```yaml
use_directory_urls: false
```
- [search]:
You will need to either disable the search plugin, or use a third-party
search plugin which is specifically designed to work with the `file://`
scheme. To disable all plugins, set the `plugins` setting to an empty list.
```yaml
plugins: []
```
If you have other plugins enabled, simply ensure that `search` is not
included in the list.
When writing your documentation, it is imperative that all internal links use
relative URLs as [documented][internal links]. Remember, each reader of your
documentation will be using a different device and the files will likely be in a
different location on that device.
If you expect your documentation to be viewed off-line, you may also need to be
careful about which themes you choose. Many themes make use of CDNs for various
support files, which require a live Internet connection. You will need to choose
a theme which includes all support files directly in the theme.
When you build your site (using the `mkdocs build` command), all of the files
are written to the directory assigned to the [site_dir] configuration option
(defaults to `"site"`) in your `mkdocs.yaml` config file. Generally, you will
simply need to copy the contents of that directory and distribute it to your
readers. Alternatively, you may choose to use a third party tool to convert the
HTML files to some other documentation format.
## 404 Pages
When MkDocs builds the documentation it will include a 404.html file in the
[build directory][site_dir]. This file will be automatically used when
deploying to [GitHub](#github-pages) but only on a custom domain. Other web
servers may be configured to use it but the feature won't always be available.
See the documentation for your server of choice for more information.
[site_dir]: ./configuration.md#site_dir
[site_url]: ./configuration.md#site_url
[use_directory_urls]: ./configuration.md#use_directory_urls
[search]: ./configuration.md#search
[internal links]: ./writing-your-docs.md#internal-links

View File

@@ -0,0 +1,107 @@
# MkDocs Installation
A detailed guide.
---
## Requirements
MkDocs requires a recent version of [Python] and the Python package
manager, [pip], to be installed on your system.
You can check if you already have these installed from the command line:
```console
$ python --version
Python 3.8.2
$ pip --version
pip 20.0.2 from /usr/local/lib/python3.8/site-packages/pip (python 3.8)
```
If you already have those packages installed, you may skip down to [Installing
MkDocs](#installing-mkdocs).
### Installing Python
Install [Python] using your package manager of choice, or by downloading an
installer appropriate for your system from [python.org] and running it.
> NOTE:
> If you are installing Python on Windows, be sure to check the box to have
> Python added to your PATH if the installer offers such an option (it's
> normally off by default).
>
> ![Add Python to PATH](../img/win-py-install.png)
### Installing pip
If you're using a recent version of Python, the Python package manager, [pip],
is most likely installed by default. However, you may need to upgrade pip to the
lasted version:
```bash
pip install --upgrade pip
```
If you need to install pip for the first time, download [get-pip.py].
Then run the following command to install it:
```bash
python get-pip.py
```
## Installing MkDocs
Install the `mkdocs` package using pip:
```bash
pip install mkdocs
```
You should now have the `mkdocs` command installed on your system. Run `mkdocs
--version` to check that everything worked okay.
```console
$ mkdocs --version
mkdocs, version 1.2.0 from /usr/local/lib/python3.8/site-packages/mkdocs (Python 3.8)
```
> NOTE:
> If you would like manpages installed for MkDocs, the [click-man] tool can
> generate and install them for you. Simply run the following two commands:
>
> ```bash
> pip install click-man
> click-man --target path/to/man/pages mkdocs
> ```
>
> See the [click-man documentation] for an explanation of why manpages are
> not automatically generated and installed by pip.
<!-- -->
> NOTE:
> If you are using Windows, some of the above commands may not work
> out-of-the-box.
>
> A quick solution may be to preface every Python command with `python -m`
> like this:
>
> ```bash
> python -m pip install mkdocs
> python -m mkdocs
> ```
>
> For a more permanent solution, you may need to edit your `PATH` environment
> variable to include the `Scripts` directory of your Python installation.
> Recent versions of Python include a script to do this for you. Navigate to
> your Python installation directory (for example `C:\Python38\`), open the
> `Tools`, then `Scripts` folder, and run the `win_add2path.py` file by double
> clicking on it. Alternatively, you can download the [script][a2p] and run it
> (`python win_add2path.py`).
[Python]: https://www.python.org/
[python.org]: https://www.python.org/downloads/
[pip]: https://pip.readthedocs.io/en/stable/installing/
[get-pip.py]: https://bootstrap.pypa.io/get-pip.py
[click-man]: https://github.com/click-contrib/click-man
[click-man documentation]: https://github.com/click-contrib/click-man#automatic-man-page-installation-with-setuptools-and-pip
[a2p]: https://github.com/python/cpython/blob/master/Tools/scripts/win_add2path.py

View File

@@ -0,0 +1,63 @@
# Localizing Your Theme
Display your theme in your preferred language.
---
NOTE:
Theme localization only translates the text elements of the theme itself
(such as "next" and "previous" links), not the actual content of your
documentation. If you wish to create multilingual documentation, you need
to combine theme localization as described here with a third-party
internationalization/localization plugin.
## Installation
For theme localization to work, you must use a theme which supports it and
enable `i18n` (internationalization) support by installing `mkdocs[i18n]`:
```bash
pip install 'mkdocs[i18n]'
```
## Supported locales
In most cases a locale is designated by the [ISO-639-1] (2-letter) abbreviation
for your language. However, a locale may also include a territory (or region or
county) code as well. The language and territory must be separated by an
underscore. For example, some possible locales for English might include `en`,
`en_AU`, `en_GB`, and `en_US`.
For a list of locales supported by the theme you are using, see that theme's
documentation.
- [mkdocs](choosing-your-theme.md#mkdocs-locale)
- [readthedocs](choosing-your-theme.md#readthedocs-locale)
WARNING:
If you configure a language locale which is not yet supported by the theme
that you are using, MkDocs will fall back to the theme's default locale.
## Usage
To specify the locale that MkDocs should use, set the [locale]
parameter of the [theme] configuration option to the appropriate code.
For example, to build the `mkdocs` theme in French you would use the following
in your `mkdocs.yml` configuration file:
```yaml
theme:
name: mkdocs
locale: fr
```
## Contributing theme translations
If a theme has not yet been translated into your language, feel free to
contribute a translation using the [Translation Guide].
[Translation Guide]: ../dev-guide/translations.md
[locale]: configuration.md#locale
[theme]: configuration.md#theme
[ISO-639-1]: https://en.wikipedia.org/wiki/ISO_639-1

View File

@@ -0,0 +1,540 @@
# Writing your docs
How to layout and write your Markdown source files.
---
## File layout
Your documentation source should be written as regular Markdown files (see
[Writing with Markdown](#writing-with-markdown) below), and placed in the
[documentation directory](configuration.md#docs_dir). By default, this directory
will be named `docs` and will exist at the top level of your project, alongside
the `mkdocs.yml` configuration file.
The simplest project you can create will look something like this:
```text
mkdocs.yml
docs/
index.md
```
By convention your project homepage should be named `index.md` (see [Index
pages](#index-pages) below for details). Any of the following file
extensions may be used for your Markdown source files: `markdown`, `mdown`,
`mkdn`, `mkd`, `md`. All Markdown files included in your documentation
directory will be rendered in the built site regardless of any settings.
NOTE:
Files and directories with names which begin with a dot (for example: `.foo.md` or `.bar/baz.md`) are ignored by MkDocs. This can be overridden with the [`exclude_docs` config](configuration.md#exclude_docs).
You can also create multi-page documentation, by creating several Markdown
files:
```text
mkdocs.yml
docs/
index.md
about.md
license.md
```
The file layout you use determines the URLs that are used for the generated
pages. Given the above layout, pages would be generated for the following URLs:
```text
/
/about/
/license/
```
You can also include your Markdown files in nested directories if that better
suits your documentation layout.
```text
docs/
index.md
user-guide/getting-started.md
user-guide/configuration-options.md
license.md
```
Source files inside nested directories will cause pages to be generated with
nested URLs, like so:
```text
/
/user-guide/getting-started/
/user-guide/configuration-options/
/license/
```
Any files which are not identified as Markdown files (by their file extension)
within the [documentation directory](configuration.md#docs_dir) are copied by
MkDocs to the built site unaltered. See
[how to link to images and media](#linking-to-images-and-media) below for details.
### Index pages
When a directory is requested, by default, most web servers will return an index
file (usually named `index.html`) contained within that directory if one exists.
For that reason, the homepage in all of the examples above has been named
`index.md`, which MkDocs will render to `index.html` when building the site.
Many repository hosting sites provide special treatment for README files by
displaying the contents of the README file when browsing the contents of a
directory. Therefore, MkDocs will allow you to name your index pages as
`README.md` instead of `index.md`. In that way, when users are browsing your
source code, the repository host can display the index page of that directory as
it is a README file. However, when MkDocs renders your site, the file will be
renamed to `index.html` so that the server will serve it as a proper index file.
If both an `index.md` file and a `README.md` file are found in the same
directory, then the `index.md` file is used and the `README.md` file is
ignored.
### Configure Pages and Navigation
The [nav](configuration.md#nav) configuration setting in your `mkdocs.yml` file
defines which pages are included in the global site navigation menu as well as
the structure of that menu. If not provided, the navigation will be
automatically created by discovering all the Markdown files in the
[documentation directory](configuration.md#docs_dir). An automatically created
navigation configuration will always be sorted alphanumerically by file name
(except that index files will always be listed first within a sub-section). You
will need to manually define your navigation configuration if you would like
your navigation menu sorted differently.
A minimal navigation configuration could look like this:
```yaml
nav:
- index.md
- about.md
```
All paths in the navigation configuration must be relative to the `docs_dir`
configuration option. If that option is set to the default value, `docs`, the
source files for the above configuration would be located at `docs/index.md` and
`docs/about.md`.
The above example will result in two navigation items being created at the top
level and with their titles inferred from the contents of the Markdown file or,
if no title is defined within the file, of the file name. To override the title
in the `nav` setting add a title right before the filename.
```yaml
nav:
- Home: index.md
- About: about.md
```
Note that if a title is defined for a page in the navigation, that title will be
used throughout the site for that page and will override any title defined
within the page itself.
Navigation sub-sections can be created by listing related pages together under a
section title. For example:
```yaml
nav:
- Home: index.md
- User Guide:
- Writing your docs: writing-your-docs.md
- Styling your docs: styling-your-docs.md
- About:
- License: license.md
- Release Notes: release-notes.md
```
With the above configuration we have three top level items: "Home", "User Guide"
and "About." "Home" is a link to the homepage for the site. Under the "User
Guide" section two pages are listed: "Writing your docs" and "Styling your
docs." Under the "About" section two more pages are listed: "License" and
"Release Notes."
Note that a section cannot have a page assigned to it. Sections are only
containers for child pages and sub-sections. You may nest sections as deeply as
you like. However, be careful that you don't make it too difficult for your
users to navigate through the site navigation by over-complicating the nesting.
While sections may mirror your directory structure, they do not have to.
Any pages not listed in your navigation configuration will still be rendered and
included with the built site, however, they will not be linked from the global
navigation and will not be included in the `previous` and `next` links. Such
pages will be "hidden" unless linked to directly.
## Writing with Markdown
MkDocs pages must be authored in [Markdown][md], a lightweight markup language
which results in easy-to-read, easy-to-write plain text documents that can be
converted to valid HTML documents in a predictable manner.
MkDocs uses the [Python-Markdown] library to render Markdown documents to HTML.
Python-Markdown is almost completely compliant with the [reference
implementation][md], although there are a few very minor [differences].
In addition to the base Markdown [syntax] which is common across all Markdown
implementations, MkDocs includes support for extending the Markdown syntax with
Python-Markdown [extensions]. See the MkDocs' [markdown_extensions]
configuration setting for details on how to enable extensions.
MkDocs includes some extensions by default, which are highlighted below.
[Python-Markdown]: https://python-markdown.github.io/
[md]: https://daringfireball.net/projects/markdown/
[differences]: https://python-markdown.github.io/#differences
[syntax]: https://daringfireball.net/projects/markdown/syntax
[extensions]: https://python-markdown.github.io/extensions/
[markdown_extensions]: configuration.md#markdown_extensions
### Internal links
MkDocs allows you to interlink your documentation by using regular Markdown
[links]. However, there are a few additional benefits to formatting those links
specifically for MkDocs as outlined below.
[links]: https://daringfireball.net/projects/markdown/syntax#link
#### Linking to pages
When linking between pages in the documentation you can simply use the regular
Markdown [linking][links] syntax, including the *relative path* to the Markdown
document you wish to link to.
```markdown
Please see the [project license](license.md) for further details.
```
When the MkDocs build runs, these Markdown links will automatically be
transformed into an HTML hyperlink to the appropriate HTML page.
WARNING:
Using absolute paths with links is not officially supported. Relative paths
are adjusted by MkDocs to ensure they are always relative to the page. Absolute
paths are not modified at all. This means that your links using absolute paths
might work fine in your local environment but they might break once you deploy
them to your production server.
If the target documentation file is in another directory you'll need to make
sure to include any relative directory path in the link.
```markdown
Please see the [project license](../about/license.md) for further details.
```
The [toc] extension is used by MkDocs to generate an ID for every header in your
Markdown documents. You can use that ID to link to a section within a target
document by using an anchor link. The generated HTML will correctly transform
the path portion of the link, and leave the anchor portion intact.
```markdown
Please see the [project license](about.md#license) for further details.
```
Note that IDs are created from the text of a header. All text is converted to
lowercase and any disallowed characters, including white-space, are converted to
dashes. Consecutive dashes are then reduced to a single dash.
There are a few configuration settings provided by the toc extension which you
can set in your `mkdocs.yml` configuration file to alter the default behavior:
* **`permalink`**
Generate permanent links at the end of each header. Default: `False`.
When set to True the paragraph symbol (&para; or `&para;`) is used as the
link text. When set to a string, the provided string is used as the link
text. For example, to use the hash symbol (`#`) instead, do:
```yaml
markdown_extensions:
- toc:
permalink: "#"
```
* **`baselevel`**
Base level for headers. Default: `1`.
This setting allows the header levels to be automatically adjusted to fit
within the hierarchy of your HTML templates. For example, if the Markdown
text for a page should not contain any headers higher than level 2 (`<h2>`),
do:
```yaml
markdown_extensions:
- toc:
baselevel: 2
```
Then any headers in your document would be increased by 1. For example, the
header `# Header` would be rendered as a level 2 header (`<h2>`) in the HTML
output.
* **`separator`**
Word separator. Default: `-`.
Character which replaces white-space in generated IDs. If you prefer
underscores, then do:
```yaml
markdown_extensions:
- toc:
separator: "_"
```
Note that if you would like to define multiple of the above settings, you must
do so under a single `toc` entry in the `markdown_extensions` configuration
option.
```yml
markdown_extensions:
- toc:
permalink: "#"
baselevel: 2
separator: "_"
```
[toc]: https://python-markdown.github.io/extensions/toc/
#### Linking to images and media
As well as the Markdown source files, you can also include other file types in
your documentation, which will be copied across when generating your
documentation site. These might include images and other media.
For example, if your project documentation needed to include a [GitHub Pages
CNAME file] and a PNG formatted screenshot image then your file layout might
look as follows:
```text
mkdocs.yml
docs/
CNAME
index.md
about.md
license.md
img/
screenshot.png
```
To include images in your documentation source files, simply use any of the
regular Markdown image syntaxes:
```Markdown
Cupcake indexer is a snazzy new project for indexing small cakes.
![Screenshot](img/screenshot.png)
*Above: Cupcake indexer in progress*
```
Your image will now be embedded when you build the documentation, and should
also be previewed if you're working on the documentation with a Markdown editor.
[GitHub Pages CNAME file]: https://help.github.com/articles/using-a-custom-domain-with-github-pages/
#### Linking from raw HTML
Markdown allows document authors to fall back to raw HTML when the Markdown
syntax does not meets the author's needs. MkDocs does not limit Markdown in this
regard. However, as all raw HTML is ignored by the Markdown parser, MkDocs is
not able to validate or convert links contained in raw HTML. When including
internal links within raw HTML, you will need to manually format the link
appropriately for the rendered document.
### Meta-Data
MkDocs includes support for both YAML and MultiMarkdown style meta-data (often
called front-matter). Meta-data consists of a series of keywords and values
defined at the beginning of a Markdown document, which are stripped from the
document prior to it being processing by Python-Markdown. The key/value pairs
are passed by MkDocs to the page template. Therefore, if a theme includes
support, the values of any keys can be displayed on the page or used to control
the page rendering. See your theme's documentation for information about which
keys may be supported, if any.
In addition to displaying information in a template, MkDocs includes support for
a few predefined meta-data keys which can alter the behavior of MkDocs for that
specific page. The following keys are supported:
* **`template`**
The template to use with the current page.
By default, MkDocs uses the `main.html` template of a theme to render
Markdown pages. You can use the `template` meta-data key to define a
different template file for that specific page. The template file must be
available on the path(s) defined in the theme's environment.
* **`title`**
The "title" to use for the document.
MkDocs will attempt to determine the title of a document in the following
ways, in order:
1. A title defined in the [nav] configuration setting for a document.
2. A title defined in the `title` meta-data key of a document.
3. A level 1 Markdown header on the first line of the document body.
([Setext-style] headers are supported *only since MkDocs 1.5*.)
4. The filename of a document.
Upon finding a title for a page, MkDoc does not continue checking any
additional sources in the above list.
[Setext-style]: https://daringfireball.net/projects/markdown/syntax#header
#### YAML Style Meta-Data
YAML style meta-data consists of [YAML] key/value pairs wrapped in YAML style
delimiters to mark the start and/or end of the meta-data. The first line of
a document must be `---`. The meta-data ends at the first line containing an
end deliminator (either `---` or `...`). The content between the delimiters is
parsed as [YAML].
```text
---
title: My Document
summary: A brief description of my document.
authors:
- Waylan Limberg
- Tom Christie
date: 2018-07-10
some_url: https://example.com
---
This is the first paragraph of the document.
```
YAML is able to detect data types. Therefore, in the above example, the values
of `title`, `summary` and `some_url` are strings, the value of `authors` is a
list of strings and the value of `date` is a `datetime.date` object. Note that
the YAML keys are case sensitive and MkDocs expects keys to be all lowercase.
The top level of the YAML must be a collection of key/value pairs, which results
in a Python `dict` being returned. If any other type is returned or the YAML
parser encounters an error, then MkDocs does not recognize the section as
meta-data, the page's `meta` attribute will be empty, and the section is not
removed from the document.
#### MultiMarkdown Style Meta-Data
MultiMarkdown style meta-data uses a format first introduced by the
[MultiMarkdown] project. The data consists of a series of keywords and values
defined at the beginning of a Markdown document, like this:
```text
Title: My Document
Summary: A brief description of my document.
Authors: Waylan Limberg
Tom Christie
Date: January 23, 2018
blank-value:
some_url: https://example.com
This is the first paragraph of the document.
```
The keywords are case-insensitive and may consist of letters, numbers,
underscores and dashes and must end with a colon. The values consist of anything
following the colon on the line and may even be blank.
If a line is indented by 4 or more spaces, that line is assumed to be an
additional line of the value for the previous keyword. A keyword may have as
many lines as desired. All lines are joined into a single string.
The first blank line ends all meta-data for the document. Therefore, the first
line of a document must not be blank.
NOTE:
MkDocs does not support YAML style delimiters (`---` or `...`) for
MultiMarkdown style meta-data. In fact, MkDocs relies on the the presence or
absence of the delimiters to determine whether YAML style meta-data or
MultiMarkdown style meta-data is being used. If the delimiters are
detected, but the content between the delimiters is not valid YAML
meta-data, MkDocs does not attempt to parse the content as MultiMarkdown
style meta-data.
[YAML]: https://yaml.org
[MultiMarkdown]: https://fletcherpenney.net/MultiMarkdown_Syntax_Guide#metadata
[nav]: configuration.md#nav
### Tables
The [tables] extension adds a basic table syntax to Markdown which is popular
across multiple implementations. The syntax is rather simple and is generally
only useful for simple tabular data.
A simple table looks like this:
```markdown
First Header | Second Header | Third Header
------------ | ------------- | ------------
Content Cell | Content Cell | Content Cell
Content Cell | Content Cell | Content Cell
```
If you wish, you can add a leading and tailing pipe to each line of the table:
```markdown
| First Header | Second Header | Third Header |
| ------------ | ------------- | ------------ |
| Content Cell | Content Cell | Content Cell |
| Content Cell | Content Cell | Content Cell |
```
Specify alignment for each column by adding colons to separator lines:
```markdown
First Header | Second Header | Third Header
:----------- |:-------------:| -----------:
Left | Center | Right
Left | Center | Right
```
Note that table cells cannot contain any block level elements and cannot contain
multiple lines of text. They can, however, include inline Markdown as defined in
Markdown's [syntax] rules.
Additionally, a table must be surrounded by blank lines. There must be a blank
line before and after the table.
[tables]: https://python-markdown.github.io/extensions/tables/
### Fenced code blocks
The [fenced code blocks] extension adds an alternate method of defining code
blocks without indentation.
The first line should contain 3 or more backtick (`` ` ``) characters, and the
last line should contain the same number of backtick characters (`` ` ``):
````markdown
```
Fenced code blocks are like Standard
Markdowns regular code blocks, except that
theyre not indented and instead rely on
start and end fence lines to delimit the
code block.
```
````
With this approach, the language can optionally be specified on the first line
after the backticks which informs any syntax highlighters of the language used:
````markdown
```python
def fn():
pass
```
````
Note that fenced code blocks can not be indented. Therefore, they cannot be
nested inside list items, blockquotes, etc.
[fenced code blocks]: https://python-markdown.github.io/extensions/fenced_code_blocks/