diff --git a/.gitignore b/.gitignore
index c617f1e7f..d67b34ca3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ site/
*.swp
vendor/bundle
.venv
+__pycache__/
# these files are generated during documentation build
docs/actions.md
diff --git a/docs/css/color-mode.css b/docs/css/color-mode.css
new file mode 100644
index 000000000..7a35032a5
--- /dev/null
+++ b/docs/css/color-mode.css
@@ -0,0 +1,660 @@
+/* Color mode toggle */
+.wy-side-nav-search {
+ position: relative;
+}
+
+/* Hidden until the pre-paint script tags , so visitors without
+ JavaScript are not offered a control that cannot work for them. */
+.color-mode-toggle {
+ display: none;
+}
+
+html[data-color-mode-preference] .color-mode-toggle {
+ align-items: center;
+ background: transparent;
+ border: 0;
+ border-radius: 50%;
+ color: #555;
+ cursor: pointer;
+ display: flex;
+ height: 32px;
+ justify-content: center;
+ padding: 0;
+ position: absolute;
+ right: 8px;
+ top: 10px;
+ width: 32px;
+}
+
+.color-mode-toggle:hover {
+ background-color: rgba(0, 0, 0, .08);
+ color: #111;
+}
+
+.color-mode-icon {
+ display: none;
+ height: 18px;
+ width: 18px;
+}
+
+html[data-color-mode-preference="auto"] .color-mode-icon-auto,
+html[data-color-mode-preference="light"] .color-mode-icon-light,
+html[data-color-mode-preference="dark"] .color-mode-icon-dark {
+ display: block;
+}
+
+a:focus-visible,
+button:focus-visible,
+input:focus-visible,
+select:focus-visible,
+summary:focus-visible {
+ outline: 3px solid #9b6cff;
+ outline-offset: 2px;
+}
+
+.fastlane-wordmark {
+ display: inline-block;
+ max-width: 100%;
+ position: relative;
+}
+
+.fastlane-wordmark > img {
+ display: block;
+ max-width: 100%;
+}
+
+/* Dark mode */
+@media screen {
+html[data-color-mode="dark"] {
+ color-scheme: dark;
+ --fl-dark-background: #0d1117;
+ --fl-dark-surface: #161b22;
+ --fl-dark-surface-raised: #21262d;
+ --fl-dark-border: #30363d;
+ --fl-dark-control-border: #6e7681;
+ --fl-dark-text: #e6edf3;
+ --fl-dark-muted: #9da7b3;
+ --fl-dark-heading: #f0f6fc;
+ --fl-dark-link: #79c0ff;
+ --fl-dark-link-hover: #a5d6ff;
+ --fl-dark-focus: #f2cc60;
+}
+
+html[data-color-mode="dark"] body,
+html[data-color-mode="dark"] .wy-body-for-nav,
+html[data-color-mode="dark"] .wy-nav-content-wrap,
+html[data-color-mode="dark"] .wy-nav-content {
+ background: var(--fl-dark-background);
+ background-image: none;
+ color: var(--fl-dark-text);
+}
+
+html[data-color-mode="dark"] .wy-nav-side,
+html[data-color-mode="dark"] .wy-side-scroll,
+html[data-color-mode="dark"] .wy-side-nav-search {
+ background: var(--fl-dark-surface);
+}
+
+html[data-color-mode="dark"] .wy-nav-side {
+ border-right-color: var(--fl-dark-border);
+}
+
+html[data-color-mode="dark"] .wy-nav-top {
+ background: var(--fl-dark-surface-raised);
+ border-bottom: 1px solid var(--fl-dark-border);
+}
+
+html[data-color-mode="dark"] h1,
+html[data-color-mode="dark"] h2,
+html[data-color-mode="dark"] h3,
+html[data-color-mode="dark"] h4,
+html[data-color-mode="dark"] h5,
+html[data-color-mode="dark"] h6,
+html[data-color-mode="dark"] legend {
+ color: var(--fl-dark-heading) !important;
+}
+
+html[data-color-mode="dark"] a,
+html[data-color-mode="dark"] a:visited {
+ color: var(--fl-dark-link);
+}
+
+html[data-color-mode="dark"] a:hover,
+html[data-color-mode="dark"] a:active {
+ color: var(--fl-dark-link-hover);
+}
+
+html[data-color-mode="dark"] a > code,
+html[data-color-mode="dark"] .rst-content a code,
+html[data-color-mode="dark"] .rst-content .headeranchor {
+ color: var(--fl-dark-link) !important;
+}
+
+html[data-color-mode="dark"] a:focus-visible,
+html[data-color-mode="dark"] button:focus-visible,
+html[data-color-mode="dark"] input:focus-visible,
+html[data-color-mode="dark"] select:focus-visible,
+html[data-color-mode="dark"] summary:focus-visible,
+html[data-color-mode="dark"] .rst-content .headeranchor-link:focus-visible {
+ outline-color: var(--fl-dark-focus);
+}
+
+html[data-color-mode="dark"] hr {
+ border-top-color: var(--fl-dark-border);
+}
+
+html[data-color-mode="dark"] blockquote {
+ border-left-color: #59636f;
+ color: var(--fl-dark-muted) !important;
+}
+
+html[data-color-mode="dark"] footer,
+html[data-color-mode="dark"] .wy-breadcrumbs-extra,
+html[data-color-mode="dark"] .via-text,
+html[data-color-mode="dark"] .score-details,
+html[data-color-mode="dark"] .wy-table-secondary,
+html[data-color-mode="dark"] .wy-table-tertiary,
+html[data-color-mode="dark"] #search-results .context {
+ color: var(--fl-dark-muted);
+}
+
+@supports (mask-image: url("")) or (-webkit-mask-image: url("")) {
+ html[data-color-mode="dark"] .fastlane-wordmark::after {
+ background: var(--fl-dark-heading);
+ clip-path: inset(0 0 0 34%);
+ content: "";
+ inset: 0;
+ mask: url("../img/fastlane_text.png") center / 100% 100% no-repeat;
+ pointer-events: none;
+ position: absolute;
+ -webkit-mask: url("../img/fastlane_text.png") center / 100% 100% no-repeat;
+ }
+}
+
+html[data-color-mode="dark"] .githubButton {
+ /* Match the embedded document's light color scheme; a mismatch makes the
+ browser disable iframe transparency and paint an opaque backdrop. */
+ color-scheme: light;
+ filter: invert(1) hue-rotate(180deg);
+}
+
+/* Netlify's deploy-preview drawer is a cross-origin iframe, so it cannot
+ follow the page palette; invert it like the GitHub button above. The
+ injector keeps a `height: 48px` inline style only while the drawer is a
+ collapsed banner and swaps it for viewport-sized values when expanded,
+ where inverting would distort screenshots and camera previews. */
+html[data-color-mode="dark"] div[data-netlify-site-id] iframe[title="Netlify Drawer"][style*="height: 48px"] {
+ filter: invert(1) hue-rotate(180deg);
+}
+
+/* Navigation */
+html[data-color-mode="dark"] .wy-menu-vertical header,
+html[data-color-mode="dark"] .wy-menu-vertical p.caption,
+html[data-color-mode="dark"] .wy-menu-vertical span,
+html[data-color-mode="dark"] .wy-menu-vertical li span.toctree-expand {
+ color: var(--fl-dark-muted);
+}
+
+html[data-color-mode="dark"] .wy-menu-vertical a,
+html[data-color-mode="dark"] .wy-menu-vertical li.current a,
+html[data-color-mode="dark"] .wy-menu-vertical li ul li a,
+html[data-color-mode="dark"] .wy-menu-vertical li.on a,
+html[data-color-mode="dark"] .wy-menu-vertical li.current > a,
+html[data-color-mode="dark"] .wy-menu-vertical li.current.active > a {
+ background: transparent;
+ color: var(--fl-dark-link);
+}
+
+html[data-color-mode="dark"] .wy-menu-vertical li.toctree-l3,
+html[data-color-mode="dark"] .wy-menu-vertical li.on a,
+html[data-color-mode="dark"] .wy-menu-vertical li.current > a {
+ background-color: transparent;
+}
+
+html[data-color-mode="dark"] .wy-menu-vertical li.current > a.toc-no-link {
+ color: var(--fl-dark-muted) !important;
+}
+
+html[data-color-mode="dark"] .wy-menu-vertical a:hover,
+html[data-color-mode="dark"] .wy-menu-vertical li.on a:hover,
+html[data-color-mode="dark"] .wy-menu-vertical li.current > a:hover,
+html[data-color-mode="dark"] .wy-menu-vertical li.current.active > a {
+ background-color: var(--fl-dark-surface-raised);
+ color: var(--fl-dark-link-hover);
+}
+
+html[data-color-mode="dark"] .wy-menu-vertical li.divide-top {
+ border-top-color: var(--fl-dark-border);
+}
+
+html[data-color-mode="dark"] .wy-menu-vertical li.divide-bottom {
+ border-bottom-color: var(--fl-dark-border);
+}
+
+html[data-color-mode="dark"] .color-mode-toggle {
+ color: var(--fl-dark-muted);
+}
+
+html[data-color-mode="dark"] .color-mode-toggle:hover {
+ background-color: var(--fl-dark-surface-raised);
+ color: var(--fl-dark-heading);
+}
+
+html[data-color-mode="dark"] .wy-breadcrumbs input[type="text"],
+html[data-color-mode="dark"] input,
+html[data-color-mode="dark"] select,
+html[data-color-mode="dark"] textarea {
+ background-color: var(--fl-dark-surface-raised);
+ border-color: var(--fl-dark-control-border);
+ color: var(--fl-dark-text);
+ caret-color: var(--fl-dark-text);
+}
+
+html[data-color-mode="dark"] input::placeholder,
+html[data-color-mode="dark"] textarea::placeholder {
+ color: var(--fl-dark-muted);
+ opacity: 1;
+}
+
+html[data-color-mode="dark"] input:disabled,
+html[data-color-mode="dark"] select:disabled,
+html[data-color-mode="dark"] textarea:disabled {
+ color: #7d8590;
+ opacity: 1;
+}
+
+/* Inline code and code block surfaces */
+html[data-color-mode="dark"] code,
+html[data-color-mode="dark"] .rst-content tt,
+html[data-color-mode="dark"] .rst-content code,
+html[data-color-mode="dark"] .rst-content .admonition code {
+ background-color: var(--fl-dark-surface-raised) !important;
+ border-color: var(--fl-dark-border);
+ color: var(--fl-dark-text) !important;
+}
+
+html[data-color-mode="dark"] .codeblock-example,
+html[data-color-mode="dark"] .codeblock,
+html[data-color-mode="dark"] pre.literal-block,
+html[data-color-mode="dark"] .rst-content .literal-block,
+html[data-color-mode="dark"] .rst-content pre.literal-block,
+html[data-color-mode="dark"] div[class^="highlight"] {
+ background: var(--fl-dark-surface);
+ border-color: var(--fl-dark-border);
+}
+
+html[data-color-mode="dark"] div[class^="highlight"] pre,
+html[data-color-mode="dark"] .rst-content pre code,
+html[data-color-mode="dark"] .rst-content .no-highlight {
+ background: var(--fl-dark-surface) !important;
+ color: var(--fl-dark-text) !important;
+}
+
+html[data-color-mode="dark"] .linenodiv pre {
+ border-right-color: var(--fl-dark-border);
+ color: #7d8590;
+}
+
+html[data-color-mode="dark"] .hll {
+ background-color: #2d333b;
+}
+
+/* Syntax highlighting */
+html[data-color-mode="dark"] .c,
+html[data-color-mode="dark"] .cm,
+html[data-color-mode="dark"] .c1,
+html[data-color-mode="dark"] .cs,
+html[data-color-mode="dark"] .cp,
+html[data-color-mode="dark"] .gh,
+html[data-color-mode="dark"] .go,
+html[data-color-mode="dark"] .gp,
+html[data-color-mode="dark"] .w,
+html[data-color-mode="dark"] .bp,
+html[data-color-mode="dark"] .gc,
+html[data-color-mode="dark"] .hljs-comment,
+html[data-color-mode="dark"] .hljs-quote {
+ color: #9da7b3;
+}
+
+html[data-color-mode="dark"] .k,
+html[data-color-mode="dark"] .kc,
+html[data-color-mode="dark"] .kd,
+html[data-color-mode="dark"] .kn,
+html[data-color-mode="dark"] .kp,
+html[data-color-mode="dark"] .kr,
+html[data-color-mode="dark"] .kt,
+html[data-color-mode="dark"] .o,
+html[data-color-mode="dark"] .ow,
+html[data-color-mode="dark"] .hljs-keyword,
+html[data-color-mode="dark"] .hljs-selector-tag,
+html[data-color-mode="dark"] .hljs-literal,
+html[data-color-mode="dark"] .hljs-section,
+html[data-color-mode="dark"] .hljs-link {
+ color: #ff7b72;
+}
+
+html[data-color-mode="dark"] .s,
+html[data-color-mode="dark"] .sb,
+html[data-color-mode="dark"] .sc,
+html[data-color-mode="dark"] .sd,
+html[data-color-mode="dark"] .s1,
+html[data-color-mode="dark"] .s2,
+html[data-color-mode="dark"] .se,
+html[data-color-mode="dark"] .sh,
+html[data-color-mode="dark"] .si,
+html[data-color-mode="dark"] .sr,
+html[data-color-mode="dark"] .sx,
+html[data-color-mode="dark"] .ss,
+html[data-color-mode="dark"] .hljs-string,
+html[data-color-mode="dark"] .hljs-doctag,
+html[data-color-mode="dark"] .hljs-regexp,
+html[data-color-mode="dark"] .hljs-template-tag,
+html[data-color-mode="dark"] .hljs-template-variable {
+ color: #a5d6ff;
+}
+
+html[data-color-mode="dark"] .m,
+html[data-color-mode="dark"] .mf,
+html[data-color-mode="dark"] .mh,
+html[data-color-mode="dark"] .mi,
+html[data-color-mode="dark"] .mo,
+html[data-color-mode="dark"] .no,
+html[data-color-mode="dark"] .il,
+html[data-color-mode="dark"] .hljs-number,
+html[data-color-mode="dark"] .hljs-meta {
+ color: #79c0ff;
+}
+
+html[data-color-mode="dark"] .na,
+html[data-color-mode="dark"] .nb,
+html[data-color-mode="dark"] .nc,
+html[data-color-mode="dark"] .nd,
+html[data-color-mode="dark"] .ne,
+html[data-color-mode="dark"] .nf,
+html[data-color-mode="dark"] .ni,
+html[data-color-mode="dark"] .nl,
+html[data-color-mode="dark"] .nt,
+html[data-color-mode="dark"] .nv,
+html[data-color-mode="dark"] .vc,
+html[data-color-mode="dark"] .vg,
+html[data-color-mode="dark"] .vi,
+html[data-color-mode="dark"] .hljs-attr,
+html[data-color-mode="dark"] .hljs-attribute,
+html[data-color-mode="dark"] .hljs-name,
+html[data-color-mode="dark"] .hljs-selector-id,
+html[data-color-mode="dark"] .hljs-selector-class,
+html[data-color-mode="dark"] .hljs-title,
+html[data-color-mode="dark"] .hljs-type,
+html[data-color-mode="dark"] .hljs-symbol,
+html[data-color-mode="dark"] .hljs-bullet {
+ color: #d2a8ff;
+}
+
+html[data-color-mode="dark"] .n,
+html[data-color-mode="dark"] .nn,
+html[data-color-mode="dark"] .hljs-variable,
+html[data-color-mode="dark"] .hljs-params {
+ color: var(--fl-dark-text);
+}
+
+html[data-color-mode="dark"] .hljs-built_in,
+html[data-color-mode="dark"] .hljs-builtin-name,
+html[data-color-mode="dark"] .hljs-selector-attr,
+html[data-color-mode="dark"] .hljs-selector-pseudo {
+ color: #ffa657;
+}
+
+html[data-color-mode="dark"] .err,
+html[data-color-mode="dark"] .gr,
+html[data-color-mode="dark"] .gt,
+html[data-color-mode="dark"] .hljs-deletion {
+ background-color: #3d1f24;
+ color: #ffa198;
+}
+
+html[data-color-mode="dark"] .gd,
+html[data-color-mode="dark"] .gd .x {
+ background-color: #3d1f24;
+ color: #ffa198;
+}
+
+html[data-color-mode="dark"] .gi,
+html[data-color-mode="dark"] .gi .x,
+html[data-color-mode="dark"] .hljs-addition {
+ background-color: #1b4721;
+ color: #aff5b4;
+}
+
+html[data-color-mode="dark"] .gu {
+ color: #d2a8ff;
+}
+
+/* Tables and secondary surfaces */
+html[data-color-mode="dark"] .wy-table thead,
+html[data-color-mode="dark"] .rst-content table.docutils thead,
+html[data-color-mode="dark"] .rst-content table.field-list thead,
+html[data-color-mode="dark"] .wy-table caption,
+html[data-color-mode="dark"] .rst-content table.docutils caption,
+html[data-color-mode="dark"] .rst-content table.field-list caption {
+ color: var(--fl-dark-heading);
+}
+
+html[data-color-mode="dark"] .wy-table-odd td,
+html[data-color-mode="dark"] .wy-table-striped tr:nth-child(odd) td,
+html[data-color-mode="dark"] .rst-content table.docutils:not(.field-list) tr:nth-child(odd) td {
+ background-color: var(--fl-dark-surface);
+}
+
+html[data-color-mode="dark"] .wy-table-striped tr:nth-child(even) td,
+html[data-color-mode="dark"] .rst-content table.docutils:not(.field-list) tr:nth-child(even) td,
+html[data-color-mode="dark"] .wy-table-backed,
+html[data-color-mode="dark"] .rst-content table.docutils thead th {
+ background-color: var(--fl-dark-surface-raised);
+}
+
+html[data-color-mode="dark"] .wy-table-bordered-all,
+html[data-color-mode="dark"] .wy-table-bordered,
+html[data-color-mode="dark"] .rst-content table.docutils,
+html[data-color-mode="dark"] .rst-content table.docutils td,
+html[data-color-mode="dark"] .rst-content table.docutils th,
+html[data-color-mode="dark"] .wy-table-horizontal td,
+html[data-color-mode="dark"] .wy-table-horizontal th {
+ border-color: var(--fl-dark-border) !important;
+}
+
+html[data-color-mode="dark"] .quality-index,
+html[data-color-mode="dark"] .rst-content .sidebar,
+html[data-color-mode="dark"] .wy-dropdown-menu {
+ background: var(--fl-dark-surface-raised);
+ border-color: var(--fl-dark-border);
+ color: var(--fl-dark-text);
+}
+
+html[data-color-mode="dark"] [style*="#558000"],
+html[data-color-mode="dark"] [style*="#63b319"],
+html[data-color-mode="dark"] [style*="#72CC1D"] {
+ color: #a8e063 !important;
+}
+
+html[data-color-mode="dark"] [style*="#a03030"],
+html[data-color-mode="dark"] [style*="#ff6666"] {
+ color: #ff9b95 !important;
+}
+
+html[data-color-mode="dark"] .rst-content .sidebar .sidebar-title,
+html[data-color-mode="dark"] .wy-dropdown-menu > dd > a:hover {
+ background: var(--fl-dark-border);
+ color: var(--fl-dark-heading);
+}
+
+html[data-color-mode="dark"] .btn-neutral {
+ background-color: var(--fl-dark-surface-raised) !important;
+ border: 1px solid var(--fl-dark-control-border);
+ color: var(--fl-dark-text) !important;
+}
+
+html[data-color-mode="dark"] .btn-neutral:hover {
+ background-color: #30363d !important;
+}
+
+html[data-color-mode="dark"] mark,
+html[data-color-mode="dark"] .rst-content .highlighted {
+ background: #6e5800;
+ color: #fff8c5;
+}
+
+html[data-color-mode="dark"] ins {
+ background: #1b4721;
+ color: #aff5b4;
+}
+
+html[data-color-mode="dark"] kbd,
+html[data-color-mode="dark"] .guilabel {
+ background: var(--fl-dark-surface-raised);
+ border-color: var(--fl-dark-control-border);
+ color: var(--fl-dark-text);
+}
+
+/* Alerts and setup details */
+html[data-color-mode="dark"] .rst-content .danger,
+html[data-color-mode="dark"] .rst-content .error,
+html[data-color-mode="dark"] .wy-alert.wy-alert-danger {
+ background: #3d1f24;
+}
+
+html[data-color-mode="dark"] .rst-content .danger .admonition-title,
+html[data-color-mode="dark"] .rst-content .error .admonition-title,
+html[data-color-mode="dark"] .wy-alert.wy-alert-danger .wy-alert-title,
+html[data-color-mode="dark"] .wy-alert.wy-alert-danger .admonition-title {
+ background: #8e3039;
+}
+
+html[data-color-mode="dark"] .rst-content .attention,
+html[data-color-mode="dark"] .rst-content .caution,
+html[data-color-mode="dark"] .rst-content .warning,
+html[data-color-mode="dark"] .rst-content .admonition-todo,
+html[data-color-mode="dark"] .wy-alert.wy-alert-warning {
+ background: #3b2e16;
+}
+
+html[data-color-mode="dark"] .rst-content .attention .admonition-title,
+html[data-color-mode="dark"] .rst-content .caution .admonition-title,
+html[data-color-mode="dark"] .rst-content .warning .admonition-title,
+html[data-color-mode="dark"] .rst-content .admonition-todo .admonition-title,
+html[data-color-mode="dark"] .wy-alert.wy-alert-warning .wy-alert-title,
+html[data-color-mode="dark"] .wy-alert.wy-alert-warning .admonition-title {
+ background: #8a6116;
+}
+
+html[data-color-mode="dark"] .rst-content .note,
+html[data-color-mode="dark"] .rst-content .seealso,
+html[data-color-mode="dark"] .wy-alert.wy-alert-info {
+ background: #172a3d;
+}
+
+html[data-color-mode="dark"] .rst-content .note .admonition-title,
+html[data-color-mode="dark"] .rst-content .seealso .admonition-title,
+html[data-color-mode="dark"] .wy-alert.wy-alert-info .wy-alert-title,
+html[data-color-mode="dark"] .wy-alert.wy-alert-info .admonition-title {
+ background: #245b83;
+}
+
+html[data-color-mode="dark"] .rst-content .hint,
+html[data-color-mode="dark"] .rst-content .important,
+html[data-color-mode="dark"] .rst-content .tip,
+html[data-color-mode="dark"] .wy-alert.wy-alert-success {
+ background: #17352b;
+}
+
+html[data-color-mode="dark"] .rst-content .hint .admonition-title,
+html[data-color-mode="dark"] .rst-content .important .admonition-title,
+html[data-color-mode="dark"] .rst-content .tip .admonition-title,
+html[data-color-mode="dark"] .wy-alert.wy-alert-success .wy-alert-title,
+html[data-color-mode="dark"] .wy-alert.wy-alert-success .admonition-title {
+ background: #1f6f55;
+}
+
+html[data-color-mode="dark"] .rst-content .wy-alert-neutral,
+html[data-color-mode="dark"] .wy-alert.wy-alert-neutral {
+ background: var(--fl-dark-surface-raised);
+}
+
+html[data-color-mode="dark"] .rst-content .wy-alert-neutral .admonition-title,
+html[data-color-mode="dark"] .wy-alert.wy-alert-neutral .wy-alert-title,
+html[data-color-mode="dark"] .wy-alert.wy-alert-neutral .admonition-title {
+ background: var(--fl-dark-border);
+ color: var(--fl-dark-text);
+}
+
+html[data-color-mode="dark"] .rst-content .fastlane-setup-wrapper {
+ background-color: rgba(0, 176, 255, .08);
+}
+
+html[data-color-mode="dark"] .rst-content .fastlane-setup {
+ background-color: rgba(0, 176, 255, .14);
+ border-bottom-color: rgba(121, 192, 255, .35);
+ color: var(--fl-dark-text);
+}
+
+/* Algolia DocSearch v2 */
+html[data-color-mode="dark"] .algolia-autocomplete .ds-dropdown-menu {
+ box-shadow: 0 0 0 1px var(--fl-dark-border), 0 8px 24px rgba(0, 0, 0, .45);
+}
+
+html[data-color-mode="dark"] .algolia-autocomplete .ds-dropdown-menu::before {
+ background: var(--fl-dark-surface-raised);
+ border-color: var(--fl-dark-border);
+}
+
+html[data-color-mode="dark"] .algolia-autocomplete .ds-dropdown-menu [class^="ds-dataset-"],
+html[data-color-mode="dark"] .algolia-autocomplete .algolia-docsearch-suggestion {
+ background: var(--fl-dark-surface-raised);
+ border-color: var(--fl-dark-border);
+ color: var(--fl-dark-text);
+}
+
+html[data-color-mode="dark"] .algolia-autocomplete .algolia-docsearch-suggestion--category-header,
+html[data-color-mode="dark"] .algolia-autocomplete .algolia-docsearch-suggestion--content::before,
+html[data-color-mode="dark"] .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column::before,
+html[data-color-mode="dark"] .algolia-autocomplete .suggestion-layout-simple.algolia-docsearch-suggestion {
+ border-color: var(--fl-dark-border);
+}
+
+html[data-color-mode="dark"] .algolia-autocomplete .algolia-docsearch-suggestion--category-header,
+html[data-color-mode="dark"] .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column,
+html[data-color-mode="dark"] .algolia-autocomplete .algolia-docsearch-suggestion--title {
+ color: var(--fl-dark-text);
+}
+
+html[data-color-mode="dark"] .algolia-autocomplete .algolia-docsearch-suggestion--text,
+html[data-color-mode="dark"] .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl0,
+html[data-color-mode="dark"] .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1 {
+ color: var(--fl-dark-muted);
+}
+
+html[data-color-mode="dark"] .algolia-autocomplete .algolia-docsearch-suggestion--highlight,
+html[data-color-mode="dark"] .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title,
+html[data-color-mode="dark"] .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title::before {
+ color: var(--fl-dark-link);
+}
+
+html[data-color-mode="dark"] .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text {
+ background: var(--fl-dark-surface);
+}
+
+html[data-color-mode="dark"] .algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion,
+html[data-color-mode="dark"] .algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion--content {
+ background-color: #28384d;
+}
+
+html[data-color-mode="dark"] .algolia-autocomplete .algolia-docsearch-suggestion code {
+ background: var(--fl-dark-surface) !important;
+ color: var(--fl-dark-text) !important;
+}
+}
+
+@media print {
+ html[data-color-mode-preference] .color-mode-toggle {
+ display: none;
+ }
+}
diff --git a/docs/index.md b/docs/index.md
index 8bda3120b..71ec585e3 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,9 +1,9 @@
-
+
fastlane
============
-[](https://twitter.com/FastlaneTools){: .badge }
+[](https://x.com/FastlaneTools){: .badge }
[](https://github.com/fastlane/fastlane/blob/master/LICENSE){: .badge }
[](https://rubygems.org/gems/fastlane){: .badge }
[](#){: .badge }
diff --git a/docs/js/color-mode.js b/docs/js/color-mode.js
new file mode 100644
index 000000000..9828a78a9
--- /dev/null
+++ b/docs/js/color-mode.js
@@ -0,0 +1,112 @@
+(function () {
+ 'use strict';
+
+ var storageKey = 'fastlane-docs-color-mode';
+ var preferenceCycle = ['auto', 'light', 'dark'];
+ var root = document.documentElement;
+ var mediaQuery = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
+ var reducedMotionQuery = window.matchMedia ? window.matchMedia('(prefers-reduced-motion: reduce)') : null;
+
+ function isValidPreference(preference) {
+ return preferenceCycle.indexOf(preference) !== -1;
+ }
+
+ function getPreference() {
+ var preference = root.getAttribute('data-color-mode-preference');
+ return isValidPreference(preference) ? preference : 'auto';
+ }
+
+ function nextPreference(preference) {
+ return preferenceCycle[(preferenceCycle.indexOf(preference) + 1) % preferenceCycle.length];
+ }
+
+ function describePreference(preference) {
+ return preference === 'auto' ? 'auto (follows your system)' : preference;
+ }
+
+ function resolveColorMode(preference) {
+ if (preference !== 'auto') {
+ return preference;
+ }
+
+ return mediaQuery && mediaQuery.matches ? 'dark' : 'light';
+ }
+
+ function applyPreference(preference) {
+ var safePreference = isValidPreference(preference) ? preference : 'auto';
+ root.setAttribute('data-color-mode', resolveColorMode(safePreference));
+ root.setAttribute('data-color-mode-preference', safePreference);
+
+ var toggle = document.getElementById('color-mode-toggle');
+ if (toggle) {
+ var label = 'Color mode: ' + describePreference(safePreference) +
+ '. Switch to ' + describePreference(nextPreference(safePreference)) + '.';
+ toggle.setAttribute('aria-label', label);
+ toggle.setAttribute('title', label);
+ }
+ }
+
+ function applyPreferenceWithCrossfade(preference) {
+ var colorModeChanges = resolveColorMode(preference) !== root.getAttribute('data-color-mode');
+
+ if (colorModeChanges &&
+ typeof document.startViewTransition === 'function' &&
+ !(reducedMotionQuery && reducedMotionQuery.matches)) {
+ document.startViewTransition(function () {
+ applyPreference(preference);
+ });
+ return;
+ }
+
+ applyPreference(preference);
+ }
+
+ function storePreference(preference) {
+ try {
+ window.localStorage.setItem(storageKey, preference);
+ } catch (error) {
+ // The selected mode still applies for this page when storage is blocked.
+ }
+ }
+
+ function handleSystemColorModeChange() {
+ if (getPreference() === 'auto') {
+ applyPreference('auto');
+ }
+ }
+
+ function handleStoredPreferenceChange(event) {
+ if (event.key === storageKey) {
+ applyPreference(isValidPreference(event.newValue) ? event.newValue : 'auto');
+ }
+ }
+
+ function initializeColorModeControl() {
+ var toggle = document.getElementById('color-mode-toggle');
+ applyPreference(getPreference());
+
+ if (toggle) {
+ toggle.addEventListener('click', function () {
+ var preference = nextPreference(getPreference());
+ applyPreferenceWithCrossfade(preference);
+ storePreference(preference);
+ });
+ }
+
+ if (mediaQuery) {
+ if (mediaQuery.addEventListener) {
+ mediaQuery.addEventListener('change', handleSystemColorModeChange);
+ } else if (mediaQuery.addListener) {
+ mediaQuery.addListener(handleSystemColorModeChange);
+ }
+ }
+
+ window.addEventListener('storage', handleStoredPreferenceChange);
+ }
+
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', initializeColorModeControl);
+ } else {
+ initializeColorModeControl();
+ }
+}());
diff --git a/mkdocs.yml b/mkdocs.yml
index 66b3455f8..b79ca65ed 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -32,6 +32,9 @@ extra_css:
- "./css/fabric.css"
- "./css/sidenav.css"
- https://maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css
+- "./css/color-mode.css"
+extra_javascript:
+- "./js/color-mode.js"
nav:
- Home: index.md
- Getting Started:
diff --git a/theme/base.html b/theme/base.html
index 876e25d37..97ed734ee 100644
--- a/theme/base.html
+++ b/theme/base.html
@@ -6,6 +6,27 @@
+
+
{% if page and page.is_homepage %}{% endif %}
{% if config.site_author %}{% endif %}
{% if page and page.canonical_url %}{% endif %}
@@ -25,6 +46,7 @@
{%- if config.theme.highlightjs %}
{%- endif %}
+
{%- for path in config['extra_css'] %}
{%- endfor %}
@@ -81,6 +103,19 @@
{%- block site_name %}
{{ config.site_name }}
{%- endblock %}
+
{%- block search_button %}
{% if 'search' in config['plugins'] %}{% include "searchbox.html" %}{% endif %}
{%- endblock %}
diff --git a/theme/breadcrumbs.html b/theme/breadcrumbs.html
index db5aa711f..bf0990db2 100644
--- a/theme/breadcrumbs.html
+++ b/theme/breadcrumbs.html
@@ -58,13 +58,20 @@
-
+