[
  {
    "path": ".babelrc",
    "content": "{\n  presets: [\"es2015\"]\n}\n"
  },
  {
    "path": ".banner",
    "content": "      ___          ___          ___          ___       ___       ___\n     /  /\\        /__/\\        /  /\\        /  /\\     /  /\\     /  /\\\n    /  /:/        \\  \\:\\      /  /::\\      /  /::\\   /  /::\\   /  /:/_\n   /  /:/          \\__\\:\\    /  /:/\\:\\    /  /:/\\:\\ /  /:/\\:\\ /  /:/ /\\\n  /  /:/  ___  ___ /  /::\\  /  /:/~/::\\  /  /:/~/://  /:/~/://  /:/ /:/_\n /__/:/  /  /\\/__/\\  /:/\\:\\/__/:/ /:/\\:\\/__/:/ /://__/:/ /://__/:/ /:/ /\\\n \\  \\:\\ /  /:/\\  \\:\\/:/__\\/\\  \\:\\/:/__\\/\\  \\:\\/:/ \\  \\:\\/:/ \\  \\:\\/:/ /:/\n  \\  \\:\\  /:/  \\  \\::/      \\  \\::/      \\  \\::/   \\  \\::/   \\  \\::/ /:/\n   \\  \\:\\/:/    \\  \\:\\       \\  \\:\\       \\  \\:\\    \\  \\:\\    \\  \\:\\/:/\n    \\  \\::/      \\  \\:\\       \\  \\:\\       \\  \\:\\    \\  \\:\\    \\  \\::/\n     \\__\\/        \\__\\/        \\__\\/        \\__\\/     \\__\\/     \\__\\/\n\n — {{bundle}} by Crisp\n"
  },
  {
    "path": ".bowerrc",
    "content": "{\n  \"registry\": \"https://registry.bower.io\"\n}\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "on:\n  push:\n    tags:\n      - \"v*.*.*\"\n\npermissions:\n  id-token: write\n\nname: Build and Release\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v2\n\n      - name: Install NodeJS\n        uses: actions/setup-node@v1\n        with:\n          node-version: 24.x\n          registry-url: https://registry.npmjs.org\n\n      - name: Verify versions\n        run: node --version && npm --version && node -p process.versions.v8\n\n      - name: Release package\n        run: npm publish --ignore-scripts --provenance\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "on: [push, pull_request]\n\nname: Test and Build\n\njobs:\n  test:\n    strategy:\n      matrix:\n        os: [ubuntu-latest]\n        node-version: [20.x, 22.x, 24.x]\n      fail-fast: false\n\n    runs-on: ${{ matrix.os }}\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v2\n\n      - name: Install NodeJS\n        uses: actions/setup-node@v1\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Verify versions\n        run: node --version && npm --version && node -p process.versions.v8\n\n      - name: Cache build artifacts\n        id: cache-node\n        uses: actions/cache@v4\n        with:\n          path: |\n            ~/.npm\n            .chappe\n            node_modules\n          key: test-${{ runner.os }}-node-${{ matrix.node-version }}\n\n      - name: Install dependencies\n        run: npm install\n\n      - name: Run tests\n        run: npm test\n\n      - name: Build 'acme-docs' example\n        run: npm run build\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nThumbs.db\nnpm-debug.log\n\npackage-lock.json\n\nnode_modules/\n.chappe/\n\ndist/\n"
  },
  {
    "path": ".jscsrc",
    "content": "{\n  \"maximumLineLength\": 80,\n\n  \"validateIndentation\": 2,\n  \"validateQuoteMarks\": \"\\\"\",\n\n  \"disallowTrailingComma\": true,\n  \"disallowTrailingWhitespace\": true,\n  \"disallowNewlineBeforeBlockStatements\": true,\n  \"disallowMixedSpacesAndTabs\": true,\n  \"disallowMultipleLineStrings\": true,\n  \"disallowNamedUnassignedFunctions\": true,\n  \"disallowQuotedKeysInObjects\": true,\n  \"disallowSpacesInsideParentheses\": true,\n  \"disallowKeywordsOnNewLine\": [\"else\"],\n  \"disallowIdentifierNames\": [\"console\"],\n\n  \"requireCurlyBraces\": true,\n  \"requireDotNotation\": true,\n  \"requireSemicolons\": true,\n  \"requireSpaceBeforeObjectValues\": true,\n  \"requireSpaceBetweenArguments\": true,\n  \"requireSpacesInForStatement\": true,\n  \"requireSpaceAfterObjectKeys\": true,\n  \"requireSpaceBeforeBinaryOperators\": true,\n  \"requireSpaceBeforeBlockStatements\": true,\n  \"requireSpacesInConditionalExpression\": true,\n  \"requireBlocksOnNewline\": true,\n  \"requireCommaBeforeLineBreak\": true,\n  \"requireLineFeedAtFileEnd\": true,\n  \"requirePaddingNewLineAfterVariableDeclaration\": true,\n  \"requirePaddingNewLinesAfterUseStrict\": true,\n  \"requirePaddingNewLinesBeforeExport\": true,\n  \"requirePaddingNewLinesInObjects\": true\n}\n"
  },
  {
    "path": ".jshintrc",
    "content": "{\n  \"camelcase\": false,\n  \"esversion\": 6,\n  \"node\": true,\n  \"predef\": [\n    \"require\",\n    \"define\",\n    \"escape\",\n    \"Buffer\",\n    \"module\"\n  ]\n}\n"
  },
  {
    "path": ".npmignore",
    "content": "package-lock.json\n\n.github/**\n.chappe/**\ndist/**\nexamples/**\n\n"
  },
  {
    "path": ".pug-lintrc",
    "content": "{\n  \"requireLowerCaseTags\": true,\n  \"requireLowerCaseAttributes\": true,\n  \"requireLineFeedAtFileEnd\": true,\n\n  \"requireIdLiteralsBeforeAttributes\": true,\n  \"requireClassLiteralsBeforeIdLiterals\": true,\n  \"requireClassLiteralsBeforeAttributes\": true,\n\n  \"requireStrictEqualityOperators\": true,\n\n  \"validateSelfClosingTags\": true,\n  \"validateIndentation\": 2,\n  \"validateDivTags\": true,\n  \"validateAttributeQuoteMarks\": \"\\\"\",\n\n  \"disallowStringConcatenation\": true,\n  \"disallowDuplicateAttributes\": true,\n\n  \"disallowSpecificAttributes\": [\n    \"xmlns\",\n\n    {\n      \"br\": \"role\"\n    },\n\n    {\n      \"hr\": \"role\"\n    },\n\n    {\n      \"nav\": \"role\"\n    }\n  ],\n\n  \"disallowSpecificTags\": [\n    \"b\",\n    \"hgroup\",\n    \"i\",\n    \"s\",\n    \"u\"\n  ],\n\n  \"requireSpecificAttributes\": [\n    {\n      \"html\": [\n        \"lang\"\n      ]\n    },\n\n    {\n      \"a\": [\n        \"href\"\n      ]\n    },\n\n    {\n      \"img\": [\n        \"src\",\n        \"alt\"\n      ]\n    },\n\n    {\n      \"form\": [\n        \"action\",\n        \"method\"\n      ]\n    },\n\n    {\n      \"input\": [\n        \"type\",\n        \"name\"\n      ]\n    },\n\n    {\n      \"textarea\": [\n        \"name\",\n        \"cols\",\n        \"rows\"\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": ".stylelintrc.yml",
    "content": "extends:\n  - stylelint-config-standard-scss\n\nrules:\n  selector-id-pattern: null\n  selector-class-pattern: null\n\n  at-rule-empty-line-before: null\n  no-descending-specificity: null\n\n  scss/comment-no-empty: null\n  scss/load-no-partial-leading-underscore: null\n  scss/dollar-variable-empty-line-before: null\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## 1.16.0 (2026-05-03)\n\n### New Features\n\n* Added support for footnotes in guides.\n\n## 1.15.2 (2026-03-31)\n\n### Bug Fixes\n\n* Fixed category icon overflow in guides details section.\n\n## 1.15.1 (2026-02-02)\n\n### Bug Fixes\n\n* Fixed generated slug for anchor links in Markdown articles (in some cases).\n\n## 1.15.0 (2026-01-28)\n\n### Changes\n\n* Migrate `.jade` files to `.pug` syntax (and update the syntax to Pug 3).\n\n## 1.14.0 (2026-01-27)\n\n### Changes\n\n* Migrate `.sass` files to `.scss` syntax.\n* Re-instate linting of stylesheet files with `gulp-stylelint-esm`.\n\n## 1.13.0 (2026-01-27)\n\n### Changes\n\n* Migrate Sass syntax away from `@import` to `@use`.\n\n## 1.12.0 (2025-11-11)\n\n### Changes\n\n* Migrate `gulp-cssmin` to `gulp-clean-css` for Node.js 24 compatibility.\n\n## 1.11.0 (2025-10-16)\n\n### Changes\n\n* Migrate to NPMJS OIDC publishing tokens (since Classic Tokens will be removed in November 2025).\n\n## 1.10.2 (2025-07-15)\n\n### Bug Fixes\n\n* Fixed dropped enumeration members with empty values (such as `0` and `false`).\n* Fixed enumeration descriptions, that were not showing at all.\n\n## 1.10.1 (2025-06-22)\n\n### New Features\n\n* Added a way to configure navigation link targets (`self` or `blank`, defaults to `self`).\n\n## 1.10.0 (2025-06-22)\n\n### New Features\n\n* Added a way to disable customer support CTAs with the `features.support` feature flag option (enabled by default).\n\n## 1.9.8 (2025-05-17)\n\n### Bug Fixes\n\n* Fixed concurrency issues on the copying of images while the `sass` is also running.\n\n## 1.9.7 (2025-05-17)\n\n### New Features\n\n* Added a way to customize `async` and `defer` attributes in the `includes.scripts.urls` option.\n\n### Bug Fixes\n\n* The warnings thrown by `sass` are now hidden (until they are fixed).\n\n## 1.9.6 (2025-05-16)\n\n### Changes\n\n* Replaced deprecated `node-sass` dependency with `sass`.\n\n## 1.9.5 (2023-11-06)\n\n### Changes\n\n* Added provenance information upon building NPM package over GitHub Actions.\n\n## 1.9.4 (2023-08-02)\n\n### Bug Fixes\n\n* Fixed performance issues when installing Chappe with NPM v9.\n\n## 1.9.3 (2023-01-06)\n\n### New Features\n\n* Automated the package release process via GitHub Actions (ie. `npm publish`).\n\n## 1.9.2 (2022-12-02)\n\n### Changes\n\n* Improved detection of page titles when generating Open Graph previews.\n\n## 1.9.1 (2022-12-02)\n\n### New Features\n\n* Added support for the `twitter:image:src` and `twitter:card` tags.\n\n## 1.9.0 (2022-12-02)\n\n### ⚠️ Breaking Changes\n\n* The Open Graph images are now auto-generated from the page content, using the provided `opengraph` configuration property; since this option already existed before, you will need to make sure to update your Open Graph image so that it can contain inserted text in the foreground (ie. you need to clear any text from your existing Open Graph image).\n\n## 1.8.1 (2022-11-03)\n\n### Changes\n\n* Added a background hover effect on table rows.\n\n## 1.8.0 (2022-06-24)\n\n### New Features\n\n* Added a \"copy to clipboard\" button in all code blocks.\n\n## 1.7.1 (2022-05-06)\n\n### Changes\n\n* The generated platform changes RSS feed now uses the configured title.\n\n## 1.7.0 (2022-05-06)\n\n### ⚠️ Breaking Changes\n\n* Changed how the theme accent color gets configured with the `theme` configuration property (`light` and `dark` mode variants must now be set).\n\n### New Features\n\n* Support for Crisp Status (aside from Vigil).\n\n## 1.6.4 (2022-02-13)\n\n### Changes\n\n* Moved the search engine opening shortcut from ⌘F to ⌘K (after gathering user feedback).\n\n## 1.6.3 (2022-01-12)\n\n### Bug Fixes\n\n* Fixed an issue with non-highlighted code blocks in Dark Mode, where code text would appear black-on-black.\n* Fixed an issue with inline code blocks in Dark Mode, where selected text would appear white-on-white.\n\n## 1.6.2 (2022-01-11)\n\n### Changes\n\n* Improved management of CSS colors.\n\n## 1.6.1 (2022-01-11)\n\n### Changes\n\n* Improved Dark Mode colors.\n* Moved logos to embedded images (this prevents visual glitches when loading docs).\n\n## 1.6.0 (2022-01-10)\n\n### New Features\n\n* Implemented Dark Mode.\n* Added the ability to configure the theme accent color with the `theme` configuration property.\n\n## 1.5.1 (2022-01-07)\n\n### Changes\n\n* Improved the performance of watching for changes in `data/` while using `chappe serve` or `chappe watch`.\n\n### Bug Fixes\n\n* Fixed Gulp meta-events showing in the Chappe CLI output when using `--verbose` (they are now hidden).\n\n## 1.5.0 (2022-01-06)\n\n### New Features\n\n* The Chappe CLI now embeds a preview server, used to ease with writing and previewing docs (via `chappe serve`).\n\n### Changes\n\n* Improved logging in the Chappe CLI.\n\n### Bug Fixes\n\n* Fixed the abrupt stopping of the Chappe CLI whenever a resource build failed while using `chappe watch`.\n\n## 1.4.1 (2022-01-06)\n\n### Changes\n\n* Refactored the README to add the Chappe logo.\n\n## 1.4.0 (2022-01-05)\n\n### Changes\n\n* Moved the project to a dedicated GitHub organization: [Crisp OSS](https://github.com/crisp-oss).\n\n## 1.3.2 (2022-01-05)\n\n### Changes\n\n* Moved the Chappe CLI from ES5 to ES6.\n\n## 1.3.1 (2022-01-05)\n\n### Changes\n\n* Refactored Chappe CLI terminal outputs.\n* Improved the `acme-docs` example.\n\n## 1.3.0 (2022-01-05)\n\n### Changes\n\n* Reworked the lint pipeline to reduce the number of dependencies.\n\n### Bug Fixes\n\n* Fixed an issue where 404 and private pages appeared in the sitemap.\n* Fixed the configuration path for the SASS linter, which could not read its configuration file in some cases.\n\n## 1.2.1 (2022-01-05)\n\n### Bug Fixes\n\n* Fixed the normalization of paths passed to the Chappe CLI via `--config`.\n\n## 1.2.0 (2022-01-05)\n\n### New Features\n\n* The Chappe CLI `--config` argument now accepts multiple configuration files (comma-separated, merged together).\n* Added the ability to override certain Chappe internal values with the `overrides` configuration property.\n\n### Bug Fixes\n\n* Fixed an issue in Chappe CLI, where a build failure could cause the process to hang indefinitely.\n\n## 1.1.2 (2022-01-04)\n\n### Bug Fixes\n\n* Fixed the NPMJS distribution package, that was missing some more hidden files (such as `.babelrc`).\n\n## 1.1.1 (2022-01-04)\n\n### Bug Fixes\n\n* Fixed the NPMJS distribution package, that was missing the `.banner` file.\n\n## 1.1.0 (2022-01-04)\n\n### Changes\n\n* Moved the build pipeline to Gulp 4.\n* Improved the Chappe CLI with terminal spinners.\n\n## 1.0.3 (2022-01-04)\n\n### Changes\n\n* Now showing a Chappe logo when calling the Chappe CLI.\n* Better temporary files management.\n\n### Bug Fixes\n\n* All internal paths are now absolute (this fixes some build environments).\n\n## 1.0.2 (2022-01-03)\n\n### Bug Fixes\n\n* Fix dependencies for published `chappe` package.\n\n## 1.0.1 (2022-01-03)\n\n### Bug Fixes\n\n* Fix the path of Chappe CLI.\n\n## 1.0.0 (2022-01-03)\n\n### New Features\n\n* Initial release.\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2021 Crisp IM SAS\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<img alt=\"Chappe\" src=\"https://crisp-oss.github.io/chappe/images/chappe.png\" width=\"300\">\n\n[![Test and Build](https://github.com/crisp-oss/chappe/actions/workflows/test.yml/badge.svg)](https://github.com/crisp-oss/chappe/actions/workflows/test.yml) [![Build and Release](https://github.com/crisp-oss/chappe/actions/workflows/build.yml/badge.svg)](https://github.com/crisp-oss/chappe/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/chappe.svg)](https://www.npmjs.com/package/chappe) [![Downloads](https://img.shields.io/npm/dt/chappe.svg)](https://www.npmjs.com/package/chappe)\n\n**Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine.**\n\nChappe is a Developer Docs builder, that produces static assets. No runtime, just lightweight static files. It was built to address SaaS companies needs, and can serve as a first-class modern alternative to hosted services such as [ReadMe](https://readme.com/).\n\nThe reason behind why we made Chappe is the following: while looking for a Developer Docs builder at [Crisp](https://crisp.chat/en/), all that we could find were either outdated open-source projects, or commercial documentation builders. We wanted a modern Developer Docs website hosted on our premises, as pure-static assets. The latter is especially important, as we do not want to rely on a plethora of external services that can go down anytime.\n\n**Using Chappe is as easy as:**\n\n1. Writing all your docs in Markdown;\n2. Building your docs in a single command;\n3. Finally, deploying static build assets to your Web servers (or GitHub Pages, Cloudflare Pages, etc. — _this can be automated via GitHub Actions_);\n\n**😘 Maintainer**: [@valeriansaliou](https://github.com/valeriansaliou)\n\n## Screenshots & Demo\n\n**👉 See a live demo of Chappe on the [Crisp Developer Hub](https://docs.crisp.chat/).**\n\n1️⃣ Chappe can generate your REST API reference:\n\n[![Chappe References](https://crisp-oss.github.io/chappe/images/screenshot-references.gif)](https://docs.crisp.chat/references/rest-api/v1/)\n\n2️⃣ It also generates Markdown-based developer guides:\n\n[![Chappe Guides](https://crisp-oss.github.io/chappe/images/screenshot-guides.gif)](https://docs.crisp.chat/guides/rest-api/rate-limits/)\n\n3️⃣ Oh, and it also lets your users search anything in your Developer Docs:\n\n[![Chappe Search](https://crisp-oss.github.io/chappe/images/screenshot-search.gif)](https://docs.crisp.chat/)\n\n_👉 Note that the search engine feature is 100% local. This means that it does not run on an external service like [Algolia](https://www.algolia.com/), though it does provides similar search performance and results. The search index is generated at build time as a JSON file, which gets loaded on-demand when the search box gets opened._\n\n## Who uses it?\n\n<table>\n<tr>\n<td align=\"center\"><a href=\"https://crisp.chat/\"><img src=\"https://crisp-oss.github.io/chappe/images/logo-crisp.png\" width=\"64\" /></a></td>\n<td align=\"center\"><a href=\"https://meowtel.com/\"><img src=\"https://crisp-oss.github.io/chappe/images/logo-meowtel.png\" width=\"64\" /></a></td>\n</tr>\n<tr>\n<td align=\"center\">Crisp</td>\n<td align=\"center\">Meowtel</td>\n</tr>\n</table>\n\n_👋 You use Chappe and you want to be listed there? [Open an issue](https://github.com/crisp-oss/chappe/issues)._\n\n## Last changes\n\nThe version history can be found in the [CHANGELOG.md](https://github.com/crisp-oss/chappe/blob/master/CHANGELOG.md) file.\n\n## Features\n\n* **Simple & fast**: generate a Developer Docs with optimized static assets. No runtime\n* **Guides**: write developer guides in Markdown (rich content support: images, videos, tables, etc.)\n* **References**: document your HTTP REST API specification using API Blueprint\n* **Changes**: maintain a changelog of your platform (eg. your REST API, your SDKs)\n* **RSS feed**: users can subscribe to your changelog over RSS\n* **Beautiful Markdown rendering**: all content that you write gets rendered with a clear and modern style\n* **Syntax highlighting**: coloring for your code examples in 100+ programming languages\n* **Built-in search engine**: the index is generated during build and is hosted locally\n* **Fully responsive**: full support of desktop, tablet and phone screens\n* **Dark Mode**: read your docs either in light mode or dark mode\n* **Customizable theme**: configure an accent color for your docs theme\n* **SEO-friendly**: a deep sitemap is generated for search engines\n* **Sharing-friendly**: full support of the Open Graph protocol, with the auto-generation of preview images\n* **Private pages support**: mark any guide or reference as private or unlisted (prefix its name with `_`)\n* **Local preview server**: skip setting up a local Web server to preview your docs while writing them, Chappe embeds a preview server that can be started in a single command\n\n_The following optional features can also be enabled:_\n\n* **Chatbox**: integrate with the [Crisp Chatbox](https://crisp.chat/en/livechat/) to handle tech support and collect user feedback\n* **Status page**: integrate with your [Vigil](https://github.com/valeriansaliou/vigil) status page to show live system status (can also be [Crisp Status](https://crisp.chat/en/status/))\n\n## How to use it?\n\n### Installation & Overview\n\nTo install and use Chappe, please follow those steps:\n\n1. Create a new, empty Git repository;\n2. Copy the `examples/acme-docs/` folder contents from the Chappe repository into your project root;\n3. Run: `npm install` (make sure that you have a recent NodeJS version installed);\n4. Run: `npx chappe serve` to build the docs and serve them over a local Web server (it will also watch for changes);\n5. Open: [http://localhost:8080](http://localhost:8080/) in your Web browser to access your docs;\n6. Write your Markdown guides and references in the `data/` directory (changes will be hot-reloaded in your browser);\n\nPlease refer to sections below for more details on how to write docs, customize Chappe, and deploy your final docs to your Web server.\n\n_👉 If Chappe fails to install on your Mac with an Apple Silicon chip, please refer to the [Common questions](#-common-questions) section below._\n\n### Configuration\n\nThe configuration of your Chappe docs is stored in a single JSON file, usually named `config.json`. Your configuration file will make references to images, such as your docs logo, which are stored in the `assets/` folder.\n\nAn empty definition of the Chappe configuration file is available in: [res/config/user.json](https://github.com/crisp-oss/chappe/blob/master/res/config/user.json), although you may rather want to see a filled example: [examples/acme-docs/config.json](https://github.com/crisp-oss/chappe/blob/master/examples/acme-docs/config.json) (if you copy-paste it, **make sure** to change all of its contents).\n\n_👇 Notes on certain configuration rules can be found in the [Advanced settings](#%EF%B8%8F-advanced-settings) section._\n\n### Chappe CLI usage\n\nChappe provides you with the `chappe` command, that builds your docs.\n\nIt supports the following actions, defaulting to `build` if none is specified:\n\n* `build` to build docs\n* `clean` to clean `dist/` and all temporary files\n* `watch` to watch for changes and re-build (useful while writing docs)\n* `serve` to serve built assets on your local/development computer (useful while testing and writing docs, **not used for production**)\n* `lint` to run lints on Chappe internal resources\n\nIt supports the following parameters, with a default value if not set:\n\n* `--config` (paths to the configuration files, comma-separated, _default value:_ `./config.json`)\n* `--assets` (path to the assets directory, _default value:_ `./assets`)\n* `--data` (path to the data directory, _default value:_ `./data`)\n* `--dist` (path where to write built resources, _default value:_ `./dist`)\n* `--temp` (path where to write temporary files, _default value:_ `./.chappe`)\n* `--env` (environment, either `development` or `production`, _default value:_ `production`)\n\nIf you are running with the `serve` action, it accepts additional parameters:\n\n* `--host` (hostname or IP address to use for the local/development server, _default value:_ `localhost`)\n* `--port` (port number to use for the local/development server, _default value:_ `8080`)\n\nSome special parameters are also available:\n\n* `--quiet` (show less output when performing task)\n* `--verbose` (show more output when performing task)\n* `--example` (name of the Chappe docs example to build, useful for Chappe developers and quick tests, eg. `acme-docs`)\n\nTo build your docs, you can call the Chappe CLI as such:\n\n```bash\nnpx chappe build --config=./config.json --assets=./assets --data=./data --dist=./dist\n```\n\nYou can also call the Chappe CLI without any argument, in which case defaults will be used:\n\n```bash\nnpx chappe build\n```\n\nBy default, docs are built for a `production` target, meaning that all assets produced are optimized for speed and size. In most use cases, you will never need to set it to `development`, unless you are trying to extend or modify the Chappe core and therefore need to see uncompressed assets output.\n\nTo create a local development server on [http://localhost:8080](http://localhost:8080/), used to write and preview your docs, use:\n\n```bash\nnpx chappe serve\n```\n\n_👉 If the `chappe` command is not found, make sure to add `chappe` to your `package.json` and call `npm install`._\n\n### Writing docs\n\nDocs can be either: `guides`, `references` or `changes`. The corresponding folders are stored in the `data/` directory, which is passed to the Chappe CLI whenever building your docs.\n\n* `guides` are articles that walk your users through using your systems ([example here](https://docs.crisp.chat/guides/rest-api/rate-limits/)). They are written in Markdown, and are organized in sub-folders if deep nesting of guides in several sections is required. Chappe will auto-generate the navigation sidebar for you, based on this folder hierarchy.\n* `references` are formal specifications of your systems (examples of: [API Blueprint](https://docs.crisp.chat/references/rest-api/v1/) and [Markdown](https://docs.crisp.chat/references/rtm-api/v1/)). They are written in [API Blueprint](https://apiblueprint.org/) for your HTTP REST API (a pseudo-Markdown format), or traditional Markdown for other systems (eg. a WebSocket server).\n* `changes` is a timeline of updates that you made to your systems ([example here](https://docs.crisp.chat/changes/)). They are defined in a JSON format. In addition to the timeline, an RSS feed also gets generated at the `/changes.rss` URL.\n\n### Deploying your docs\n\nTo deploy your docs:\n\n1. First, create a Virtual Host on your Web server, using a dedicated domain, eg. `docs.acme.com`;\n2. Then, build Chappe with `npx chappe build` (on your local computer or a CI/CD runner such as GitHub Actions);\n3. Finally, copy the contents of the `dist/` folder to your server folder for your docs Virtual Host (eg. `/var/www/docs.acme.com`);\n\n⚠️ Chappe **must** be hosted at the root of your docs domain — it **will not** work if hosted in a sub-directory!\n\nHere is an example configuration file for NGINX on the Virtual Host `docs.acme.com`:\n\n```\nserver {\n  listen 443 ssl http2;\n  server_name docs.acme.com;\n\n  root /var/www/docs.acme.com;\n\n  error_page 404 /not_found/;\n}\n```\n\n_👉 Note that if possible, you should make sure that you have a rule to catch 404 errors and show the `not_found` page (as the NGINX configuration file above shows)._\n\n## Syntax guide\n\n### How to write guides?\n\nGuides are stored within `guides/` in your data directory. A guide is stored as a Markdown file named `index.md` in a sub-directory with the guide name eg. `hello-world`. The sub-directory structure directly maps to the final URL that you get: for instance `guides/hello-world/index.md` results in eg. `http://docs.acme.com/guides/hello-world/`.\n\n#### Structure of a guide file\n\nEach guide Markdown file **must** start with a meta-data header, which holds information on:\n\n* `TITLE`: The guide article name\n  * Example: `TITLE: Hello World`\n* `INDEX`: Number used to position the article relative to others in the navigation sidebar\n  * Example: `INDEX: 1`\n* `UPDATED`: The date at which the guide article has been updated\n  * Example: `UPDATED: 2021-09-22`\n* `LINK`: Additional navigation links to be added in the navigation sidebar\n  * _Optional_, _Multiple possible_\n  * Example: `LINK: Reference -> /references/rest-api/v1/`\n\nRight after the header is defined, you can start writing Markdown for your guide, as normal.\n\nAn example of a full Markdown code for a guide is available at: [examples/acme-docs/data/guides/hello-world/index.md](https://raw.githubusercontent.com/crisp-oss/chappe/master/examples/acme-docs/data/guides/hello-world/index.md)\n\n#### Adding icons to guide sections\n\nEach guide main section can have its icon shown in the navigation sidebar (first-level sections only).\n\nSection icons are defined in the `config.json` configuration file, within `images.categories.guides`. The section folder name, eg. `hello-world`, should be added to the `guides` object, associated to an SVG icon image from your `assets/` folder.\n\nFor example:\n\n```json\n{\n  \"images\" : {\n    \"categories\" : {\n      \"guides\" : {\n        \"hello-world\" : \"images/categories/guides/hello-world.svg\"\n      }\n    }\n  }\n}\n```\n\n#### List of special Markdown syntax\n\nWhile the [Markdown specification](https://daringfireball.net/projects/markdown/syntax) defines most of the syntax that we need to build a full-featured Developer Docs (text formatting, images, tables, etc.), some non-standard elements had to be defined in Chappe.\n\n---\n\n##### Video embeds\n\nTo embed a video in a page, use the following Markdown syntax:\n\n```markdown\n${provider}[Video Title](video-id)\n```\n\nSupported providers: `youtube`\n\nExample:\n\n```markdown\n${youtube}[In-depth Introduction to the Crisp RTM API](vS-h6k2ML6M)\n```\n\n---\n\n##### Text emphasis (notice, info or warning blocks)\n\nTo insert text in an emphasis block, use one of the following Markdown syntaxes:\n\n```markdown\n! This is a notice text.\n!! This is an info text.\n!!! This is a warning text.\n```\n\n---\n\n##### Image with caption\n\nTo insert an image with a caption, use the following Markdown syntax:\n\n```markdown\n$[Caption Text](![Image Title](image-path.png))\n```\n\nExample:\n\n```markdown\n$[Copy your Website ID](![](copy-website-id.png))\n```\n\n---\n\n##### Navigation links\n\nTo insert a navigation block, with one or multiple links to other pages, use the following Markdown syntax:\n\n```markdown\n+ Navigation\n  | Link Title 1: Link Description -> ./link/target/1/\n  | Link Title 2: Link Description -> http://external-url.com/target/page/ [blank]\n```\n\nExample:\n\n```markdown\n+ Navigation\n  | Quickstart: Learn how to use the REST API in minutes. -> ./quickstart/\n  | Authentication: Read how to authenticate to the REST API. -> ./authentication/\n  | Rate-Limits: Learn about request rate-limits. -> ./rate-limits/\n  | API Libraries: Libraries for your programming language. -> ./api-libraries/\n```\n\n---\n\n##### Interact with the Crisp Chatbox\n\nIf you need to interact with the Crisp Chatbox from your Markdown code, you can include a traditional Markdown link with an URL pointing to special anchors.\n\nThe following anchors are available:\n\n* Pop open the chatbox: `#crisp-chat-open`\n* Prompt to submit feedback on the current page: `#crisp-chat-feedback`\n\nExample:\n\n```markdown\nIf you have any question on this guide, please [contact our chat support](#crisp-chat-open).\n```\n\n_👉 Note that this only works if you are using the Crisp Chatbox integration, and if the Crisp Chatbox is appearing on your docs._\n\n---\n\n### How to write references?\n\nReferences are stored within `references/` in your data directory. A reference is stored either as an API Blueprint or Markdown file named for example `v1.md` for the API version, in a sub-directory corresponding to the name of the API, eg. `rest-api`. The sub-directory structure directly maps to the final URL that you get: for instance `references/rest-api/v1.md` results in eg. `http://docs.acme.com/references/rest-api/v1/`.\n\n#### API Blueprint references\n\nAPI Blueprint-formatted references are used to specify an HTTP REST API.\n\nEach reference written with API Blueprint **must** start with a meta-data header, which holds information on:\n\n* `TYPE`: The type of the reference\n  * Value: `API Blueprint`\n* `TITLE`: The reference title (with its version number)\n  * Example: `TITLE: REST API Reference (V1)`\n* `UPDATED`: The date at which the reference has been updated\n  * Example: `UPDATED: 2021-12-22`\n\nImmediately following, come API Blueprint meta-datas:\n\n* `FORMAT`: The API Blueprint format (_do not change this_)\n  * Value: `1A`\n* `HOST`: The HTTP REST API host URL\n  * Example: `https://api.crisp.chat/v1`\n\nThen, a main title with the following mandatory content:\n\n```markdown\n# Reference\n```\n\nAfter that, you can specify all your HTTP REST API routes in API Blueprint as normal.\n\nAlso, note that as done with guides above, reference sections can have their own icon images. Section icons are defined in the `config.json` configuration file, within `images.categories.references`.\n\nAn example of a full API Blueprint code for a reference is available at: [examples/acme-docs/data/references/rest-api/v1.md](https://raw.githubusercontent.com/crisp-oss/chappe/master/examples/acme-docs/data/references/rest-api/v1.md)\n\n#### Markdown references\n\nMarkdown-formatted references are used to specify anything that is not an HTTP REST API. For instance, a WebSocket endpoint, a network protocol or a programmatic interface.\n\nEach reference Markdown file **must** start with a meta-data header, which holds information on:\n\n* `TYPE`: The type of the reference\n  * Value: `Markdown`\n* `TITLE`: The reference title (with its version number)\n  * Example: `TITLE: RTM API Reference (V1)`\n* `UPDATED`: The date at which the reference has been updated\n  * Example: `UPDATED: 2021-09-22`\n\nAfter that, you can write the specification contents in Markdown.\n\nAlso, note that as done with guides above, reference sections can have their own icon images. Section icons are defined in the `config.json` configuration file, within `images.categories.references`.\n\nAn example of a full Markdown code for a reference is available at: [examples/acme-docs/data/references/rtm-api/v1.md](https://raw.githubusercontent.com/crisp-oss/chappe/master/examples/acme-docs/data/references/rtm-api/v1.md)\n\n### How to write changelogs?\n\nChanges are stored within `changes/` in your data directory. They are organized in JSON files for each year, eg. `2021.json`.\n\n#### Structure of a changelog file\n\nA changelog file for a year contains an array of all individual change entries. Think of it as a yearly feed of all dated changes.\n\nFor instance, a `2021.json` file with a single change would contain:\n\n```json\n[\n  {\n    \"group\" : \"rest_api\",\n    \"type\"  : \"change\",\n    \"date\"  : \"2021-12-03\",\n    \"text\"  : \"Markdown-formatted text for this change on the REST API.\"\n  }\n]\n```\n\nAn example of a full changelog file is available at: [examples/acme-docs/data/changes/2021.json](https://raw.githubusercontent.com/crisp-oss/chappe/master/examples/acme-docs/data/changes/2021.json)\n\n#### Allowed values for a change\n\nA change is structured as such:\n\n* `group`: the category of this change — _define your custom categories labels in `texts.changes.groups` and colors in `colors.changes.groups` within your `config.json`_;\n* `type`: the type of the change (either: `change` or `deprecation`);\n* `date`: a date for the change (formatted as: `YYYY-MM-DD`);\n* `text`: the description text for the change, Markdown-formatted — _make sure that any URL you define there is a full URL, as this is also used in RSS feeds_\n\n## ⚛️ Advanced settings\n\n### Available code coloring\n\nCode coloring rules for programming languages must be added manually, for each syntax that you intend use. As the rules are quite heavy for each syntax, Chappe includes none by default.\n\nFor instance, if you need to show examples of Java code, you'd need to add the `java` code coloring rule in `plugins.code.syntaxes` in your `config.json`. Chappe runs on [Prism](https://github.com/PrismJS/prism) for code coloring.\n\nMost often used syntaxes are listed below (pick yours!):\n\n```\nmarkup\nmarkup-templating\ncss\nclike\nc\njavascript\nbash\ngo\njava\ngroovy\njson\nobjectivec\nphp\npython\nruby\nrust\nswift\nobjectivec\n```\n\nAll available Prism rules can be found [here](https://github.com/PrismJS/prism/tree/master/components).\n\n_👉 Note that some rules depend on others. For instance, `objectivec` requires the `c` rule to be also included. If you do not get code coloring for a certain syntax after including it, then it probably means that one of its dependency is missing. Please refer to the list of Prism components for more details._\n\n### Check file sizes during build\n\nOnce Chappe is done building your docs, it checks for all built files sizes against maximum build size rules. This is done to ensure that you do not get bad surprises about your Developer Docs users experiencing slow load times, especially when including a lot of heavy images in guides.\n\nIn the event a build size rule threshold is reached, the Chappe CLI will error out, informing you which file is over-sized.\n\nTo adjust size thresholds or disable this checker rule, open your `config.json` file and refer to the `rules.build_size` property:\n\n* To circumvent build failure when a file is over-sized, set the `fail` property to `false`;\n* Maximum sizes can be adjusted where relevant with the `sizes` property (note that sizes are in bytes, so 10KB is about `10000`);\n\n## 🙋 Common questions\n\n### The installation of Chappe fails on my Mac with Apple Silicon\n\nChappe relies on the `gulp-ogimage` dependency to auto-generate Open Graph images, which itself uses a library named `canvas`. Unfortunately, as of December 2022, `canvas` does not provide any pre-built binary for the `arm64` CPU architecture, leading to Chappe failing to install on Macs with Apple Silicon chips.\n\nIn order to install Chappe on `arm64` architectures, you will need to ensure that [Homebrew](https://brew.sh/) is setup on your system, then run:\n\n```bash\nbrew install pkg-config cairo pango libpng jpeg giflib librsvg pixman\n```\n\nOnce those tools are installed, try installing Chappe again.\n\n### How can I customize my docs style?\n\nIn order to customize your docs style — _ie. override the default Chappe style past what can already be customized in the `config.json` configuration file_ — open `config.json` and look for the `includes` property (that contains `stylesheets`, that contains `urls` and `inline`).\n\nYou can easily deploy your own custom stylesheet on your docs domain, along with Chappe-generated `dist/` assets, with CSS classes overriding Chappe default styles:\n\n```json\n{\n  \"includes\" : {\n    \"stylesheets\" : {\n      \"urls\"   : [\n        \"/overrides/style.css\"\n      ],\n\n      \"inline\" : []\n    }\n  }\n}\n```\n\n### How can I add scripts like Google Tag Manager?\n\nTo add inline scripts such as Google Tag Manager, open your `config.json` configuration file for Chappe, and look for the `includes` property (that contains `scripts`, that contains `urls` and `inline`).\n\nAdd a new entry to the `urls` and `inline` array, separately, giving eg.:\n\n```json\n{\n  \"includes\" : {\n    \"scripts\" : {\n      \"urls\"   : [\n        \"https://www.googletagmanager.com/gtag/js?id={YOUR_GTM_ID}\",\n\n        {\n          \"src\"   : \"https://scripts.simpleanalyticscdn.com/latest.js\",\n          \"async\" : true\n        }\n      ],\n\n      \"inline\" : [\n        \"window.dataLayer = window.dataLayer || [];\\nfunction gtag(){dataLayer.push(arguments);}\\ngtag(\\\"js\\\", new Date());\\ngtag(\\\"config\\\", \\\"{YOUR_GTM_ID}\\\");\"\n      ]\n    }\n  }\n}\n```\n\nThe `urls` property will include the JavaScript at the provided URL on all pages, while the `inline` property will append the inline JavaScript in a `script` element on all pages.\n\n### How can I deploy my docs to GitHub Pages?\n\nTo build your docs to GitHub Pages, you will first need to host your docs project as a GitHub repository. Then, make sure that GitHub Actions is configured and running for your project.\n\nYou can then use the [deploy-to-github-pages](https://github.com/marketplace/actions/deploy-to-github-pages) action to proceed with building your docs via `npx chappe build` and then deploying the `dist/` folder to GitHub Pages.\n\n### Where does the Chappe name come from?\n\nChappe was named after [Claude Chappe](https://en.wikipedia.org/wiki/Claude_Chappe), a French inventor, pioneer in long-distance communications. He invented the [optical telegraph](https://en.wikipedia.org/wiki/Optical_telegraph) (a.k.a. semaphore telegraph), later replaced by the [electrical telegraph](https://en.wikipedia.org/wiki/Electrical_telegraph). Those technologies were the founding blocks of what took over the world next: analog and digital telecommunications.\n\nQuoting from his page on Wikipedia:\n\n> This [the optical telegraph] was the first practical telecommunications system of the industrial age, and was used until the 1850s when electric telegraph systems replaced it.\n\n_Credits to [Baptiste Jamin](https://github.com/baptistejamin) for the name idea._\n"
  },
  {
    "path": "bin/chappe.js",
    "content": "#!/usr/bin/env node\n\n/*\n * chappe\n *\n * Copyright 2021, Crisp IM SAS\n * Author: Valerian Saliou <valerian@valeriansaliou.name>\n */\n\n\n\"use strict\";\n\n\n// Suppress all warnings from Node, as this is a CLI script\nprocess.removeAllListeners(\"warning\");\n\n\nvar fs       = require(\"fs\");\nvar path     = require(\"path\");\n\nvar ora      = require(\"ora\");\n\nvar version  = require(\"../package.json\").version;\nvar args     = require(\"yargs\").argv;\n\n\n/**\n * Chappe CLI\n * @class\n * @classdesc Chappe CLI class.\n */\nclass ChappeCLI {\n  /**\n   * Constructor\n   */\n  constructor() {\n    // Constants\n    this.__context_defaults  = {\n      default : {\n        config : \"./config.json\",\n        assets : \"./assets\",\n        data   : \"./data\",\n        dist   : \"./dist\",\n        temp   : \"./.chappe\",\n        env    : \"production\",\n        host   : \"localhost\",\n        port   : 8080\n      },\n\n      example : {\n        config : \"./examples/{{target}}/config.json\",\n        assets : \"./examples/{{target}}/assets\",\n        data   : \"./examples/{{target}}/data\",\n        dist   : \"./dist\",\n        temp   : \"./.chappe\",\n        env    : \"development\",\n        host   : \"localhost\",\n        port   : 8080\n      }\n    };\n\n    this.__actions_available = [\n      \"build\",\n      \"clean\",\n      \"watch\",\n      \"serve\",\n      \"lint\"\n    ];\n\n    this.__actions_logging   = [\n      \"watch\",\n      \"serve\"\n    ];\n\n    this.__actions_no_aborts = [\n      \"watch\",\n      \"serve\"\n    ];\n\n    this.__spinner_successes = {\n      default : {\n        method : \"succeed\",\n        text   : \"Success!\"\n      },\n\n      clean   : {\n        method : \"succeed\",\n        text   : \"Cleaned up.\"\n      },\n\n      build   : {\n        method : \"succeed\",\n        text   : \"Build done!\"\n      },\n\n      lint    : {\n        method : \"succeed\",\n        text   : \"Lint passed.\"\n      },\n\n      serve   : {\n        method : \"start\",\n        text   : \"Now listening...\"\n      },\n\n      watch   : {\n        method : \"start\",\n        text   : \"Watching...\\n\"\n      }\n    };\n\n    this.__path_expand_keys  = [\n      \"config\",\n      \"assets\",\n      \"data\",\n      \"dist\",\n      \"temp\"\n    ];\n\n    this.__env_available     = [\n      \"development\",\n      \"production\"\n    ];\n\n    // Storage\n    this.__has_setup_gulp_logging = false;\n  }\n\n\n  // jscs:disable disallowIdentifierNames\n\n\n  /**\n   * Runs the class\n   * @public\n   * @return {undefined}\n   */\n  run() {\n    // Run help?\n    if (args.help) {\n      return this.__run_help();\n    }\n\n    // Run version?\n    if (args.version) {\n      return this.__run_version();\n    }\n\n    // Run default (clean or build)\n    this.__run_default();\n  }\n\n\n  /**\n   * Runs help\n   * @private\n   * @return {undefined}\n   */\n  __run_help() {\n    console.log(\n      \"Builds given Chappe documentation resources into static assets.\\n\\n\"  +\n\n      \"Available actions:\\n\"                                                 +\n        (this.__format_help_actions(this.__actions_available) + \"\\n\\n\")      +\n\n      \"Available arguments:\\n\"                                               +\n        (this.__format_help_argument(\"config\") + \"\\n\")                       +\n        (this.__format_help_argument(\"assets\") + \"\\n\")                       +\n        (this.__format_help_argument(\"data\")   + \"\\n\")                       +\n        (this.__format_help_argument(\"dist\")   + \"\\n\")                       +\n        (this.__format_help_argument(\"temp\")   + \"\\n\")                       +\n        (this.__format_help_argument(\"env\")    + \"\\n\")                       +\n        (this.__format_help_argument(\"host\")   + \"\\n\")                       +\n        (this.__format_help_argument(\"port\")   + \"\\n\\n\")                     +\n\n      \"Other arguments:\\n\"                                                   +\n        (this.__format_help_argument(\"quiet\") + \"\\n\")                        +\n        (this.__format_help_argument(\"verbose\") + \"\\n\")                      +\n        this.__format_help_argument(\"example\")\n    );\n\n    process.exit(0);\n  }\n\n\n  /**\n   * Runs version\n   * @private\n   * @return {undefined}\n   */\n  __run_version() {\n    console.log(\"Chappe CLI v\" + version);\n\n    process.exit(0);\n  }\n\n\n  /**\n   * Runs default\n   * @private\n   * @return {undefined}\n   */\n  __run_default() {\n    let _has_output = (\n      (args.quiet && !args.verbose) ? false : true\n    );\n\n    // Acquire task from action\n    let _task = this.__acquire_action();\n\n    // Dump banner?\n    if (_has_output === true) {\n      console.log(this.__dump_banner());\n    }\n\n    // Acquire context\n    global.CONTEXT = this.__acquire_context(_task);\n\n    if (_has_output === true) {\n      console.log(\n        (\"Chappe will \" + _task + \" docs with context:\\n\")  +\n          (this.__dump_context(global.CONTEXT) + \"\\n\")\n      );\n    }\n\n    // Setup spinner\n    // Throttle down spinner refresh interval, to ease w/ terminal CPU usage \\\n    //   when performing long operations eg. 'watch'.\n    let _spinner = ora({\n      text     : \"Working...\\n\",\n      color    : \"cyan\",\n      interval : 350\n    });\n\n    // Import Gulp instance\n    let _gulp = require(\"gulp\");\n\n    // Setup error traps\n    this.__setup_error_traps(_gulp, _spinner, _task);\n\n    // Setup Gulp logging? (pre-task mode, only if verbose)\n    if (args.verbose) {\n      this.__setup_gulp_logging(_gulp, _spinner);\n    }\n\n    // Import the Gulpfile\n    let _gulpfile = require(\"../gulpfile.js\");\n\n    // Start spinner\n    _spinner.start();\n\n    // Build docs\n    _gulpfile[_task]((error) => {\n      // Any error occured?\n      if (error) {\n        // Throw error and stop spinner\n        _spinner.fail(\"Error:\");\n\n        console.log(error);\n\n        _spinner.stop();\n\n        process.exit(1);\n      } else {\n        // Show success spinner (depending on task success rule)\n        let _success_rules = (\n          this.__spinner_successes[_task] || this.__spinner_successes.default\n        );\n\n        _spinner[_success_rules.method](_success_rules.text);\n\n        // Setup Gulp logging? (post-task mode, only for certain actions)\n        if (this.__actions_logging.includes(_task) === true) {\n          this.__setup_gulp_logging(_gulp, _spinner);\n        }\n      }\n    });\n  }\n\n\n  /**\n   * Formats help actions\n   * @private\n   * @param  {object} actions\n   * @return {string} Formatted help actions\n   */\n  __format_help_actions(actions) {\n    let _actions = actions.map((action) => {\n      return (\" \" + action);\n    });\n\n    return _actions.join(\"\\n\");\n  }\n\n\n  /**\n   * Formats help argument\n   * @private\n   * @param  {string} name\n   * @return {string} Formatted help argument\n   */\n  __format_help_argument(name) {\n    let _argument = (\" --\" + name);\n\n    // Append default value? (if any)\n    let _default_value = this.__context_defaults.default[name];\n\n    if (typeof _default_value !== \"undefined\") {\n      _argument += (\"  (defaults to: '\" + _default_value + \"')\");\n    }\n\n    return _argument;\n  }\n\n\n  /**\n   * Dumps the banner\n   * @private\n   * @return {string} Dumped banner\n   */\n  __dump_banner() {\n    // Generate bundle name\n    let _bundle_name = (\"Chappe v\" + version);\n\n    // Read banner file\n    let _buffer = (\n      fs.readFileSync(path.join(__dirname, \"../.banner\"))\n    );\n\n    // Convert banner to string and inject bundle name\n    let _banner = _buffer.toString().replace(\"{{bundle}}\", _bundle_name);\n\n    return _banner;\n  }\n\n\n  /**\n   * Dumps the context\n   * @private\n   * @param  {object} context\n   * @return {string} Dumped context\n   */\n  __dump_context(context) {\n    let _context = Object.keys(context).map((key) => {\n      return (\" \" + key + \" -> \" + context[key]);\n    });\n\n    return _context.join(\"\\n\");\n  }\n\n\n  /**\n   * Acquires current action\n   * @private\n   * @return {string} Current action\n   */\n  __acquire_action() {\n    let _action;\n\n    for (let _i = 0; _i < this.__actions_available.length; _i++) {\n      let _cur_action = this.__actions_available[_i];\n\n      if (process.argv.includes(_cur_action) === true) {\n        _action = _cur_action;\n\n        break;\n      }\n    }\n\n    return (_action || \"build\");\n  }\n\n\n  /**\n   * Acquires current context\n   * @private\n   * @param  {string} task\n   * @return {object} Current context\n   */\n  __acquire_context(task) {\n    // Acquire defaults\n    let _defaults = (\n      this.__context_defaults[(args.example ? \"example\" : \"default\")]\n    );\n\n    // Inject target in defaults?\n    if (args.example) {\n      _defaults = this.__inject_defaults_target(\n        _defaults, args.example\n      );\n    }\n\n    // Generate context\n    // Notice: the values are temporarily represented as arrays, to ease with \\\n    //   data normalization steps.\n    let _context = {\n      config : (args.config  || _defaults.config).split(\",\"),\n      assets : [(args.assets || _defaults.assets)],\n      data   : [(args.data   || _defaults.data)],\n      dist   : [(args.dist   || _defaults.dist)],\n      temp   : [(args.temp   || _defaults.temp)],\n      env    : [(args.env    || _defaults.env)]\n    };\n\n    // Append serve-related context?\n    if (task === \"serve\") {\n      _context.host = [(args.host || _defaults.host)];\n      _context.port = [parseInt((args.port || _defaults.port), 10)];\n    }\n\n    // Expand context paths\n    let _base_path = process.cwd();\n\n    this.__path_expand_keys.forEach((key) => {\n      let _context_values = _context[key];\n\n      for (let _i = 0; _i < _context_values.length; _i++) {\n        // Path is not already in absolute format? (convert to absolute)\n        if (path.isAbsolute(_context_values[_i]) !== true) {\n          _context_values[_i] = (\n            path.join(_base_path, _context_values[_i])\n          );\n        }\n      }\n    });\n\n    // Re-join context values as bare strings (from lists)\n    for (let _key in _context) {\n      // Notice, this retains non-string types untouched\n      if (_context[_key].length === 1) {\n        _context[_key] = _context[_key][0];\n      } else {\n        _context[_key] = _context[_key].join(\",\");\n      }\n    }\n\n    // Validate final context\n    if (this.__env_available.includes(_context.env) !== true) {\n      throw new Error(\n        \"Environment value not recognized: \" + _context.env\n      );\n    }\n\n    return _context;\n  }\n\n\n  /**\n   * Inject target into defaults\n   * @private\n   * @param  {object} defaults\n   * @param  {string} target\n   * @return {object} Defaults w/ injections\n   */\n  __inject_defaults_target(defaults, target) {\n    // Important: create a new defaults object, as not to alter the given one, \\\n    //   which could be re-used later.\n    let _injected_defaults = {};\n\n    for (let _key in defaults) {\n      let _cur_default = defaults[_key];\n\n      if (typeof _cur_default === \"string\") {\n        _cur_default = _cur_default.replace(\"{{target}}\", target);\n      }\n\n      _injected_defaults[_key] = _cur_default;\n    }\n\n    return _injected_defaults;\n  }\n\n\n  /**\n   * Setups error traps\n   * @private\n   * @param  {object} gulp\n   * @param  {object} spinner\n   * @param  {string} task\n   * @return {undefined}\n   */\n  __setup_error_traps(gulp, spinner, task) {\n    // Check if should crash on error\n    let _crash_on_error = (\n      (this.__actions_no_aborts.includes(task) === true) ? false : true\n    );\n\n    // Setup process events\n    process.once(\"exit\", (code) => {\n      if (code > 0) {\n        // Self-kill, because apparently even if calling process.exit(1), the \\\n        //   process stays active and sticky in certain cases (due to \\\n        //   registered listeners and file descriptors in some Gulp libraries).\n        // Warning: this is a bit hacky!\n        process.exitCode = code;\n\n        process.kill(process.pid, \"SIGKILL\");\n      }\n    });\n\n    process.on(\"uncaughtException\", (error) => {\n      // Throw error and stop spinner\n      spinner.fail(\"Unexpected failure:\");\n\n      console.log(\n        (error && error.context) ? error.context : error\n      );\n\n      spinner.stop();\n\n      process.exit(1);\n    });\n\n    // Important: setup Gulp error listener, otherwise any error will get \\\n    //   uncaught and be handled by 'uncaughtException' at the process-level.\n    gulp.on(\"error\", (event) => {\n      // Freeze spinner w/ failure\n      spinner.fail(\n        \"Error in '\" + event.name + \"':\"\n      );\n\n      if (event.error) {\n        console.log(event.error);\n      }\n\n      // Restart the spinner\n      spinner.start();\n\n      if (_crash_on_error === true) {\n        process.exit(1);\n      }\n    });\n  }\n\n\n  /**\n   * Setups Gulp logging\n   * @private\n   * @param  {object} gulp\n   * @param  {object} spinner\n   * @return {undefined}\n   */\n  __setup_gulp_logging(gulp, spinner) {\n    // Not quiet and not already setup?\n    if (!args.quiet && this.__has_setup_gulp_logging !== true) {\n      this.__has_setup_gulp_logging = true;\n\n      gulp.on(\"start\", (event) => {\n        // Freeze spinner w/ information\n        // Notice: do not log meta-events eg. '<series>'\n        if (event.name.startsWith(\"<\") !== true) {\n          spinner.info(\n            \"Starting '\" + event.name + \"'...\"\n          );\n        }\n      });\n\n      gulp.on(\"stop\", (event) => {\n        // Freeze spinner w/ success\n        // Notice: do not log meta-events eg. '<series>'\n        if (event.name.startsWith(\"<\") !== true) {\n          spinner.succeed(\n            \"Finished '\" + event.name + \"'\"\n          );\n\n          // Restart the spinner\n          spinner.start();\n        }\n      });\n    }\n  }\n\n\n  // jscs:enable disallowIdentifierNames\n}\n\n\n(new ChappeCLI()).run();\n"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"chappe\",\n\n  \"dependencies\": {\n    \"reset.css\": \"https://github.com/shannonmoeller/reset-css.git#d8bfbea7095dd54700fdb7ff05953b27862c5363\",\n    \"console\": \"https://github.com/valeriansaliou/console.js.git#fe138ee08caba80aadf7f2794e40131f40988755\",\n    \"cookies\": \"https://github.com/crisp-dev/Cookies.git#ba57f18775e726a80bab9aaad35ee479c6520963\",\n    \"cash\": \"https://github.com/fabiospampinato/cash.git#f7b4fc2ce0fc02eb367ecc6e5092d27e7c9809ba\",\n    \"minisearch\": \"https://github.com/lucaong/minisearch.git#917cf84b2ff79f3ba5612f4a5d5f542b74f78bbf\",\n    \"prism.js\": \"https://github.com/PrismJS/prism.git#61221218f1773ef5d4d8c60b02ed02c2d2e976ac\"\n  }\n}\n"
  },
  {
    "path": "examples/acme-docs/config.json",
    "content": "{\n  \"identity\" : {\n    \"title\"     : \"Acme Developer Hub\",\n    \"copyright\" : \"Acme Inc.\"\n  },\n\n  \"theme\" : {\n    \"accent\" : {\n      \"light\" : {\n        \"base\"   : \"#2275f1\",\n        \"active\" : \"#0f69ef\"\n      },\n\n      \"dark\" : {\n        \"base\"   : \"#e0e7ed\",\n        \"active\" : \"#d5dde4\"\n      }\n    }\n  },\n\n  \"urls\" : {\n    \"base\" : \"https://docs.acme.com\"\n  },\n\n  \"favicons\" : {\n    \"main\"  : \"favicons/favicon.ico\",\n\n    \"sizes\" : {\n      \"default\" : \"favicons/favicon.png\",\n      \"512x512\" : \"favicons/favicon-512x512.png\",\n      \"256x256\" : \"favicons/favicon-256x256.png\",\n      \"128x128\" : \"favicons/favicon-128x128.png\",\n      \"32x32\"   : \"favicons/favicon-32x32.png\"\n    }\n  },\n\n  \"images\" : {\n    \"illustrations\" : {\n      \"home\" : \"images/illustrations/home.png\"\n    },\n\n    \"logos\" : {\n      \"header_full\"  : \"images/logos/logo-header-full.svg\",\n      \"header_short\" : \"images/logos/logo-header-short.svg\",\n      \"footer\"       : \"images/logos/logo-footer.svg\"\n    },\n\n    \"metas\" : {\n      \"opengraph\" : \"images/metas/opengraph.png\"\n    },\n\n    \"categories\" : {\n      \"guides\" : {\n        \"hello-world\"     : \"images/categories/guides/hello-world.svg\",\n        \"markdown-syntax\" : \"images/categories/guides/markdown-syntax.svg\"\n      },\n\n      \"references\" : {\n        \"rest-api\" : \"images/categories/references/rest-api.svg\",\n        \"rtm-api\"  : \"images/categories/references/rtm-api.svg\",\n        \"team\"     : \"images/categories/references/team.svg\"\n      }\n    }\n  },\n\n  \"dimensions\" : {\n    \"logos\" : {\n      \"header_full\"  : {\n        \"width\" : 136\n      },\n\n      \"header_short\" : {\n        \"width\" : 46\n      },\n\n      \"footer\"       : {\n        \"width\" : 84\n      }\n    }\n  },\n\n  \"colors\" : {\n    \"changes\" : {\n      \"groups\" : {\n        \"rest_api\"  : \"#d34040\",\n        \"rtm_api\"   : \"#dc9b37\",\n        \"web_hooks\" : \"#1a71f5\"\n      }\n    }\n  },\n\n  \"texts\" : {\n    \"home\" : {\n      \"title\" : \"Welcome to the Acme Developer Hub\",\n      \"label\" : \"Build apps for 10M+ Acme app users.\"\n    },\n\n    \"changes\" : {\n      \"groups\" : {\n        \"rest_api\"  : \"REST API\",\n        \"rtm_api\"   : \"RTM API\",\n        \"web_hooks\" : \"Web Hooks\"\n      },\n\n      \"titles\" : {\n        \"feed\"   : \"Acme Platform Changes\",\n        \"latest\" : \"Latest Platform Changes\",\n        \"year\"   : \"Platform Changes In\"\n      },\n\n      \"notice\" : \"**The Acme Platform is updated every day with improvements and new features.** This page is a ledger of all notable changes that occured over time, and which may impact your integrations.\\n\\n**Note that breaking changes appear in red.**\\n\\nIf you would like to monitor those changes, an RSS feed is available at: [`changes.rss`](/changes.rss)\"\n    }\n  },\n\n  \"links\" : {\n    \"header\" : {\n      \"navigation\" : [\n        {\n          \"route\" : [\"guides\"],\n          \"label\" : \"Guides\"\n        },\n\n        {\n          \"route\"    : [\"references\"],\n          \"label\"    : \"References\",\n\n          \"dropdown\" : [\n            {\n              \"route\" : [\"rest-api\", \"v1\"],\n              \"label\" : \"REST API Reference\"\n            },\n\n            {\n              \"route\" : [\"rtm-api\", \"v1\"],\n              \"label\" : \"RTM API Reference\"\n            }\n          ]\n        }\n      ],\n\n      \"actions\" : [\n        {\n          \"label\"    : \"Sign Up\",\n\n          \"dropdown\" : [\n            {\n              \"title\"    : \"Sign Up to Acme\",\n              \"subtitle\" : \"Create your Acme user account.\",\n              \"target\"   : \"https://dashboard.acme.com/\"\n            },\n\n            {\n              \"title\"    : \"Sign Up to Developer Center\",\n              \"subtitle\" : \"Start building Acme integrations.\",\n              \"target\"   : \"https://developers.acme.com/\"\n            }\n          ]\n        }\n      ]\n    },\n\n    \"footer\" : {\n      \"navigation\" : [\n        {\n          \"label\"  : \"Acme Website\",\n          \"target\" : \"https://acme.com/\"\n        },\n\n        {\n          \"label\"  : \"Terms of Use\",\n          \"target\" : \"https://acme.com/terms/\"\n        },\n\n        {\n          \"label\"  : \"Privacy Policy\",\n          \"target\" : \"https://acme.com/privacy/\"\n        },\n\n        {\n          \"label\"  : \"About Us\",\n          \"target\" : \"https://acme.com/about/\"\n        },\n\n        {\n          \"label\"  : \"Read our Blog\",\n          \"target\" : \"https://blog.acme.com/\"\n        }\n      ]\n    }\n  },\n\n  \"actions\" : {\n    \"home\" : [\n      {\n        \"route\" : [\"guides\"],\n        \"label\" : \"Build your First Acme App\"\n      }\n    ]\n  },\n\n  \"bulletpoints\" : {\n    \"home\" : {\n      \"quickstart\" : {\n        \"description\" : \"Start building on the top of the Acme Platform in a matter of minutes.\",\n\n        \"actions\"     : [\n          {\n            \"route\" : [\"guides\"],\n            \"label\" : \"Start building\"\n          }\n        ]\n      },\n\n      \"guides\" : {\n        \"description\" : \"Guides & tutorials on how to integrate Acme with your systems.\",\n\n        \"actions\"     : [\n          {\n            \"route\" : [\"guides\", \"hello-world\"],\n            \"label\" : \"Introduction\"\n          },\n\n          {\n            \"route\" : [\"guides\", \"markdown-syntax\", \"syntax\"],\n            \"label\" : \"Markdown Syntax\"\n          }\n        ]\n      },\n\n      \"references\" : {\n        \"description\" : \"In-depth technical references of all available Acme APIs.\",\n\n        \"actions\"     : [\n          {\n            \"route\" : [\"references\", \"rest-api\", \"v1\"],\n            \"label\" : \"REST API\"\n          },\n\n          {\n            \"route\" : [\"references\", \"rtm-api\", \"v1\"],\n            \"label\" : \"RTM API\"\n          }\n        ]\n      }\n    }\n  },\n\n  \"includes\" : {\n    \"scripts\" : {\n      \"urls\"   : [],\n      \"inline\" : []\n    },\n\n    \"stylesheets\" : {\n      \"urls\"   : [],\n      \"inline\" : []\n    }\n  },\n\n  \"tokens\" : {\n    \"crisp_website_id\" : \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n  },\n\n  \"plugins\" : {\n    \"code\" : {\n      \"syntaxes\" : [\n        \"markup\",\n        \"markup-templating\",\n        \"css\",\n        \"clike\",\n        \"c\",\n        \"javascript\",\n        \"bash\",\n        \"go\",\n        \"java\",\n        \"groovy\",\n        \"json\",\n        \"php\",\n        \"python\",\n        \"ruby\",\n        \"rust\",\n        \"swift\",\n        \"objectivec\"\n      ]\n    }\n  },\n\n  \"features\" : {\n    \"support\" : true\n  },\n\n  \"rules\" : {\n    \"build_size\" : {\n      \"fail\"  : true,\n\n      \"sizes\" : {\n        \"references\" : {\n          \"pages\" : {\n            \"gzip_maximum\" : 80000\n          }\n        },\n\n        \"guides\" : {\n          \"pages\" : {\n            \"gzip_maximum\" : 25000\n          },\n\n          \"images\" : {\n            \"maximum\" : 500000\n          }\n        },\n\n        \"data\" : {\n          \"objects\" : {\n            \"maximum\"      : 140000,\n            \"gzip_maximum\" : 25000\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "examples/acme-docs/data/changes/2020.json",
    "content": "[\n  {\n    \"group\" : \"rest_api\",\n    \"type\"  : \"change\",\n    \"date\"  : \"2020-12-08\",\n\n    \"text\"  : \"The REST API now rejects all requests without an `application/json` `Content-Type` header. Please update your apps!\"\n  },\n\n  {\n    \"group\" : \"web_hooks\",\n    \"type\"  : \"change\",\n    \"date\"  : \"2020-05-28\",\n\n    \"text\"  : \"The Web Hooks delivery user-agent has been updated to: `Acme-Hooks-Deliver (1.0)`\"\n  }\n]\n"
  },
  {
    "path": "examples/acme-docs/data/changes/2021.json",
    "content": "[\n  {\n    \"group\" : \"rest_api\",\n    \"type\"  : \"change\",\n    \"date\"  : \"2021-12-03\",\n\n    \"text\"  : \"New official REST API libraries have been added for Golang and Java.\"\n  },\n\n  {\n    \"group\" : \"web_hooks\",\n    \"type\"  : \"deprecation\",\n    \"date\"  : \"2021-05-23\",\n\n    \"text\"  : \"The Web Hooks retry system **has been phased out**. If your server does not handle Web Hooks from us immediately, we will not attempt to deliver them again anymore.\"\n  },\n\n  {\n    \"group\" : \"rtm_api\",\n    \"type\"  : \"change\",\n    \"date\"  : \"2021-01-04\",\n\n    \"text\"  : \"The RTM API now **auto-closes the socket channel** if the authentication payload is not received in a timely manner (20 seconds maximum).\"\n  }\n]\n"
  },
  {
    "path": "examples/acme-docs/data/guides/hello-world/index.md",
    "content": "TITLE: Hello World\nINDEX: 1\nUPDATED: 2021-09-22\nLINK: REST API Reference -> /references/rest-api/v1/\nLINK: RTM API Reference -> /references/rtm-api/v1/\n\nHello World! We advise that you start with the [Quickstart guide](./quickstart/), which will get you running in a few minutes.\n\n+ Navigation\n  | Quickstart: Learn how to use our systems in minutes. -> ./quickstart/\n"
  },
  {
    "path": "examples/acme-docs/data/guides/hello-world/quickstart/index.md",
    "content": "TITLE: Quickstart\nINDEX: 1\nUPDATED: 2021-09-22\n\n**To retrieve and send data on behalf of Acme teams, you need to use the REST API. The REST API can be connected to over HTTPS.**\n\nThis guide walks you through using the REST API. Note that the REST API can be used aside to the RTM API which lets you receive real-time events associated to any action that you take with the REST API.\n\n# Overview Schematic\n\n![Overview schematic](schematic-overview.png)\n\n_👉 The REST API lets your integration access features that regular Acme operator users have access to, such as sending messages. Note that the RTM API, not covered in this guide, lets you receive real-time events, and can be used side by side with the REST API._\n\n---\n\n# In-Depth Video Tutorial\n\n${youtube}[In-depth Introduction to the Acme REST API](4A37k4KAHfo)\n\n---\n\n# How To Use\n\n## Use a library\n\nThe most convenient way to use the REST API is to use a REST API-compatible library that we provide.\n\nLet's provide an example on how you can make requests to the REST API in your code.\n\n#### 1. Import and configure the library\n\n```javascript\nvar Acme = require(\"acme-api\");\n\n// Create the Acme client (it lets you access the REST API and RTM API at once)\nvar AcmeClient = new Acme();\n\n// Configure your Acme authentication tokens ('app' token)\nAcmeClient.authenticateTier(\"app\", \"<token_identifier>\", \"<token_key>\");\n```\n\n#### 2. Make requests to the API\n\n```javascript\n// <previous code eluded>\n\n// Send text message for `team_id` and `session_id`\n// Note: replace '<team_id>' and '<session_id>' with your values\nAcmeClient.team.sendMessageInConversation(\n  \"<team_id>\", \"<session_id>\",\n\n  {\n    type    : \"text\",\n    from    : \"operator\",\n    origin  : \"chat\",\n    content : \"This was sent from the REST API!\"\n  }\n)\n  .then(() => {\n    console.info(\"Message has been sent.\");\n  })\n  .catch((error) => {\n    console.error(\"Message could not be sent, because:\", error);\n  });\n```\n\n---\n\n## Direct usage (no library)\n\n**In case no library is available, it is still easy to use the REST API using an HTTP library** available for your programming language. As long as you are able to make requests in all available HTTP methods, set body content, and configure request headers, then you are good to go.\n\n**The HTTP endpoint base you will need to use is:** `https://api.acme.com/v1/`. Note that all requests must be done via HTTPS (HTTP over TLS), we do not support HTTP (plain text HTTP).\n\n**The full list of HTTP routes available to you**, as well as the data input that they expect, is available on the [REST API Reference](/references/rest-api/v1/).\n\n! The examples that follow **will use cURL over the command-line**, as API users that do not rely on a library are most-likely using the command-line instead.\n\n!! Make sure to replace `{identifier}:{key}` with your token keypair (keep the middle `:` separator). Also, replace `{team_id}` with your team identifier, and any other value with `{brackets}`.\n\n#### Example 1: Get team details\n\n```bash\ncurl https://api.acme.com/v1/team/{team_id} \\\n  --get \\\n  --user \"{identifier}:{key}\" \\\n  --header \"X-Acme-Tier: app\"\n```\n\n#### Example 2: Send a text message\n\n```bash\ncurl https://api.acme.com/v1/team/{team_id}/conversation/{session_id}/message \\\n  --user \"{identifier}:{key}\" \\\n  --header \"Content-Type: application/json\" \\\n  --header \"X-Acme-Tier: app\" \\\n  --data '{ \"type\": \"text\", \"from\": \"operator\", \"origin\": \"chat\", \"content\": \"This was sent with cURL!\" }'\n```\n\n_Note that `--post` does not need to be passed there, as cURL assumes it implicitly if `--data` is being used._\n\n---\n\n# Usage Considerations\n\n## Rate-limits (quotas)\n\n**The REST API is subject to rate-limits and daily quotas** to protect our systems against abuse. It guarantees that the REST API stays as fast and responsive as possible for all Acme users.\n\n---\n\n## Submitted data\n\n**Whenever you submit data to the API using `POST`, `PUT` or `PATCH`, it gets validated against a data schema.**\n\nThe [REST API Reference](/references/rest-api/v1/) specifies the data format that schemas enforce, within the Data Structure section, for each request.\n\nWhenever you submit data to the REST API, if it gets rejected by schema validators, **you will receive the following response error: `invalid_data`**. A message may also be provided, with explanations of what you did wrong.\n"
  },
  {
    "path": "examples/acme-docs/data/guides/index.md",
    "content": "TITLE: Guides\nINDEX: 1\nUPDATED: 2021-09-22\n\n**👋 Welcome to the Acme Developer Hub!** We have written extensive guides to help you build powerful integrations with Acme. This ranges from our REST API to our RTM API.\n\nGuides are classified in categories, for each system available to you. Aside from guides, you may also find references. References are more technical, as they serve as full specifications of our systems. You may want to use guides and references aside, switching between both of them.\n\n+ Navigation\n  | Hello World: Guides on how to start using our systems. -> ./hello-world/ [blank]\n  | Markdown Syntax: Guides on the Markdown syntax you can use. -> ./markdown-syntax/\n\n! If you have any technical question while building your integration with Acme, feel free to [chat with our support team](#crisp-chat-open). Our technical support team will gladly help you fix any issue you encounter.\n"
  },
  {
    "path": "examples/acme-docs/data/guides/markdown-syntax/index.md",
    "content": "TITLE: Markdown Syntax\nINDEX: 2\nUPDATED: 2022-01-05\n\nThis is the Markdown syntax guide, it contains two sections:\n\n+ Navigation\n  | Syntax: Guide of all available Markdown elements. -> ./syntax/\n  | Section Example: Examples of sub-sections. -> ./section-example/\n"
  },
  {
    "path": "examples/acme-docs/data/guides/markdown-syntax/section-example/index.md",
    "content": "TITLE: Section Example\nINDEX: 2\nUPDATED: 2022-01-05\n\nThis section contains two example sub-sections. **Pick one in the sidebar**, or the navigation menu below.\n\n+ Navigation\n  | Sub-Section One: First example sub-section. -> ./sub-section-one/\n  | Sub-Section Two: Second example sub-section. -> ./sub-section-two/\n"
  },
  {
    "path": "examples/acme-docs/data/guides/markdown-syntax/section-example/sub-section-one/index.md",
    "content": "TITLE: Sub-Section One\nINDEX: 1\nUPDATED: 2022-01-05\n\n! This is an example of a sub-section! **(number one)**\n"
  },
  {
    "path": "examples/acme-docs/data/guides/markdown-syntax/section-example/sub-section-two/index.md",
    "content": "TITLE: Sub-Section Two\nINDEX: 1\nUPDATED: 2022-01-05\n\n!! This is an example of a sub-section! **(number two)**\n"
  },
  {
    "path": "examples/acme-docs/data/guides/markdown-syntax/syntax/index.md",
    "content": "TITLE: Syntax\nINDEX: 1\nUPDATED: 2022-01-05\n\n# Navigation Links\n\nYou can easily insert navigation links anywhere as such:\n\n+ Navigation\n  | Hello World Quickstart: Read the Hello World guide. -> /guides/hello-world/quickstart/\n  | Example Sub-Section One: First example sub-section. -> /guides/markdown-syntax/section-example/sub-section-one/\n  | Example Sub-Section Two: Second example sub-section. -> /guides/markdown-syntax/section-example/sub-section-two/\n\n---\n\n# Text Samples\n\nSem integer vitae justo eget magna. Euismod lacinia at quis risus. Tellus cras adipiscing enim eu turpis egestas. Fringilla urna porttitor rhoncus dolor purus non. Commodo viverra maecenas accumsan lacus vel. **Feugiat in fermentum posuere urna nec tincidunt praesent semper feugiat**.\n\n**Turpis egestas integer eget aliquet nibh.** Pharetra convallis posuere morbi leo urna molestie at. Metus aliquam eleifend mi in. Scelerisque varius morbi enim nunc faucibus a. Condimentum id venenatis a condimentum vitae sapien. Neque [ornare aenean](#) euismod elementum nisi quis. Lacus laoreet non curabitur gravida arcu ac tortor. Vestibulum lectus mauris ultrices eros in!\n\nLeo urna molestie at _elementum_ eu **_facilisis_** sed, eg. `team:conversation:messages` Nam aliquam sem et tortor consequat id porta nibh.\n\nSagittis vitae et leo duis ut. **Suspendisse faucibus interdum** posuere lorem ipsum dolor sit amet. Nisl tincidunt eget nullam non nisi est sit amet. Urna nunc id cursus metus aliquam eleifend. 😀\n\nTurpis massa tincidunt dui ut ornare lectus sit. Eget dolor morbi non arcu risus quis varius quam quisque. Maecenas sed enim ut sem viverra. Etiam tempor orci eu lobortis elementum nibh. **Tristique senectus et netus et malesuada fames.**\n\n!!! Commodo viverra maecenas accumsan lacus vel! Eget egestas purus viverra accumsan in nisl nisi. Blandit aliquam etiam erat velit scelerisque in dictum non consectetur. Nam at lectus urna duis convallis. 😅\n\n!! Mattis molestie a **iaculis at erat** pellentesque adipiscing. Posuere lorem ipsum dolor sit amet. Nec tincidunt praesent semper feugiat nibh sed. Vestibulum lorem sed risus ultricies tristique nulla aliquet enim.\n\n! Id diam maecenas ultricies mi eget mauris pharetra et ultrices. Pretium lectus quam id leo in vitae. Posuere ac ut consequat semper viverra. Massa massa ultricies mi quis hendrerit. Nullam ac tortor vitae purus faucibus.\n\nIn tellus integer feugiat scelerisque varius morbi enim nunc:\n\n> “Id diam maecenas ultricies mi eget mauris pharetra et ultrices. Pretium lectus quam id leo in vitae. Posuere ac ut consequat semper viverra. Massa massa ultricies mi quis hendrerit. Nullam ac tortor vitae purus faucibus.”\n>\n> Valerian.\n\n---\n\n# Footnote Samples\n\nThis sentence has two[^foo] footnotes[^bar].\n\n---\n\n# List Samples\n\nNunc lobortis mattis aliquam faucibus purus in massa. Risus feugiat in ante metus dictum at tempor. Felis eget velit aliquet sagittis id consectetur.\n\n**Vestibulum lectus mauris ultrices eros in:**\n\n* Access to buckets: `bucket:url`\n* Access to availability information: `team:availability`\n* Access to conversation initiate: `team:conversation:initiate`\n* Access to conversation sessions: `team:conversation:sessions`\n* Access to conversation messages: `team:conversation:messages`\n* Access to a people profiles: `team:people:profiles`\n* Access to a people conversations: `team:people:conversations`\n* Access to a people events: `team:people:events`\n\n**Vestibulum lectus mauris ultrices eros in:**\n\n1. Access to buckets: `bucket:url`\n2. Access to availability information: `team:availability`\n3. Access to conversation initiate: `team:conversation:initiate`\n\n---\n\n# Code Sample\n\nInline code: `echo \"hello world\"`\n\nBlock code:\n\n```javascript\nvar dump = {\n  error : false\n};\n\nconsole.log(\"Hello World\", dump);\n```\n\n---\n\n# Video Embed Sample\n\nYouTube videos can be included in Markdown, as such:\n\n${youtube}[An Architect's Own House Situated on a Remote Beach](LildjJAG0fk)\n\n---\n\n# Full-Width Image Sample\n\nFull-width images can be included as such:\n\n![](image-caption.jpg)\n\n---\n\n# Caption Image Sample\n\nImages can be included with a caption, as such:\n\n$[Sample Mountain Image](![](image-caption.jpg))\n\n---\n\n# Table Sample\n\nEst ullamcorper eget nulla facilisi etiam dignissim diam quis enim. **Sit amet dictum sit amet justo donec enim diam vulputate.**\n\nElit eget gravida cum sociis natoque penatibus et. Ut faucibus pulvinar elementum integer enim neque:\n\n| Route Name | API Reference | Associated Scopes | Comments |\n| --- | --- | --- | --- |\n| Check If Conversation Exists | [Read reference](/references/rest-api/v1/) | `team:conversation:sessions` | — |\n| Initiate Conversation With Session | [Read reference](/references/rest-api/v1/) | `team:conversation:initiate` | Write permission required |\n| Send Message In Conversation | [Read reference](/references/rest-api/v1/) | `team:conversation:messages` | Write permission required |\n| Assign Conversation Routing | [Read reference](/references/rest-api/v1/) | `team:conversation:routing` | Write permission required |\n| List Conversation Pages | [Read reference](/references/rest-api/v1/) | `team:conversation:pages` | — |\n| Get Conversation State | [Read reference](/references/rest-api/v1/) | `team:conversation:states` | — |\n| Request Email Transcript | [Read reference](/references/rest-api/v1/) | `team:conversation:actions` | Write permission required |\n\nAc tortor vitae purus faucibus ornare suspendisse. Egestas sed tempus urna et pharetra. Euismod nisi porta lorem mollis aliquam ut porttitor! 👏\n\n---\n\n# Title Samples\n\n## Level 2 Title\n\n### Level 3 Title\n\n#### Level 4 Title\n\n##### Level 5 Title\n\n###### Level 6 Title\n\n---\n\n[^foo]: This is an example footnote.\n[^bar]: Note that it has to be defined **on a single line**, but using `<br/>`<br/>you can still make it multiline if needed.\n"
  },
  {
    "path": "examples/acme-docs/data/references/rest-api/_private.md",
    "content": "TYPE: API Blueprint\nTITLE: REST API Reference (Private)\nUPDATED: 2021-11-03\nFORMAT: 1A\nHOST: https://api.acme.com/v1\n\n# Reference\n\nThis reference documents private Acme API routes.\n\n☢️ **Those routes are solely used by Acme systems to perform eg. account management and other tasks. This may not be shared to public users, although routes are still available for them to access through the same API endpoint than the public one.**\n\n# Group Email\n\nManages Acme emails.\n\n## Subscription [/email/{email_hash}/subscription]\n\nManages email subscriptions. Used to subscribe or unsubscribe from Acme notification emails.\n\n### Get Subscription Status [GET /email/{email_hash}/subscription/{key}{?team_id}]\n\nResolves current subscription status (subscribed or unsubscribed).\n\n+ Attributes\n    + error (boolean)\n    + reason (string)\n    + data (object)\n        + subscribed (boolean) - Whether email is subscribed or not\n\n+ Parameters\n    + email_hash (string) - Email secure hash\n    + key (string) - Private security for given email\n    + team_id (string, optional) - Team identifier for email\n\n+ Request Get Subscription Status (application/json)\n\n    + Body\n\n+ Response 200 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": false,\n            \"reason\": \"resolved\",\n\n            \"data\": {\n                \"subscribed\": true\n            }\n        }\n        ```\n\n+ Response 404 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": true,\n            \"reason\": \"email_not_found\",\n            \"data\": {}\n        }\n        ```\n"
  },
  {
    "path": "examples/acme-docs/data/references/rest-api/v1.md",
    "content": "TYPE: API Blueprint\nTITLE: REST API Reference (V1)\nUPDATED: 2021-12-22\nFORMAT: 1A\nHOST: https://api.acme.com/v1\n\n# Reference\n\nThe Acme REST API offers access and control over all Acme data.\n\nAll resources that you will most likely use are prefixed with a star symbol (⭐).\n\n**While integrating the REST API, you may be interested in the following guides:**\n\n+ Navigation\n  | Quickstart: Get started in minutes. -> /guides/hello-world/quickstart/\n\n# Group Team\n\nManages Acme teams.\n\n## Base [/team]\n\nManages teams.\n\n### Check If Team Exists [HEAD /team{?domain}]\n\nChecks if given team exists (by domain).\n\n+ Parameters\n    + domain (string, required) - The team domain to check against\n\n+ Request Check If Team Exists (application/json)\n\n    + Tiers: `user` `app`\n\n    + Body\n\n+ Response 200 (application/json)\n\n+ Response 404 (application/json)\n\n### Create Team [POST /team]\n\nCreates a new team.\n\n+ Attributes\n    + name (string, required) - Team name\n    + domain (string, required) - Team domain\n\n+ Request Create Team (application/json)\n\n    + Tiers: `user`\n\n    + Body\n\n        ```\n        {\n            \"name\": \"Acme, Inc.\",\n            \"domain\": \"acme-inc.com\"\n        }\n        ```\n\n+ Response 201 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": false,\n            \"reason\": \"added\",\n\n            \"data\": {\n                \"team_id\": \"e2efddb0-d1ce-47fd-99f5-d3a5b69f1def\"\n            }\n        }\n        ```\n\n+ Response 423 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": true,\n            \"reason\": \"quota_limit_exceeded\",\n            \"data\": {}\n        }\n        ```\n\n### Get A Team [GET /team/{team_id}]\n\nResolves an existing team information.\n\n+ Attributes\n    + error (boolean)\n    + reason (string)\n    + data (object)\n        + team_id (string) - Team identifier\n        + name (string) - Team name\n        + domain (string) - Team domain\n\n+ Parameters\n    + team_id (string) - The team identifier\n\n+ Request Get Team Information (application/json)\n\n    + Tiers: `user` `app`\n\n    + Body\n\n+ Response 200 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": false,\n            \"reason\": \"resolved\",\n\n            \"data\": {\n                \"team_id\": \"8c842203-7ed8-4e29-a608-7cf78a7d2fcc\",\n                \"name\": \"Acme\",\n                \"domain\": \"acme.com\"\n            }\n        }\n        ```\n\n+ Response 403 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": true,\n            \"reason\": \"not_allowed\",\n            \"data\": {}\n        }\n        ```\n\n+ Response 404 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": true,\n            \"reason\": \"not_subscribed\",\n            \"data\": {}\n        }\n        ```\n\n### Delete A Team [DELETE /team/{team_id}]\n\nDeletes an existing team.\n\n+ Attributes\n    + verify (string, required) - User password (used to double-authenticate deletion)\n\n+ Parameters\n    + team_id (string) - The team identifier\n\n+ Request Delete A Team (application/json)\n\n    + Tiers: `user`\n\n    + Body\n\n        ```\n        {\n            \"verify\": \"MySuperSecurePassword\"\n        }\n        ```\n\n+ Response 200 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": false,\n            \"reason\": \"deleted\",\n            \"data\": {}\n        }\n        ```\n\n+ Response 403 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": true,\n            \"reason\": \"not_allowed\",\n            \"data\": {}\n        }\n        ```\n\n+ Response 404 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": true,\n            \"reason\": \"team_not_found\",\n            \"data\": {}\n        }\n        ```\n\n+ Response 423 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": true,\n            \"reason\": \"password_unverified\",\n            \"data\": {}\n        }\n        ```\n\n## Conversations [/team/{team_id}/conversations]\n\nManages multiple team conversations.\n\n### ⭐ List Conversations [GET /team/{team_id}/conversations/{page_number}{?search_query}{&search_type}{&search_operator}]\n\nLists conversations for team.\n\n+ Attributes\n    + error (boolean)\n    + reason (string)\n    + data (array)\n        + (object)\n            + session_id (string) - Session identifier\n            + team_id (string) - Team identifier\n            + people_id (string) - People identifier\n            + state (enum[string]) - Conversation state\n                + Members\n                    + `pending`\n                    + `unresolved`\n                    + `resolved`\n            + status (enum[number]) - Conversation status (an alias of state; useful for sorting conversations)\n                + Members\n                    + `0` - Numeric code for pending status\n                    + `1` - Numeric code for unresolved status\n                    + `2` - Numeric code for resolved status\n            + is_verified (boolean) - Whether session is verified or not (user email ownership is authenticated)\n            + is_blocked (boolean) - Whether session is blocked or not (block messages from visitor)\n            + availability (enum[string]) - Visitor availability\n                + Members\n                    + `online`\n                    + `offline`\n            + active (object) - User activity statistics\n                + now (boolean) - Whether user is considered active right now or not\n                + last (number) - Timestamp at which the user was last active\n            + last_message (string) - Last message excerpt\n            + participants (array) - External participants for this conversation\n                + (object)\n                    + type (enum[string]) - External participant type\n                        + Members\n                            + `email`\n                    + target (string) - External participant target (ie. email address, identifier, etc.)\n            + mentions (array[string]) - Mentioned user identifiers (from conversation messages)\n            + created_at (number) - Conversation creation timestamp\n            + updated_at (number) - Conversation update timestamp\n            + compose (object) - Compose states\n                + operator (object) - Compose state for operator\n                    + type (enum[string]) - Compose state type\n                        + Members\n                            + `start`\n                            + `stop`\n                    + excerpt (string) - Message excerpt for compose state\n                    + timestamp (number) - Timestamp for compose state\n                    + user (object) - Compose user information\n                        + user_id (string) - Compose user identifier\n                        + nickname (string) - Compose user nickname\n                        + avatar (string) - Compose user avatar\n                + visitor (object) - Compose state for visitor\n                    + type (enum[string]) - Compose state type\n                        + Members\n                            + `start`\n                            + `stop`\n                    + excerpt (string) - Message excerpt for compose state\n                    + timestamp (number) - Timestamp for compose state\n            + unread (object) - Unread messages counters\n                + operator (number) - Unread messages counter for operator\n                + visitor (number) - Unread messages counter for visitor\n            + assigned (object) - Assigned operator (if any)\n                + user_id (string) - Operator user identifier\n            + meta (object) - Meta-data for conversation\n                + nickname (string) - Visitor nickname\n                + email (string) - Visitor email\n                + phone (string) - Visitor phone\n                + address (string) - Visitor address\n                + ip (string) - Visitor IP address\n                + data (object) - Visitor data\n                + avatar (string) - Visitor avatar\n                + device (object) - Device information\n                    + capabilities (array) - Visitor device capabilities\n                        + (enum[string])\n                            + Members\n                                + `browsing`\n                                + `call`\n                    + geolocation (object) - Geolocation information for visitor device\n                        + country (string) - Country code\n                        + region (string) - Region code\n                        + city (string) - City name\n                        + coordinates (object) - Location coordinates\n                            + latitude (number) - Latitude coordinate\n                            + longitude (number) - Longitude coordinate\n                    + system (object) - Visitor device system information\n                        + os (object) - Operating system information\n                            + version (string) - OS version\n                            + name (string) - OS name\n                        + engine (object) - Rendering engine information\n                            + version (string) - Engine version\n                            + name (string) - Engine name\n                        + browser (object) - Browser information\n                            + major (string) - Browser major version (eg: version 8.1 has a major of 8)\n                            + version (string) - Browser version\n                            + name (string) - Browser name\n                        + useragent (string) - Visitor user agent\n                    + timezone (number) - Visitor device timezone offset (UTC)\n                    + locales (array[string]) - Visitor device locales\n                + segments (array[string]) - Segments attributed to conversation\n\n+ Parameters\n    + team_id (string) - The team identifier\n    + page_number (number, optional) - Page number for conversations paging\n    + search_query (string, optional) - Search query in all conversations (text if type is `text` or `segment`, filter if type is `filter`)\n    + search_type (string, optional) - Search type (either `text`, `segment` or `filter`)\n    + search_operator (string, optional) - Search operator if search type is `filter` (`or` or `and` respectful to boolean algebra, defaults to `and` if not set)\n\n+ Request List Conversations (application/json)\n\n    + Tiers: `user` `app`\n    + Scopes: `team:conversation:sessions`\n\n    + Body\n\n+ Response 206 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": false,\n            \"reason\": \"listed\",\n\n            \"data\": [\n                {\n                    \"session_id\": \"session_aaea8e1d-d6e3-4238-9252-d8c2e5579f5c\",\n                    \"team_id\": \"8c842203-7ed8-4e29-a608-7cf78a7d2fcc\",\n                    \"people_id\": \"0cb89450-34fb-4d51-8905-040c1d14a594\",\n                    \"status\": 1,\n                    \"state\": \"unresolved\",\n                    \"is_verified\": false,\n                    \"is_blocked\": false,\n                    \"availability\": \"offline\",\n\n                    \"active\": {\n                        \"now\": false\n                    },\n\n                    \"last_message\": \"All right, thanks.\",\n                    \"mentions\": [],\n\n                    \"participants\": [\n                        {\n                            \"type\": \"email\",\n                            \"target\": \"jane.doe@acme-inc.com\"\n                        }\n                    ],\n\n                    \"updated_at\": 1468401603070,\n                    \"created_at\": 1468341857826,\n\n                    \"unread\": {\n                        \"operator\": 0,\n                        \"visitor\": 1\n                    },\n\n                    \"assigned\": {\n                        \"user_id\": \"a4c32c68-be91-4e29-8a05-976e93abbe3f\"\n                    },\n\n                    \"meta\": {\n                        \"nickname\": \"Dan Boy\",\n                        \"email\": \"dan.boy@acme-inc.com\",\n                        \"ip\": \"104.236.186.68\",\n                        \"avatar\": null,\n\n                        \"device\": {\n                            \"capabilities\": [\n                                \"call\"\n                            ],\n\n                            \"geolocation\": {\n                                \"country\": \"US\",\n                                \"region\": \"CA\",\n                                \"city\": \"San Francisco\",\n\n                                \"coordinates\": {\n                                    \"latitude\": 37.7749,\n                                    \"longitude\": -122.4194\n                                }\n                            },\n\n                            \"system\": {\n                                \"os\": {\n                                    \"version\": \"10.11.5\",\n                                    \"name\": \"Mac OS\"\n                                },\n\n                                \"engine\": {\n                                    \"name\": \"WebKit\",\n                                    \"version\": \"537.36\"\n                                },\n\n                                \"browser\": {\n                                    \"major\": \"51\",\n                                    \"version\": \"51.0.2683.0\",\n                                    \"name\": \"Chrome\"\n                                },\n\n                                \"useragent\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2683.0 Safari/537.36\"\n                            },\n\n                            \"timezone\": -120,\n\n                            \"locales\": [\n                                \"en\",\n                                \"fr\"\n                            ]\n                        },\n\n                        \"segments\": [\n                            \"customer\",\n                            \"friend\"\n                        ]\n                    }\n                }\n            ]\n        }\n        ```\n\n+ Response 400 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": true,\n            \"reason\": \"search_query_too_long\",\n            \"data\": {}\n        }\n        ```\n\n+ Response 402 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": true,\n            \"reason\": \"subscription_upgrade_required\",\n            \"data\": {}\n        }\n        ```\n\n+ Response 403 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": true,\n            \"reason\": \"not_allowed\",\n            \"data\": {}\n        }\n        ```\n\n+ Response 404 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": true,\n            \"reason\": \"not_subscribed\",\n            \"data\": {}\n        }\n        ```\n\n+ Response 423 (application/json)\n\n    + Body\n\n        ```\n        {\n            \"error\": true,\n            \"reason\": \"quota_limit_exceeded\",\n            \"data\": {}\n        }\n        ```\n"
  },
  {
    "path": "examples/acme-docs/data/references/rtm-api/v1.md",
    "content": "TYPE: Markdown\nTITLE: RTM API Reference (V1)\nUPDATED: 2021-09-22\n\nEvents are sent on the RTM Events API WebSocket channel that you can open alongside your REST API channel, which allows you to receive asynchronous replies and events for some of your actions via the REST API.\n\n+ Navigation\n  | Quickstart: Get started in minutes. -> /guides/hello-world/quickstart/\n\n# Endpoint\n\nYou may subscribe to events by opening a [Socket.IO](https://socket.io/) connection to the WebSocket endpoint.\n\n**The RTM API endpoint URL is:** `wss://rtm.acme.com/`\n\n! **There is a limit on the maximum number of connections that can be simultaneously open** with the RTM API (per-token, per-team and per-IP). This limit is quite high and can be revised at any time. Please make sure to teardown any unused connection before opening a new one. _Most use cases require a single RTM API connection._\n\n---\n\n# Namespaces\n\nAvailable RTM event namespaces are listed below.\n\n!! Note that if your API token is a `app` tier token, then you will **only have access to the RTM events that your token scopes allow**, based on the API routes that you have access to. Required scopes and tiers are listed for each event below.\n\n# Events\n\n## Session Events\n\n### Session Update Availability\n\n* **Event**: `session:update_availability`\n* **Description:** session availability changed _(eg. online to offline)_\n* **Tiers:** `user` `app`\n* **Scopes:** `team:conversation:sessions` + `read`\n\n```json\n{\n  \"team_id\": \"42286ab3-b29a-4fde-8538-da0ae501d825\",\n  \"session_id\": \"session_36ba3566-9651-4790-afc8-ffedbccc317f\",\n  \"availability\": \"online\"\n}\n```\n\n### Session Update Verify\n\n* **Event**: `session:update_verify`\n* **Description:** session verification status changed\n* **Tiers:** `user` `app`\n* **Scopes:** `team:conversation:sessions` + `read`\n\n```json\n{\n  \"team_id\": \"42286ab3-b29a-4fde-8538-da0ae501d825\",\n  \"session_id\": \"session_36ba3566-9651-4790-afc8-ffedbccc317f\",\n  \"is_verified\": true\n}\n```\n\n---\n\n## Message Events\n\n### Message Updates\n\n* **Event**: `message:updated`\n* **Description:** message has been updated\n* **Tiers:** `user` `app`\n* **Scopes:** `team:conversation:messages` + `read`\n\n```json\n{\n  \"team_id\": \"42286ab3-b29a-4fde-8538-da0ae501d825\",\n  \"session_id\": \"session_36ba3566-9651-4790-afc8-ffedbccc317f\",\n  \"fingerprint\": 163240180126629,\n  \"content\": \"This is an edited message!\"\n}\n```\n"
  },
  {
    "path": "examples/acme-docs/package.json",
    "content": "{\n  \"dependencies\": {\n    \"chappe\": \"1.x.x\"\n  }\n}\n"
  },
  {
    "path": "gulpfile.js",
    "content": "/*\n * chappe\n *\n * Copyright 2021, Crisp IM SAS\n * Author: Valerian Saliou <valerian@valeriansaliou.name>\n */\n\n\nvar fs                     = require(\"fs\");\nvar path                   = require(\"path\");\n\nvar lodash                 = require(\"lodash\");\nvar del                    = require(\"del\");\nvar glob                   = require(\"glob\");\nvar merge                  = require(\"merge-stream\");\nvar marked                 = require(\"marked\").marked;\nvar marked_footnote        = require(\"marked-footnote\");\nvar marked_gfm_heading_id  = require(\"marked-gfm-heading-id\").gfmHeadingId;\nvar marked_mangle          = require(\"marked-mangle\").mangle;\nvar remove_markdown        = require(\"@tommoor/remove-markdown\");\nvar MiniSearch             = require(\"minisearch\");\nvar Feed                   = require(\"feed\").Feed;\n\nvar gulp                   = require(\"gulp\");\n\nvar gulp_connect           = require(\"gulp-connect\");\nvar gulp_file              = require(\"gulp-file\");\nvar gulp_bower             = require(\"gulp-bower\");\nvar gulp_pug               = require(\"gulp-pug\");\nvar gulp_sass              = require(\"gulp-sass\")(require(\"sass\"));\nvar gulp_inline_image      = require(\"gulp-inline-image\");\nvar gulp_ogimage           = require(\"gulp-ogimage\");\nvar gulp_sass_variables    = require(\"gulp-sass-variables\");\nvar gulp_concat            = require(\"gulp-concat\");\nvar gulp_babel             = require(\"gulp-babel\");\nvar gulp_clean_css         = require(\"gulp-clean-css\");\nvar gulp_uglify            = require(\"gulp-uglify\");\nvar gulp_rename            = require(\"gulp-rename\");\nvar gulp_replace           = require(\"gulp-replace\");\nvar gulp_header            = require(\"gulp-header\");\nvar gulp_pug_lint          = require(\"gulp-pug-lint\");\nvar gulp_stylelint         = require(\"gulp-stylelint-esm\").default;\nvar gulp_jshint            = require(\"gulp-jshint\");\nvar gulp_jscs              = require(\"gulp-jscs\");\nvar gulp_sizereport        = require(\"gulp-sizereport\");\nvar gulp_sitemap           = require(\"gulp-sitemap\");\nvar gulp_notify            = require(\"gulp-notify\");\nvar gulp_noop              = require(\"gulp-noop\");\n\nvar package                = require(\"./package.json\");\n\nvar gulp_pug_templates     = require(\"./res/plugins/gulp/pug-templates\");\nvar gulp_minisearch        = require(\"./res/plugins/gulp/minisearch\");\n\nvar marked_renderers       = {\n  heading : require(\"./res/plugins/marked/renderers/heading\"),\n  code    : require(\"./res/plugins/marked/renderers/code\")\n};\n\nvar marked_extensions      = {\n  navigation      : require(\"./res/plugins/marked/extensions/navigation\"),\n  navigation_item : require(\"./res/plugins/marked/extensions/navigation-item\"),\n  emphasis        : require(\"./res/plugins/marked/extensions/emphasis\"),\n  figcaption      : require(\"./res/plugins/marked/extensions/figcaption\"),\n  embed           : require(\"./res/plugins/marked/extensions/embed\")\n};\n\n\n// Global context\n\nvar PARENT_CONTEXT = (\n  (typeof global.CONTEXT !== \"undefined\") ? global.CONTEXT : {}\n);\n\nvar CONTEXT        = {\n  // Data\n  CONFIG        : {},\n  SEARCH_INDEX  : null,\n\n  // Configurations\n  PATH_CONFIG       : (\n    PARENT_CONTEXT.config ? PARENT_CONTEXT.config.split(\",\") : null\n  ),\n\n  PATH_ASSETS       : (PARENT_CONTEXT.assets || null),\n  PATH_DATA         : (PARENT_CONTEXT.data   || null),\n  PATH_TEMP         : (PARENT_CONTEXT.temp   || null),\n  PATH_DIST         : (PARENT_CONTEXT.dist   || null),\n\n  SERVE_HOST        : (PARENT_CONTEXT.host || null),\n  SERVE_PORT        : (PARENT_CONTEXT.port || null),\n\n  PATH_CHAPPE       : null,\n  PATH_SOURCES      : null,\n  PATH_LIBRARIES    : null,\n  PATH_BUILD_PAGES  : null,\n  PATH_BUILD_ASSETS : null,\n\n  IS_PRODUCTION     : ((PARENT_CONTEXT.env === \"production\") ? true : false),\n  IS_WATCH          : false\n};\n\n\n// Initializers\n\nmarked.use({\n  renderer   : {\n    heading : marked_renderers.heading,\n    code    : marked_renderers.code\n  },\n\n  extensions : [\n    marked_extensions.navigation,\n    marked_extensions.navigation_item,\n    marked_extensions.emphasis,\n    marked_extensions.figcaption,\n    marked_extensions.embed\n  ]\n});\n\nmarked.use(\n  marked_footnote(),\n  marked_gfm_heading_id(),\n  marked_mangle()\n);\n\n\n// Tasks\n\n/*\n  Acquires the configuration\n*/\nvar get_configuration = function(next) {\n  // Assert that all paths are set\n  if (CONTEXT.PATH_CONFIG === null || CONTEXT.PATH_ASSETS === null  ||\n        CONTEXT.PATH_DATA === null || CONTEXT.PATH_TEMP === null    ||\n        CONTEXT.PATH_DIST === null) {\n    throw new Error(\n      \"A build path was not passed properly, please pass them as globals\"\n    );\n  }\n\n  // Make sure that the config and data paths exist\n  // Notice: do not check the 'temp' and 'dist' path, as they will get \\\n  //   auto-created.\n  if (fs.existsSync(CONTEXT.PATH_ASSETS) !== true) {\n    throw new Error(\"The assets path provided does not exist!\");\n  }\n  if (fs.existsSync(CONTEXT.PATH_DATA) !== true) {\n    throw new Error(\"The data path provided does not exist!\");\n  }\n\n  CONTEXT.PATH_CONFIG.forEach(function(config_path) {\n    if (fs.existsSync(config_path) !== true) {\n      throw new Error(\n        \"One of the configuration path provided does not exist!\"\n      );\n    }\n  });\n\n  // Initialize final source paths\n  CONTEXT.PATH_CHAPPE    = __dirname;\n  CONTEXT.PATH_SOURCES   = path.join(CONTEXT.PATH_CHAPPE, \"./src\");\n  CONTEXT.PATH_LIBRARIES = path.join(CONTEXT.PATH_TEMP, \"./lib\");\n\n  // Initialize final build paths\n  CONTEXT.PATH_BUILD_PAGES  = CONTEXT.PATH_DIST;\n  CONTEXT.PATH_BUILD_ASSETS = (CONTEXT.PATH_DIST + \"/static\");\n\n  // Read atomic configurations (merge them together)\n  var _merge_pipeline = [\n    // #1: Common configuration (project static)\n    require(\"./res/config/common.json\"),\n\n    // #2: Site configuration (project defaults)\n    {\n      SITE : require(\"./res/config/user.json\")\n    }\n  ];\n\n  // Read vector configurations\n  CONTEXT.PATH_CONFIG.forEach(function(config_path) {\n    // #3: Site configuration (user-provided)\n    _merge_pipeline.push({\n      SITE : require(config_path)\n    });\n  });\n\n  // Unwind merge pipeline\n  _merge_pipeline.forEach(function(merge_object) {\n    CONTEXT.CONFIG = lodash.merge(CONTEXT.CONFIG, merge_object);\n  });\n\n  // Assign contextual values\n  CONTEXT.CONFIG.ENVIRONMENT = (\n    (CONTEXT.IS_PRODUCTION === true) ? \"production\" : \"development\"\n  );\n\n  CONTEXT.CONFIG.REVISION = (\n    \"v\" + (package.version || \"0.0.0\")\n  );\n\n  next();\n};\n\n\n/*\n  Installs bower packages\n*/\nvar bower = function() {\n  return gulp_bower({\n    directory : CONTEXT.PATH_LIBRARIES,\n    cwd       : CONTEXT.PATH_CHAPPE,\n    verbosity : 1\n  });\n};\n\n\n/*\n  Copies all user assets\n*/\nvar copy_user_assets = function() {\n  return gulp.src(\n    CONTEXT.PATH_ASSETS + \"/**/*\"\n  )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/user\"\n      )\n    );\n};\n\n\n/*\n  Copies images (base images)\n*/\nvar copy_images_base = function() {\n  return gulp.src(\n    CONTEXT.PATH_SOURCES + \"/images/**/*\"\n  )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/images\"\n      )\n    )\n    .pipe(\n      gulp_connect.reload()\n    );\n};\n\n\n/*\n  Copies images (guides images)\n*/\nvar copy_images_guides = function() {\n  return gulp.src(\n    CONTEXT.PATH_DATA + \"/guides/**/*.{jpg,jpeg,png,gif}\"\n  )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/images/guides/content\"\n      )\n    )\n    .pipe(\n      gulp_connect.reload()\n    );\n};\n\n\n/*\n  Copies fonts\n*/\nvar copy_fonts = function() {\n  return gulp.src(\n    CONTEXT.PATH_SOURCES + \"/fonts/**/*\"\n  )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/fonts\"\n      )\n    )\n    .pipe(\n      gulp_connect.reload()\n    );\n};\n\n\n/*\n  Concats javascript libraries\n*/\nvar concat_libraries_javascripts = function() {\n  // Acquire targets\n  // Notice: append user-defined syntaxes for code coloring (from 'prism.js')\n  var _targets = CONTEXT.CONFIG.LIBRARIES.JAVASCRIPTS.map(function(library) {\n    return (CONTEXT.PATH_LIBRARIES + \"/\" + library);\n  });\n\n  CONTEXT.CONFIG.SITE.plugins.code.syntaxes.forEach(function(syntax) {\n    var _syntax_path = (\n      CONTEXT.PATH_LIBRARIES + \"/prism.js/components/prism-\" + syntax + \".js\"\n    );\n\n    if (fs.existsSync(_syntax_path) !== true) {\n      throw new Error(\n        \"Unsupported code syntax plugin provided: \" + syntax + \" at path: \"  +\n          _syntax_path\n      );\n    }\n\n    _targets.push(_syntax_path);\n  });\n\n  return gulp.src(_targets)\n    .pipe(\n      gulp_concat(\"common/libs.js\")\n    )\n    .pipe(\n      gulp_replace(\n        \"manual: _self.Prism && _self.Prism.manual,\", \"manual: true,\"\n      )\n    )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/javascripts\"\n      )\n    );\n};\n\n\n/*\n  Concats stylesheet libraries\n*/\nvar concat_libraries_stylesheets = function() {\n  return gulp.src(\n    CONTEXT.CONFIG.LIBRARIES.STYLESHEETS.map(function(library) {\n      return (CONTEXT.PATH_LIBRARIES + \"/\" + library);\n    })\n  )\n    .pipe(\n      gulp_concat(\"common/libs.css\")\n    )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/stylesheets\"\n      )\n    );\n};\n\n\n/*\n  Prepares Minisearch search index (before it gets populated)\n*/\nvar minisearch_prepare = function(next) {\n  // Initialize search index\n  CONTEXT.SEARCH_INDEX = new MiniSearch(\n    CONTEXT.CONFIG.SEARCH.OPTIONS.BASE\n  );\n\n  next();\n};\n\n\n/*\n  Consolidates Minisearch search index (after it was populated)\n*/\nvar minisearch_consolidate = function() {\n  // Generate search index data\n  return gulp_file(\n    (\"./\" + CONTEXT.CONFIG.SEARCH.INDEX),\n    JSON.stringify(CONTEXT.SEARCH_INDEX),\n\n    {\n      src : true\n    }\n  )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/data\"\n      )\n    );\n};\n\n\n/*\n  Compiles Pug templates (base templates)\n*/\nvar pug_templates_base = function() {\n  var _stream = merge();\n\n  gulp_pug_templates.list_config_locales(CONTEXT, package, marked)\n    .forEach(function(pug_data) {\n      _stream.add(\n        gulp_pug_templates.pipe_commons(CONTEXT, pug_data.locale, function() {\n          return gulp.src(\n            CONTEXT.CONFIG.SOURCES.TEMPLATES.map(function(template) {\n              return (CONTEXT.PATH_SOURCES + \"/templates/\" + template);\n            }),\n\n            {\n              base : (CONTEXT.PATH_SOURCES + \"/templates\")\n            }\n          )\n            .pipe(\n              gulp_pug(pug_data.config)\n            );\n        })\n          .pipe(\n            gulp.dest(\n              CONTEXT.PATH_BUILD_PAGES\n            )\n          )\n          .pipe(\n            gulp_connect.reload()\n          )\n      );\n    });\n\n  return _stream;\n};\n\n\n/*\n  Compiles Pug templates (guides templates)\n*/\nvar pug_templates_guides = function() {\n  var _stream = merge();\n\n  // Acquire the guides meta-data\n  // Format: { \\\n  //   segments<array>, id<string>, title<string>, index<int>, links<array>, \\\n  //   updated<date>, markdown<string>, parent<object>, subtrees<array> \\\n  // }\n  // Output: [tree<array>, linear<array>]\n  var _guide_meta_data = (\n    gulp_pug_templates.traverse_guides_tree(CONTEXT.PATH_DATA + \"/guides\")\n  );\n\n  var _guide_meta_tree   = _guide_meta_data[0],\n      _guide_meta_linear = _guide_meta_data[1];\n\n  // Append all guides to search index\n  var _categories = _guide_meta_tree[0];\n\n  if (_categories) {\n    gulp_minisearch.insert_categories(\n      CONTEXT, \"guides\", [_categories.title], (_categories.subtrees || [])\n    );\n  }\n\n  // Compile all guide pages\n  gulp_pug_templates.list_config_locales(CONTEXT, package, marked)\n    .forEach(function(pug_data) {\n      _guide_meta_linear.forEach(function(guide_level) {\n        // Generate current guide data (for locale)\n        var pug_level_config = lodash.cloneDeep(pug_data.config);\n\n        pug_level_config.data.guides = _guide_meta_tree;\n        pug_level_config.data.guide  = guide_level;\n\n        // Forbid indexing? (if any entry segment starts with an underscore)\n        var _segment_private_index = guide_level.segments.findIndex(\n          function(segment) {\n            return ((segment[0] === \"_\") ? true : false);\n          }\n        );\n\n        if (_segment_private_index !== -1) {\n          pug_level_config.data.INDEXING = false;\n        }\n\n        _stream.add(\n          gulp_pug_templates.pipe_commons(\n            CONTEXT, pug_data.locale, function() {\n              return gulp.src(\n                (CONTEXT.PATH_SOURCES + \"/templates/guides/index.pug\"),\n\n                {\n                  base : (CONTEXT.PATH_SOURCES + \"/templates\")\n                }\n              )\n                .pipe(\n                  gulp_pug(pug_level_config)\n                )\n                .pipe(\n                  gulp_rename(function(file_path) {\n                    file_path.basename = path.join.apply(this, [].concat(\n                      guide_level.segments, [\"index\"]\n                    ));\n                  })\n                );\n            }\n          )\n            .pipe(\n              gulp.dest(\n                CONTEXT.PATH_BUILD_PAGES\n              )\n            )\n        );\n      });\n    });\n\n  return _stream;\n};\n\n\n/*\n  Compiles Pug templates (references templates)\n*/\nvar pug_templates_references = function() {\n  var _stream = merge();\n\n  // Acquire the parsed references content\n  // Format: {segments<array>, type<string>, data<object>}\n  // Output: linear<array>\n  var _reference_meta_data = (\n    gulp_pug_templates.traverse_references_linear(\n      CONTEXT, (CONTEXT.PATH_DATA + \"/references\")\n    )\n  );\n\n  gulp_pug_templates.list_config_locales(CONTEXT, package, marked)\n    .forEach(function(pug_data) {\n      _reference_meta_data.forEach(function(reference_entry) {\n        // Generate current reference data (for locale)\n        var pug_entry_config = lodash.cloneDeep(pug_data.config);\n\n        pug_entry_config.data.reference = reference_entry;\n\n        // Forbid indexing? (if any entry segment starts with an underscore)\n        var _segment_private_index = reference_entry.segments.findIndex(\n          function(segment) {\n            return ((segment[0] === \"_\") ? true : false);\n          }\n        );\n\n        if (_segment_private_index !== -1) {\n          pug_entry_config.data.INDEXING = false;\n        }\n\n        // Append all reference anchors to search index\n        // Important: only if reference indexing is not forbidden\n        if (reference_entry.categories  &&\n              pug_entry_config.data.INDEXING !== false) {\n          // Acquire reference path, as well as reference path title (with \\\n          //   its common suffix extracted)\n          // Notice: 'REST API Reference (V1)' becomes 'REST API' + 'V1'\n          var _reference_path    = [\"References\"],\n              _title_split_regex = /^(.+) Reference \\(([^\\(\\)]+)\\)$/;\n\n          var _title_matcher = (\n            reference_entry.title.match(_title_split_regex)\n          );\n\n          var _reference_title_path = (\n            (_title_matcher && _title_matcher[1] && _title_matcher[2]) ?\n              [_title_matcher[1], _title_matcher[2]] : [reference_entry.title]\n          );\n\n          // Insert reference base\n          gulp_minisearch.insert_categories(\n            CONTEXT, \"references\", _reference_path, [reference_entry]\n          );\n\n          // Insert reference sub-categories\n          gulp_minisearch.insert_categories(\n            CONTEXT, \"references\",\n              [].concat(_reference_path, _reference_title_path),\n              reference_entry.categories, reference_entry.segments\n          );\n        }\n\n        _stream.add(\n          gulp_pug_templates.pipe_commons(\n            CONTEXT, pug_data.locale, function() {\n              return gulp.src(\n                (CONTEXT.PATH_SOURCES + \"/templates/references/index.pug\"),\n\n                {\n                  base : (CONTEXT.PATH_SOURCES + \"/templates\")\n                }\n              )\n                .pipe(\n                  gulp_pug(pug_entry_config)\n                )\n                .pipe(\n                  gulp_rename(function(file_path) {\n                    file_path.basename = path.join.apply(this, [].concat(\n                      reference_entry.segments, [\"index\"]\n                    ));\n                  })\n                );\n            }\n          )\n            .pipe(\n              gulp.dest(\n                CONTEXT.PATH_BUILD_PAGES\n              )\n            )\n            .pipe(\n              gulp_connect.reload()\n            )\n        );\n      });\n    });\n\n  return _stream;\n};\n\n\n/*\n  Compiles Pug templates (changes templates)\n*/\nvar pug_templates_changes = function() {\n  var _stream = merge();\n\n  // Acquire the change years data\n  // Format: [year<int>, data<object>] (ordered by most recent year first)\n  var _change_years = (\n    glob.sync(\n      (CONTEXT.PATH_DATA + \"/changes/*.json\")\n    )\n      .sort()\n      .reverse()\n      .map(function(file_path) {\n        var _file_path_parts = path.parse(file_path);\n\n        return [\n          parseInt(_file_path_parts.name, 10),\n          JSON.parse(fs.readFileSync(file_path))\n        ];\n      })\n      .map(function(year_data) {\n        // Classify each change in its month (in an orderly manner)\n        // Notice: the Map object is used as it retains natural ordering, \\\n        //   where last months come first.\n        var _year_months = new Map();\n\n        year_data[1].forEach(function(entry) {\n          // Validate entry data\n          if (!entry.group || !entry.type || !entry.date || !entry.text) {\n            throw new Error(\n              \"Invalid entry data in changes for year: \" + year_data[0]\n            );\n          }\n\n          // Acquire entry date\n          var _entry_date = (new Date(entry.date));\n\n          if (!_entry_date || isNaN(_entry_date.getTime())) {\n            throw new Error(\n              \"Invalid date in changes for year: \" + year_data[0]\n            );\n          }\n\n          // Acquire entry month key\n          var _entry_month_key = (\"\" + (_entry_date.getMonth() + 1));\n\n          if (_entry_month_key.length === 1) {\n            _entry_month_key = (\"0\" + _entry_month_key);\n          }\n\n          // Make sure entries object for month is initialized\n          if (_year_months.has(_entry_month_key) === false) {\n            _year_months.set(_entry_month_key, []);\n          }\n\n          // Append entry to month entries object\n          _year_months.get(_entry_month_key).push(entry);\n        });\n\n        // Assign new per-month data\n        // Important: convert back all ordered map entries to an array, which \\\n        //   we can iterate on right away from templates.\n        year_data[1] = Array.from(\n          _year_months.entries()\n        );\n\n        return year_data;\n      })\n  );\n\n  // Map available change years (as bare numbers)\n  // Important: before prepending latest year data\n  var _available_bare_years = _change_years.map(function(change_year) {\n    return change_year[0];\n  });\n\n  // Prepend list of years with the 'latest' year (ie. corresponding to the \\\n  //   'index'), and push only the first 3 months (ie. latest)\n  var _first_change_year = (_change_years[0] || []);\n\n  _change_years.unshift([\n    (_first_change_year[0] || -1),\n    (_first_change_year[1] || []).slice(0, 3),\n    \"index\"\n  ]);\n\n  gulp_pug_templates.list_config_locales(CONTEXT, package, marked)\n    .forEach(function(pug_data) {\n      _change_years.forEach(function(change_year) {\n        // Generate current year data (for locale)\n        var pug_year_config = lodash.cloneDeep(pug_data.config);\n\n        pug_year_config.data.years   = _available_bare_years;\n\n        pug_year_config.data.changes = {\n          year     : change_year[0],\n\n          current  : (\n            (change_year[2] === \"index\") ? \"latest\" : change_year[0]\n          ),\n\n          timeline : change_year[1]\n        };\n\n        _stream.add(\n          gulp_pug_templates.pipe_commons(\n            CONTEXT, pug_data.locale, function() {\n              return gulp.src(\n                (CONTEXT.PATH_SOURCES + \"/templates/changes/index.pug\"),\n\n                {\n                  base : (CONTEXT.PATH_SOURCES + \"/templates\")\n                }\n              )\n                .pipe(\n                  gulp_pug(pug_year_config)\n                )\n                .pipe(\n                  gulp_rename(function(file_path) {\n                    file_path.basename = (\n                      change_year[2] ? change_year[2] : (\n                        path.join((\"\" + change_year[0]), \"index\")\n                      )\n                    );\n                  })\n                );\n            }\n          )\n            .pipe(\n              gulp.dest(\n                CONTEXT.PATH_BUILD_PAGES\n              )\n            )\n            .pipe(\n              gulp_connect.reload()\n            )\n        );\n      });\n    });\n\n  return _stream;\n};\n\n\n/*\n  Compiles all Pug templates (shell task to aggregate sub-tasks)\n*/\nvar pug_templates_all = function() {\n  return gulp.parallel(\n    pug_templates_base,\n    pug_templates_guides,\n    pug_templates_references,\n    pug_templates_changes\n  );\n}();\n\n\n/*\n  Compiles SCSS stylesheets\n*/\nvar scss = function() {\n  return gulp.src(\n    CONTEXT.CONFIG.SOURCES.STYLESHEETS.map(function(stylesheet) {\n      return (CONTEXT.PATH_SOURCES + \"/stylesheets/\" + stylesheet);\n    }),\n\n    {\n      base : (CONTEXT.PATH_SOURCES + \"/stylesheets\")\n    }\n  )\n    .pipe(\n      gulp_sass_variables({\n        \"$use-inline-images\" : CONTEXT.IS_PRODUCTION\n      })\n    )\n    .pipe(\n      gulp_sass({\n        outputStyle : \"expanded\"\n      })\n    )\n    .on(\"error\", gulp_notify.onError({\n      title     : \"scss\",\n      message   : \"Error compiling\",\n      emitError : true\n    }))\n    .on(\"error\", function(error) {\n      if (CONTEXT.IS_WATCH === true) {\n        // Handle compile errors (used for development ease w/ `gulp watch`)\n        console.error(error.toString());\n      } else {\n        throw error;\n      }\n    })\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/stylesheets\"\n      )\n    );\n};\n\n\n/*\n  Imports inline images in stylesheets\n*/\nvar css_inline_images = function() {\n  return gulp.src((\n    CONTEXT.PATH_BUILD_ASSETS + \"/stylesheets/**/*.css\"\n  ), {\n    base : (CONTEXT.PATH_BUILD_ASSETS + \"/images\")\n  })\n    .pipe(\n      gulp_inline_image()\n    )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/stylesheets\"\n      )\n    )\n    .pipe(\n      gulp_connect.reload()\n    );\n};\n\n\n/*\n  Compiles Babel templates\n*/\nvar babel = function() {\n  return gulp.src(\n    CONTEXT.CONFIG.SOURCES.JAVASCRIPTS.map(function(javascript) {\n      return (CONTEXT.PATH_SOURCES + \"/javascripts/\" + javascript);\n    }),\n\n    {\n      base : (CONTEXT.PATH_SOURCES + \"/javascripts\")\n    }\n  )\n    .pipe(\n      gulp_babel()\n    )\n    .on(\"error\", gulp_notify.onError({\n      title     : \"babel\",\n      message   : \"Error compiling\",\n      emitError : true\n    }))\n    .on(\"error\", function(error) {\n      if (CONTEXT.IS_WATCH === true) {\n        // Handle compile errors (used for development ease w/ `gulp watch`)\n        console.error(error.toString());\n      } else {\n        throw error;\n      }\n    })\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/javascripts\"\n      )\n    );\n};\n\n\n/*\n  Replaces values from template files (guides templates)\n*/\nvar replace_templates_guides = function() {\n  return gulp.src(\n    CONTEXT.PATH_BUILD_PAGES + \"/guides/**/*.html\"\n  )\n    .pipe(\n      gulp_replace(\n        /src=\"(?:\\.\\/)?([^\\.\\/\"']+\\.(?:jpg|jpeg|png|gif))\"/g,\n\n        function(_, file_name) {\n          // Acquire base path segments\n          var _image_base_segments = this.file.relative.split(\"/\");\n\n          _image_base_segments.pop();\n\n          // Build final path segments\n          var _image_final_segments = (\n            [].concat(\n              [\"\", \"static\", \"images\", \"guides\", \"content\"],\n              _image_base_segments,\n              [file_name]\n            )\n          );\n\n          // Replace with final path to image file within static assets\n          return (\n            \"src=\\\"\" + _image_final_segments.join(\"/\") + \"\\\"\"\n          );\n        }\n      )\n    )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_PAGES + \"/guides\"\n      )\n    )\n    .pipe(\n      gulp_connect.reload()\n    );\n};\n\n\n/*\n  Replaces values from javascript files\n*/\nvar replace_javascripts = function() {\n  return gulp.src(\n    CONTEXT.PATH_BUILD_ASSETS + \"/javascripts/**/*.js\"\n  )\n    .pipe(\n      gulp_replace(\n        \"@:revision\", CONTEXT.CONFIG.REVISION\n      )\n    )\n    .pipe(\n      gulp_replace(\n        \"\\\"@:url_status\\\"\",\n\n        JSON.stringify({\n          provider : (\n            CONTEXT.CONFIG.SITE.urls.vigil ? \"vigil\" : (\n              CONTEXT.CONFIG.SITE.urls.crisp_status ? \"crisp\" : null\n            )\n          ),\n\n          target   : (\n            CONTEXT.CONFIG.SITE.urls.vigil  ||\n              CONTEXT.CONFIG.SITE.urls.crisp_status || null\n          )\n        })\n      )\n    )\n    .pipe(\n      gulp_replace(\n        \"@:search_index\", CONTEXT.CONFIG.SEARCH.INDEX\n      )\n    )\n    .pipe(\n      gulp_replace(\n        \"\\\"@:search_options_base\\\"\",\n        JSON.stringify(CONTEXT.CONFIG.SEARCH.OPTIONS.BASE)\n      )\n    )\n    .pipe(\n      gulp_replace(\n        \"\\\"@:search_options_query\\\"\",\n        JSON.stringify(CONTEXT.CONFIG.SEARCH.OPTIONS.QUERY)\n      )\n    )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/javascripts\"\n      )\n    )\n    .pipe(\n      gulp_connect.reload()\n    );\n};\n\n\n/*\n  Replaces values from stylesheet files\n*/\nconst replace_stylesheets = function() {\n  return gulp.src(\n    CONTEXT.PATH_BUILD_ASSETS + \"/stylesheets/**/*.css\"\n  )\n    .pipe(\n      gulp_replace(\n        \"@@revision\",\n        CONTEXT.CONFIG.REVISION\n      )\n    )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/stylesheets\"\n      )\n    );\n};\n\n\n/*\n  Compiles feeds (changes templates)\n*/\nvar feed_changes = function() {\n  var _title_maximum_length = 80;\n\n  // Acquire paths for the last 3 years (sort by last year first)\n  var _feed_years_back = 3;\n\n  var _last_year_paths = glob.sync(\n    (CONTEXT.PATH_DATA + \"/changes/*.json\")\n  )\n    .sort()\n    .reverse()\n    .splice(0, _feed_years_back);\n\n  // Acquire the change years data\n  var _change_years = (\n    _last_year_paths.map(function(file_path) {\n      return JSON.parse(\n        fs.readFileSync(file_path)\n      );\n    })\n  );\n\n  // Concatenate all changes together\n  var _changes_flat = [].concat.apply(\n    [], _change_years\n  );\n\n  // Acquire feed title\n  var _feed_title = (\n    CONTEXT.CONFIG.SITE.texts.changes.titles.feed || \"Platform Changes\"\n  );\n\n  // Construct feed object\n  var _feed = new Feed({\n    title       : _feed_title,\n    description : (\"Feed of \" + _feed_title + \".\"),\n\n    link        : CONTEXT.CONFIG.SITE.urls.base,\n    language    : \"en\",\n    generator   : package.name,\n\n    author      : {\n      name : package.author.name\n    },\n\n    favicon     : (\n      CONTEXT.CONFIG.SITE.urls.base + \"/favicon.ico\"\n    ),\n\n    updated     : (new Date())\n  });\n\n  _changes_flat.forEach(function(change) {\n    // Strip Markdown from text\n    var _text_barebone = (\n      remove_markdown(change.text || \"\")\n    );\n\n    // Split text into a title\n    var _title;\n\n    if (_text_barebone.length > _title_maximum_length) {\n      _title = (\n        _text_barebone.substr(0, _title_maximum_length) + \" (..)\"\n      );\n    } else {\n      _title = _text_barebone;\n    }\n\n    // Add entry\n    _feed.addItem({\n      title   : _title,\n      content : marked(change.text),\n      link    : (CONTEXT.CONFIG.SITE.urls.base + \"/changes/\"),\n      date    : (new Date(change.date))\n    });\n  });\n\n  // Generate feed data\n  return gulp_file(\n    \"changes.rss\", _feed.rss2(), {\n      src : true\n    }\n  )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_PAGES\n      )\n    );\n};\n\n\n/*\n  Generates sitemap\n*/\nvar sitemap = function() {\n  // Scan all templates, while ignoring the 'not_found' page and all private \\\n  //   pages (ie. those starting w/ '_')\n  return gulp.src([\n    (CONTEXT.PATH_BUILD_PAGES + \"/**/*.html\"),\n\n    (\"!\" + CONTEXT.PATH_BUILD_PAGES + \"/not_found/index.html\"),\n    (\"!\" + CONTEXT.PATH_BUILD_PAGES + \"/**/_*/index.html\")\n  ])\n    .pipe(\n      gulp_sitemap({\n        siteUrl : CONTEXT.CONFIG.SITE.urls.base,\n\n        getLoc  : function(site_url, location) {\n          // Nuke file name and extension from URL? (if found)\n          // Notice: built files are stored in directories, corresponding to \\\n          //   their path, ending in a file named eg. 'index.html'.\n          var _file_index = location.lastIndexOf(\"index.html\");\n\n          // Rewrite location?\n          if (_file_index > 0) {\n            location = location.substring(0, _file_index);\n          }\n\n          return location;\n        }\n      })\n    )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_PAGES\n      )\n    );\n};\n\n\n/*\n  Generates robots\n*/\nvar robots = function() {\n  return gulp_file(\n    \"robots.txt\",\n\n    (\n      \"User-Agent: *\\n\"  +\n      \"Allow: /\\n\"       +\n      (\"Sitemap: \" + CONTEXT.CONFIG.SITE.urls.base + \"/sitemap.xml\\n\")\n    ),\n\n    {\n      src : true\n    }\n  )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_PAGES\n      )\n    );\n};\n\n\n/*\n  Generates Open Graph images\n*/\nvar ogimage = function() {\n  // Generate Open Graph images (if any background is configured)\n  return gulp.src(\n    CONTEXT.PATH_BUILD_PAGES + \"/**/*.html\"\n  )\n    .pipe(function() {\n      if (CONTEXT.CONFIG.SITE.images.metas.opengraph) {\n        return gulp_ogimage({\n          base            : function(file) {\n            // Acquire base path segments\n            // Notice: pop the last two items\n            var _page_base_segments = file.relative.split(\"/\");\n\n            _page_base_segments.splice(\n              (_page_base_segments.length - 2), 2\n            );\n\n            if (_page_base_segments.length > 0) {\n              _page_base_segments.unshift(\"\");\n            }\n\n            // Generate base path for generated image, which will be output to \\\n            //   the final HTML file.\n            return (\n              CONTEXT.CONFIG.SITE.urls.base + \"/static/images/opengraph\"  +\n                _page_base_segments.join(\"/\")\n            );\n          },\n\n          directory       : function(file) {\n            // Acquire base path segments\n            // Notice: pop the last two items\n            var _page_base_segments = file.relative.split(\"/\");\n\n            _page_base_segments.splice(\n              (_page_base_segments.length - 2), 2\n            );\n\n            // Build final path segments\n            var _page_final_segments = (\n              [].concat(\n                [\"\", \"images\", \"opengraph\"],\n                _page_base_segments\n              )\n            );\n\n            // Return full directory path (except from the first directory, \\\n            //   which becomes the image name); this will get used to output \\\n            //   the generated image file on the file system.\n            return (\n              CONTEXT.PATH_BUILD_ASSETS + _page_final_segments.join(\"/\")\n            );\n          },\n\n          name            : function(file) {\n            // Acquire base path segments\n            // Notice: pop the last item\n            var _page_base_segments = file.relative.split(\"/\");\n\n            _page_base_segments.pop();\n\n            // Return last directory name (if there is none, this means this \\\n            //   is the 'index' page); this will get used to output the \\\n            //   generated image file on the file system.\n            return (\n              _page_base_segments[(_page_base_segments.length - 1)] || \"index\"\n            );\n          },\n\n          backgroundImage : function() {\n            // Return base background image, which will get used as a baseline \\\n            //   to generate all Open Graph images (ie. as their background \\\n            //   image).\n            return (\n              CONTEXT.PATH_ASSETS + \"/\"  +\n                CONTEXT.CONFIG.SITE.images.metas.opengraph\n            );\n          },\n\n          title           : function(_, $) {\n            return (\n              $(\"head title\").first().text() || \"\"\n            );\n          },\n\n          description     : function(_, $) {\n            return (\n              $(\"head meta[name=\\\"description\\\"]\").first().attr(\"content\") || \"\"\n            );\n          }\n        })\n      }\n\n      // No Open Graph background image configured, warn and skip\n      console.warn(\n        \"⚠️  No Open Graph background image configured, skipping the \"  +\n          \"auto-generation of 'og:image' metadata.\\n   \"                +\n          \"You can configure this with the 'images.metas.opengraph' \"   +\n          \"configuration namespace.\\n\"\n      );\n\n      return gulp_noop();\n    }())\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_PAGES\n      )\n    );\n};\n\n\n/*\n  Minifies stylesheet files\n*/\nvar cssmin = function() {\n  return gulp.src(\n    CONTEXT.PATH_BUILD_ASSETS + \"/stylesheets/**/*.css\"\n  )\n    .pipe(\n      gulp_clean_css()\n    )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/stylesheets\"\n      )\n    );\n};\n\n\n/*\n  Minifies javascript files\n*/\nvar uglify = function() {\n  return gulp.src(\n    CONTEXT.PATH_BUILD_ASSETS + \"/javascripts/**/*.js\"\n  )\n    .pipe(\n      gulp_uglify()\n    )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS + \"/javascripts\"\n      )\n    );\n};\n\n\n/*\n  Insert banner in build files\n*/\nvar build_banner = function() {\n  var date   = new Date();\n\n  var today  = (\n    (date.getMonth() + 1) + \"/\" + date.getDate() + \"/\" + date.getFullYear()\n  );\n\n  var banner = [\n    \"/**\",\n    \" * <%= pkg.name %> - <%= pkg.description %>\",\n    \" * @version v<%= pkg.version %>\",\n    \" * @author <%= pkg.author.name %> <%= pkg.author.url %>\",\n    \" * @date <%= today %>\",\n    \" */\",\n    \"\"\n  ].join(\"\\n\");\n\n  return gulp.src(\n    CONTEXT.PATH_BUILD_ASSETS + \"/**/*.{css,js}\"\n  )\n    .pipe(\n      gulp_header(banner, {\n        pkg   : package,\n        today : today\n      })\n    )\n    .pipe(\n      gulp.dest(\n        CONTEXT.PATH_BUILD_ASSETS\n      )\n    );\n};\n\n\n/*\n  Shows final build size\n*/\nvar build_size = function() {\n  var _stream = merge();\n\n  // Acquire sizes\n  var _fail  = (CONTEXT.CONFIG.SITE.rules.build_size.fail || false),\n      _sizes = CONTEXT.CONFIG.SITE.rules.build_size.sizes;\n\n  // Map all sources and options\n  var _sources = [\n    {\n      glob    : (\n        CONTEXT.PATH_BUILD_PAGES + \"/references/**/*.html\"\n      ),\n\n      options : {\n        production : {\n          gzip  : true,\n          total : true,\n          fail  : _fail,\n\n          \"*\"   : {\n            maxGzippedSize : _sizes.references.pages.gzip_maximum\n          }\n        }\n      }\n    },\n\n    {\n      glob    : (\n        CONTEXT.PATH_BUILD_PAGES + \"/guides/**/*.html\"\n      ),\n\n      options : {\n        production : {\n          gzip  : true,\n          total : true,\n          fail  : _fail,\n\n          \"*\"   : {\n            maxGzippedSize : _sizes.guides.pages.gzip_maximum\n          }\n        }\n      }\n    },\n\n    {\n      glob    : (\n        CONTEXT.PATH_BUILD_ASSETS + \"/images/guides/content/**/\"  +\n          \"*.{jpg,jpeg,png,gif}\"\n      ),\n\n      options : {\n        production : {\n          total : true,\n          fail  : _fail,\n\n          \"*\"   : {\n            maxSize : _sizes.guides.images.maximum\n          }\n        }\n      }\n    },\n\n    {\n      glob    : (\n        CONTEXT.PATH_BUILD_ASSETS + \"/data/**/*.json\"\n      ),\n\n      options : {\n        production : {\n          gzip  : true,\n          total : true,\n          fail  : _fail,\n\n          \"*\"   : {\n            maxSize        : _sizes.data.objects.maximum,\n            maxGzippedSize : _sizes.data.objects.gzip_maximum\n          }\n        }\n      }\n    }\n  ];\n\n  // Compute size report for each source\n  _sources.forEach(function(source) {\n    _stream.add(\n      gulp.src(source.glob)\n        .pipe(\n          gulp_sizereport(\n            (CONTEXT.IS_PRODUCTION === true) ? source.options.production : (\n              source.options.production.default || {\n                total : true\n              }\n            )\n          )\n        )\n    );\n  });\n\n  return _stream;\n};\n\n\n/*\n  Builds all resources\n*/\nvar build_resources = function() {\n  // Generate main series\n  var _series = [\n    gulp.parallel(\n      robots,\n      feed_changes,\n\n      copy_user_assets,\n      copy_images_guides,\n\n      gulp.series(\n        bower,\n\n        gulp.parallel(\n          concat_libraries_stylesheets,\n\n          gulp.series(\n            gulp.parallel(\n              concat_libraries_javascripts,\n              babel\n            ),\n\n            replace_javascripts\n          )\n        )\n      ),\n\n      gulp.series(\n        gulp.parallel(\n          copy_images_base,\n          copy_fonts\n        ),\n\n        scss,\n        replace_stylesheets,\n        css_inline_images\n      ),\n\n      gulp.series(\n        pug_templates_all,\n\n        gulp.parallel(\n          replace_templates_guides,\n          minisearch_consolidate,\n          sitemap\n        ),\n\n        ogimage\n      )\n    )\n  ];\n\n  // Append production final tasks?\n  if (CONTEXT.IS_PRODUCTION === true) {\n    _series.push(\n      gulp.parallel(\n        cssmin,\n        uglify\n      ),\n\n      build_banner,\n      build_size\n    );\n  }\n\n  return gulp.series(_series);\n}();\n\n\n/*\n  Cleans all build files\n*/\nvar build_clean = function() {\n  return del([\n    (CONTEXT.PATH_BUILD_ASSETS + \"/*\"),\n    (CONTEXT.PATH_BUILD_PAGES + \"/*\")\n  ]);\n};\n\n\n/*\n  Starts connect server\n*/\nvar connect_server = function(next) {\n  gulp_connect.server(\n    {\n      name       : \"Server\",\n      root       : CONTEXT.PATH_DIST,\n      host       : CONTEXT.SERVE_HOST,\n      port       : CONTEXT.SERVE_PORT,\n      silent     : true,\n\n      livereload : {\n        hostname : CONTEXT.SERVE_HOST,\n        port     : (CONTEXT.SERVE_PORT + 1),\n      }\n    },\n\n    function() {\n      console.log(\n        \"\\n🔥 Preview server started on: http://\" + CONTEXT.SERVE_HOST + \":\"  +\n          CONTEXT.SERVE_PORT + \"\\n\"\n      );\n    }\n  );\n\n  next();\n};\n\n\n/*\n  Watches for changes on resources\n*/\nvar watch_resources = function(next) {\n  CONTEXT.IS_WATCH = true;\n\n  // Internal files (Chappe files)\n  // Notice: only if not 'production', ie. in 'development' mode, as this is \\\n  //   used by Chappe developers only.\n  if (CONTEXT.IS_PRODUCTION !== true) {\n    gulp.watch(\"bower.json\", bower);\n    gulp.watch(\"res/config/*\", get_configuration);\n    gulp.watch(\"src/images/**/*\", copy_images_base);\n    gulp.watch(\"src/fonts/**/*\", copy_fonts);\n\n    gulp.watch(\n      \"src/locales/**/*\",\n\n      gulp.series(\n        pug_templates_all,\n        replace_templates_guides\n      )\n    );\n\n    gulp.watch(\n      \"src/templates/**/*\",\n\n      gulp.series(\n        pug_templates_all,\n\n        gulp.parallel(\n          replace_templates_guides,\n          sitemap\n        )\n      )\n    );\n\n    gulp.watch(\n      \"src/stylesheets/**/*\",\n\n      gulp.series(\n        scss,\n        replace_stylesheets,\n        css_inline_images\n      )\n    );\n\n    gulp.watch(\n      \"src/javascripts/**/*\",\n\n      gulp.series(\n        babel,\n        replace_javascripts\n      )\n    );\n  }\n\n  // External files (user files)\n  CONTEXT.PATH_CONFIG.forEach(function(config_path) {\n    gulp.watch(config_path, get_configuration);\n  });\n\n  gulp.watch(\n    (CONTEXT.PATH_DATA + \"/guides/**/*.{jpg,jpeg,png,gif}\"),\n    copy_images_guides\n  );\n\n  gulp.watch(\n    (CONTEXT.PATH_DATA + \"/guides/**/*.md\"),\n\n    gulp.series(\n      pug_templates_guides,\n      replace_templates_guides\n    )\n  );\n\n  gulp.watch(\n    (CONTEXT.PATH_DATA + \"/references/**/*.md\"),\n    pug_templates_references\n  );\n\n  gulp.watch(\n    (CONTEXT.PATH_DATA + \"/changes/**/*.json\"),\n\n    gulp.parallel(\n      pug_templates_changes,\n      feed_changes\n    )\n  );\n\n  next();\n};\n\n\n/*\n  Lints Pug templates\n*/\nvar lint_pug_templates = function() {\n  return gulp.src(\n    CONTEXT.PATH_SOURCES + \"/templates/**/*.pug\"\n  )\n    .pipe(\n      gulp_pug_lint({\n        defaultFile : path.join(\n          CONTEXT.PATH_CHAPPE, \".pug-lintrc\"\n        )\n      })\n    );\n};\n\n\n/*\n  Lints SCSS stylesheets\n*/\nvar lint_scss_stylesheets = function() {\n  return gulp.src(\n    CONTEXT.PATH_SOURCES + \"/stylesheets/**/*.scss\"\n  )\n    .pipe(\n      gulp_stylelint({\n        failAfterError : true,\n\n        configFile     : path.join(\n          CONTEXT.PATH_CHAPPE, \".stylelintrc.yml\"\n        ),\n\n        reporters      : [\n          {\n            formatter : \"verbose\",\n            console   : true\n          }\n        ]\n      })\n    );\n};\n\n\n/*\n  Lints JS scripts (with JSHint)\n*/\nvar lint_js_scripts_jshint = function() {\n  return gulp.src([\n    CONTEXT.PATH_SOURCES + \"/javascripts/**/*.js\",\n    CONTEXT.PATH_CHAPPE + \"/bin/*.js\"\n  ])\n    .pipe(\n      gulp_jshint({\n        defaultFile : path.join(\n          CONTEXT.PATH_CHAPPE, \".jshintrc\"\n        )\n      })\n    )\n    .pipe(\n      gulp_jshint.reporter(\"fail\")\n    );\n};\n\n\n/*\n  Lints JS scripts (with JSCS)\n*/\nvar lint_js_scripts_jscs = function() {\n  return gulp.src([\n    CONTEXT.PATH_SOURCES + \"/javascripts/**/*.js\",\n    CONTEXT.PATH_CHAPPE + \"/bin/*.js\"\n  ])\n    .pipe(\n      gulp_jscs({\n        configPath : path.join(\n          CONTEXT.PATH_CHAPPE, \".jscsrc\"\n        )\n      })\n    )\n    .pipe(\n      gulp_jscs.reporter(\"fail\")\n    );\n};\n\n\n/*\n  Lints project built code\n*/\nvar lint = function() {\n  return gulp.series(\n    get_configuration,\n\n    gulp.parallel(\n      lint_pug_templates,\n      lint_scss_stylesheets,\n      lint_js_scripts_jshint,\n      lint_js_scripts_jscs\n    )\n  );\n}();\n\n\n/*\n  Serves project\n*/\nvar serve = function() {\n  return gulp.series(\n    get_configuration,\n    minisearch_prepare,\n    build_clean,\n    build_resources,\n    watch_resources,\n    connect_server\n  );\n}();\n\n\n/*\n  Cleans all build files\n*/\nvar clean = function() {\n  return gulp.series(\n    get_configuration,\n    build_clean\n  )\n}();\n\n\n/*\n  Builds project\n*/\nvar build = function() {\n  return gulp.series(\n    get_configuration,\n    minisearch_prepare,\n    build_resources\n  );\n}();\n\n\n/*\n  Watches for project changes\n*/\nvar watch = function() {\n  return gulp.series(\n    get_configuration,\n    minisearch_prepare,\n    watch_resources\n  )\n}();\n\n\n/*\n  Export all public tasks\n*/\nexports.lint    = lint;\nexports.serve   = serve;\nexports.clean   = clean;\nexports.watch   = watch;\nexports.build   = build;\nexports.default = build;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"chappe\",\n  \"description\": \"Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine.\",\n  \"version\": \"1.16.0\",\n  \"homepage\": \"https://github.com/crisp-oss/chappe\",\n  \"license\": \"MIT\",\n  \"bin\": {\n    \"chappe\": \"bin/chappe.js\"\n  },\n  \"author\": {\n    \"name\": \"Valerian Saliou\",\n    \"email\": \"valerian@valeriansaliou.name\",\n    \"url\": \"https://valeriansaliou.name/\"\n  },\n  \"contributors\": [\n    {\n      \"name\": \"Baptiste Jamin\",\n      \"url\": \"https://jam.in/\"\n    }\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/crisp-oss/chappe.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/crisp-oss/chappe/issues\"\n  },\n  \"licenses\": [\n    {\n      \"type\": \"MIT\",\n      \"url\": \"https://github.com/crisp-oss/chappe/blob/master/LICENSE\"\n    }\n  ],\n  \"engines\": {\n    \"node\": \">= 20.0.0\"\n  },\n  \"scripts\": {\n    \"dev\": \"./bin/chappe.js serve --example=acme-docs\",\n    \"build\": \"./bin/chappe.js build --example=acme-docs\",\n    \"test\": \"./bin/chappe.js lint --example=acme-docs\"\n  },\n  \"dependencies\": {\n    \"@tommoor/remove-markdown\": \"0.3.x\",\n    \"babel-preset-es2015\": \"6.9.x\",\n    \"del\": \"6.0.x\",\n    \"drafter.js\": \"3.2.x\",\n    \"escape-html\": \"1.0.x\",\n    \"feed\": \"4.2.x\",\n    \"glob\": \"7.2.x\",\n    \"gulp\": \"4.0.x\",\n    \"gulp-babel\": \"6.1.x\",\n    \"gulp-bower\": \"0.0.15\",\n    \"gulp-clean-css\": \"4.3.x\",\n    \"gulp-concat\": \"2.6.x\",\n    \"gulp-connect\": \"5.7.x\",\n    \"gulp-file\": \"0.4.x\",\n    \"gulp-header\": \"2.0.x\",\n    \"gulp-inline-image\": \"1.0.x\",\n    \"gulp-jscs\": \"4.1.x\",\n    \"gulp-jshint\": \"2.1.x\",\n    \"gulp-noop\": \"1.0.x\",\n    \"gulp-notify\": \"4.0.x\",\n    \"gulp-ogimage\": \"2.0.x\",\n    \"gulp-pug\": \"5.0.x\",\n    \"gulp-pug-lint\": \"0.1.x\",\n    \"gulp-rename\": \"2.0.x\",\n    \"gulp-replace\": \"1.1.x\",\n    \"gulp-sass\": \"6.0.x\",\n    \"gulp-sass-variables\": \"1.2.x\",\n    \"gulp-sitemap\": \"8.0.x\",\n    \"gulp-sizereport\": \"1.2.x\",\n    \"gulp-stylelint-esm\": \"3.0.x\",\n    \"gulp-uglify\": \"3.0.x\",\n    \"http-status-codes\": \"2.2.x\",\n    \"jshint\": \"2.13.x\",\n    \"lodash\": \"4.17.x\",\n    \"markdown-toc\": \"1.2.x\",\n    \"marked\": \"7.0.x\",\n    \"marked-footnote\": \"1.4.x\",\n    \"marked-gfm-heading-id\": \"3.2.x\",\n    \"marked-mangle\": \"1.1.x\",\n    \"merge-stream\": \"2.0.x\",\n    \"minisearch\": \"3.1.x\",\n    \"ora\": \"5.4.x\",\n    \"sass\": \"1.89.x\",\n    \"slug\": \"5.2.x\",\n    \"stylelint-config-standard-scss\": \"16.0.x\",\n    \"trunc-text\": \"1.0.x\",\n    \"yargs\": \"4.8.x\"\n  },\n  \"keywords\": [\n    \"docs\",\n    \"documentation\",\n    \"api-documentation\",\n    \"documentation-tool\",\n    \"docs-generator\",\n    \"developer-documentation\",\n    \"developer-tool\",\n    \"markdown\",\n    \"blueprint\",\n    \"developer\",\n    \"api-rest\",\n    \"github-pages\",\n    \"cloudflare-pages\"\n  ]\n}\n"
  },
  {
    "path": "res/config/common.json",
    "content": "{\n  \"LANGS\" : [\n    \"en\"\n  ],\n\n  \"URLS\" : {\n    \"CRISP_WEB\" : \"https://crisp.chat/\"\n  },\n\n  \"LIBRARIES\" : {\n    \"JAVASCRIPTS\" : [\n      \"console/console.js\",\n      \"cookies/src/cookies.js\",\n      \"cash/dist/cash.js\",\n      \"minisearch/dist/umd/index.js\",\n      \"prism.js/prism.js\"\n    ],\n\n    \"STYLESHEETS\" : [\n      \"reset.css/reset.css\"\n    ]\n  },\n\n  \"SOURCES\" : {\n    \"TEMPLATES\" : [\n      \"home/index.pug\",\n      \"not_found/index.pug\"\n    ],\n\n    \"JAVASCRIPTS\" : [\n      \"common/common.js\"\n    ],\n\n    \"STYLESHEETS\" : [\n      \"common/common.scss\",\n      \"home/home.scss\",\n      \"guides/guides.scss\",\n      \"references/references.scss\",\n      \"changes/changes.scss\",\n      \"not_found/not_found.scss\"\n    ]\n  },\n\n  \"FORMAT\" : {\n    \"DESCRIPTION\" : {\n      \"TRUNCATE\" : 140\n    },\n\n    \"DATES\" : {\n      \"LOCALE_DATE_STRING\" : {\n        \"AREA\"    : \"en-US\",\n\n        \"OPTIONS\" : {\n          \"year\"  : \"numeric\",\n          \"month\" : \"long\",\n          \"day\"   : \"numeric\"\n        }\n      }\n    }\n  },\n\n  \"COLORS\" : {\n    \"BADGES\" : {\n      \"HTTP\" : {\n        \"head\"   : \"blue\",\n        \"get\"    : \"blue\",\n        \"post\"   : \"green\",\n        \"put\"    : \"yellow\",\n        \"patch\"  : \"yellow\",\n        \"delete\" : \"red\"\n      }\n    }\n  },\n\n  \"SEARCH\" : {\n    \"INDEX\" : \"search/index.json\",\n\n    \"OPTIONS\" : {\n      \"BASE\" : {\n        \"fields\"      : [\n          \"title\",\n          \"path\",\n          \"summary\"\n        ],\n\n        \"storeFields\" : [\n          \"id\",\n          \"path\",\n          \"title\",\n          \"summary\"\n        ]\n      },\n\n      \"QUERY\" : {\n        \"searchOptions\" : {\n          \"prefix\"  : true,\n          \"fuzzy\"   : 0.15,\n\n          \"boost\"   : {\n            \"title\"   : 6,\n            \"path\"    : 4,\n            \"summary\" : 1\n          },\n\n          \"weights\" : {\n            \"fuzzy\"  : 0.5,\n            \"prefix\" : 0.25\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "res/config/user.json",
    "content": "{\n  \"identity\" : {\n    \"title\"     : \"\",\n    \"copyright\" : \"\"\n  },\n\n  \"theme\" : {\n    \"accent\" : {\n      \"light\" : {\n        \"base\"   : \"#2275f1\",\n        \"active\" : \"#0f69ef\"\n      },\n\n      \"dark\" : {\n        \"base\"   : \"#e0e7ed\",\n        \"active\" : \"#d5dde4\"\n      }\n    }\n  },\n\n  \"urls\" : {\n    \"base\"         : \"\",\n    \"vigil\"        : \"\",\n    \"crisp_status\" : \"\"\n  },\n\n  \"favicons\" : {\n    \"main\"  : \"\",\n\n    \"sizes\" : {\n      \"default\" : \"\",\n      \"512x512\" : \"\",\n      \"256x256\" : \"\",\n      \"128x128\" : \"\",\n      \"32x32\"   : \"\"\n    }\n  },\n\n  \"images\" : {\n    \"illustrations\" : {\n      \"home\"      : \"\",\n      \"not_found\" : \"\"\n    },\n\n    \"logos\" : {\n      \"header_full\"  : \"\",\n      \"header_short\" : \"\",\n      \"footer\"       : \"\"\n    },\n\n    \"metas\" : {\n      \"opengraph\" : \"\"\n    },\n\n    \"categories\" : {\n      \"guides\"     : {},\n      \"references\" : {}\n    }\n  },\n\n  \"dimensions\" : {\n    \"logos\" : {\n      \"header_full\"  : {\n        \"width\" : 160\n      },\n\n      \"header_short\" : {\n        \"width\" : 50\n      },\n\n      \"footer\"       : {\n        \"width\" : 120\n      }\n    }\n  },\n\n  \"colors\" : {\n    \"changes\" : {\n      \"groups\" : {}\n    }\n  },\n\n  \"texts\" : {\n    \"home\" : {\n      \"title\" : \"\",\n      \"label\" : \"\"\n    },\n\n    \"changes\" : {\n      \"groups\" : {},\n\n      \"titles\" : {\n        \"feed\"   : \"\",\n        \"latest\" : \"\",\n        \"year\"   : \"\"\n      },\n\n      \"notice\" : \"\"\n    }\n  },\n\n  \"links\" : {\n    \"header\" : {\n      \"navigation\" : [],\n      \"actions\"    : []\n    },\n\n    \"footer\" : {\n      \"navigation\" : []\n    }\n  },\n\n  \"actions\" : {\n    \"home\" : []\n  },\n\n  \"bulletpoints\" : {\n    \"home\" : {\n      \"quickstart\" : {\n        \"description\" : \"\",\n        \"actions\"     : []\n      },\n\n      \"guides\" : {\n        \"description\" : \"\",\n        \"actions\"     : []\n      },\n\n      \"references\" : {\n        \"description\" : \"\",\n        \"actions\"     : []\n      }\n    }\n  },\n\n  \"includes\" : {\n    \"scripts\" : {\n      \"urls\"   : [],\n      \"inline\" : []\n    },\n\n    \"stylesheets\" : {\n      \"urls\"   : [],\n      \"inline\" : []\n    }\n  },\n\n  \"tokens\" : {\n    \"crisp_website_id\" : \"\"\n  },\n\n  \"plugins\" : {\n    \"code\" : {\n      \"syntaxes\" : []\n    }\n  },\n\n  \"features\" : {\n    \"support\" : true\n  },\n\n  \"rules\" : {\n    \"build_size\" : {\n      \"fail\"  : true,\n\n      \"sizes\" : {\n        \"references\" : {\n          \"pages\" : {\n            \"gzip_maximum\" : 100000\n          }\n        },\n\n        \"guides\" : {\n          \"pages\" : {\n            \"gzip_maximum\" : 50000\n          },\n\n          \"images\" : {\n            \"maximum\" : 1000000\n          }\n        },\n\n        \"data\" : {\n          \"objects\" : {\n            \"maximum\"      : 200000,\n            \"gzip_maximum\" : 50000\n          }\n        }\n      }\n    }\n  },\n\n  \"overrides\" : {\n    \"crisp_chatbox_url\" : \"\"\n  }\n}\n"
  },
  {
    "path": "res/plugins/gulp/minisearch.js",
    "content": "/*\n * chappe\n *\n * Copyright 2021, Crisp IM SAS\n * Author: Valerian Saliou <valerian@valeriansaliou.name>\n */\n\n\nvar remove_markdown  = require(\"@tommoor/remove-markdown\");\n\n\nmodule.exports = {\n  insert_categories : function(\n    context, type, path_origin, categories, override_segments\n  ) {\n    var _self = this;\n\n    // Define a maximum length for summaries\n    var _summary_maximum_length = 100;\n\n    categories.forEach(function(category) {\n      // Generate target page URL\n      var _target_segments = (\n        override_segments || category.segments || []\n      );\n\n      var _target_url = (\n        \"/\" + type + \"/\" + _target_segments.join(\"/\")  +\n          ((_target_segments.length > 0) ? \"/\" : \"\")\n      );\n\n      // Append page anchor? (if not a category with segments)\n      if (!category.segments && category.id) {\n        _target_url += (\"#\" + category.id);\n      }\n\n      // Acquire summary (based on available data)\n      var _summary_full = \"\";\n\n      if (category.data) {\n        switch (category.type) {\n          case \"API Blueprint\": {\n            if (category.data.content) {\n              var _blueprint_first_line;\n\n              // Acquire first line from content\n              for (var _i = 0; _i < category.data.content.length; _i++) {\n                var _entry = category.data.content[_i];\n\n                if (_entry.element === \"copy\" && _entry.content) {\n                  _blueprint_first_line = _entry.content;\n\n                  break;\n                }\n              }\n\n              // Strip Markdown from first line?\n              if (_blueprint_first_line) {\n                _summary_full = remove_markdown(_blueprint_first_line);\n              }\n            }\n\n            break;\n          }\n\n          case \"Markdown\": {\n            // Strip Markdown from data\n            _summary_full = remove_markdown(category.data);\n\n            break;\n          }\n        }\n\n        // Use title as summary? (fallback)\n        if (!_summary_full) {\n          _summary_full = category.title;\n        }\n      } else if (category.markdown) {\n        // Strip Markdown from text\n        _summary_full = remove_markdown(category.markdown);\n      }\n\n      // Generate short summary\n      var _summary;\n\n      if (_summary_full.length > _summary_maximum_length) {\n        _summary = (\n          _summary_full.substr(0, _summary_maximum_length) + \".\"\n        );\n      } else {\n        _summary = _summary_full;\n      }\n\n      // Insert entry to index\n      context.SEARCH_INDEX.add({\n        id      : _target_url,\n        path    : path_origin.join(\" > \"),\n        title   : category.title,\n        summary : (_summary || \"\")\n      });\n\n      // Recurse to subtrees?\n      if ((category.subtrees || []).length > 0) {\n        // Generate current path origin\n        var _parent_path_origin = [].concat(path_origin);\n\n        _parent_path_origin.push(category.title);\n\n        // Insert all child categories to the index\n        _self.insert_categories(\n          context, type, _parent_path_origin, category.subtrees,\n            override_segments\n        );\n      }\n    });\n  }\n};\n"
  },
  {
    "path": "res/plugins/gulp/pug-templates.js",
    "content": "/*\n * chappe\n *\n * Copyright 2021, Crisp IM SAS\n * Author: Valerian Saliou <valerian@valeriansaliou.name>\n */\n\n\nvar fs                 = require(\"fs\");\nvar path               = require(\"path\");\nvar url                = require(\"url\");\n\nvar lodash             = require(\"lodash\");\nvar glob               = require(\"glob\");\nvar slug               = require(\"slug\");\nvar drafter            = require(\"drafter.js\");\nvar toc                = require(\"markdown-toc\");\nvar truncate_text      = require(\"trunc-text\");\nvar remove_markdown    = require(\"@tommoor/remove-markdown\");\nvar http_status_codes  = require(\"http-status-codes\");\n\nvar gulp_notify        = require(\"gulp-notify\");\nvar gulp_rename        = require(\"gulp-rename\");\n\n\nmodule.exports = {\n  list_config_locales : function(context, package, marked) {\n    var pug_config = {\n      data : lodash.merge(\n        lodash.clone(context.CONFIG),\n\n        {\n          PACKAGE : package,\n\n          DATE    : {\n            YEAR : (new Date()).getUTCFullYear()\n          },\n\n          METHODS : {\n            marked          : marked,\n            remove_markdown : remove_markdown,\n            truncate_text   : truncate_text,\n            slug            : slug\n          }\n        }\n      )\n    };\n\n    if (context.IS_PRODUCTION) {\n      pug_config.pretty = false;\n    } else {\n      pug_config.pretty = true;\n    }\n\n    return context.CONFIG.LANGS.map(function(lang) {\n      var pug_config_lang = lodash.cloneDeep(pug_config);\n\n      // Assign locale code\n      pug_config_lang.data.LOCALE = {\n        CODE      : lang,\n        DIRECTION : \"ltr\"\n      };\n\n      // Assign locale strings\n      pug_config_lang.data.$_ = JSON.parse(\n        fs.readFileSync(context.PATH_SOURCES + \"/locales/\" + lang + \".json\")\n      );\n\n      return {\n        locale : lang,\n        config : pug_config_lang\n      };\n    });\n  },\n\n  pipe_commons : function(context, locale, fn_pipeline) {\n    return fn_pipeline()\n      .on(\"error\", gulp_notify.onError({\n        title     : \"pug_templates_base\",\n        message   : \"Error compiling\",\n        emitError : true\n      }))\n      .on(\"error\", function(error) {\n        if (context.IS_WATCH === true) {\n          // Handle compile errors (used for development ease w/ `gulp watch`)\n          console.error(error.toString());  // jscs:ignore\n        } else {\n          throw error;\n        }\n      })\n      .pipe(\n        gulp_rename(function(file_path) {\n          // Append locale code to base name (only if not default locale, ie. \\\n          //   'en')\n          if (locale !== context.CONFIG.LANGS[0]) {\n            file_path.basename += (\".\" + locale);\n          }\n\n          // Map home directory to base directory? (as 'home' must be set at \\\n          //   the root, ie. as the entry 'index.html')\n          if (file_path.dirname === \"home\") {\n            file_path.dirname = \"\";\n          }\n        })\n      );\n  },\n\n  traverse_guides_tree : function(\n    root_path, base_path, linear_output, parent_tree\n  ) {\n    var _self = this;\n\n    // Apply defaults\n    base_path     = (base_path     || root_path);\n    linear_output = (linear_output || []);\n    parent_tree   = (parent_tree   || null);\n\n    var _tree_output = glob.sync(\"./index.md\", {\n      cwd : base_path\n    })\n      .map(function(file_path) {\n        // Acquire file paths (full paths from build root, and page paths)\n        var _file_path_full  = path.join(base_path, file_path),\n            _file_path_pages = path.relative(root_path, _file_path_full);\n\n        // Split file path into its final URL segments\n        var _path_segments = (\n          _file_path_pages.split(\"/\")\n            .filter(function(file_path_segment) {\n              // Ignore segment?\n              if (file_path_segment === \".\" || file_path_segment === \"index.md\") {\n                return false;\n              }\n\n              // Accept segment\n              return true;\n            })\n        );\n\n        // Read guide metas: title, order and others (and then, Markdown)\n        var _file_data  = fs.readFileSync(_file_path_full, \"utf-8\"),\n            _file_lines = _file_data.split(/\\r?\\n/);\n\n        var _guide_title    = null,\n            _guide_index    = null,\n            _guide_updated  = null,\n            _guide_links    = [],\n            _guide_markdown = \"\";\n\n        // Read content line-by-line\n        var _has_scanned_metas = false;\n\n        for (var _i = 0; _i < _file_lines.length; _i++) {\n          // Match on meta line? (if has not already scanned all metas)\n          if (_has_scanned_metas !== true) {\n            var _match_meta_line = _file_lines[_i].match(/^([A-Z]+):(.+)$/);\n\n            if (_match_meta_line) {\n              var _match_meta_value = (\n                (_match_meta_line[2] || \"\").trim() || null\n              );\n\n              switch (_match_meta_line[1]) {\n                case \"TITLE\": {\n                  _guide_title = _match_meta_value;\n\n                  break;\n                }\n\n                case \"INDEX\": {\n                  if (!isNaN(_match_meta_value)) {\n                    _guide_index = parseInt(_match_meta_value, 10);\n                  }\n\n                  break;\n                }\n\n                case \"UPDATED\": {\n                  _guide_updated = (new Date(_match_meta_value));\n\n                  break;\n                }\n\n                case \"LINK\": {\n                  var _link_parts = _match_meta_value.split(\"->\"),\n                      _link_name  = (_link_parts[0] || \"\").trim(),\n                      _link_url   = (_link_parts[1] || \"\").trim();\n\n                  if (!_link_name || !_link_url || _link_parts.length !== 2) {\n                    throw new Error(\n                      \"Guide link value is invalid: \" + _match_meta_value\n                    );\n                  }\n\n                  _guide_links.push({\n                    name : _link_name,\n                    url  : _link_url\n                  });\n\n                  break;\n                }\n\n                default: {\n                  throw new Error(\n                    \"Guide meta-data value is unsupported: \" + _match_meta_line[1]\n                  );\n                }\n              }\n\n              continue;\n            }\n\n            // Non-meta line found, consider as having scanned them all.\n            _has_scanned_metas = true;\n          }\n\n          // Handle Markdown line\n          _guide_markdown += _file_lines[_i];\n          _guide_markdown += \"\\n\";\n        }\n\n        // Perform a final trim of the parsed Markdown (as extra end-lines may \\\n        //   be held)\n        _guide_markdown = _guide_markdown.trim();\n\n        // Validate acquired values\n        if (!_guide_title || !_guide_updated                          ||\n              isNaN(_guide_updated.getTime()) || isNaN(_guide_index)  ||\n              _guide_index < 1) {\n          throw new Error(\"Guide meta-data is invalid at: \" + _file_path_full);\n        }\n\n        // Generate tree entry\n        var _entry = {\n          segments : _path_segments,\n          id       : _path_segments.join(\"_\"),\n          title    : _guide_title,\n          index    : _guide_index,\n          links    : _guide_links,\n          updated  : _guide_updated,\n          markdown : _guide_markdown,\n          parent   : parent_tree,\n          subtrees : []\n        };\n\n        // List sub-trees (traverse tree recursively)\n        var _sub_tree_parts = (\n          glob.sync(\"./*/\", {\n            cwd : base_path\n          })\n            .map(function(directory_path) {\n              var _sub_tree_data = _self.traverse_guides_tree(\n                root_path, path.join(base_path, directory_path), linear_output,\n                  _entry\n              );\n\n              // Take tree output, drop the linear output aggregator\n              return _sub_tree_data[0];\n            })\n            .filter(function(sub_tree) {\n              // Filter-out empty sub-trees\n              return (sub_tree.length > 0 ? true : false);\n            })\n        );\n\n        // Reduce sub-trees into a single array\n        _sub_tree_parts.forEach(function(sub_tree_part) {\n          _entry.subtrees = _entry.subtrees.concat(sub_tree_part);\n        });\n\n        // Sort sub-trees by lower index first\n        _entry.subtrees = _entry.subtrees.sort(function(previous, next) {\n          return (previous.index - next.index);\n        });\n\n        // Append this entry to the linear output (w/ the same object reference \\\n        //   as the tree-based output)\n        linear_output.push(_entry);\n\n        return _entry;\n      })\n      .sort(function(previous, next) {\n        // Sort main-tree by lower index first\n        return (previous.index - next.index);\n      });\n\n    return [_tree_output, linear_output];\n  },\n\n  traverse_references_linear : function(context, root_path) {\n    var _self = this;\n\n    return glob.sync(\"./**/*.md\", {\n      cwd : root_path\n    })\n      .map(function(file_path) {\n        // Acquire file paths (full paths from build root, and page paths)\n        var _file_path_full = path.join(root_path, file_path);\n\n        // Split file path into its final URL segments\n        var _path_segments = (\n          file_path.split(\"/\")\n            .filter(function(file_path_segment) {\n              // Ignore segment?\n              if (file_path_segment === \".\") {\n                return false;\n              }\n\n              // Accept segment\n              return true;\n            })\n            .map(function(file_path_segment) {\n              // Remove all file extensions in all segments\n              return file_path_segment.split(\".\")[0];\n            })\n        );\n\n        // Read reference metas: type and others (and then, content)\n        var _file_data  = fs.readFileSync(_file_path_full, \"utf-8\"),\n            _file_lines = _file_data.split(/\\r?\\n/);\n\n        var _reference_type    = null,\n            _reference_title   = null,\n            _reference_updated = null,\n            _reference_content = \"\";\n\n        // Read content line-by-line\n        var _has_scanned_metas = false;\n\n        for (var _i = 0; _i < _file_lines.length; _i++) {\n          // Match on meta line? (if has not already scanned all metas)\n          if (_has_scanned_metas !== true) {\n            // Only scan on certain meta keys, as some keys may be used for \\\n            //   eg. API Blueprint.\n            var _match_meta_line = (\n              _file_lines[_i].match(/^(TYPE|TITLE|UPDATED):(.+)$/)\n            );\n\n            if (_match_meta_line) {\n              var _match_meta_value = (\n                (_match_meta_line[2] || \"\").trim() || null\n              );\n\n              switch (_match_meta_line[1]) {\n                case \"TYPE\": {\n                  _reference_type = _match_meta_value;\n\n                  break;\n                }\n\n                case \"TITLE\": {\n                  _reference_title = _match_meta_value;\n\n                  break;\n                }\n\n                case \"UPDATED\": {\n                  _reference_updated = (new Date(_match_meta_value));\n\n                  break;\n                }\n\n                default: {\n                  throw new Error(\n                    \"Reference meta-data value is unsupported: \"  +\n                      _match_meta_line[1]\n                  );\n                }\n              }\n\n              continue;\n            }\n\n            // Non-meta line found, consider as having scanned them all.\n            _has_scanned_metas = true;\n          }\n\n          // Handle content line\n          _reference_content += _file_lines[_i];\n          _reference_content += \"\\n\";\n        }\n\n        // Perform a final trim of the parsed content (as extra end-lines may \\\n        //   be held)\n        _reference_content = _reference_content.trim();\n\n        // Validate acquired values\n        if (!_reference_type || !_reference_title || !_reference_updated  ||\n              isNaN(_reference_updated.getTime())) {\n          throw new Error(\n            \"Reference meta-data is invalid at: \" + _file_path_full\n          );\n        }\n\n        // Return entry data\n        return [\n          {\n            segments : _path_segments,\n            id       : _path_segments.join(\"_\"),\n            type     : _reference_type,\n            title    : _reference_title,\n            updated  : _reference_updated,\n          },\n\n          _reference_content\n        ];\n      })\n      .map(function(entry_items) {\n        var _entry = entry_items[0];\n\n        switch (_entry.type) {\n          case \"API Blueprint\": {\n            var _parse_result = drafter.parseSync(entry_items[1], {\n              requireBlueprintName      : true,\n              generateSourceMap         : false,\n              generateMessageBody       : false,\n              generateMessageBodySchema : false\n            });\n\n            // Blueprint has error? (likely invalid)\n            if (!_parse_result || _parse_result.content.length === 0  ||\n                  _parse_result.content[0].element !== \"category\") {\n              throw new Error(\n                \"Reference API Blueprint could not be parsed for: \"  +\n                  _entry.segments.join(\"/\")\n              );\n            }\n\n            // Blueprint has warnings? (we want to be strict there and abort \\\n            //   build, as ignored warnings can result in unseen issues in the \\\n            //   final built reference document)\n            // Notice: render errors in a readable format.\n            var _warns_count = (_parse_result.content.length - 1);\n\n            if (_warns_count > 0) {\n              var _warns_limit = 50;\n\n              throw new Error(\n                \"Reference API Blueprint has warnings for: \"  +\n                  _entry.segments.join(\"/\")                   +\n                  \"\\n\\n\"                                      +\n                  \"Warnings to be resolved:\"                  +\n                  \"\\n\"                                        +\n\n                  (\n                    _parse_result.content.splice(1, _warns_limit)\n                      .map(function(warning) {\n                        var _text = (\n                          (typeof warning.content === \"string\") ?\n                            (warning.content || \"[no text]\") : \"[wrong type]\"\n                        );\n\n                        return (\" |- (\" + warning.element + \") -> \" + _text);\n                      })\n                      .join(\"\\n\")\n                  )                                           +\n\n                  (\n                    (_warns_count <= _warns_limit) ? \"\" : (\n                      \"\\n\" + (\n                        \" \\\\+ (\" + (_warns_count - _warns_limit) + \" more)\"\n                      )\n                    )\n                  )\n              );\n            }\n\n            // Acquire final entry data\n            var _data = _parse_result.content[0];\n\n            // Find API host URL from parent attributes\n            var _host_url = null;\n\n            (_data.attributes.metadata.content || []).forEach(\n              function(metadata) {\n                if (!_host_url && metadata.element === \"member\"  &&\n                      metadata.content                           &&\n                      metadata.content.key.content === \"HOST\") {\n                  _host_url = metadata.content.value.content;\n                }\n              }\n            );\n\n            // Still did not find host URL? This is unexpected.\n            if (!_host_url) {\n              throw new Error(\n                \"Reference API Blueprint HOST value could not be found\"\n              );\n            }\n\n            // Assign parsed Blueprint result tree (first entry contains the \\\n            //   whole result tree)\n            _entry.data       = _data;\n\n            _entry.baseline   = (\n              _self.map_references_blueprint_baseline(_host_url)\n            );\n            _entry.categories = (\n              _self.map_references_blueprint_categories(context, _data.content)\n            );\n            _entry.examples   = (\n              _self.map_references_blueprint_examples(_data.content)\n            );\n\n            break;\n          }\n\n          case \"Markdown\": {\n            // Acquire final entry data\n            var _data = entry_items[1];\n\n            // Assign parsed Markdown data\n            _entry.data       = _data;\n\n            _entry.categories = (\n              _self.map_references_markdown_categories(_data)\n            );\n\n            break;\n          }\n\n          default: {\n            throw new Error(\n              \"Reference type: \" + _entry.type + \" is not recognized on: \"  +\n                _entry.segments.join(\"/\")\n            );\n          }\n        }\n\n        return _entry;\n      });\n  },\n\n  map_references_blueprint_baseline : function(host_url) {\n    var _baseline = {};\n\n    // Map host URL parts\n    _baseline.host = {\n      url  : host_url,\n      path : (url.parse(host_url).pathname || \"/\")\n    };\n\n    return _baseline;\n  },\n\n  map_references_blueprint_categories : function(context, entries, parent_id) {\n    var _self = this;\n\n    var _categories = [];\n\n    entries.forEach(function(entry) {\n      // Match on category groups only\n      if (entry.element === \"category\" || entry.element === \"resource\"  ||\n            entry.element === \"transition\") {\n        // Acquire HTTP method associated to 'transition' element (ie. request)\n        var _badge = null;\n\n        if (entry.element === \"transition\") {\n          var _http_method = (\n            _self.find_references_blueprint_http_method(entry)\n          );\n\n          if (_http_method) {\n            // Acquire badge color for HTTP method\n            var _badge_color = (\n              context.CONFIG.COLORS.BADGES.HTTP[_http_method] || null\n            );\n\n            // Create final badge object\n            _badge = [_http_method, _badge_color];\n          }\n        }\n\n        // Generate category identifier (prepend parent identifier, if any)\n        var _id = slug(entry.meta.title.content);\n\n        if (parent_id) {\n          _id = (parent_id + \"-\" + _id);\n        }\n\n        // Push current category to the level of categories being scanned\n        _categories.push({\n          id       : _id,\n          title    : entry.meta.title.content,\n          badge    : _badge,\n\n          subtrees : (\n            _self.map_references_blueprint_categories(\n              context, entry.content,\n\n              ((entry.element === \"category\") ? _id : null)  //-[parent_id]\n            )\n          )\n        });\n      }\n    });\n\n    return _categories;\n  },\n\n  map_references_blueprint_examples : function(entries, parent_map) {\n    var _self = this;\n\n    parent_map = (parent_map || {});\n\n    entries.forEach(function(entry) {\n      switch (entry.element) {\n        case \"category\":\n        case \"resource\": {\n          // Recurse on children (parent groups of transition group)\n          _self.map_references_blueprint_examples(\n            entry.content, parent_map\n          );\n\n          break;\n        }\n\n        case \"transition\": {\n          // Acquire HTTP method\n          var _http_method = (\n            _self.find_references_blueprint_http_method(entry)\n          );\n\n          // Acquire restrictions\n          var _restrictions = (\n            _self.find_references_blueprint_restrictions(entry)\n          );\n\n          // Parse URL parts (if any)\n          var _url_parts = (\n            _self.find_references_blueprint_url_parts(entry)\n          );\n\n          // Parse available request-response flows\n          var _flows = (\n            _self.find_references_blueprint_flows(entry)\n          );\n\n          // Validate acquired data\n          if (!_http_method || Object.keys(_flows).length === 0) {\n            throw new Error(\n              \"Reference API Blueprint transition entry has no example request\"\n            );\n          }\n\n          // Generate example data\n          var _example_data = {\n            method : _http_method,\n\n            url    : {\n              parts : _url_parts\n            },\n\n            tiers  : (_restrictions.tiers  || []),\n            scopes : (_restrictions.scopes || []),\n\n            flows  : _flows\n          };\n\n          // Assign example data to parent map\n          var _id = slug(entry.meta.title.content);\n\n          parent_map[_id] = _example_data;\n\n          break;\n        }\n      }\n    });\n\n    return parent_map;\n  },\n\n  map_references_markdown_categories : function(data) {\n    var _self = this;\n\n    // Generate navigation menu from Markdown\n    var _navigation = toc(data, {\n      maxdepth : 3,\n      firsth1  : true\n    });\n\n    // Generate navigation tree (based on indent levels)\n    return (\n      _self.transduce_references_markdown_categories_tree(_navigation.json)\n    );\n  },\n\n  transduce_references_markdown_categories_tree : function(items, level) {\n    var _self = this;\n\n    level = (level || 1);\n\n    // Append entries to tree\n    var _tree           = [],\n        _current_entry  = null,\n        _children_stack = [];\n\n    for (var _i = 0; _i < (items.length + 1); _i++) {\n      var _item = (items[_i] || null);\n\n      // Scanning is finished as we have overflown? Or are we still scanning?\n      if (_item === null || _item.lvl === level) {\n        // Push last current entry before, if any\n        if (_current_entry !== null) {\n          // Transduce eventual children from the stack\n          _current_entry.subtrees = (\n            _self.transduce_references_markdown_categories_tree(\n              _children_stack, (level + 1)\n            )\n          );\n\n          // Append current entry to tree (with its eventual children)\n          _tree.push(_current_entry);\n\n          // Reset children stack back to empty state\n          _children_stack = [];\n        }\n\n        // Start a new current entry?\n        if (_item !== null) {\n          _current_entry = {\n            id       : slug(_item.content),\n            title    : _item.content,\n            subtrees : []\n          };\n        }\n      } else {\n        // Append raw child entry to children stack\n        _children_stack.push(_item);\n      }\n    }\n\n    return _tree;\n  },\n\n  find_references_blueprint_http_method : function(transition) {\n    var _http_method = null;\n\n    transition.content.forEach(function(entry) {\n      if (_http_method === null && entry.element === \"httpTransaction\") {\n        entry.content.forEach(function(transaction) {\n          if (_http_method === null && transaction.element === \"httpRequest\") {\n            if (transaction.attributes && transaction.attributes.method) {\n              // Read HTTP method\n              _http_method = (\n                transaction.attributes.method.content.toLowerCase()\n              );\n            }\n          }\n        });\n      }\n    });\n\n    return _http_method;\n  },\n\n  find_references_blueprint_restrictions : function(transition) {\n    var _restrictions = null;\n\n    // Iterate on each 'copy' text line found\n    transition.content.forEach(function(entry) {\n      if (_restrictions === null && entry.element === \"httpTransaction\") {\n        entry.content.forEach(function(transaction) {\n          if (_restrictions === null && transaction.element === \"httpRequest\") {\n            transaction.content.forEach(function(sub_entry) {\n              if (sub_entry.element === \"copy\") {\n                sub_entry.content.split(\"\\n\").forEach(function(line) {\n                  var _match = line.trim().match(/^\\+ (Tiers|Scopes):(.+)$/);\n\n                  if (_match && _match[1] && _match[2]) {\n                    var _key = _match[1].toLowerCase();\n\n                    // Initialize restrictions map or key array (if needed)\n                    _restrictions       = (_restrictions       || {});\n                    _restrictions[_key] = (_restrictions[_key] || []);\n\n                    // Iterate on parts sub-matches\n                    var _part_regex = /(?:^| )`([^`]+)`/g,\n                        _part_match;\n\n                    while (_part_match = _part_regex.exec(_match[2])) {\n                      // Assign found restriction type and values\n                      _restrictions[_key].push(\n                        _part_match[1].trim()\n                      );\n                    }\n                  }\n                });\n              }\n            });\n          }\n        });\n      }\n    });\n\n    return (_restrictions || {});\n  },\n\n  find_references_blueprint_url_parts : function(transition) {\n    // Notice: this is a simple and (relatively) hacky way to parse URL \\\n    //   parts into tokens, w/o using more complex yet complete ways \\\n    //   eg. backtracking. This does the job in our use-case.\n    var _url_parts = [];\n\n    if (transition.attributes && transition.attributes.href) {\n      var _href         = (transition.attributes.href.content || \"\"),\n          _current_part = null;\n\n      for (var _i = 0; _i < _href.length; _i++) {\n        var _slice = _href[_i];\n\n        // Initialize current part?\n        var _was_initiated = false;\n\n        if (_current_part === null) {\n          _current_part  = {\n            type  : ((_slice === \"{\") ? \"parameter\" : \"segment\"),\n            value : \"\"\n          };\n\n          _was_initiated = true;\n        }\n\n        // Push current character? (ignore '{' and '}' enclosure characters)\n        if (_slice !== \"{\" && _slice !== \"}\") {\n          _current_part.value += _slice;\n        }\n\n        // Push current part? (closure or opener character detected, or \\\n        //   end-of-string, or next character opens a different enclosed \\\n        //   type)\n        if (_current_part !== null && ((_i === (_href.length - 1))   ||\n              ((_slice === \"{\" || _slice === \"}\" || _slice === \"/\")  &&\n                (_was_initiated !== true || _href[_i + 1] === \"{\")))) {\n          var _first_slice = _current_part.value[0];\n\n          // Ignore argument parameters\n          if (_first_slice !== \"?\" && _first_slice !== \"&\") {\n            _url_parts.push(_current_part);\n          }\n\n          _current_part = null;\n        }\n      }\n    }\n\n    return _url_parts;\n  },\n\n  find_references_blueprint_flows : function(transition) {\n    var _self = this;\n\n    var _flows_map = {};\n\n    transition.content.forEach(function(entry) {\n      if (entry.element === \"httpTransaction\") {\n        // Map entry contents as a direct-access map\n        var _entry_contents = {};\n\n        entry.content.forEach(function(sub_entry) {\n          if (sub_entry.element.startsWith(\"http\") === true) {\n            _entry_contents[sub_entry.element] = sub_entry;\n          }\n        });\n\n        if (_entry_contents.httpRequest && _entry_contents.httpResponse) {\n          var _request_name = (\n            ((_entry_contents.httpRequest.meta || {}).title || {}).content || \"?\"\n          );\n\n          var _request_id   = slug(_request_name);\n\n          // Assign first flow request data? (if not already set)\n          if (!_flows_map[_request_id]) {\n            var _request_params = (\n              _self.parse_references_blueprint_flow_direction(\n                _request_name, _entry_contents.httpRequest\n              )\n            );\n\n            // Assign new flow data\n            _flows_map[_request_id] = {\n              request   : _request_params,\n              responses : []\n            };\n          }\n\n          // Assign current flow response data\n          var _http_status_name = (\n            _entry_contents.httpResponse.attributes.statusCode.content\n          );\n\n          // Attempt to resolve reason text from status code?\n          if (!isNaN(_http_status_name)) {\n            // Notice: 'http_status_codes.getReasonPhrase()' throws if it \\\n            //   cannot find the reason for a status code, therefore we need \\\n            //   to wrap this in a try/catch block.\n            try {\n              var _http_status_reason = http_status_codes.getReasonPhrase(\n                parseInt(_http_status_name, 10)\n              );\n\n              if (_http_status_reason) {\n                _http_status_name += (\" \" + _http_status_reason);\n              }\n            } catch (error) {\n              // Ignore error (this will skip appending the reason to the \\\n              //   status name)\n            }\n          }\n\n          var _response_params = (\n            _self.parse_references_blueprint_flow_direction(\n              _http_status_name, _entry_contents.httpResponse\n            )\n          );\n\n          _flows_map[_request_id].responses.push(_response_params);\n        }\n      }\n    });\n\n    return _flows_map;\n  },\n\n  parse_references_blueprint_flow_direction : function(name, entry) {\n    // Look for direction content type and data\n    var _direction_type = null,\n        _direction_data = null;\n\n    entry.content.forEach(function(direction_entry) {\n      if (!_direction_type && direction_entry.element === \"asset\"  &&\n            (direction_entry.meta.classes.content[0].content  ===\n              \"messageBody\")) {\n        // Read type\n        // Notice: if type is a MIME? Extract last chunk\n        _direction_type = direction_entry.attributes.contentType.content;\n\n        if (_direction_type.includes(\"/\") === true) {\n          var _type_chunks = _direction_type.split(\"/\");\n\n          _direction_type    = (\n            (_type_chunks[_type_chunks.length - 1] || \"\").toUpperCase()\n          );\n        }\n\n        // Read data\n        _direction_data = (direction_entry.content || \"\").trim();\n\n        // Collapse tabulations from 4 spaces to 2 spaces (this improves \\\n        //   legibility)\n        _direction_data = _direction_data.replace(/^( +)\\1/gm, \"$1\");\n      }\n    });\n\n    return {\n      name : name,\n      type : (_direction_type || null),\n      data : (_direction_data || null)\n    };\n  }\n};\n"
  },
  {
    "path": "res/plugins/marked/extensions/embed.js",
    "content": "/*\n * chappe\n *\n * Copyright 2021, Crisp IM SAS\n * Author: Valerian Saliou <valerian@valeriansaliou.name>\n */\n\n\nvar _s  = require(\"escape-html\");\n\n\n// Format: `${type}[title](target)`\n\nmodule.exports = {\n  name  : \"embed\",\n  level : \"inline\",\n\n  start : function(source) {\n    var _match = source.match(\n      /^\\${(?:[^{}]+)}\\[(?:[^\\[\\]]*)\\]\\((?:[^\\(\\)]+)\\)/\n    );\n\n    if (_match) {\n      return _match.index;\n    }\n  },\n\n  tokenizer : function(source) {\n    var _match = /^\\${([^{}]+)}\\[([^\\[\\]]*)\\]\\(([^\\(\\)]+)\\)$/.exec(source);\n\n    if (_match) {\n      return {\n        type     : \"embed\",\n        raw      : _match[0],\n        injector : _match[1],\n        title    : _match[2],\n        target   : _match[3]\n      };\n    }\n  },\n\n  renderer : function(token) {\n    // Generate preview code\n    var _preview_url;\n\n    switch (token.injector) {\n      case \"youtube\": {\n        // Generate YouTube preview image URL\n        _preview_url = (\n          \"https://img.youtube.com/vi/\" + token.target + \"/maxresdefault.jpg\"\n        );\n\n        break;\n      }\n\n      default: {\n        throw new Error(\n          \"Unsupported embed injector: \" + token.injector\n        );\n      }\n    }\n\n    // Generate caption code\n    var _caption_code = (\n      !token.title ? \"\" : (\n        \"<div class=\\\"embed-caption\\\">\" + _s(token.title) + \"</div>\"\n      )\n    );\n\n    // Generate final embed code\n    return (\n      \"<div \"                                                              +\n          \"class=\\\"embed\\\" \"                                               +\n          \"data-injector=\\\"\" + token.injector + \"\\\" \"                      +\n          \"data-target=\\\"\" + token.target + \"\\\" \"                          +\n          \"data-loaded=\\\"false\\\">\"                                         +\n        \"<div class=\\\"embed-wrap\\\">\"                                       +\n          \"<div class=\\\"embed-frame\\\"></div>\"                              +\n\n          \"<div \"                                                          +\n              \"class=\\\"embed-preview\\\" \"                                   +\n              \"style=\\\"background-image: url('\" + _preview_url + \"');\\\">\"  +\n          \"</div>\"                                                         +\n        \"</div>\"                                                           +\n\n        _caption_code                                                      +\n      \"</div>\"\n    );\n  }\n};\n"
  },
  {
    "path": "res/plugins/marked/extensions/emphasis.js",
    "content": "/*\n * chappe\n *\n * Copyright 2021, Crisp IM SAS\n * Author: Valerian Saliou <valerian@valeriansaliou.name>\n */\n\n// Format: `!!! text`, `!! text` or `! text`\n\nmodule.exports = {\n  name  : \"emphasis\",\n  level : \"block\",\n\n  start : function(source) {\n    var _match = source.match(/^(?:[!]{1,3})(?:[ ]{1,})(?:[^\\n]*)/);\n\n    if (_match) {\n      return _match.index;\n    }\n  },\n\n  tokenizer : function(source) {\n    var _match = /^([!]{1,3})(?:[ ]{1,})([^\\n]+)/.exec(source);\n\n    if (_match) {\n      // Parse level\n      var _level;\n\n      switch (_match[1].trim()) {\n        case \"!!!\": {\n          _level = \"warning\";\n\n          break;\n        }\n\n        case \"!!\": {\n          _level = \"info\";\n\n          break;\n        }\n\n        default: {\n          _level = \"notice\";\n        }\n      }\n\n      return {\n        type  : \"emphasis\",\n        raw   : _match[0],\n        level : _level,\n        text  : this.lexer.inlineTokens(_match[2].trim())\n      };\n    }\n  },\n\n  renderer : function(token) {\n    return (\n      \"<div class=\\\"emphasis emphasis--\" + token.level + \"\\\">\"  +\n        this.parser.parseInline(token.text)                     +\n      \"</div>\"\n    );\n  }\n};\n"
  },
  {
    "path": "res/plugins/marked/extensions/figcaption.js",
    "content": "/*\n * chappe\n *\n * Copyright 2021, Crisp IM SAS\n * Author: Valerian Saliou <valerian@valeriansaliou.name>\n */\n\n\nvar _s  = require(\"escape-html\");\n\n\n// Format: `$[caption](<code>)`\n\nmodule.exports = {\n  name  : \"figcaption\",\n  level : \"block\",\n\n  start : function(source) {\n    var _match = source.match(/^(?:\\$\\[[^\\[\\]\\n]+\\]\\([^\\n]+)\\)/);\n\n    if (_match) {\n      return _match.index;\n    }\n  },\n\n  tokenizer : function(source) {\n    var _match = /^\\$\\[([^\\[\\]\\n]+)\\]\\(([^\\n]+)\\)/.exec(source);\n\n    if (_match) {\n      return {\n        type    : \"figcaption\",\n        raw     : _match[0],\n        caption : _match[1],\n        code    : this.lexer.inlineTokens(_match[2].trim())\n      };\n    }\n  },\n\n  renderer : function(token) {\n    return (\n      \"<figure>\"                                              +\n        this.parser.parseInline(token.code)                   +\n        \"<figcaption>\" + _s(token.caption) + \"</figcaption>\"  +\n      \"</figure>\"\n    );\n  }\n};\n"
  },
  {
    "path": "res/plugins/marked/extensions/navigation-item.js",
    "content": "/*\n * chappe\n *\n * Copyright 2021, Crisp IM SAS\n * Author: Valerian Saliou <valerian@valeriansaliou.name>\n */\n\n\nvar _s  = require(\"escape-html\");\n\n\n// Format: `* Title: Description -> URL` for child\n\nvar RULE = (\n  /^(?:[ ]*\\|[ ]?([^:\\n]+):[ ]?([^:\\n]+)[ ]?->[ ]([^>\\n\\[\\]]+)(?: \\[(blank|self)\\])?(?:\\n|$))/\n);\n\nmodule.exports = {\n  name  : \"navigation-item\",\n  level : \"inline\",\n\n  start : function(source) {\n    var _match = source.match(RULE);\n\n    if (_match) {\n      return _match.index;\n    }\n  },\n\n  tokenizer : function(source) {\n    var _match = RULE.exec(source);\n\n    if (_match) {\n      return {\n        type        : \"navigation-item\",\n        raw         : _match[0],\n        title       : _match[1].trim(),\n        description : _match[2].trim(),\n        url         : _match[3].trim(),\n        target      : (_match[4] || \"self\").trim()\n      };\n    }\n  },\n\n  renderer : function(token) {\n    return (\n      \"<li class=\\\"navigation-item\\\">\"                                        +\n        \"<a \"                                                                 +\n            \"class=\\\"navigation-link\\\" \"                                      +\n            \"href=\\\"\" + _s(token.url) + \"\\\" \"                                 +\n            \"target=\\\"_\" + _s(token.target) + \"\\\">\"                           +\n          \"<span class=\\\"navigation-text\\\">\"                                  +\n            \"<span class=\\\"navigation-title font-sans-semibold\\\">\"            +\n              _s(token.title)                                                 +\n            \"</span>\"                                                         +\n\n            \"<span class=\\\"navigation-label font-sans-regular\\\">\"             +\n              _s(token.description)                                           +\n            \"</span>\"                                                         +\n          \"</span>\"                                                           +\n\n          \"<span class=\\\"navigation-action font-sans-semibold\\\">Read</span>\"  +\n        \"</a>\"                                                                +\n      \"</li>\"\n    );\n  }\n};\n"
  },
  {
    "path": "res/plugins/marked/extensions/navigation.js",
    "content": "/*\n * chappe\n *\n * Copyright 2021, Crisp IM SAS\n * Author: Valerian Saliou <valerian@valeriansaliou.name>\n */\n\n// Format: `+ Navigation` for parent\n\nvar RULE = (\n  /^(?:\\+ Navigation[ ]*\\n{1,2})((?:(?:[ ]*\\|[ ]?(?:[^:\\n]+):[ ]?(?:[^:\\n]+)[ ]?->[ ](?:[^>\\n]+))(?:\\n|$))+)/\n);\n\nmodule.exports = {\n  name  : \"navigation\",\n  level : \"block\",\n\n  start : function(source) {\n    var _match = source.match(RULE);\n\n    if (_match) {\n      return _match.index;\n    }\n  },\n\n  tokenizer : function(source) {\n    var _match = RULE.exec(source);\n\n    if (_match) {\n      var _token = {\n        type   : \"navigation\",\n        raw    : _match[0],\n        text   : _match[1],\n        tokens : []\n      };\n\n      this.lexer.inline(\n        _token.text, _token.tokens\n      );\n\n      return _token;\n    }\n  },\n\n  renderer : function(token) {\n    return (\n      \"<ul class=\\\"navigation\\\">\"              +\n        this.parser.parseInline(token.tokens)  +\n      \"</ul>\"\n    );\n  }\n};\n"
  },
  {
    "path": "res/plugins/marked/renderers/code.js",
    "content": "/*\n * chappe\n *\n * Copyright 2022, Crisp IM SAS\n * Author: Valerian Saliou <valerian@valeriansaliou.name>\n */\n\n\nvar _s  = require(\"escape-html\");\n\n\nmodule.exports = function(code, infostring, escaped) {\n  // Acquire language name and its associated class (if any)\n  var _language = (infostring || \"\").trim();\n\n  var _class    = (\n    _language ? (this.options.langPrefix + _s(_language)) : null\n  );\n\n  return (\n    \"<pre\" + (code ? \" class=\\\"copy\\\"\" : \"\") + \">\"                         +\n      \"<span class=\\\"code-clipboard copy-button\\\"></span>\"                 +\n\n      \"<code class=\\\"copy-value\" + (_class ? (\" \" + _class) : \"\") + \"\\\">\"  +\n        (escaped ? code : _s(code))                                        +\n      \"</code>\"                                                            +\n    \"</pre>\"                                                               +\n    \"\\n\"\n  );\n};\n"
  },
  {
    "path": "res/plugins/marked/renderers/heading.js",
    "content": "/*\n * chappe\n *\n * Copyright 2021, Crisp IM SAS\n * Author: Valerian Saliou <valerian@valeriansaliou.name>\n */\n\n\nvar slug  = require(\"slug\");\n\n\nmodule.exports = function(text, level) {\n  var _id = slug(text);\n\n  return (\n    \"<h\" + level + \" id=\\\"\" + _id + \"\\\">\"                 +\n      \"<a class=\\\"title-anchor\\\" href=\\\"#\" + _id + \"\\\">\"  +\n        text                                              +\n      \"</a>\"                                              +\n    \"</h\" + level + \">\"\n  );\n};\n"
  },
  {
    "path": "src/javascripts/common/common.js",
    "content": "/*\n * chappe\n *\n * Copyright 2021, Crisp IM SAS\n * Author: Valerian Saliou <valerian@valeriansaliou.name>\n */\n\n\n/**\n * Common\n * @class\n * @classdesc Common class.\n */\nclass Common {\n  /**\n   * Constructor\n   */\n  constructor() {\n    let fn = \"constructor\";\n\n    try {\n      this.ns = \"Common\";\n      this._$ = $;\n\n      // Selectors\n      this._document_sel = this._$(document);\n\n      // Configuration\n      this.__revision                    = \"@:revision\";\n      this.__url_status                  = \"@:url_status\";\n\n      this.__search_index_path           = \"@:search_index\";\n      this.__search_index_options_base   = \"@:search_options_base\";\n      this.__search_index_options_query  = \"@:search_options_query\";\n\n      this.__second_in_milliseconds      = 1000;   // 1 second\n\n      this.__copy_state_confirm_delay    = 2000;   // 2 seconds\n\n      this.__content_anchor_viewed_delay = 100;    // 1/10 second\n\n      this.__status_poll_interval        = 5000;   // 5 seconds\n      this.__status_poll_refresh         = 90000;  // 90 seconds\n\n      this.__chatbox_z_index             = 120;\n      this.__search_results_limit        = 12;\n\n      this.__cookie_prefix               = \"chappe/\";\n\n      this.__status_known_health         = [\n        \"healthy\",\n        \"sick\",\n        \"dead\"\n      ];\n\n      // Instances\n      this.__escape_html_text_rules = {\n        \"&amp;\"  : /&/g,\n        \"&lt;\"   : /</g,\n        \"&gt;\"   : />/g,\n        \"&quot;\" : /\"/g\n      };\n\n      // Storage\n      this.__crisp_chat_feedback_shown     = false;\n      this.__search_opened                 = false;\n      this.__appearance_mode               = \"light\";\n\n      this.__content_anchor_viewed_timeout = null;\n      this.__status_poll_scheduler         = null;\n\n      this.__search_index_responder        = null;\n      this.__search_index_load_pending     = null;\n\n      this.__search_field_value_last       = \"\";\n\n      this.__status_poll_health            = null;\n      this.__status_poll_last_check        = 0;\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Initializes the class\n   * @public\n   * @return {undefined}\n   */\n  init() {\n    let fn = \"init\";\n\n    try {\n      this.__chatbox();\n      this.__options();\n      this.__events();\n      this.__schedules();\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Configures the chatbox\n   * @private\n   * @return {undefined}\n   */\n  __chatbox() {\n    let fn = \"__chatbox\";\n\n    try {\n      // Adjust chatbox z-index? (if chatbox is included)\n      if (typeof window.$crisp !== \"undefined\") {\n        window.$crisp.push([\n          \"config\", \"container:index\", this.__chatbox_z_index\n        ]);\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Setups all user options\n   * @private\n   * @return {undefined}\n   */\n  __options() {\n    let fn = \"__options\";\n\n    try {\n      // Restore appearance options\n      this.__toggle_appearance(\n        this.__detect_appearance_preference()  //-[mode]\n      );\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds all events\n   * @private\n   * @return {undefined}\n   */\n  __events() {\n    let fn = \"__events\";\n\n    try {\n      // Bind common events\n      this.__bind_copy_click();\n\n      // Bind search events\n      this.__bind_search_open_keydown();\n      this.__bind_search_open_click();\n      this.__bind_search_close_click();\n      this.__bind_search_field_keyup();\n\n      // Bind appearance events\n      this.__bind_appearance_toggle_click();\n\n      // Bind sidebar events\n      this.__bind_sidebar_toggler_click();\n      this.__bind_sidebar_nest_level_toggle_click();\n\n      // Bind content events\n      this.__bind_content_anchor_viewed();\n\n      // Bind code events\n      this.__bind_code_metas_picker_change();\n      this.__bind_code_block_viewed();\n\n      // Bind Markdown events\n      this.__bind_markdown_embed_click();\n\n      // Bind chatbox events\n      this.__bind_crisp_chat_open_click();\n      this.__bind_crisp_chat_feedback_click();\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds all schedules\n   * @private\n   * @return {undefined}\n   */\n  __schedules() {\n    let fn = \"__schedules\";\n\n    try {\n      // Bind all schedules\n      this.__bind_status_poll_schedule();\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Reads cookie\n   * @private\n   * @param  {string} cookie_key\n   * @return {string} Cookie value (if any)\n   */\n  __read_cookie(cookie_key) {\n    let fn = \"__read_cookie\";\n\n    let _cookie_value;\n\n    try {\n      _cookie_value = Cookies.get(\n        (this.__cookie_prefix + cookie_key)\n      );\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    } finally {\n      return _cookie_value;\n    }\n  }\n\n\n  /**\n   * Writes cookie\n   * @private\n   * @param  {string} cookie_key\n   * @param  {string} cookie_value\n   * @return {undefined}\n   */\n  __write_cookie(cookie_key, cookie_value) {\n    let fn = \"__write_cookie\";\n\n    try {\n      Cookies.set(\n        (this.__cookie_prefix + cookie_key), cookie_value,\n\n        {\n          domain   : location.hostname,\n          expires  : Infinity,\n          sameSite : \"strict\"\n        }\n      );\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Copies text (from element)\n   * @private\n   * @param  {object} text_el\n   * @return {undefined}\n   */\n  __copy_text(text_el) {\n    let fn = \"__copy_text\";\n\n    let _was_copied = false;\n\n    try {\n      // Select text (clear out existing selections first)\n      let _range = document.createRange();\n\n      _range.selectNode(text_el);\n\n      window.getSelection().removeAllRanges();\n      window.getSelection().addRange(_range);\n\n      // Copy text\n      document.execCommand(\"copy\");\n\n      window.getSelection().removeAllRanges();\n\n      _was_copied = true;\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    } finally {\n      return _was_copied;\n    }\n  }\n\n\n  /**\n   * Escapes text (to be included in HTML)\n   * @private\n   * @param  {string} text_raw\n   * @return {string} Escaped text\n   */\n  __escape_html_text(text_raw) {\n    let fn = \"__escape_html_text\";\n\n    let _text_safe = \"\";\n\n    try {\n      let _text_buffer = text_raw;\n\n      // Apply all escape rules to text\n      for (let _value in this.__escape_html_text_rules) {\n        let _rule = this.__escape_html_text_rules[_value];\n\n        _text_buffer = _text_buffer.replace(_rule, _value);\n      }\n\n      _text_safe = _text_buffer;\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    } finally {\n      return _text_safe;\n    }\n  }\n\n\n  /**\n   * Opens up search\n   * @private\n   * @return {undefined}\n   */\n  __open_search() {\n    let fn = \"__open_search\";\n\n    try {\n      if (this.__search_opened !== true) {\n        this.__search_opened = true;\n\n        // Open search\n        let _search_sel = this._$(\"#search\"),\n            _input_sel  = _search_sel.find(\".spotlight-input\");\n\n        _search_sel.css(\"display\", \"block\");\n\n        // Focus on search input?\n        if (_input_sel.length > 0) {\n          _input_sel[0].focus();\n        }\n\n        // Ensure that search index is loaded? (if not already loaded, or not \\\n        //   already loading)\n        this.__ensure_load_search_index(_search_sel);\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Closes search\n   * @private\n   * @param  {boolean} [do_reset]\n   * @return {undefined}\n   */\n  __close_search(do_reset=false) {\n    let fn = \"__close_search\";\n\n    try {\n      if (this.__search_opened === true) {\n        this.__search_opened = false;\n\n        // Close search\n        let _search_sel = this._$(\"#search\");\n\n        _search_sel.css(\"display\", \"none\");\n\n        // Reset search value and results?\n        if (do_reset === true) {\n          let _spotlight_sel = _search_sel.find(\".spotlight\"),\n              _input_sel     = _spotlight_sel.find(\".spotlight-input\"),\n              _entries_sel   = _spotlight_sel.find(\".spotlight-entries\");\n\n          // Reset input value\n          this.__search_field_value_last = \"\";\n\n          _input_sel.val(\"\");\n\n          // Reset search results\n          _entries_sel.empty();\n\n          _spotlight_sel.attr(\"data-has-results\", \"false\");\n        }\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Toggles selected search result\n   * @private\n   * @param  {object} results_all_sel\n   * @param  {number} [increment]\n   * @return {undefined}\n   */\n  __toggle_search_result_selected(results_all_sel, increment=1) {\n    let fn = \"__toggle_search_result_selected\";\n\n    try {\n      // Any result?\n      if (results_all_sel.length > 0) {\n        // Find the index of the active result\n        let _index = -1;\n\n        for (let _i = 0; _i < results_all_sel.length; _i++) {\n          if (results_all_sel[_i].getAttribute(\"data-selected\") === \"true\") {\n            _index = _i;\n\n            break;\n          }\n        }\n\n        // Process the index of the next element to activate\n        _index += increment;\n\n        if (_index < 0) {\n          _index = (results_all_sel.length - 1);\n        } else if (_index >= results_all_sel.length) {\n          _index = 0;\n        }\n\n        // Select target element?\n        if (_index > -1) {\n          let _result_selected_el = results_all_sel[_index];\n\n          // Set selected state to selected element\n          results_all_sel.removeAttr(\"data-selected\");\n\n          this._$(_result_selected_el).attr(\"data-selected\", \"true\");\n\n          // Scroll to selected element\n          _result_selected_el.scrollIntoView({\n            behavior : \"smooth\",\n            block    : \"nearest\"\n          });\n        }\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Proceeds search query\n   * @private\n   * @param  {object} spotlight_sel\n   * @param  {object} results_sel\n   * @param  {object} entries_sel\n   * @param  {string} [search_query]\n   * @return {undefined}\n   */\n  __proceed_search_query(\n    spotlight_sel, results_sel, entries_sel, search_query=\"\"\n  ) {\n    let fn = \"__proceed_search_query\";\n\n    try {\n      // Initialize 'has results' state\n      let _has_results = false;\n\n      // Acquire search results\n      if (search_query) {\n        // Responder is not available? Throw an error, as this should \\\n        //   never happen.\n        if (this.__search_index_responder === null) {\n          throw new Error(\n            \"Search index responder is not available (yet?)\"\n          );\n        }\n\n        // Obtain search results from index responder\n        let _search_results = (\n          this.__search_index_responder.search(search_query)\n        );\n\n        if (_search_results.length > 0) {\n          _has_results = true;\n\n          // Limit results to N first results\n          if (_search_results.length > this.__search_results_limit) {\n            _search_results = _search_results.slice(\n              0, this.__search_results_limit\n            );\n          }\n\n          // Inject search results\n          this.__inject_search_results(\n            results_sel, entries_sel, _search_results\n          );\n        }\n      }\n\n      // Mark as having results (or not)\n      spotlight_sel.attr(\n        \"data-has-results\", ((_has_results === true) ? \"true\" : \"false\")\n      );\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Ensures that search index is loaded\n   * @private\n   * @param  {object} search_sel\n   * @return {undefined}\n   */\n  __ensure_load_search_index(search_sel) {\n    let fn = \"__ensure_load_search_index\";\n\n    try {\n      // Not already loaded, and load not already pending?\n      if (this.__search_index_responder === null  &&\n            this.__search_index_load_pending === null) {\n        // Select spotlight element\n        let _spotlight_sel = search_sel.find(\".spotlight\");\n\n        // Mark as pending (initialize pending operations stack)\n        this.__search_index_load_pending = [];\n\n        // Reset any previously-set error state\n        _spotlight_sel.removeAttr(\"data-index-error\");\n\n        // Load index data from network\n        fetch(\n          `/static/data/${this.__search_index_path}?${this.__revision}`,\n\n          {\n            mode : \"same-origin\"\n          }\n        )\n          .then((response) => {\n            // Non-success response?\n            if (response.status >= 400) {\n              return Promise.reject(null);\n            }\n\n            // Examine the text in the response\n            return response.text();\n          })\n          .then((index_data) => {\n            // Build search index responder (this parses from text JSON)\n            this.__search_index_responder = MiniSearch.loadJSON(\n              index_data,\n\n              Object.assign(\n                {},\n                this.__search_index_options_base,\n                this.__search_index_options_query\n              )\n            );\n\n            // Handle all pending operations\n            while (this.__search_index_load_pending.length > 0) {\n              this.__search_index_load_pending.shift()();\n            }\n\n            return Promise.resolve();\n          })\n          .catch(() => {\n            // Show index loading error\n            _spotlight_sel.attr(\"data-index-error\", \"true\");\n\n            // Pass-through (ignore for this session)\n            return Promise.resolve();\n          })\n          .then(() => {\n            // Hide load spinner (as all deferred actions have been fired, \\\n            //   if index was successfully loaded)\n            _spotlight_sel.removeAttr(\"data-index-loading\");\n\n            // Mark as not pending anymore\n            this.__search_index_load_pending = null;\n          });\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Toggles sidebar nest level\n   * @private\n   * @param  {object}  target_sel\n   * @param  {boolean} [is_anchor]\n   * @return {undefined}\n   */\n  __toggle_sidebar_nest_level(target_sel, is_anchor=false) {\n    let fn = \"__toggle_sidebar_nest_level\";\n\n    try {\n      let _parent_sel = (\n        target_sel.parents(\".nest-navigate-level\")\n      );\n\n      if (_parent_sel) {\n        let _new_state = (\n          (_parent_sel.attr(\"data-expanded\") === \"true\") ? \"false\" : \"true\"\n        );\n\n        // Unexpand all other expanded levels first? (only if new state is \\\n        //   expanded)\n        // Notice #1: we do not want to allow multiple levels to be expanded \\\n        //   at the same time, as this can consume quite a large amount of \\\n        //   browser height and clutter the UI.\n        // Notice #2: only when click originates from a viewed anchor.\n        if (_new_state === \"true\" && is_anchor === true) {\n          let _expanded_all_sel = this._$(\n            \"#content .sidebar .nest .nest-navigate-level\"  +\n              \"[data-expanded=\\\"true\\\"]\"\n          );\n\n          _expanded_all_sel.attr(\"data-expanded\", \"false\");\n        }\n\n        // Toggle expanded state on selected level\n        _parent_sel.attr(\"data-expanded\", _new_state);\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Toggles appearance mode\n   * @private\n   * @param  {string} [new_mode]\n   * @return {undefined}\n   */\n  __toggle_appearance(new_mode=null) {\n    let fn = \"__toggle_appearance\";\n\n    try {\n      if (new_mode !== null && this.__appearance_mode !== new_mode) {\n        // Store new appearance mode (now current mode)\n        this.__appearance_mode = new_mode;\n\n        // Update current appearance mode (in toggle)\n        this._$(\"#header .coloring\").attr(\"data-mode\", new_mode);\n\n        // Update dark mode in document\n        document.body.setAttribute(\"data-appearance\", new_mode);\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Detects appearance preference\n   * @private\n   * @return {string} Detected appearance preference\n   */\n  __detect_appearance_preference() {\n    let fn = \"__detect_appearance_preference\";\n\n    let _mode = null;\n\n    try {\n      // Attempt to detect mode from cookies? (user override)\n      let _mode_cookies = (\n        this.__read_cookie(\"appearance-mode\") || null\n      );\n\n      if (_mode_cookies !== null) {\n        _mode = _mode_cookies;\n\n        // Abort detection there.\n        return;\n      }\n\n      // Attempt to detect mode from media query? (operating system)\n      if (typeof window.matchMedia === \"function\"  &&\n            (window.matchMedia(\"(prefers-color-scheme: dark)\").matches  ===\n              true)) {\n        _mode = \"dark\";\n\n        // Abort detection there.\n        return;\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    } finally {\n      return (_mode || \"light\");\n    }\n  }\n\n\n  /**\n   * Refreshes code metas picker section\n   * @private\n   * @param  {string} direction\n   * @param  {object} code_box_sel\n   * @param  {object} section_sel\n   * @param  {string} selected_value\n   * @param  {object} code_chunk\n   * @return {undefined}\n   */\n  __refresh_code_metas_picker_section(\n    direction, code_box_sel, section_sel, selected_value, code_chunk\n  ) {\n    let fn = \"__refresh_code_metas_picker_section\";\n\n    try {\n      if (section_sel.length > 0 && selected_value) {\n        // Acquire selectors\n        let _type_sel    = section_sel.find(\".code-meta-type\"),\n            _content_sel = section_sel.find(\".code-content\");\n\n        if (_type_sel.length > 0 && _content_sel.length > 0) {\n          // Update code type\n          _type_sel.text(code_chunk.type);\n\n          // Inject code block?\n          if (code_chunk.data) {\n            // Generate the list of classes on code element\n            let _class_list = [];\n\n            if (code_chunk.type) {\n              _class_list.push(`language-${code_chunk.type.toLowerCase()}`);\n            }\n            if (direction === \"request\") {\n              _class_list.push(\"copy-value\");\n            }\n\n            let _code_class_spaced = (\n              (_class_list.length === 0) ? \"\" :\n                ` class=\"${_class_list.join(\" \")}\"`\n            );\n\n            _content_sel.html(\n              `<code${_code_class_spaced}></code>`\n            );\n\n            // Select newly-injected code block\n            let _code_block_sel = _content_sel.find(\"code\");\n\n            if (_code_block_sel.length > 0) {\n              // Update code content\n              _code_block_sel.text(code_chunk.data);\n\n              // Re-apply code coloring\n              _code_block_sel[0].setAttribute(\"data-viewed\", \"\");\n\n              window.Prism.highlightElement(_code_block_sel[0]);\n            }\n          } else {\n            // Replace w/ empty placeholder\n            _content_sel.html(\"—\");\n          }\n\n          // Update 'has request' marker? (only if direction is 'request')\n          if (direction === \"request\") {\n            code_box_sel.attr(\n              \"data-has-request\", (code_chunk.data ? \"true\" : \"false\")\n            );\n          }\n        }\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Schedules the handling of content anchor viewed event\n   * @private\n   * @param  {object} link_all_sel\n   * @param  {object} sidebar_left_sel\n   * @param  {string} anchor_id\n   * @param  {number} [header_height]\n   * @return {undefined}\n   */\n  __schedule_handle_content_anchor_viewed(\n    link_all_sel, sidebar_left_sel, anchor_id, header_height=0\n  ) {\n    let fn = \"__schedule_handle_content_anchor_viewed\";\n\n    try {\n      // Cancel last scheduled update?\n      if (this.__content_anchor_viewed_timeout !== null) {\n        clearTimeout(this.__content_anchor_viewed_timeout);\n      }\n\n      // Schedule next update (defer)\n      this.__content_anchor_viewed_timeout = setTimeout(() => {\n        this.__content_anchor_viewed_timeout = null;\n\n        // Trigger content anchor viewed event handler\n        this.__handle_content_anchor_viewed(\n          link_all_sel, sidebar_left_sel, anchor_id, header_height\n        );\n      }, this.__content_anchor_viewed_delay);\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Handles content anchor viewed event\n   * @private\n   * @param  {object} link_all_sel\n   * @param  {object} sidebar_left_sel\n   * @param  {string} anchor_id\n   * @param  {number} [header_height]\n   * @return {undefined}\n   */\n  __handle_content_anchor_viewed(\n    link_all_sel, sidebar_left_sel, anchor_id, header_height=0\n  ) {\n    let fn = \"__handle_content_anchor_viewed\";\n\n    try {\n      let _anchor_active_el = (\n        (link_all_sel.filter(`[data-anchor=\"#${anchor_id}\"]`) || [])[0] || null\n      );\n\n      if (_anchor_active_el !== null) {\n        let _anchor_active_sel = this._$(_anchor_active_el);\n\n        // Swap active attributes to active anchor link\n        link_all_sel.removeAttr(\"data-active\");\n        _anchor_active_sel.attr(\"data-active\", \"true\");\n\n        // If active link belongs to a slice parent, then auto-expand this \\\n        //   slice\n        let _level_active_sel = (\n          _anchor_active_sel.parents(\".nest-navigate-level--first\").first()\n        );\n\n        if (_level_active_sel.length > 0  &&\n              _level_active_sel.attr(\"data-expanded\") !== \"true\") {\n          // Trigger a virtual click event to toggle visibility\n          let _toggle_active_sel = _level_active_sel.find(\n            \".nest-navigate-link--slice .nest-navigate-toggle\"\n          );\n\n          if (_toggle_active_sel.length > 0) {\n            // Toggle sidebar nest level\n            this.__toggle_sidebar_nest_level(\n              _toggle_active_sel,\n\n              true  //-[is_anchor]\n            );\n          }\n        }\n\n        // Ensure that active link is into view (otherwise, scroll to link)\n        // Important: do not use 'smooth' scrolling, as we need the scroll \\\n        //   position change to be instant.\n        _anchor_active_el.scrollIntoView({\n          behavior : \"auto\",\n          block    : \"nearest\"\n        });\n\n        // Element is hidden below header? Scroll a bit up once more (note \\\n        //   that this only applies when scrolling upwards)\n        let _bounding_box = _anchor_active_el.getBoundingClientRect(),\n            _bounding_top = Math.floor(_bounding_box.top || 0.0);\n\n        if (_bounding_top < header_height) {\n          sidebar_left_sel[0].scrollTop -= (header_height - _bounding_top);\n        }\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Handles code metas picker change event\n   * @private\n   * @param  {object} code_box_sel\n   * @return {undefined}\n   */\n  __handle_code_metas_picker_change(code_box_sel) {\n    let fn = \"__handle_code_metas_picker_change\";\n\n    try {\n      // Acquire code data\n      let _code_data = (\n        JSON.parse(code_box_sel.find(\".code-data\").val() || \"{}\")\n      );\n\n      // Select request and response targets\n      let _section_request_sel  = (\n        code_box_sel.find(\".code-section.code-section--request\")\n      );\n      let _section_response_sel = (\n        code_box_sel.find(\".code-section.code-section--response\")\n      );\n\n      // Acquire selected values\n      let _selected_request  = (\n        _section_request_sel.find(\".code-meta-picker select\").val()\n      );\n      let _selected_response = (\n        _section_response_sel.find(\".code-meta-picker select\").val()\n      );\n\n      // Acquire code chunk associated to selected request\n      let _code_chunk = (_code_data[_selected_request] || null);\n\n      // Re-compute code section for request?\n      if (_code_chunk !== null) {\n        this.__refresh_code_metas_picker_section(\n          \"request\", code_box_sel, _section_request_sel, _selected_request,\n            _code_chunk.request\n        );\n\n        // Acquire code chunk associated to selected response\n        let _code_response = (\n          _code_chunk.responses[parseInt(_selected_response, 10)] || null\n        );\n\n        // Re-compute code section for response?\n        if (_code_response !== null) {\n          this.__refresh_code_metas_picker_section(\n            \"response\", code_box_sel, _section_response_sel, _selected_response,\n              _code_response\n          );\n        }\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Handles code block viewed event\n   * @private\n   * @param  {object} target_sel\n   * @return {undefined}\n   */\n  __handle_code_block_viewed(target_sel) {\n    let fn = \"__handle_code_block_viewed\";\n\n    try {\n      // Load full code box parent element? (if any)\n      // Notice: only if it was not already previously loaded, also note that \\\n      //   this only applies to 'pre' element wrapped in a '.code' box \\\n      //   wrapper. Not all 'pre' are being wrapped as such, therefore this \\\n      //   is not necessary in all cases.\n      let _code_box_sel = (\n        target_sel.parents(\".code[data-was-loaded=\\\"false\\\"]\").first()\n      );\n\n      if (_code_box_sel.length > 0) {\n        _code_box_sel[0].setAttribute(\"data-was-loaded\", \"true\");\n\n        // Trigger the first code box picker 'change' event to load default \\\n        //   code into view.\n        this.__handle_code_metas_picker_change(_code_box_sel);\n      }\n\n      // Highlight code? Do not re-apply if already applied before.\n      let _code_el = (\n        target_sel.find(\"code[class*=\\\"language-\\\"]\")[0] || null\n      );\n\n      if (_code_el !== null  &&\n            _code_el.hasAttribute(\"data-viewed\") !== true) {\n        _code_el.setAttribute(\"data-viewed\", \"\");\n\n        window.Prism.highlightElement(_code_el);\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Handles Crisp chat open click event\n   * @private\n   * @return {boolean} False value (prevents default click event)\n   */\n  __handle_crisp_chat_open_click() {\n    let fn = \"__handle_crisp_chat_open_click\";\n\n    try {\n      if (typeof window.$crisp !== \"undefined\") {\n        window.$crisp.do(\"chat:open\");\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    } finally {\n      return false;\n    }\n  }\n\n\n  /**\n   * Handles Crisp chat feedback click event\n   * @private\n   * @return {boolean} False value (prevents default click event)\n   */\n  __handle_crisp_chat_feedback_click() {\n    let fn = \"__handle_crisp_chat_feedback_click\";\n\n    try {\n      if (typeof window.$crisp !== \"undefined\") {\n        // Open chatbox\n        window.$crisp.do(\"chat:open\");\n\n        // Show feedback message? (if not already shown in current session)\n        if (this.__crisp_chat_feedback_shown !== true) {\n          this.__crisp_chat_feedback_shown = true;\n\n          window.$crisp.push([\n            \"do\",\n            \"message:show\",\n\n            [\n              \"text\",\n\n              (\n                `Do you have feedback on this page? `                  +\n                `Please reply to this message with your comments! :)`  +\n                `\\n\\n`                                                 +\n                `➡️ ${document.location.href}`\n              )\n            ]\n          ]);\n        }\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    } finally {\n      return false;\n    }\n  }\n\n\n  /**\n   * Injects search results\n   * @private\n   * @param  {object} results_sel\n   * @param  {object} entries_sel\n   * @param  {object} results\n   * @return {undefined}\n   */\n  __inject_search_results(results_sel, entries_sel, results) {\n    let fn = \"__inject_search_results\";\n\n    try {\n      // Generate results HTML\n      let _results_html = \"\";\n\n      for (let _i = 0; _i < results.length; _i++) {\n        let _result = results[_i];\n\n        // Generate result path HTML\n        let _path_html   = \"\",\n            _path_chunks = _result.path.split(\" > \");\n\n        for (let _j = 0; _j < _path_chunks.length; _j++) {\n          _path_html += (\n            this.__escape_html_text(_path_chunks[_j])\n          );\n\n          // Append separator?\n          if (_path_chunks.length > 1 && _j < (_path_chunks.length - 1)) {\n            _path_html += (\n              \"<span class=\\\"spotlight-entry-separator\\\"></span>\"\n            );\n          }\n        }\n\n        // Acquire entry type\n        let _type = (\n          (_result.id.includes(\"#\") === true) ? \"anchor\" : \"page\"\n        );\n\n        // Append whole result HTML to results HTML\n        _results_html += (\n          `<li class=\"spotlight-entry spotlight-entry--${_type}\">`      +\n            `<a class=\"spotlight-entry-link\" href=\"${_result.id}\">`     +\n              `<span class=\"spotlight-entry-path font-sans-semibold\">`  +\n                _path_html                                              +\n              `</span>`                                                 +\n\n              `<h6 class=\"spotlight-entry-title font-sans-bold\">`       +\n                this.__escape_html_text(_result.title)                  +\n              `</h6>`                                                   +\n\n              (\n                _result.summary ? (\n                  `<p class=\"spotlight-entry-preview\">`       +\n                    this.__escape_html_text(_result.summary)  +\n                  `</p>`\n                ) : \"\"\n              )                                                         +\n            `</a>`                                                      +\n          `</li>`\n        );\n      }\n\n      // Inject generated HTML\n      entries_sel.html(_results_html);\n\n      // Select results\n      let _results_sel = entries_sel.find(\".spotlight-entry\");\n\n      // Select first result (if any)\n      _results_sel.first().attr(\"data-selected\", \"true\");\n\n      // Make sure that scroll position is restored to top of scroll area\n      if (results_sel.length > 0) {\n        results_sel[0].scrollTop = 0;\n      }\n\n      // Bind mouse enter + mouse leave events on new results\n      _results_sel.on(\"mouseenter\", (event) => {\n        try {\n          _results_sel.removeAttr(\"data-selected\");\n\n          this._$(event.target).attr(\"data-selected\", \"true\");\n        } catch (_error) {\n          Console.error(`${this.ns}.${fn}:mouseenter`, _error);\n        }\n      });\n\n      // Bind click event on new results (just to auto-hide search in case an \\\n      //   anchor gets clicked)\n      _results_sel.on(\"click\", () => {\n        try {\n          // Close search immediately\n          this.__close_search(\n            true  //-[do_reset]\n          );\n        } catch (_error) {\n          Console.error(`${this.ns}.${fn}:click`, _error);\n        }\n      });\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Fetches status health (based on provider)\n   * @private\n   * @param  {string} provider\n   * @param  {string} target\n   * @return {undefined}\n   */\n  __fetch_status_health(provider, target) {\n    switch (provider) {\n      case \"vigil\": {\n        // Vigil provider\n        return fetch(`${target}/status/text/`, {\n          mode : \"cors\"\n        })\n          .then((response) => {\n            // Non-success response?\n            if (response.status !== 200) {\n              return Promise.reject(null);\n            }\n\n            // Examine the text in the response\n            return response.text();\n          });\n      }\n\n      case \"crisp\": {\n        // Crisp Status provider\n        return fetch(`${target}/includes/report/`, {\n          mode : \"cors\"\n        })\n          .then((response) => {\n            // Non-success response?\n            if (response.status !== 200) {\n              return Promise.reject(null);\n            }\n\n            // Examine the JSON in the response\n            return response.json();\n          })\n          .then((report) => {\n            // Extract health from report\n            return Promise.resolve(report.health);\n          });\n      }\n\n      default: {\n        // Provider unknown, hard-fail\n        return Promise.reject(null);\n      }\n    }\n  }\n\n\n  /**\n   * Refreshes status indicator\n   * @private\n   * @param  {object} status_sel\n   * @param  {object} seconds_sel\n   * @return {undefined}\n   */\n  __refresh_status_indicator(status_sel, seconds_sel) {\n    let fn = \"__refresh_status_indicator\";\n\n    try {\n      // Refresh status\n      status_sel.attr(\n        \"data-status\", (this.__status_poll_health || \"none\")\n      );\n\n      // Refresh 'last checked since' seconds\n      seconds_sel.text(\n        `${(this.__status_poll_last_check || 1)}`\n      );\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds search open keydown event\n   * @private\n   * @return {undefined}\n   */\n  __bind_search_open_keydown() {\n    let fn = \"__bind_search_open_keydown\";\n\n    try {\n      const _KEY_CODE_K          = 75,\n            _KEY_CODE_ESCAPE     = 27,\n            _KEY_CODE_ENTER      = 13,\n            _KEY_CODE_ARROW_UP   = 38,\n            _KEY_CODE_ARROW_DOWN = 40;\n\n      window.addEventListener(\"keydown\", (event) => {\n        switch (event.keyCode) {\n          // 'K' pressed?\n          case _KEY_CODE_K: {\n            // Check if 'CTRL' is pressed (or 'COMMAND' on Mac computers)\n            let _has_control = (\n              ((navigator.platform || \"\").toLowerCase().indexOf(\"mac\") !== -1) ?\n                event.metaKey : event.ctrlKey\n            );\n\n            if (_has_control === true) {\n              if (this.__search_opened !== true) {\n                // Block 'CTRL + K' event default behavior (otherwise, browser \\\n                //   URL search prompt will be shown)\n                event.preventDefault();\n\n                // Open search\n                this.__open_search();\n              } else {\n                // Close search\n                // Notice: this will show the browser default URL search \\\n                //   prompt, letting the users press 'CTRL + K' twice quickly \\\n                //   if they want to perform a regular browser URL search.\n                this.__close_search();\n              }\n            }\n\n            break;\n          }\n\n          // 'ESCAPE' pressed?\n          case _KEY_CODE_ESCAPE: {\n            if (this.__search_opened === true) {\n              // Block event default behavior\n              event.preventDefault();\n\n              // Close search\n              this.__close_search();\n            }\n\n            break;\n          }\n\n          // 'ENTER' pressed?\n          case _KEY_CODE_ENTER: {\n            if (this.__search_opened === true) {\n              // Block event default behavior\n              event.preventDefault();\n\n              // Select selected result\n              let _result_selected_sel = this._$(\n                \"#search .spotlight-entry[data-selected=\\\"true\\\"] \"  +\n                  \".spotlight-entry-link\"\n              );\n\n              // Open selected search result?\n              if (_result_selected_sel.length > 0) {\n                let _target_url = _result_selected_sel.first().attr(\"href\");\n\n                if (_target_url) {\n                  // Close search immediately\n                  this.__close_search(\n                    true  //-[do_reset]\n                  );\n\n                  // Split hash part from target URL?\n                  let _target_url_parts = _target_url.split(\"#\");\n\n                  // Navigate to URL? (if different than current one)\n                  if (_target_url_parts[0] !== document.location.pathname) {\n                    // Navigate to new full URL\n                    document.location.href = _target_url;\n                  } else if (_target_url_parts[1]) {\n                    // Navigate to new hash (or same)\n                    document.location.hash = `#${_target_url_parts[1]}`;\n                  } else if ((document.location.hash || \"\").length > 1) {\n                    // Reset current hash\n                    document.location.hash = \"\";\n                  }\n                }\n              }\n            }\n\n            break;\n          }\n\n          // 'ARROW UP' or 'ARROW DOWN' pressed?\n          case _KEY_CODE_ARROW_UP:\n          case _KEY_CODE_ARROW_DOWN: {\n            if (this.__search_opened === true) {\n              // Block event default behavior\n              event.preventDefault();\n\n              // Select search results\n              let _results_all_sel = this._$(\"#search .spotlight-entry\");\n\n              // Compute increment\n              let _increment = (\n                (event.keyCode === _KEY_CODE_ARROW_DOWN) ? 1 : -1\n              );\n\n              // Select next or previous search result\n              this.__toggle_search_result_selected(\n                _results_all_sel, _increment\n              );\n            }\n\n            break;\n          }\n        }\n      });\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds search open click event\n   * @private\n   * @return {undefined}\n   */\n  __bind_search_open_click() {\n    let fn = \"__bind_search_open_click\";\n\n    try {\n      this._$(\"#header .search\").on(\n        \"click\", this.__open_search.bind(this)\n      );\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds search close click event\n   * @private\n   * @return {undefined}\n   */\n  __bind_search_close_click() {\n    let fn = \"__bind_search_close_click\";\n\n    try {\n      this._$(\"#search\").on(\"click\", (event) => {\n        try {\n          if (event.target) {\n            let _target_sel = this._$(event.target);\n\n            // Click away from spotlight box? Close search.\n            if (_target_sel.is(\"#search .spotlight\") === true) {\n              this.__close_search();\n            }\n          }\n        } catch (_error) {\n          Console.error(`${this.ns}.${fn}:click`, _error);\n        }\n      });\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds search field keyup event\n   * @private\n   * @return {undefined}\n   */\n  __bind_search_field_keyup() {\n    let fn = \"__bind_search_field_keyup\";\n\n    try {\n      // Select all targets\n      let _spotlight_sel = this._$(\"#search .spotlight\"),\n          _input_sel     = _spotlight_sel.find(\".spotlight-input\"),\n          _results_sel   = _spotlight_sel.find(\".spotlight-results\"),\n          _entries_sel   = _results_sel.find(\".spotlight-entries\");\n\n      // Initialize last search value\n      _input_sel.on(\"keyup\", (event) => {\n        try {\n          // Acquire normalized search query value\n          let _field_value = (\n            (event.target.value || \"\").trim().toLowerCase()\n          );\n\n          if (_field_value !== this.__search_field_value_last) {\n            // Retain last search value\n            this.__search_field_value_last = _field_value;\n\n            // Define proceed function\n            let _fnProceed = () => {\n              this.__proceed_search_query(\n                _spotlight_sel, _results_sel, _entries_sel, _field_value\n              );\n            };\n\n            // Proceed immediately? (pending stack is not defined)\n            if (this.__search_index_load_pending === null) {\n              // Proceed immediately\n              _fnProceed();\n            } else {\n              // Show load spinner (as search was deferred)\n              _spotlight_sel.attr(\"data-index-loading\", \"true\");\n\n              // Stack for later (note: reset stack, as we do not want any \\\n              //   previous operation from being processed, only the last one)\n              this.__search_index_load_pending = [_fnProceed];\n            }\n          }\n        } catch (_error) {\n          Console.error(`${this.ns}.${fn}:keyup`, _error);\n        }\n      });\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds copy click event\n   * @private\n   * @return {undefined}\n   */\n  __bind_copy_click() {\n    let fn = \"__bind_copy_click\";\n\n    try {\n      // Select all copy targets\n      let _click_all_sel = this._$(\".copy-button\");\n\n      // Bind all click events\n      _click_all_sel.each((click_el) => {\n        let _state_confirm_timeout = null;\n\n        // Notice: use 'addEventListener' instead of '.on' as this is ~500% \\\n        //   faster, which avoids blocking page rendering for ~10ms when there \\\n        //   are a lot of copy elements.\n        click_el.addEventListener(\"click\", () => {\n          try {\n            // Find click parent (from clicked basis)\n            let _click_parent_sel = this._$(click_el);\n\n            if (_click_parent_sel.is(\".copy\") === false) {\n              _click_parent_sel = _click_parent_sel.parents(\".copy\").first();\n            }\n\n            // Select clicked value\n            let _value_sel = _click_parent_sel.find(\".copy-value\");\n\n            if (_click_parent_sel.length > 0 && _value_sel.length > 0) {\n              // Select text and copy it\n              let _was_copied = this.__copy_text(_value_sel[0]);\n\n              // Change state to 'copied'?\n              if (_was_copied === true) {\n                // Clear out state clear timeout (if any)\n                if (_state_confirm_timeout !== null) {\n                  clearTimeout(_state_confirm_timeout);\n                }\n\n                // Change state to 'copied'\n                _click_parent_sel.attr(\"data-copy-state\", \"copied\");\n\n                // Schedule state clear timeout\n                _state_confirm_timeout = setTimeout(() => {\n                  _state_confirm_timeout = null;\n\n                  // Change state back to 'none'\n                  _click_parent_sel.attr(\"data-copy-state\", \"none\");\n                }, this.__copy_state_confirm_delay);\n              }\n            }\n          } catch (_error) {\n            Console.error(`${this.ns}.${fn}:click`, _error);\n          }\n        });\n      });\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds appearance toggle click event\n   * @private\n   * @return {undefined}\n   */\n  __bind_appearance_toggle_click() {\n    let fn = \"__bind_appearance_toggle_click\";\n\n    try {\n      this._$(\"#header .coloring\").on(\"click\", () => {\n        try {\n          // Acquire new appearance mode\n          let _new_mode = (\n            (this.__appearance_mode === \"light\") ? \"dark\" : \"light\"\n          );\n\n          // Toggle appearance mode\n          this.__toggle_appearance(_new_mode);\n\n          // Remember choice (with a cookie)\n          this.__write_cookie(\n            \"appearance-mode\", _new_mode\n          );\n        } catch (_error) {\n          Console.error(`${this.ns}.${fn}:click`, _error);\n        }\n      });\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds sidebar toggler click event\n   * @private\n   * @return {undefined}\n   */\n  __bind_sidebar_toggler_click() {\n    let fn = \"__bind_sidebar_toggler_click\";\n\n    try {\n      let _sidebar_left_sel = (\n        this._$(\"#content .sidebar.sidebar--left\")\n      );\n\n      if (_sidebar_left_sel.length > 0) {\n        // Bind sidebar toggler click events\n        let _sidebar_togglers_sel = this._$(\n          \"#content .sidebar-toggler, #content .sidebar-toggler-retract\"\n        );\n\n        _sidebar_togglers_sel.on(\"click\", () => {\n          try {\n            // Compute new sidebar visibility state\n            let _new_state = (\n              (_sidebar_left_sel.attr(\"data-visible\") === \"true\") ?\n                \"false\" : \"true\"\n            );\n\n            // Toggle visible state on sidebar\n            _sidebar_left_sel.attr(\"data-visible\", _new_state);\n          } catch (_error) {\n            Console.error(`${this.ns}.${fn}:click`, _error);\n          }\n        });\n\n        // Bind sidebar link click events (anchor links)\n        let _sidebar_link_click = _sidebar_left_sel.find(\n          \".nest-navigate-link[href^=\\\"#\\\"], .nest-navigate-slice[href^=\\\"#\\\"]\"\n        );\n\n        if (_sidebar_link_click.length > 0) {\n          _sidebar_link_click.on(\"click\", () => {\n            try {\n              // Forcibly hide sidebar\n              _sidebar_left_sel.attr(\"data-visible\", \"false\");\n            } catch (_error) {\n              Console.error(`${this.ns}.${fn}:click`, _error);\n            }\n          });\n        }\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds sidebar nest level toggle click event\n   * @private\n   * @return {undefined}\n   */\n  __bind_sidebar_nest_level_toggle_click() {\n    let fn = \"__bind_sidebar_nest_level_toggle_click\";\n\n    try {\n      this._$(\"#content .sidebar .nest .nest-navigate-toggle\").on(\n        \"click\",\n\n        (event) => {\n          // Toggle sidebar nest level?\n          if (event.target) {\n            this.__toggle_sidebar_nest_level(\n              this._$(event.target)\n            );\n          }\n        }\n      );\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds content anchor viewed event\n   * @private\n   * @return {undefined}\n   */\n  __bind_content_anchor_viewed() {\n    let fn = \"__bind_content_anchor_viewed\";\n\n    try {\n      let _sidebar_left_sel = (\n        this._$(\"#content .sidebar.sidebar--left\") || []\n      );\n      let _anchor_all_sel   = (\n        this._$(\"#content .panes .content [id]\") || []\n      );\n      let _link_all_sel     = (\n        (_sidebar_left_sel.length > 0) ?\n          _sidebar_left_sel.find(\".nest-navigate-link[data-anchor^=\\\"#\\\"]\") : []\n      );\n\n      // Anchors exist on page? Bind viewed listeners.\n      if (_sidebar_left_sel.length > 0 && _anchor_all_sel.length > 0  &&\n            _link_all_sel.length > 0) {\n        // Bind intersection observer? (if browser API is available, otherwise \\\n        //   ignore, as this is only a nice-to-have feature)\n        // Notice: this will apply anchor viewed markers when the anchor \\\n        //   comes into view.\n        if (typeof window.IntersectionObserver !== \"undefined\") {\n          // Acquire header height (used to compute root margins, ie. \\\n          //   accounting for the fact that an element that went into view but \\\n          //   is still sliding below the header, is not really into view yet)\n          let _header_sel    = (this._$(\"#header\") || []),\n              _header_height = 0;\n\n          if (_header_sel.length > 0) {\n            _header_height = (\n              _header_sel[0].offsetHeight || 0\n            );\n          }\n\n          // Allocate viewed context\n          let _viewed_context = {\n            active  : null,\n            visible : {}\n          };\n\n          // Create intersection observer\n          let _observer = new IntersectionObserver(\n            (entries) => {\n              for (let _i = 0; _i < entries.length; _i++) {\n                let _entry = entries[_i];\n\n                if (_entry.target && _entry.target.id  &&\n                      (_link_all_sel.filter(\n                          `[data-anchor=\"#${_entry.target.id}\"]`\n                        ).length > 0)) {\n                  // Entry intersecting?\n                  if (_entry.isIntersecting === true) {\n                    _viewed_context.visible[_entry.target.id] = true;\n                  } else {\n                    delete _viewed_context.visible[_entry.target.id];\n                  }\n                }\n              }\n\n              // Schedule content anchor viewed event? (only if active anchor \\\n              //   has changed; we need to find first active anchor from all \\\n              //   available anchors, ie. this is an ordered list)\n              let _active_anchor = null;\n\n              for (let _i = 0; _i < _anchor_all_sel.length; _i++) {\n                let _cur_anchor_el = _anchor_all_sel[_i];\n\n                // This anchor is visible? Stop there, as we found the first \\\n                //   visible anchor.\n                if (_viewed_context.visible[_cur_anchor_el.id] === true) {\n                  _active_anchor = _cur_anchor_el;\n\n                  break;\n                }\n              }\n\n              if (_active_anchor !== null  &&\n                    _active_anchor !== _viewed_context.active) {\n                this.__schedule_handle_content_anchor_viewed(\n                  _link_all_sel, _sidebar_left_sel, _active_anchor.id,\n                    _header_height\n                );\n              }\n\n              // Apply new active item? (or retain last active if stack is \\\n              //   empty)\n              if (_active_anchor !== null) {\n                _viewed_context.active = _active_anchor;\n              }\n            },\n\n            {\n              threshold  : 0.0,\n\n              rootMargin : (\n                `${(-1 * _header_height)}px 0px 0px 0px`\n              )\n            }\n          );\n\n          // Bind intersection observer on all elements\n          for (let _i = 0; _i < _anchor_all_sel.length; _i++) {\n            _observer.observe(_anchor_all_sel[_i]);\n          }\n        }\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds code metas picker change event\n   * @private\n   * @return {undefined}\n   */\n  __bind_code_metas_picker_change() {\n    let fn = \"__bind_code_metas_picker_change\";\n\n    try {\n      let _code_meta_picker_all_sel = (\n        this._$(\".code .code-meta-picker select\") || []\n      );\n\n      // Code boxes exist on page? Bind change listener on select.\n      if (_code_meta_picker_all_sel.length > 0) {\n        // Bind all change events\n        _code_meta_picker_all_sel.each((picker_el) => {\n          // Notice: use 'addEventListener' instead of '.on' as this is ~200% \\\n          //   faster, which avoids blocking page rendering for ~5ms when \\\n          //   there are a lot of select elements.\n          picker_el.addEventListener(\"change\", (event) => {\n            try {\n              let _code_box_sel = (\n                this._$(event.target).parents(\".code\").first()\n              );\n\n              if (_code_box_sel.length > 0) {\n                this.__handle_code_metas_picker_change(_code_box_sel);\n              }\n            } catch (_error) {\n              Console.error(`${this.ns}.${fn}:change`, _error);\n            }\n          });\n        });\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds code block viewed event\n   * @private\n   * @return {undefined}\n   */\n  __bind_code_block_viewed() {\n    let fn = \"__bind_code_block_viewed\";\n\n    try {\n      let _pre_all_sel = (this._$(\"pre\") || []);\n\n      // Code blocks exist on page? Bind code block listener.\n      if (_pre_all_sel.length > 0) {\n        // Bind intersection observer? (if browser API is available)\n        // Notice: this will apply code highlighting when the code block \\\n        //   enters into view, or will load code boxes when they enter into \\\n        //   view.\n        if (typeof window.IntersectionObserver !== \"undefined\") {\n          // Create intersection observer\n          let _observer = new IntersectionObserver(\n            (entries) => {\n              // Code blocks entered into view?\n              for (let _i = 0; _i < entries.length; _i++) {\n                let _entry = entries[_i];\n\n                // Entry intersecting?\n                if (_entry.isIntersecting === true && _entry.target) {\n                  // Handle code block viewed event\n                  this.__handle_code_block_viewed(\n                    this._$(_entry.target)\n                  );\n                }\n              }\n            },\n\n            {\n              threshold : 0.0\n            }\n          );\n\n          // Bind intersection observer on all elements\n          for (let _i = 0; _i < _pre_all_sel.length; _i++) {\n            _observer.observe(_pre_all_sel[_i]);\n          }\n        } else {\n          Console.warn(\n            `${this.ns}.${fn}`, (\n              \"Optimized code block formatting is disabled, as the browser \"  +\n                \"does not support the IntersectionObserver API.\"\n            )\n          );\n\n          // Highlight all code elements, and load all code boxes, as the \\\n          //   browser does not support intersection observing (this is \\\n          //   obviously much slower and can degrade performances on page \\\n          //   first draw)\n          for (let _i = 0; _i < _pre_all_sel.length; _i++) {\n            // Trigger code block viewed event for block (virtual event, as \\\n            //   the code block is probably not even in view)\n            this.__handle_code_block_viewed(\n              this._$(_pre_all_sel[_i])\n            );\n          }\n        }\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds Markdown embed click event\n   * @private\n   * @return {undefined}\n   */\n  __bind_markdown_embed_click() {\n    let fn = \"__bind_markdown_embed_click\";\n\n    try {\n      let _embed_preview_all_sel = (\n        this._$(\".embed .embed-preview\") || []\n      );\n\n      // Embeds exist on page? Bind click listener (this injects embed content)\n      if (_embed_preview_all_sel.length > 0) {\n        _embed_preview_all_sel.on(\"click\", (event) => {\n          try {\n            // Acquire embed parent\n            let _embed_sel = this._$(event.target).parents(\".embed\").first();\n\n            if (_embed_sel.length > 0) {\n              let _injector = _embed_sel.attr(\"data-injector\"),\n                  _target   = _embed_sel.attr(\"data-target\");\n\n              if (_injector && _target) {\n                // Generate injector code\n                let _inject_code;\n\n                switch (_injector) {\n                  case \"youtube\": {\n                    // Generate YouTube embed URL\n                    let _youtube_url = (\n                      \"https://www.youtube.com/embed/\" + _target  +\n                        \"?hl=en&rel=0&modestbranding=1&autoplay=1\"\n                    );\n\n                    _inject_code = (\n                      \"<iframe \"                         +\n                        \"type=\\\"text/html\\\" \"            +\n                        \"width=\\\"560\\\" \"                 +\n                        \"height=\\\"349\\\" \"                +\n                        \"src=\\\"\" + _youtube_url + \"\\\" \"  +\n                        \"frameborder=\\\"0\\\" \"             +\n                        \"allowfullscreen>\"               +\n                      \"</iframe>\"\n                    );\n\n                    break;\n                  }\n\n                  default: {\n                    // Injector is not supported.\n                    _inject_code = null;\n                  }\n                }\n\n                // Inject embedded content?\n                if (_inject_code !== null) {\n                  // Perform injection\n                  _embed_sel.find(\".embed-frame\").html(_inject_code);\n\n                  // Mark as loaded\n                  _embed_sel.attr(\"data-loaded\", \"true\");\n                }\n              }\n            }\n          } catch (_error) {\n            Console.error(`${this.ns}.${fn}:click`, _error);\n          }\n        });\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds Crisp chat open click event\n   * @private\n   * @return {undefined}\n   */\n  __bind_crisp_chat_open_click() {\n    let fn = \"__bind_crisp_chat_open_click\";\n\n    try {\n      // Bind chat open click listener? (if chatbox is included)\n      if (typeof window.$crisp !== \"undefined\") {\n        // Opens the Crisp chatbox\n        let _chat_open_sel = (\n          this._$(\"a[href='#crisp-chat-open']\") || []\n        );\n\n        for (let _i = 0; _i < _chat_open_sel.length; _i++) {\n          _chat_open_sel[_i].onclick = (\n            this.__handle_crisp_chat_open_click.bind(this)\n          );\n        }\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds Crisp chat feedback click event\n   * @private\n   * @return {undefined}\n   */\n  __bind_crisp_chat_feedback_click() {\n    let fn = \"__bind_crisp_chat_feedback_click\";\n\n    try {\n      // Bind chat open click listener? (if chatbox is included)\n      if (typeof window.$crisp !== \"undefined\") {\n        // Binds to the feedback action link\n        let _chat_feedback_sel = (\n          this._$(\"a[href='#crisp-chat-feedback']\") || []\n        );\n\n        for (let _i = 0; _i < _chat_feedback_sel.length; _i++) {\n          _chat_feedback_sel[_i].onclick = (\n            this.__handle_crisp_chat_feedback_click.bind(this)\n          );\n        }\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n\n\n  /**\n   * Binds status poll schedule\n   * @private\n   * @return {undefined}\n   */\n  __bind_status_poll_schedule() {\n    let fn = \"__bind_status_poll_schedule\";\n\n    try {\n      // Pre-select all status targets\n      let _status_sel = (\n        this._$(\"#footer .status\") || []\n      );\n\n      if (this.__url_status.provider && this.__url_status.target  &&\n            _status_sel.length > 0) {\n        let _seconds_sel = _status_sel.find(\".status-time-seconds\");\n\n        // Stop any previously-set scheduler?\n        if (this.__status_poll_scheduler !== null) {\n          clearInterval(this.__status_poll_scheduler);\n        }\n\n        // Start new scheduler\n        this.__status_poll_scheduler = setInterval(() => {\n          // Increment last checked counter? (if we have an health status \\\n          //   defined)\n          if (this.__status_poll_health !== null) {\n            this.__status_poll_last_check += (\n              this.__status_poll_interval / this.__second_in_milliseconds\n            );\n          }\n\n          // Should we refresh health now?\n          if (this.__status_poll_last_check === 0  ||\n              ((this.__status_poll_last_check  *\n                  this.__second_in_milliseconds)  >=\n                this.__status_poll_refresh)) {\n            // Fetch latest status page health\n            this.__fetch_status_health(\n              this.__url_status.provider, this.__url_status.target\n            )\n              .then((text) => {\n                // Acquired status text health is unknown?\n                if (this.__status_known_health.includes(text) !== true) {\n                  return Promise.reject(null);\n                }\n\n                // Store acquired status text? (if health is known)\n                this.__status_poll_health     = text;\n                this.__status_poll_last_check = 0;\n\n                return Promise.resolve();\n              })\n              .catch(() => {\n                // Mark health refresh as failed?\n                this.__status_poll_health     = \"failure\";\n                this.__status_poll_last_check = 0;\n\n                return Promise.resolve();\n              })\n              .then(() => {\n                // Refresh the UI (both health and 'last checked' time counter)\n                this.__refresh_status_indicator(_status_sel, _seconds_sel);\n              });\n          } else {\n            // Refresh the UI (just the 'last checked' time counter)\n            this.__refresh_status_indicator(_status_sel, _seconds_sel);\n          }\n        }, this.__status_poll_interval);\n      }\n    } catch (error) {\n      Console.error(`${this.ns}.${fn}`, error);\n    }\n  }\n}\n\n\nwindow.Common = new Common();\n\nwindow.Common._document_sel.ready(function() {\n  window.Common.init();\n});\n"
  },
  {
    "path": "src/locales/en.json",
    "content": "{\n  \"COMMON\" : {\n    \"HEADER\" : {\n      \"EXTRAS\" : {\n        \"CHANGES\" : \"Last Changes\"\n      },\n\n      \"SEARCH\" : {\n        \"PLACEHOLDER\" : \"Search docs\"\n      }\n    },\n\n    \"FOOTER\" : {\n      \"ENGINE\" : {\n        \"GENERATED_BY\" : \"Documentation generated by\",\n        \"PROJECT_FROM\" : \"an open-source project from\"\n      },\n\n      \"STATUS\" : {\n        \"LABEL_FAILURE\" : \"Failed checking status\",\n        \"LABEL_HEALTHY\" : \"All systems are healthy\",\n        \"LABEL_SICK\" : \"Service slowdown ongoing\",\n        \"LABEL_DEAD\" : \"Service outage ongoing\",\n        \"TIME_LAST\" : \"Last checked\",\n        \"TIME_AGO\" : \"ago\"\n      }\n    },\n\n    \"SEARCH\" : {\n      \"FIELD\" : {\n        \"INPUT_PLACEHOLDER\" : \"Find anything in the docs…\"\n      },\n\n      \"LEGEND\" : {\n        \"SHORTCUT_NAVIGATE\" : \"Navigate\",\n        \"SHORTCUT_GO\" : \"Go To\",\n        \"SHORTCUT_CLOSE\" : \"Close\",\n        \"ERROR_INDEX\" : \"Search unavailable\"\n      }\n    },\n\n    \"CODE\" : {\n      \"META_NAME_REQUEST\" : \"Request\",\n      \"META_NAME_RESPONSE\" : \"Response\",\n      \"CONTENT_LOADING\" : \"(loading)\"\n    },\n\n    \"METAS\" : {\n      \"SITE_NAME\" : \"Developer Hub\"\n    }\n  },\n\n  \"HOME\" : {\n    \"PAGE\" : \"Developer Hub\",\n    \"TITLE\" : \"Welcome to the Developer Hub\",\n    \"LABEL\" : \"Read guides and browse references.\",\n\n    \"BULLETPOINTS\" : {\n      \"QUICKSTART\" : \"Get Started\",\n      \"GUIDES\"     : \"Guides\",\n      \"REFERENCES\" : \"References\"\n    },\n\n    \"SUPPORT\" : {\n      \"QUESTION\" : \"Anything that you cannot find in the documentation?\",\n      \"LABEL\" : \"Ask our tech support team, anytime.\",\n      \"ACTION\" : \"Chat with Support\"\n    }\n  },\n\n  \"GUIDES\" : {\n    \"DETAILS\" : {\n      \"FEEDBACK\" : \"Give Feedback\",\n      \"UPDATED\" : \"Updated on\",\n      \"TITLE_INDEX\" : \"Introduction\"\n    }\n  },\n\n  \"REFERENCES\" : {\n    \"BLUEPRINT\" : {\n      \"GROUP\" : {\n        \"ORIGIN_IN\" : \"in\"\n      },\n\n      \"SPECIFICATION\" : {\n        \"REQUEST_FORMAT_TITLES\" : {\n          \"DATA_REQUEST\" : \"Request Body\",\n          \"DATA_RESPONSE\" : \"Response Data\",\n          \"URI_PARAMETERS\" : \"URI Parameters\"\n        },\n\n        \"REQUEST_FORMAT_REQUIRED\" : \"Required\",\n        \"REQUEST_FORMAT_OPTIONAL\" : \"Optional\",\n        \"REQUEST_FORMAT_MEMBERS\" : \"Values\",\n        \"REQUEST_FORMAT_TOGGLE_SHOW\": \"Show child parameters\",\n        \"REQUEST_FORMAT_TOGGLE_HIDE\": \"Hide child parameters\"\n      },\n\n      \"EXAMPLES\" : {\n        \"DETAILS\" : {\n          \"TIERS\" : \"Tiers\",\n          \"SCOPES\" : \"Scopes\"\n        }\n      }\n    },\n\n    \"MARKDOWN\" : {\n      \"DETAILS\" : {\n        \"UPDATED\" : \"Updated on\"\n      }\n    }\n  },\n\n  \"CHANGES\" : {\n    \"PAGE\" : \"Last Changes\",\n    \"TITLE_FEED\" : \"Platform Changes\",\n    \"TITLE_LATEST\" : \"Latest Platform Changes\",\n    \"TITLE_YEAR\" : \"Platform Changes In\",\n    \"NOTICE\" : \"This page is a ledger of all notable changes that occured over time.\\n\\n**Note that breaking changes appear in red.**\\n\\nIf you would like to monitor those changes, an RSS feed is available at: [`changes.rss`](/changes.rss)\",\n\n    \"NAVIGATE\" : {\n      \"LATEST\" : \"Latest\"\n    },\n\n    \"MONTHS\" : {\n      \"01\" : \"January\",\n      \"02\" : \"February\",\n      \"03\" : \"March\",\n      \"04\" : \"April\",\n      \"05\" : \"May\",\n      \"06\" : \"June\",\n      \"07\" : \"July\",\n      \"08\" : \"August\",\n      \"09\" : \"September\",\n      \"10\" : \"October\",\n      \"11\" : \"November\",\n      \"12\" : \"December\"\n    }\n  },\n\n  \"NOT_FOUND\" : {\n    \"PAGE\" : \"Page Not Found\",\n    \"TITLE\" : \"Oops. We cannot find this page!\",\n    \"LABEL_FIRST\" : \"You have hit a page that does not exist.\",\n    \"LABEL_SECOND\" : \"It may have been removed.\",\n    \"ACTION\" : \"Go to Homepage\"\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/_colors.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n// Palette\n$color-white: #fff;\n$color-black: #000;\n$color-grey: #e0e7ef;\n$color-grey-light: #dee6ee;\n$color-grey-dark: #dfe5ec;\n$color-blue-black: #44566b;\n$color-blue-grey: #738aa3;\n$color-blue-grey-light: #c1ccd8;\n$color-blue-grey-dark: #607282;\n$color-green: #009d24;\n$color-red: #c21717;\n\n// Base\n$color-base: #f5f6f8;\n$color-base-background: #f8f9fa;\n\n// Selection\n$color-selection-default: #bfe2ff;\n$color-selection-code: $color-white;\n\n// Badges\n$color-badge-black: $color-black;\n$color-badge-white: $color-white;\n$color-badge-blue: #0299e5;\n$color-badge-green: #86a546;\n$color-badge-yellow: #b2a64e;\n$color-badge-red: #c14a75;\n$color-badge-special-light: #dde4e8;\n$color-badge-special-dark: #e8f5fa;\n\n// Methods\n$color-method-get-head-light: #ddf1fc;\n$color-method-get-head-dark: #0099e5;\n$color-method-post-light: #e4f2c8;\n$color-method-post-dark: #85a546;\n$color-method-put-patch-light: #f7f2c3;\n$color-method-put-patch-dark: #b1a74e;\n$color-method-delete-light: #f2d8e1;\n$color-method-delete-dark: #c14a74;\n\n// Markdown\n$color-markdown-code-inline: #a9c6dd;\n$color-markdown-code-inline-light: #e8f5fa;\n$color-markdown-code-inline-dark: #275190;\n$color-markdown-code-block: #1f2224;\n$color-markdown-blockquote: #c0cedd;\n$color-markdown-emphasis-warning-light: #fff4e1;\n$color-markdown-emphasis-warning-dark: #f49c0b;\n$color-markdown-emphasis-info-light: #fffcd9;\n$color-markdown-emphasis-info-dark: #f4e200;\n$color-markdown-emphasis-notice-light: #e2ffdd;\n$color-markdown-emphasis-notice-dark: #48cc30;\n\n// Status\n$color-status-default: #787b84;\n$color-status-healthy: #0db033;\n$color-status-sick: #f18000;\n$color-status-dead: #e10000;\n\n// Code\n$color-code-header-light: #151517;\n$color-code-header-dark: #0c0c0d;\n$color-code-metas: #1e1e20;\n$color-code-content: #222628;\n\n// Highlight\n$color-highlight-main: #f7f7f7;\n$color-highlight-entity-light: #a09f93;\n$color-highlight-entity-dark: #393939;\n$color-highlight-comment: #a2a2a2;\n$color-highlight-punctuation: #f7f7f7;\n$color-highlight-variable: #f2777a;\n$color-highlight-number: #ff8c4d;\n$color-highlight-class: #fc6;\n$color-highlight-property: #c2ee65;\n$color-highlight-regex: #6cc;\n$color-highlight-function: #88c4ff;\n$color-highlight-keyword: #ff9eff;\n$color-highlight-doctype: #d27b53;\n\n// Search\n$color-search-lock: #495368;\n$color-search-spotlight-shortcut: #929fac;\n\n// Header\n$color-header-search-border-clear: #d5dadf;\n$color-header-search-shortcut-text-dark: #a2aeb9;\n$color-header-search-shortcut-text-light: #e4e9ed;\n$color-header-coloring-toggle-icon-light: #e0a142;\n$color-header-coloring-toggle-icon-dark: #5162b7;\n\n// Content\n$color-content-sidebar-right: #2d3134;\n\n// Footer\n$color-footer-text: #9fb0c2;\n$color-footer-link-target: #677789;\n$color-footer-copyright: #859ab0;\n$color-footer-status-time: #93a0ad;\n\n// References\n$color-references-request-format-required: #eb4e3a;\n$color-references-request-format-optional: #a2a8b0;\n$color-references-request-format-label: #717982;\n$color-references-examples-detail-value: #858b8e;\n$color-references-examples-detail-segment: #dde4e8;\n$color-references-examples-detail-parameter: #c2ee65;\n\n// Changes\n$color-changes-month-event-deprecated: #fdeaee;"
  },
  {
    "path": "src/stylesheets/_config.scss",
    "content": "// chappe\n//\n// Copyright 2026, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n$inlining: false !default;"
  },
  {
    "path": "src/stylesheets/_functions.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"sass:math\";\n\n@use \"_variables\" as vars;\n\n@function background-image-width($image) {\n  $image-width: image-width(\"1x/#{$image}\");\n\n  @return $image-width;\n}\n\n@function background-image-height($image) {\n  $image-height: image-height(\"1x/#{$image}\");\n\n  @return $image-height;\n}\n\n@function font-size-calculate-px($font-size) {\n  $calculated-font-size: (vars.$font-resize-ratio * $font-size);\n\n  @return $calculated-font-size;\n}\n\n@function font-size-calculate-percent($font-size) {\n  $calculated-font-size: (vars.$font-resize-ratio * math.div($font-size, vars.$font-size-base)) * 100%;\n\n  @return $calculated-font-size;\n}\n\n@function font-size-calculate-em($font-size) {\n  $calculated-font-size: (vars.$font-resize-ratio * math.div($font-size, vars.$font-size-base)) * 1em;\n\n  @return $calculated-font-size;\n}\n\n@function font-size-calculate-rem($font-size, $relative-font-size) {\n  $calculated-font-size: (vars.$font-resize-ratio * math.div($font-size, $relative-font-size)) * 1rem;\n\n  @return $calculated-font-size;\n}\n"
  },
  {
    "path": "src/stylesheets/_globals.scss",
    "content": "// chappe\n//\n// Copyright 2026, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@forward \"_mixins\";\n@forward \"_functions\";\n@forward \"_variables\";\n@forward \"_colors\";"
  },
  {
    "path": "src/stylesheets/_mixins.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"_config\" as config;\n@use \"_functions\" as functions;\n@use \"_variables\" as vars;\n\n/* stylelint-disable property-no-vendor-prefix, selector-no-vendor-prefix */\n\n@mixin font-size($font-size) {\n  font-size: functions.font-size-calculate-px($font-size);\n}\n\n@mixin font-face-include($font-family, $font-path, $font-weight, $font-style) {\n  @font-face {\n    font-family: $font-family;\n    src: url('/static/fonts/#{$font-path}.woff2?#{vars.$cache-buster-hash}') format('woff2'), url('/static/fonts/#{$font-path}.woff?#{vars.$cache-buster-hash}') format('woff');\n    font-weight: $font-weight;\n    font-style: $font-style;\n    font-display: swap;\n  }\n}\n\n@mixin background-image-internal($image) {\n  @if config.$inlining == true {\n    background-image: inline-image($image);\n  } @else {\n    background-image: url('/static/images/#{$image}?#{vars.$cache-buster-hash}');\n  }\n}\n\n@mixin background-image-fill($display: true) {\n  background-size: contain;\n  background-repeat: no-repeat;\n  background-position: center;\n\n  @if $display == true {\n    display: inline-block;\n  }\n}\n\n@mixin background-image-external($image) {\n  background-image: url('/static/images/#{$image}?#{vars.$cache-buster-hash}');\n}\n@mixin background-image-internal-fill($image, $display: true) {\n  @include background-image-internal($image);\n  @include background-image-fill($display);\n}\n\n@mixin background-image-external-fill($image, $display: true) {\n  @include background-image-external($image);\n  @include background-image-fill($display);\n}\n\n@mixin mask-image-internal($image) {\n  @if config.$inlining == true {\n    @include mask-image(inline-image($image));\n  } @else {\n    @include mask-image(url('/static/images/#{$image}?#{vars.$cache-buster-hash}'));\n  }\n}\n\n@mixin mask-image-fill($display: true) {\n  @include mask-size(contain);\n  @include mask-repeat(no-repeat);\n  @include mask-position(center);\n\n  @if $display == true {\n    display: inline-block;\n  }\n}\n\n@mixin mask-image-internal-fill($image, $display: true) {\n  @include mask-image-internal($image);\n  @include mask-image-fill($display);\n}\n\n@mixin placeholder {\n  &::placeholder {\n    @content;\n  }\n\n  &:-moz-placeholder {\n    @content;\n  }\n\n  &::-moz-placeholder {\n    @content;\n  }\n\n  &:-ms-input-placeholder {\n    @content;\n  }\n\n  &::-webkit-input-placeholder {\n    @content;\n  }\n}\n\n@mixin input-placeholder {\n  @include placeholder {\n    @content;\n  }\n}\n\n@mixin textarea-placeholder {\n  @include placeholder {\n    @content;\n  }\n}\n\n@mixin input-search {\n  &::-webkit-search-decoration,\n  &::-webkit-search-cancel-button,\n  &::-webkit-search-results-button,\n  &::-webkit-search-results-decoration {\n    @content;\n  }\n}\n\n@mixin selection {\n  ::selection {\n    @content;\n  }\n\n  ::-moz-selection {\n    @content;\n  }\n}\n\n@mixin appearance($value) {\n  -webkit-appearance: $value;\n  -moz-appearance: $value;\n  appearance: $value;\n}\n\n@mixin backdrop-filter($rules) {\n  -webkit-backdrop-filter: $rules;\n  backdrop-filter: $rules;\n}\n\n@mixin mask-image($value) {\n  --mask-image: #{$value};\n\n  -webkit-mask-image: var(--mask-image);\n  mask-image: var(--mask-image);\n}\n\n@mixin mask-size($value) {\n  -webkit-mask-size: $value;\n  mask-size: $value;\n}\n\n@mixin mask-repeat($value) {\n  -webkit-mask-repeat: $value;\n  mask-repeat: $value;\n}\n\n@mixin mask-position($value) {\n  -webkit-mask-position: $value;\n  mask-position: $value;\n}\n\n@mixin font-smoothing($value: antialiased) {\n  @if $value == antialiased {\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n  } @else {\n    -webkit-font-smoothing: subpixel-antialiased;\n    -moz-osx-font-smoothing: auto;\n  }\n}\n\n@mixin keyframes-fix($name) {\n  @keyframes #{$name} {\n    @content;\n  }\n}\n\n/* stylelint-enable property-no-vendor-prefix, selector-no-vendor-prefix */\n"
  },
  {
    "path": "src/stylesheets/_variables.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n$cache-buster-hash: \"@@revision\";\n\n$font-size-base: 15px;\n$font-resize-ratio: 1;\n\n$screen-large-width-breakpoint: 1140px;\n$screen-medium-width-breakpoint: 1020px;\n$screen-small-width-breakpoint: 880px;\n$screen-tiny-width-breakpoint: 640px;\n$screen-lilliput-width-breakpoint: 400px;\n\n$wrapper-default-padding-sides: 48px;\n$wrapper-medium-padding-sides: 28px;\n$wrapper-small-padding-sides: 20px;\n$wrapper-tiny-padding-sides: 16px;\n$wrapper-lilliput-padding-sides: 12px;\n\n$header-height: 58px;\n$header-border-size: 1px;\n$header-height-outer: ($header-height + $header-border-size);\n\n$content-wrap-max-width: 1200px;\n$content-sidebar-left-width: 248px;\n$content-sidebar-right-default-width: 460px;\n$content-sidebar-right-large-width: 360px;\n\n$button-default-height: 40px;\n$button-small-height: 34px;\n\n$badge-default-height: 18px;\n$badge-small-height: 14px;\n$badge-special-value-height: 18px;"
  },
  {
    "path": "src/stylesheets/changes/changes.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_config\" with (\n  $inlining: $use-inline-images\n);\n\n@use \"../_globals\" as *;\n\n#changes {\n  position: relative;\n\n  .navigate {\n    user-select: none;\n    position: absolute;\n    right: 0;\n    top: 70px;\n    z-index: 1;\n\n    &::before {\n      content: \"\";\n      background-color: var(--color-grey-light);\n      width: 2px;\n      position: absolute;\n      top: 0;\n      bottom: 0;\n      left: 0;\n      z-index: 2;\n    }\n\n    .navigate-item {\n      margin-bottom: 4px;\n\n      &:last-of-type {\n        margin-bottom: 0;\n      }\n\n      &.navigate-item--active {\n        &,\n        &:hover,\n        &:active {\n          .navigate-link {\n            color: var(--color-white);\n            background-color: var(--color-accent-base);\n            z-index: 3;\n          }\n        }\n      }\n\n      &,\n      .navigate-link {\n        display: block;\n      }\n\n      .navigate-link {\n        background-color: transparent;\n        color: var(--color-black);\n        letter-spacing: -.12px;\n        text-align: left;\n        line-height: 24px;\n        padding-left: 17px;\n        padding-right: 12px;\n        border-top-right-radius: 3px;\n        border-bottom-right-radius: 3px;\n        transition: background-color linear 75ms;\n        position: relative;\n        z-index: 1;\n\n        @include font-size(14px);\n\n        &:hover {\n          background-color: var(--color-changes-navigate-link-background-hover);\n        }\n\n        &:active {\n          background-color: var(--color-changes-navigate-link-background-active);\n        }\n      }\n    }\n  }\n\n  .content {\n    width: 100%;\n    max-width: 860px;\n    margin: 0 auto;\n    padding-top: 30px;\n    padding-bottom: 126px;\n\n    .title {\n      color: var(--color-black);\n\n      @include font-size(22px);\n    }\n\n    .notice {\n      background: var(--color-changes-notice-background);\n      margin-top: 18px;\n      padding: 18px 28px;\n      overflow: hidden;\n      position: relative;\n      border-radius: 2px;\n\n      &::before {\n        content: \"\";\n        background-color: var(--color-grey-light);\n        width: 5px;\n        position: absolute;\n        top: 0;\n        bottom: 0;\n        left: 0;\n      }\n    }\n\n    .timeline {\n      .month {\n        margin-top: 52px;\n\n        .month-name {\n          color: var(--color-black);\n\n          @include font-size(22px);\n        }\n\n        .month-events {\n          margin-top: 25px;\n\n          .month-event {\n            $event-default-padding-top-bottom: 4px;\n            $event-deprecated-padding-top-bottom: 10px;\n\n            border-bottom: 1px solid var(--color-changes-month-event-border);\n            margin-bottom: 14px;\n            padding-bottom: 14px;\n            display: flex;\n            flex-direction: row;\n\n            &.month-event--deprecated {\n              .month-event-group {\n                color: var(--color-red);\n                padding-top: $event-deprecated-padding-top-bottom;\n              }\n\n              .month-event-text {\n                background-color: var(--color-changes-month-event-deprecated-background);\n                border-color: var(--color-red);\n                padding-top: $event-deprecated-padding-top-bottom;\n                padding-bottom: $event-deprecated-padding-top-bottom;\n                padding-right: 8px;\n              }\n            }\n\n            &:last-child {\n              border-bottom: 0 none;\n              margin-bottom: 0;\n              padding-bottom: 0;\n            }\n\n            .month-event-group,\n            .month-event-text {\n              line-height: 20px;\n\n              @include font-size(14px);\n            }\n\n            .month-event-group {\n              color: var(--color-changes-month-event-group-text);\n              line-height: 22px;\n              width: 120px;\n              padding-top: $event-default-padding-top-bottom;\n              padding-right: 8px;\n              flex: 0 0 auto;\n            }\n\n            .month-event-text {\n              border-color: var(--color-grey);\n              border-width: 0 0 0 2px;\n              border-style: solid;\n              padding: $event-default-padding-top-bottom 0 $event-default-padding-top-bottom 14px;\n              flex: 1;\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (width <= 1140px) {\n  #changes {\n    .content {\n      .content-aside {\n        padding-right: 80px;\n      }\n    }\n  }\n}\n\n@media screen and (max-width: $screen-tiny-width-breakpoint) {\n  #changes {\n    .navigate {\n      text-align: center;\n      margin-top: 14px;\n      position: static;\n\n      &::before {\n        display: none;\n      }\n\n      .navigate-item {\n        margin-right: 1px;\n\n        &:last-of-type {\n          margin-right: 0;\n        }\n\n        &,\n        .navigate-link {\n          display: inline-block;\n        }\n\n        .navigate-link {\n          padding: 0 10px;\n          border-radius: 3px;\n        }\n      }\n    }\n\n    .content {\n      padding-top: 22px;\n      padding-bottom: 74px;\n\n      .content-aside {\n        padding-right: 0;\n      }\n\n      .title,\n      .timeline .month .month-name {\n        text-align: center;\n      }\n\n      .notice {\n        padding: 14px 24px;\n      }\n\n      .timeline {\n        .month {\n          margin-top: 36px;\n\n          .month-events {\n            .month-event {\n              border-bottom: 0 none;\n              margin-bottom: 20px;\n              padding-bottom: 0;\n              display: block;\n\n              &,\n              &.month-event--deprecated {\n                .month-event-group {\n                  padding: 0 0 2px;\n                }\n\n                .month-event-text {\n                  padding-top: 6px;\n                }\n              }\n\n              &.month-event--deprecated {\n                .month-event-text {\n                  padding-left: 10px;\n                  padding-right: 10px;\n                }\n              }\n\n              .month-event-group {\n                text-align: center;\n                width: 100%;\n              }\n\n              .month-event-text {\n                border-width: 2px 0 0;\n                padding-left: 2px;\n                padding-right: 2px;\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/common/_appearance.scss",
    "content": "// chappe\n//\n// Copyright 2022, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"sass:color\";\n\n@use \"../_globals\" as *;\n\n.appearance {\n  // Palette\n  --color-white: #{$color-white};\n  --color-black: #{$color-black};\n  --color-grey: #{$color-grey};\n  --color-grey-light: #{$color-grey-light};\n  --color-grey-dark: #{$color-grey-dark};\n  --color-blue-black: #{$color-blue-black};\n  --color-blue-grey: #{$color-blue-grey};\n  --color-blue-grey-light: #{$color-blue-grey-light};\n  --color-blue-grey-dark: #{$color-blue-grey-dark};\n  --color-green: #{$color-green};\n  --color-red: #{$color-red};\n\n  // Base\n  --color-base: #{$color-base};\n  --color-base-background: #{$color-base-background};\n\n  // Selection\n  --color-selection-default: #{$color-selection-default};\n\n  // Buttons\n  --color-button-default: var(--color-accent-base);\n  --color-button-active: var(--color-accent-active);\n  --color-button-shadow: #{rgba($color-black, .1)};\n\n  // Badges\n  --color-badge-black: #{$color-badge-black};\n  --color-badge-white: #{$color-badge-white};\n  --color-badge-blue: #{$color-badge-blue};\n  --color-badge-green: #{$color-badge-green};\n  --color-badge-yellow: #{$color-badge-yellow};\n  --color-badge-red: #{$color-badge-red};\n  --color-badge-special-background: #{rgba($color-badge-special-dark, .15)};\n  --color-badge-special-border: #{rgba($color-white, .25)};\n  --color-badge-special-text: #{$color-badge-special-light};\n\n  // Methods\n  --color-method-get-head-light: #{$color-method-get-head-light};\n  --color-method-get-head-dark: #{$color-method-get-head-dark};\n  --color-method-post-light: #{$color-method-post-light};\n  --color-method-post-dark: #{$color-method-post-dark};\n  --color-method-put-patch-light: #{$color-method-put-patch-light};\n  --color-method-put-patch-dark: #{$color-method-put-patch-dark};\n  --color-method-delete-light: #{$color-method-delete-light};\n  --color-method-delete-dark: #{$color-method-delete-dark};\n\n  // Status\n  --color-status-default-text: #{$color-status-default};\n  --color-status-healthy-text: #{$color-status-healthy};\n  --color-status-sick-text: #{$color-status-sick};\n  --color-status-dead-text: #{$color-status-dead};\n\n  // Viewer\n  --color-viewer-panes-footer-border: #{rgba($color-grey, .5)};\n\n  // Markdown\n  --color-markdown-title-small-text: var(--color-blue-black);\n  --color-markdown-list-item-bullet: var(--color-blue-black);\n  --color-markdown-code-inline-background: #{$color-markdown-code-inline-light};\n  --color-markdown-code-inline-border: #{$color-markdown-code-inline};\n  --color-markdown-code-inline-text: #{$color-markdown-code-inline-dark};\n  --color-markdown-code-block-background: #{$color-markdown-code-block};\n  --color-markdown-code-block-text: #{$color-white};\n  --color-markdown-code-block-clipboard-background-default: #{color.adjust($color-markdown-code-block, $lightness: 3%)};\n  --color-markdown-code-block-clipboard-background-hover: #{color.adjust($color-markdown-code-block, $lightness: 7%)};\n  --color-markdown-code-block-clipboard-background-active: #{color.adjust($color-markdown-code-block, $lightness: 5%)};\n  --color-markdown-code-block-clipboard-border-default: #{rgba($color-blue-grey-light, .25)};\n  --color-markdown-code-block-clipboard-border-hover: #{rgba($color-blue-grey-light, .5)};\n  --color-markdown-code-block-clipboard-border-active: #{rgba($color-blue-grey-light, .35)};\n  --color-markdown-code-block-clipboard-border-copied: #{color.adjust($color-green, $lightness: 7%)};\n  --color-markdown-code-block-clipboard-icon-background-default: #{$color-blue-grey-light};\n  --color-markdown-code-block-clipboard-icon-background-copied: var(--color-markdown-code-block-clipboard-border-copied);\n  --color-markdown-code-selection: #{rgba($color-selection-code, .25)};\n  --color-markdown-figure-text: var(--color-blue-grey);\n  --color-markdown-figure-border: #{rgba($color-grey, .9)};\n  --color-markdown-table-head-background: var(--color-base);\n  --color-markdown-table-head-text: var(--color-blue-black);\n  --color-markdown-table-body-row-background-hover: #{rgba($color-blue-grey-light, .08)};\n  --color-markdown-table-cell-border: #{rgba($color-blue-grey-light, .65)};\n  --color-markdown-blockquote-background: var(--color-base);\n  --color-markdown-blockquote-text: var(--color-blue-black);\n  --color-markdown-blockquote-border: #{$color-markdown-blockquote};\n  --color-markdown-embed-wrap-background: #{$color-black};\n  --color-markdown-emphasis-warning-background: #{$color-markdown-emphasis-warning-light};\n  --color-markdown-emphasis-warning-border: #{$color-markdown-emphasis-warning-dark};\n  --color-markdown-emphasis-info-background: #{$color-markdown-emphasis-info-light};\n  --color-markdown-emphasis-info-border: #{$color-markdown-emphasis-info-dark};\n  --color-markdown-emphasis-notice-background: #{$color-markdown-emphasis-notice-light};\n  --color-markdown-emphasis-notice-border: #{$color-markdown-emphasis-notice-dark};\n\n  // Code\n  --color-code-selection: #{rgba($color-selection-code, .3)};\n  --color-code-border-hover: #{rgba($color-white, .25)};\n  --color-code-header-default: #{$color-code-header-light};\n  --color-code-header-hover: #{$color-code-header-dark};\n  --color-code-language-text: #{rgba($color-white, .6)};\n  --color-code-action-background: #{$color-white};\n  --color-code-metas-background: #{$color-code-metas};\n  --color-code-meta-name-border: #{rgba($color-white, .06)};\n  --color-code-meta-name-text: #{rgba($color-white, .8)};\n  --color-code-meta-type-text: #{rgba($color-white, .4)};\n  --color-code-content-background: #{$color-code-content};\n  --color-code-content-text: #{rgba($color-white, .4)};\n  --color-code-select-text-default: #{rgba($color-white, .5)};\n  --color-code-select-text-hover: #{rgba($color-white, .8)};\n  --color-code-select-border-default: #{rgba($color-white, .075)};\n  --color-code-select-border-hover: #{rgba($color-white, .15)};\n\n  // Highlight\n  --color-highlight-main: #{$color-highlight-main};\n  --color-highlight-entity-light: #{$color-highlight-entity-light};\n  --color-highlight-entity-dark: #{$color-highlight-entity-dark};\n  --color-highlight-comment: #{$color-highlight-comment};\n  --color-highlight-punctuation: #{$color-highlight-punctuation};\n  --color-highlight-variable: #{$color-highlight-variable};\n  --color-highlight-number: #{$color-highlight-number};\n  --color-highlight-class: #{$color-highlight-class};\n  --color-highlight-property: #{$color-highlight-property};\n  --color-highlight-regex: #{$color-highlight-regex};\n  --color-highlight-function: #{$color-highlight-function};\n  --color-highlight-keyword: #{$color-highlight-keyword};\n  --color-highlight-doctype: #{$color-highlight-doctype};\n\n  // Search\n  --color-search-lock-background: #{rgba($color-search-lock, .45)};\n  --color-search-spotlight-shadow: #{rgba($color-black, .1)};\n  --color-search-spotlight-shortcut-text: #{$color-search-spotlight-shortcut};\n  --color-search-spotlight-field-background: #{rgba($color-base, .65)};\n  --color-search-spotlight-field-placeholder: var(--color-blue-grey-dark);\n  --color-search-spotlight-field-spinner-border: var(--color-blue-grey-dark);\n  --color-search-spotlight-entry-link-background-default: var(--color-accent-base);\n  --color-search-spotlight-entry-link-background-active: var(--color-accent-active);\n\n  // Header\n  --color-header-background: #{rgba($color-white, .85)};\n  --color-header-border: #{rgba($color-grey, .65)};\n  --color-header-shadow: #{rgba($color-black, .06)};\n  --color-header-dropdown-border: #{rgba($color-grey, .65)};\n  --color-header-dropdown-shadow: #{rgba($color-black, .05)};\n  --color-header-dropdown-link-background-hover: var(--color-accent-base);\n  --color-header-dropdown-link-background-active: var(--color-accent-active);\n  --color-header-search-background-default: var(--color-base);\n  --color-header-search-background-clear: var(--color-white);\n  --color-header-search-border-default: var(--color-grey-dark);\n  --color-header-search-border-hover: #{color.adjust($color-grey-dark, $lightness: -10%)};\n  --color-header-search-border-active: #{color.adjust($color-grey-dark, $lightness: -14%)};\n  --color-header-search-border-clear-default: #{$color-header-search-border-clear};\n  --color-header-search-border-clear-hover: #{color.adjust($color-header-search-border-clear, $lightness: -6%)};\n  --color-header-search-border-clear-active: #{color.adjust($color-header-search-border-clear, $lightness: -9%)};\n  --color-header-search-shadow: #{rgba($color-grey-dark, .25)};\n  --color-header-search-placeholder-text: var(--color-blue-grey-dark);\n  --color-header-search-shortcut-text: #{$color-header-search-shortcut-text-dark};\n  --color-header-search-shortcut-border: #{$color-header-search-border-clear};\n  --color-header-search-shortcut-shadow: #{$color-header-search-shortcut-text-light};\n  --color-header-coloring-background-default: #{color.adjust($color-grey-light, $lightness: 1%)};\n  --color-header-coloring-background-clear: #{rgba($color-white, .7)};\n  --color-header-coloring-toggle-border-default: #{rgba(color.adjust($color-grey-dark, $lightness: -50%), .2)};\n  --color-header-coloring-toggle-border-hover: #{rgba(color.adjust($color-grey-dark, $lightness: -50%), .225)};\n  --color-header-coloring-toggle-border-active: #{rgba(color.adjust($color-grey-dark, $lightness: -50%), .175)};\n  --color-header-coloring-toggle-shadow-default: #{rgba($color-black, .04)};\n  --color-header-coloring-toggle-shadow-hover: #{rgba($color-black, .06)};\n  --color-header-coloring-toggle-shadow-active: #{rgba($color-black, .02)};\n  --color-header-coloring-toggle-icon-light-background: #{$color-header-coloring-toggle-icon-light};\n  --color-header-coloring-toggle-icon-dark-background: #{$color-header-coloring-toggle-icon-dark};\n\n  // Content\n  --color-content-navigation-background: #{rgba($color-base, .25)};\n  --color-content-navigation-border-default: var(--color-blue-grey-light);\n  --color-content-navigation-border-active: #{color.adjust($color-blue-grey-light, $lightness: -5%)};\n  --color-content-navigation-shadow-outset: #{rgba($color-blue-grey-light, .25)};\n  --color-content-navigation-shadow-inset: #{rgba($color-white, .75)};\n  --color-content-navigation-shadow-active: #{rgba($color-blue-grey-light, .4)};\n  --color-content-navigation-label-text: var(--color-blue-grey);\n  --color-content-bulletpoint-label-text: var(--color-blue-grey);\n  --color-content-sidebar-left-background-default: #{rgba($color-base, .65)};\n  --color-content-sidebar-left-background-small: #{rgba($color-base, .9)};\n  --color-content-sidebar-left-background-tiny: var(--color-base);\n  --color-content-sidebar-left-shadow-small: #{rgba($color-black, .04)};\n  --color-content-sidebar-right-background: #{$color-content-sidebar-right};\n  --color-content-sidebar-toggler-background: #{rgba($color-white, .85)};\n  --color-content-sidebar-toggler-shadow-default: #{rgba($color-black, .1)};\n  --color-content-sidebar-toggler-shadow-hover: #{rgba($color-black, .125)};\n  --color-content-sidebar-toggler-shadow-active: #{rgba($color-black, .2)};\n  --color-content-sidebar-toggler-icon-background: var(--color-blue-black);\n  --color-content-sidebar-nest-navigate-link-background-hover: #{color.adjust($color-grey-light, $lightness: 3%)};\n  --color-content-sidebar-nest-navigate-link-background-active: #{color.adjust($color-grey-light, $lightness: 1%)};\n  --color-content-sidebar-nest-navigate-link-default-toggle-background-hover: #{color.adjust($color-grey-light, $lightness: -3%)};\n  --color-content-sidebar-nest-navigate-link-default-toggle-background-active: #{color.adjust($color-grey-light, $lightness: -7%)};\n  --color-content-sidebar-nest-navigate-link-active-toggle-background-hover: #{rgba($color-white, .15)};\n  --color-content-sidebar-nest-navigate-link-active-toggle-background-active: #{rgba($color-white, .2)};\n  --color-content-sidebar-nest-navigate-slice-text: var(--color-blue-black);\n  --color-content-sidebar-nest-navigate-slice-icon-background-default: var(--color-blue-black);\n  --color-content-sidebar-nest-navigate-slice-icon-background-active: var(--color-white);\n\n  // Footer\n  --color-footer-brand-separator: #{rgba($color-footer-text, .3)};\n  --color-footer-link-target-text: #{$color-footer-link-target};\n  --color-footer-copyright-text: #{$color-footer-copyright};\n  --color-footer-engine-text: #{$color-footer-text};\n  --color-footer-status-border-default: #{rgba($color-footer-text, .45)};\n  --color-footer-status-border-hover: #{rgba($color-footer-text, .65)};\n  --color-footer-status-time-text: #{$color-footer-status-time};\n\n  // Guides\n  --color-guides-details-border: #{rgba($color-grey, .65)};\n  --color-guides-details-inner-text: var(--color-blue-black);\n\n  // References\n  --color-references-separator-content: #{rgba($color-grey, .65)};\n  --color-references-separator-sidebar: #{rgba($color-grey, .15)};\n  --color-references-details-updated-text: var(--color-blue-black);\n  --color-references-group-origin-text: var(--color-blue-black);\n  --color-references-request-target-url-background: var(--color-base);\n  --color-references-request-target-url-border-default: #{color.adjust($color-grey-light, $lightness: -8%)};\n  --color-references-request-target-url-border-hover: #{color.adjust($color-grey, $lightness: -16%)};\n  --color-references-request-target-url-border-active: #{color.adjust($color-grey, $lightness: -24%)};\n  --color-references-request-format-type-text: var(--color-blue-black);\n  --color-references-request-format-required-text: #{$color-references-request-format-required};\n  --color-references-request-format-optional-text: #{$color-references-request-format-optional};\n  --color-references-request-format-label-text: #{$color-references-request-format-label};\n  --color-references-request-format-keys-depth-border-zero: #{color.adjust($color-grey-light, $lightness: 10%)};\n  --color-references-request-format-keys-depth-border-one: #{color.adjust($color-grey-light, $lightness: 5%)};\n  --color-references-request-format-keys-depth-border-two: var(--color-grey-light);\n  --color-references-request-format-keys-depth-border-three: #{color.adjust($color-grey-light, $lightness: -5%)};\n  --color-references-request-format-keys-depth-border-four: #{color.adjust($color-grey-light, $lightness: -10%)};\n  --color-references-request-format-keys-depth-border-infinity: #{color.adjust($color-grey-light, $lightness: -15%)};\n  --color-references-request-format-toggle-count-text: #{color.adjust($color-black, $lightness: 12.5%)};\n  --color-references-request-format-toggle-count-background: #{color.adjust($color-grey-light, $lightness: 2%)};\n  --color-references-request-format-toggle-button-border: #{color.adjust($color-grey-dark, $lightness: -8%)};\n  --color-references-request-format-toggle-button-shadow: #{rgba($color-grey-dark, .25)};\n  --color-references-examples-selection: #{rgba($color-selection-code, .2)};\n  --color-references-examples-detail-value-text: #{$color-references-examples-detail-value};\n  --color-references-examples-detail-segment-text: #{$color-references-examples-detail-segment};\n  --color-references-examples-detail-parameter-text: #{$color-references-examples-detail-parameter};\n\n  // Changes\n  --color-changes-notice-background: var(--color-base);\n  --color-changes-navigate-link-background-hover: #{color.adjust($color-grey-light, $lightness: 6%)};\n  --color-changes-navigate-link-background-active: #{color.adjust($color-grey-light, $lightness: 4%)};\n  --color-changes-month-event-border: #{rgba($color-grey, .65)};\n  --color-changes-month-event-group-text: var(--color-blue-grey-dark);\n  --color-changes-month-event-deprecated-background: #{$color-changes-month-event-deprecated};\n\n  &[data-appearance=\"dark\"] {\n    // Overrides (Dark Mode)\n    $color-white: #10131c;\n    $color-black: #fff;\n    $color-grey: #474b5e;\n    $color-grey-light: #4c4f62;\n    $color-grey-dark: #3e4255;\n    $color-blue-black: #cdd3db;\n    $color-blue-grey: #a3a5ae;\n    $color-blue-grey-light: $color-grey;\n    $color-blue-grey-dark: #71757d;\n    $color-green: #21fb52;\n    $color-red: #ff7e7e;\n\n    $color-base: #1e202b;\n    $color-base-background: #191c25;\n\n    $color-markdown-code-block: #2a2d39;\n    $color-markdown-blockquote: #516071;\n    $color-markdown-emphasis-warning-light: #ffb300;\n    $color-markdown-emphasis-warning-dark: #c87c00;\n    $color-markdown-emphasis-info-light: #ffef00;\n    $color-markdown-emphasis-info-dark: #c4b500;\n    $color-markdown-emphasis-notice-light: #87ff73;\n    $color-markdown-emphasis-notice-dark: #30bb17;\n\n    $color-code-header-light: #16171c;\n    $color-code-header-dark: #121317;\n    $color-code-metas: #1e1f24;\n    $color-code-content: #24252b;\n\n    $color-search-lock: #434959;\n\n    $color-header-coloring-background: #5161b7;\n    $color-header-coloring-toggle-border: $color-header-coloring-background;\n\n    $color-content-sidebar-right: #292a31;\n\n    $color-references-request-format-required: #ff6d5a;\n    $color-references-request-format-optional: #8f99a6;\n    $color-references-request-format-label: #8d98a4;\n\n    $color-changes-month-event-deprecated: #ff7b98;\n\n    // Palette (Dark Mode)\n    --color-white: #{$color-white};\n    --color-black: #{$color-black};\n    --color-grey: #{$color-grey};\n    --color-grey-light: #{$color-grey-light};\n    --color-grey-dark: #{$color-grey-dark};\n    --color-blue-black: #{$color-blue-black};\n    --color-blue-grey: #{$color-blue-grey};\n    --color-blue-grey-light: #{$color-blue-grey-light};\n    --color-blue-grey-dark: #{$color-blue-grey-dark};\n    --color-green: #{$color-green};\n    --color-red: #{$color-red};\n\n    // Base (Dark Mode)\n    --color-base: #{$color-base};\n    --color-base-background: #{$color-base-background};\n\n    // Selection (Dark Mode)\n    --color-selection-default: #{rgba($color-black, .25)};\n\n    // Badges (Dark Mode)\n    --color-badge-black: #{color.adjust($color-badge-black, $lightness: 10%)};\n    --color-badge-blue: #{color.adjust($color-badge-blue, $lightness: 10%)};\n    --color-badge-green: #{color.adjust($color-badge-green, $lightness: 10%)};\n    --color-badge-yellow: #{color.adjust($color-badge-yellow, $lightness: 10%)};\n    --color-badge-red: #{color.adjust($color-badge-red, $lightness: 10%)};\n\n    // Methods (Dark Mode)\n    --color-method-get-head-light: #{rgba($color-method-get-head-light, .3)};\n    --color-method-get-head-dark: #{color.adjust($color-method-get-head-dark, $lightness: 6%)};\n    --color-method-post-light: #{rgba($color-method-post-light, .3)};\n    --color-method-post-dark: #{color.adjust($color-method-post-dark, $lightness: 6%)};\n    --color-method-put-patch-light: #{rgba($color-method-put-patch-light, .3)};\n    --color-method-put-patch-dark: #{color.adjust($color-method-put-patch-dark, $lightness: 6%)};\n    --color-method-delete-light: #{rgba($color-method-delete-light, .3)};\n    --color-method-delete-dark: #{color.adjust($color-method-delete-dark, $lightness: 6%)};\n\n    // Viewer (Dark Mode)\n    --color-viewer-panes-footer-border: #{rgba($color-black, .1)};\n\n    // Markdown (Dark Mode)\n    --color-markdown-code-inline-background: #{rgba($color-black, .15)};\n    --color-markdown-code-inline-border: #{rgba($color-black, .35)};\n    --color-markdown-code-inline-text: #{$color-black};\n    --color-markdown-figure-border: #{rgba($color-grey, .9)};\n    --color-markdown-table-body-row-background-hover: #{rgba($color-blue-grey-light, .12)};\n    --color-markdown-table-cell-border: var(--color-grey);\n    --color-markdown-blockquote-border: #{$color-markdown-blockquote};\n    --color-markdown-emphasis-warning-background: #{rgba($color-markdown-emphasis-warning-light, .25)};\n    --color-markdown-emphasis-warning-border: #{$color-markdown-emphasis-warning-dark};\n    --color-markdown-emphasis-info-background: #{rgba($color-markdown-emphasis-info-light, .25)};\n    --color-markdown-emphasis-info-border: #{$color-markdown-emphasis-info-dark};\n    --color-markdown-emphasis-notice-background: #{rgba($color-markdown-emphasis-notice-light, .25)};\n    --color-markdown-emphasis-notice-border: #{$color-markdown-emphasis-notice-dark};\n\n    // Code (Dark Mode)\n    --color-code-header-default: #{$color-code-header-light};\n    --color-code-header-hover: #{$color-code-header-dark};\n    --color-code-metas-background: #{$color-code-metas};\n    --color-code-content-background: #{$color-code-content};\n\n    // Search (Dark Mode)\n    --color-search-lock-background: #{rgba($color-search-lock, .4)};\n    --color-search-spotlight-field-background: #{rgba($color-base, .65)};\n\n    // Header (Dark Mode)\n    --color-header-background: #{rgba($color-white, .9)};\n    --color-header-border: #{rgba($color-black, .075)};\n    --color-header-dropdown-border: #{rgba($color-grey, .65)};\n    --color-header-search-background-clear: var(--color-header-search-background-default);\n    --color-header-search-border-hover: #{color.adjust($color-grey-dark, $lightness: 10%)};\n    --color-header-search-border-active: #{color.adjust($color-grey-dark, $lightness: 14%)};\n    --color-header-search-border-clear-default: var(--color-header-search-border-default);\n    --color-header-search-border-clear-hover: var(--color-header-search-border-hover);\n    --color-header-search-border-clear-active: var(--color-header-search-border-active);\n    --color-header-search-shadow: var(--color-white);\n    --color-header-search-shortcut-text: var(--color-blue-grey-dark);\n    --color-header-search-shortcut-border: var(--color-blue-grey-dark);\n    --color-header-search-shortcut-shadow: var(--color-white);\n    --color-header-coloring-background-default: #{color.adjust($color-header-coloring-background, $lightness: -16%)};\n    --color-header-coloring-background-clear: var(--color-header-coloring-background-default);\n    --color-header-coloring-toggle-border-default: #{rgba($color-header-coloring-toggle-border, .85)};\n    --color-header-coloring-toggle-border-hover: #{$color-header-coloring-toggle-border};\n    --color-header-coloring-toggle-border-active: #{rgba($color-header-coloring-toggle-border, .95)};\n\n    // Content (Dark Mode)\n    --color-content-navigation-background: var(--color-base);\n    --color-content-navigation-border-default: var(--color-grey);\n    --color-content-navigation-border-active: #{color.adjust($color-grey, $lightness: 6%)};\n    --color-content-navigation-shadow-outset: #{rgba($color-white, .25)};\n    --color-content-navigation-shadow-inset: #{rgba($color-black, .05)};\n    --color-content-navigation-shadow-active: #{rgba($color-white, .4)};\n    --color-content-sidebar-left-background-default: var(--color-base);\n    --color-content-sidebar-left-background-small: var(--color-base);\n    --color-content-sidebar-right-background: #{$color-content-sidebar-right};\n    --color-content-sidebar-toggler-background: #{rgba($color-base, .9)};\n    --color-content-sidebar-nest-navigate-link-background-hover: #{color.adjust($color-grey-light, $lightness: -3%)};\n    --color-content-sidebar-nest-navigate-link-background-active: #{color.adjust($color-grey-light, $lightness: -1%)};\n    --color-content-sidebar-nest-navigate-link-default-toggle-background-hover: #{color.adjust($color-grey-light, $lightness: 3%)};\n    --color-content-sidebar-nest-navigate-link-default-toggle-background-active: #{color.adjust($color-grey-light, $lightness: 7%)};\n\n    // Guides (Dark Mode)\n    --color-guides-details-border: #{rgba($color-grey, .45)};\n\n    // References (Dark Mode)\n    --color-references-separator-content: #{rgba($color-grey, .45)};\n    --color-references-separator-sidebar: #{rgba($color-grey, .35)};\n    --color-references-request-target-url-border-default: transparent;\n    --color-references-request-target-url-border-hover: #{rgba($color-black, .45)};\n    --color-references-request-target-url-border-active: #{rgba($color-black, .6)};\n    --color-references-request-format-required-text: #{$color-references-request-format-required};\n    --color-references-request-format-optional-text: #{$color-references-request-format-optional};\n    --color-references-request-format-label-text: #{$color-references-request-format-label};\n    --color-references-request-format-keys-depth-border-zero: #{color.adjust($color-grey-light, $lightness: -10%)};\n    --color-references-request-format-keys-depth-border-one: #{color.adjust($color-grey-light, $lightness: -5%)};\n    --color-references-request-format-keys-depth-border-three: #{color.adjust($color-grey-light, $lightness: 5%)};\n    --color-references-request-format-keys-depth-border-four: #{color.adjust($color-grey-light, $lightness: 10%)};\n    --color-references-request-format-keys-depth-border-infinity: #{color.adjust($color-grey-light, $lightness: 15%)};\n    --color-references-request-format-toggle-count-text: #{color.adjust($color-black, $lightness: -12.5%)};\n    --color-references-request-format-toggle-count-background: #{color.adjust($color-grey-light, $lightness: -2%)};\n    --color-references-request-format-toggle-button-border: #{color.adjust($color-grey-dark, $lightness: 8%)};\n    --color-references-request-format-toggle-button-shadow: #{rgba($color-white, .25)};\n\n    // Changes (Dark Mode)\n    --color-changes-navigate-link-background-hover: #{color.adjust($color-grey-light, $lightness: -6%)};\n    --color-changes-navigate-link-background-active: #{color.adjust($color-grey-light, $lightness: -4%)};\n    --color-changes-month-event-border: #{rgba($color-grey, .65)};\n    --color-changes-month-event-deprecated-background: #{rgba($color-changes-month-event-deprecated, .3)};\n\n    // Images (Dark Mode)\n    .logo {\n      .logo-image {\n        filter: grayscale(1) invert(1) brightness(10);\n      }\n    }\n\n    .illustration {\n      filter: saturate(.15) invert(1);\n    }\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/common/_badges.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n.badge {\n  border: 1px solid var(--color-badge-black);\n  color: var(--color-badge-black);\n  user-select: none;\n  text-transform: uppercase;\n  line-height: $badge-default-height;\n  height: $badge-default-height;\n  padding: 0 5px;\n  display: inline-block;\n  border-radius: 1px;\n\n  @include font-size(10.5px);\n\n  &.badge--special-value {\n    background-color: var(--color-badge-special-background);\n    border-color: var(--color-badge-special-border);\n    color: var(--color-badge-special-text);\n    user-select: text;\n    text-transform: lowercase;\n    line-height: $badge-special-value-height;\n    height: $badge-special-value-height;\n    border-radius: 2px;\n\n    @include font-size(11.5px);\n  }\n\n  &.badge--small {\n    line-height: $badge-small-height;\n    height: $badge-small-height;\n    padding: 0 3px;\n\n    @include font-size(10px);\n  }\n\n  &.badge--white {\n    border-color: var(--color-badge-white);\n    color: var(--color-badge-white);\n  }\n\n  &.badge--blue {\n    border-color: var(--color-badge-blue);\n    color: var(--color-badge-blue);\n  }\n\n  &.badge--green {\n    border-color: var(--color-badge-green);\n    color: var(--color-badge-green);\n  }\n\n  &.badge--yellow {\n    border-color: var(--color-badge-yellow);\n    color: var(--color-badge-yellow);\n  }\n\n  &.badge--red {\n    border-color: var(--color-badge-red);\n    color: var(--color-badge-red);\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/common/_base.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\nbody {\n  font-family: \"Chappe Proxima Nova Regular\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: $font-size-base;\n  background: var(--color-base-background) !important;\n  color: var(--color-black);\n}\n\n* {\n  font-weight: normal !important;\n  margin: 0;\n  padding: 0;\n\n  @include font-smoothing(subpixel-antialiased);\n}\n\n.wrapper {\n  padding: 0 $wrapper-default-padding-sides;\n}\n\na {\n  text-decoration: none;\n}\n\n@include selection {\n  background-color: var(--color-selection-default);\n}\n\n.text-ellipsis {\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  overflow: hidden;\n}\n\n.title-group-anchor:hover .title-anchor,\n.title-anchor:hover {\n  text-decoration: underline !important;\n  text-decoration-color: var(--color-grey-dark) !important;\n}\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (max-width: $screen-medium-width-breakpoint) {\n  .wrapper {\n    padding-left: $wrapper-medium-padding-sides;\n    padding-right: $wrapper-medium-padding-sides;\n  }\n}\n\n@media screen and (max-width: $screen-small-width-breakpoint) {\n  .wrapper {\n    padding-left: $wrapper-small-padding-sides;\n    padding-right: $wrapper-small-padding-sides;\n  }\n}\n\n@media screen and (max-width: $screen-tiny-width-breakpoint) {\n  .wrapper {\n    padding-left: $wrapper-tiny-padding-sides;\n    padding-right: $wrapper-tiny-padding-sides;\n  }\n}\n\n@media screen and (max-width: $screen-lilliput-width-breakpoint) {\n  .wrapper {\n    padding-left: $wrapper-lilliput-padding-sides;\n    padding-right: $wrapper-lilliput-padding-sides;\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/common/_buttons.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n.button {\n  color: var(--color-white);\n  background-color: var(--color-button-default);\n  user-select: none;\n  line-height: $button-default-height;\n  height: $button-default-height;\n  padding: 0 20px;\n  display: inline-block;\n  box-shadow: 0 1px 0 0 var(--color-button-shadow);\n  border-radius: 3px;\n  transition: all linear 100ms;\n  transition-property: background-color, color, transform, box-shadow;\n\n  @include font-size(13px);\n\n  &:hover,\n  &:active {\n    background-color: var(--color-button-active);\n  }\n\n  &:active {\n    transform: translateY(1px);\n    box-shadow: none;\n  }\n\n  &.button--icon {\n    display: inline-flex;\n    align-items: center;\n\n    &::before,\n    &::after {\n      flex: 0 0 auto;\n    }\n\n    .button-text {\n      flex: 1;\n    }\n  }\n\n  &.button--reverse {\n    color: var(--color-button-default);\n    background-color: var(--color-white);\n    border: 1px solid var(--color-button-default);\n    transition-duration: 75ms;\n\n    &:hover {\n      color: var(--color-white);\n      background-color: var(--color-button-default);\n    }\n\n    &.button--large {\n      border-width: 1.5px;\n    }\n  }\n\n  &.button--small {\n    line-height: $button-small-height;\n    height: $button-small-height;\n  }\n\n  &.button--large {\n    padding: 0 24px;\n\n    @include font-size(14px);\n  }\n\n  &,\n  .button-text {\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    overflow: hidden;\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/common/_code.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n.code {\n  border: 1px solid transparent;\n  margin: 24px;\n  user-select: none;\n  overflow: hidden;\n  border-radius: 6px;\n  transition: border-color linear 75ms;\n\n  &[data-copy-state=\"copied\"] {\n    .code-header {\n      .code-actions {\n        .code-action {\n          &.code-action--copy {\n            width: 13px;\n            height: 16px;\n            margin-top: 1px;\n            opacity: 1;\n\n            @include mask-image-internal(\"common/code/header-action-copied.svg\");\n          }\n        }\n      }\n    }\n  }\n\n  &[data-has-request=\"false\"] {\n    .code-header {\n      .code-actions {\n        .code-action {\n          &.code-action--copy {\n            display: none;\n          }\n        }\n      }\n    }\n  }\n\n  &:hover {\n    border-color: var(--color-code-border-hover);\n\n    .code-header {\n      background-color: var(--color-code-header-hover);\n    }\n  }\n\n  .code-header,\n  .code-section .code-metas,\n  .code-section .code-content {\n    padding: 0 14px;\n  }\n\n  .code-data {\n    display: none;\n  }\n\n  .code-header {\n    background-color: var(--color-code-header-default);\n    height: 40px;\n    display: flex;\n    transition: background-color linear 75ms;\n\n    .code-language,\n    .code-actions {\n      display: flex;\n      align-items: center;\n    }\n\n    .code-language {\n      color: var(--color-code-language-text);\n      flex: 1;\n\n      @include font-size(12.5px);\n    }\n\n    .code-actions {\n      flex: 0 0 auto;\n      padding-left: 8px;\n\n      .code-action {\n        margin-right: 15px;\n        opacity: .65;\n        cursor: pointer;\n        transition: opacity linear 100ms;\n\n        @include mask-image-fill;\n\n        &:last-of-type {\n          margin-right: 0;\n        }\n\n        &:hover {\n          opacity: .85;\n        }\n\n        &:active {\n          opacity: 1;\n        }\n\n        &.code-action--copy {\n          background-color: var(--color-code-action-background);\n          width: 13px;\n          height: 15px;\n\n          @include mask-image-internal(\"common/code/header-action-copy.svg\");\n        }\n      }\n    }\n  }\n\n  .code-section {\n    .code-metas {\n      background-color: var(--color-code-metas-background);\n      line-height: 16px;\n      height: 33px;\n      display: flex;\n      flex-direction: row;\n      align-items: center;\n\n      .code-meta-name,\n      .code-meta-picker,\n      .code-meta-type {\n        margin-top: -1px;\n      }\n\n      .code-meta-name,\n      .code-meta-type {\n        letter-spacing: .1px;\n        min-width: 80px;\n        flex: 0 0 auto;\n\n        @include font-size(12.5px);\n      }\n\n      .code-meta-name,\n      .code-meta-picker {\n        border-right: 1px solid var(--color-code-meta-name-border);\n      }\n\n      .code-meta-name {\n        color: var(--color-code-meta-name-text);\n        text-align: left;\n        padding-right: 8px;\n        margin-right: 10px;\n      }\n\n      .code-meta-picker {\n        flex: 1;\n        padding-right: 10px;\n        margin-right: 8px;\n      }\n\n      .code-meta-type {\n        color: var(--color-code-meta-type-text);\n        text-align: right;\n      }\n    }\n\n    .code-content {\n      background-color: var(--color-code-content-background);\n      color: var(--color-code-content-text);\n      line-height: 16px;\n      white-space: pre;\n      user-select: none;\n      overflow: auto;\n      max-height: 640px;\n      padding-top: 10px;\n      padding-bottom: 12px;\n\n      @include font-size(11px);\n\n      code {\n        color: var(--color-white);\n        user-select: text;\n      }\n    }\n\n    @include selection {\n      background-color: var(--color-code-selection);\n    }\n  }\n\n  select {\n    $select-padding-sides: 8px;\n\n    background: transparent;\n    color: var(--color-code-select-text-default);\n    border: 1px solid var(--color-code-select-border-default);\n    background-repeat: no-repeat;\n    background-position-x: calc(100% - 8px);\n    background-position-y: 50%;\n    line-height: 23px;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    overflow: hidden;\n    width: 100%;\n    padding-left: $select-padding-sides;\n    padding-right: ($select-padding-sides + 8px);\n    outline: none;\n    cursor: pointer;\n    border-radius: 3px;\n    transition: all linear 75ms;\n    transition-property: color, border-color;\n\n    @include background-image-internal(\"common/code/select-arrow.svg\");\n    @include font-size(13px);\n    @include appearance(none);\n\n    &:hover {\n      color: var(--color-code-select-text-hover);\n      border-color: var(--color-code-select-border-hover);\n\n      option {\n        color: initial;\n      }\n    }\n\n    option {\n      color: initial;\n    }\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/common/_content.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n#content {\n  background-color: var(--color-white);\n  min-height: 120px;\n  padding-top: $header-height-outer;\n\n  .main {\n    .main-title,\n    .main-label {\n      display: block;\n      margin: 0 auto;\n    }\n\n    .main-title {\n      color: var(--color-black);\n      letter-spacing: 0;\n      text-align: center;\n\n      @include font-size(28px);\n    }\n\n    .main-label {\n      color: var(--color-blue-grey);\n      line-height: 22px;\n      letter-spacing: 0;\n      text-align: center;\n      max-width: 340px;\n      margin-top: 16px;\n\n      @include font-size(17px);\n\n      .main-label-line {\n        display: block;\n      }\n    }\n  }\n\n  .panes {\n    min-height: 100vh;\n    margin-top: (-1 * $header-height-outer);\n    display: flex;\n    flex-direction: row;\n\n    .content,\n    .sidebar {\n      overflow: hidden;\n      min-height: 200px;\n      padding-bottom: 100px;\n    }\n\n    .content {\n      flex: 1;\n      padding-top: ($header-height-outer + 34px);\n\n      &.content--padded {\n        padding-left: 36px;\n        padding-right: $wrapper-default-padding-sides;\n      }\n\n      .content-wrap {\n        width: 100%;\n        max-width: $content-wrap-max-width;\n        margin: 0 auto;\n\n        &.content-wrap--full {\n          max-width: none;\n        }\n      }\n\n      [id] {\n        &:not([data-footnote-ref], .footnotes *) {\n          $anchor-offset-top: ($header-height-outer + 16px);\n\n          pointer-events: none;\n\n          &::before {\n            content: \"\";\n            width: 0;\n            height: $anchor-offset-top;\n            margin-top: (-1 * $anchor-offset-top);\n            display: block;\n            position: relative;\n          }\n\n          a {\n            &[href^=\"#\"] {\n              pointer-events: all;\n            }\n          }\n        }\n      }\n    }\n\n    .sidebar {\n      $sidebar-item-padding-left-semi: 26px;\n      $sidebar-item-padding-right-semi: 14px;\n      $sidebar-item-icon-margin-right: 6px;\n      $sidebar-item-icon-size: ($sidebar-item-padding-left-semi - $sidebar-item-icon-margin-right);\n\n      flex: 0 0 auto;\n      position: relative;\n\n      &.sidebar--left {\n        background-color: var(--color-content-sidebar-left-background-default);\n        width: $content-sidebar-left-width;\n        padding-top: $header-height-outer;\n        padding-bottom: 30px;\n        overflow: hidden scroll;\n        position: fixed;\n        left: 0;\n        top: 0;\n        bottom: 0;\n      }\n\n      &.sidebar--right {\n        background-color: var(--color-content-sidebar-right-background);\n        width: $content-sidebar-right-default-width;\n      }\n\n      .sidebar-toggler-retract {\n        background-color: var(--color-black);\n        width: 11px;\n        height: 11px;\n        margin-right: ($sidebar-item-padding-right-semi + 2px);\n        cursor: pointer;\n        opacity: .65;\n        position: absolute;\n        top: 0;\n        right: 0;\n        display: none;\n        transition: opacity linear 100ms;\n\n        @include mask-image-internal-fill(\"common/content/sidebar-toggler-retract.svg\", $display: false);\n\n        &:hover {\n          opacity: 1;\n        }\n      }\n\n      .context {\n        border-bottom: 1px solid var(--color-grey);\n        padding: 16px $sidebar-item-padding-right-semi 16px $wrapper-default-padding-sides;\n\n        .context-name {\n          color: var(--color-black);\n          line-height: 18px;\n          letter-spacing: -.1px;\n\n          @include font-size(15px);\n\n          .context-icon {\n            vertical-align: middle;\n            width: $sidebar-item-icon-size;\n            height: $sidebar-item-icon-size;\n            margin-left: (-1 * $sidebar-item-padding-left-semi);\n            margin-right: $sidebar-item-icon-margin-right;\n            margin-top: -2px;\n            display: inline-block;\n          }\n        }\n      }\n\n      .nest {\n        user-select: none;\n        padding: 24px $sidebar-item-padding-right-semi 0 $wrapper-default-padding-sides;\n\n        &:last-of-type {\n          padding-bottom: 10px;\n        }\n\n        .nest-category {\n          letter-spacing: -.1px;\n\n          @include font-size(15px);\n\n          &,\n          .nest-category-link {\n            color: var(--color-black);\n          }\n\n          &.nest-category--active {\n            &,\n            .nest-category-link {\n              color: var(--color-accent-base);\n            }\n          }\n\n          .nest-icon {\n            vertical-align: middle;\n            width: $sidebar-item-icon-size;\n            height: $sidebar-item-icon-size;\n            margin-top: -3px;\n            margin-left: (-1 * $sidebar-item-padding-left-semi);\n            margin-right: $sidebar-item-icon-margin-right;\n            display: inline-block;\n          }\n        }\n\n        .nest-navigate {\n          margin-top: 10px;\n\n          .nest-navigate-level {\n            $level-item-margin-bottom-base: 3px;\n\n            position: relative;\n\n            &[data-expanded=\"false\"] {\n              .nest-navigate-link {\n                &.nest-navigate-link--slice {\n                  .nest-navigate-toggle {\n                    &::after {\n                      transform: rotate(-90deg);\n                    }\n                  }\n                }\n              }\n\n              .nest-navigate-level {\n                &.nest-navigate-level--second {\n                  display: none;\n                }\n              }\n            }\n\n            &.nest-navigate-level--first {\n              margin-bottom: $level-item-margin-bottom-base;\n            }\n\n            &.nest-navigate-level--second {\n              $level-item-second-padding-left-base: 13px;\n\n              margin-top: 4px;\n              margin-bottom: 10px;\n              padding-left: $level-item-second-padding-left-base;\n\n              &::before {\n                content: \"\";\n                background-color: var(--color-grey-light);\n                width: 2px;\n                position: absolute;\n                top: 0;\n                bottom: 0;\n                left: 0;\n                z-index: 2;\n              }\n\n              .nest-navigate-link {\n                margin-bottom: $level-item-margin-bottom-base;\n                margin-left: (-1 * $level-item-second-padding-left-base);\n                padding-left: $level-item-second-padding-left-base;\n                border-top-left-radius: 0;\n                border-bottom-left-radius: 0;\n                position: relative;\n                z-index: 1;\n\n                &[data-active=\"true\"] {\n                  z-index: 3;\n                }\n              }\n            }\n\n            .nest-navigate-link {\n              $link-item-padding-top-bottom: 3px;\n              $link-item-padding-right: 7px;\n\n              letter-spacing: -.1px;\n              line-height: 17px;\n              margin-left: (-1 * $sidebar-item-padding-left-semi);\n              padding: $link-item-padding-top-bottom $link-item-padding-right $link-item-padding-top-bottom $sidebar-item-padding-left-semi;\n              display: flex;\n              align-items: center;\n              border-radius: 3px;\n              transition: all linear 75ms;\n              transition-property: background-color, color;\n\n              @include font-size(13.5px);\n\n              &:hover {\n                background-color: var(--color-content-sidebar-nest-navigate-link-background-hover);\n              }\n\n              &:active {\n                background-color: var(--color-content-sidebar-nest-navigate-link-background-active);\n              }\n\n              &,\n              .nest-navigate-slice {\n                color: var(--color-content-sidebar-nest-navigate-slice-text);\n              }\n\n              &[data-active=\"true\"] {\n                background-color: var(--color-accent-base);\n\n                &,\n                .nest-navigate-slice {\n                  color: var(--color-white);\n                }\n\n                .nest-navigate-link-text {\n                  font-family: \"Chappe Proxima Nova Semibold\", sans-serif;\n                }\n\n                .nest-navigate-link-objects {\n                  .badge {\n                    border-color: var(--color-white);\n                    color: var(--color-white);\n                  }\n                }\n              }\n\n              &[data-starred=\"true\"] {\n                .nest-navigate-link-text {\n                  font-family: \"Chappe Proxima Nova Semibold\", sans-serif;\n                }\n              }\n\n              &.nest-navigate-link--external {\n                color: var(--color-accent-base);\n\n                &::after {\n                  content: \"\";\n                  background-color: var(--color-accent-base);\n                  width: 11px;\n                  height: 11px;\n                  margin-left: 6px;\n\n                  @include mask-image-internal-fill(\"common/content/sidebar-link-external-icon.svg\");\n                }\n              }\n\n              &.nest-navigate-link--slice {\n                align-items: center;\n                display: flex;\n\n                .nest-navigate-toggle {\n                  cursor: pointer;\n                  margin-left: 3px;\n                  margin-right: (-1 * ($link-item-padding-right - $link-item-padding-top-bottom));\n                  padding: 0 5px;\n                  flex: 0 0 auto;\n                  border-radius: 2px;\n                  transition: background-color linear 75ms;\n\n                  &::after {\n                    content: \"\";\n                    background-color: var(--color-content-sidebar-nest-navigate-slice-icon-background-default);\n                    vertical-align: middle;\n                    width: 9px;\n                    height: 6px;\n                    margin-top: -1px;\n                    opacity: .5;\n                    transition: all linear 100ms;\n                    transition-property: opacity, transform;\n\n                    @include mask-image-internal-fill(\"common/content/sidebar-link-slice-icon.svg\");\n                  }\n\n                  &:hover {\n                    background-color: var(--color-content-sidebar-nest-navigate-link-default-toggle-background-hover);\n\n                    &::after {\n                      opacity: 1;\n                    }\n                  }\n\n                  &:active {\n                    background-color: var(--color-content-sidebar-nest-navigate-link-default-toggle-background-active);\n                  }\n                }\n\n                &[data-active=\"true\"] {\n                  .nest-navigate-toggle {\n                    &::after {\n                      background-color: var(--color-content-sidebar-nest-navigate-slice-icon-background-active);\n                    }\n\n                    &:hover {\n                      background-color: var(--color-content-sidebar-nest-navigate-link-active-toggle-background-hover);\n                    }\n\n                    &:active {\n                      background-color: var(--color-content-sidebar-nest-navigate-link-active-toggle-background-active);\n                    }\n                  }\n                }\n\n                .nest-navigate-slice {\n                  flex: 1;\n                }\n              }\n\n              .nest-navigate-link-text {\n                text-overflow: ellipsis;\n                white-space: nowrap;\n                overflow: hidden;\n                flex: 1;\n              }\n\n              .nest-navigate-link-objects {\n                margin-left: 4px;\n                display: flex;\n                align-items: center;\n              }\n            }\n          }\n        }\n      }\n    }\n\n    .sidebar-toggler {\n      $sidebar-toggler-size: 36px;\n\n      background-color: var(--color-content-sidebar-toggler-background);\n      line-height: $sidebar-toggler-size;\n      text-align: center;\n      width: $sidebar-toggler-size;\n      height: $sidebar-toggler-size;\n      margin-left: $wrapper-default-padding-sides;\n      cursor: pointer;\n      display: none;\n      position: fixed;\n      top: ($header-height + $header-border-size);\n      left: 0;\n      z-index: 50;\n      box-shadow: 0 1px 5px 0 var(--color-content-sidebar-toggler-shadow-default);\n      border-radius: 100%;\n      transition: box-shadow linear 150ms;\n\n      &:hover {\n        box-shadow: 0 2px 6px 0 var(--color-content-sidebar-toggler-shadow-hover);\n\n        &::after {\n          opacity: .9;\n        }\n      }\n\n      &:active {\n        box-shadow: 0 1px 3px 0 var(--color-content-sidebar-toggler-shadow-active);\n\n        &::after {\n          opacity: 1;\n        }\n      }\n\n      &::after {\n        content: \"\";\n        background-color: var(--color-content-sidebar-toggler-icon-background);\n        vertical-align: middle;\n        width: 16px;\n        height: 16px;\n        margin-top: -1px;\n        opacity: .7;\n        transition: opacity linear 150ms;\n\n        @include mask-image-internal-fill(\"common/content/sidebar-toggler-icon.svg\");\n      }\n\n\n      @include backdrop-filter(blur(4px) saturate(160%) contrast(45%) brightness(140%));\n    }\n  }\n\n  .bulletpoints .bulletpoint,\n  .navigation .navigation-link {\n    background-color: var(--color-content-navigation-background);\n    border: 1px solid var(--color-content-navigation-border-default);\n    text-align: left;\n    box-shadow: 0 2px 1px 0 var(--color-content-navigation-shadow-outset), inset 0 1px 0 0 var(--color-content-navigation-shadow-inset);\n    border-radius: 2px;\n    transition: all linear 100ms;\n    transition-property: border-color, box-shadow, transform;\n  }\n\n  .bulletpoints {\n    display: grid;\n    gap: 18px 24px;\n    grid-template-columns: 1fr 1fr 1fr;\n\n    .bulletpoint {\n      padding: 20px 30px 18px 26px;\n\n      &::before {\n        content: \"\";\n        background-color: var(--color-accent-base);\n        height: 32px;\n        width: 32px;\n        margin-bottom: 16px;\n\n        @include mask-image-fill;\n      }\n\n      .bulletpoint-title {\n        color: var(--color-black);\n\n        @include font-size(18.5px);\n      }\n\n      .bulletpoint-label {\n        color: var(--color-content-bulletpoint-label-text);\n        line-height: 18px;\n        margin-top: 9px;\n\n        @include font-size(14px);\n      }\n\n      .bulletpoint-actions {\n        margin-top: 18px;\n        margin-bottom: -1px;\n\n        .bulletpoint-action {\n          color: var(--color-accent-base);\n          line-height: 18px;\n          margin-right: 22px;\n\n          @include font-size(14px);\n\n          &:last-of-type {\n            margin-right: 0;\n          }\n\n          &:hover {\n            text-decoration: underline;\n          }\n\n          &.bulletpoint-action--single {\n            &:hover {\n              &::after {\n                transform: translateX(1px);\n              }\n            }\n\n            &,\n            &:active {\n              &::after {\n                transform: translateX(0);\n              }\n            }\n\n            &::after {\n              content: \"\";\n              background-color: var(--color-accent-base);\n              width: 13px;\n              height: 9px;\n              margin-left: 4px;\n              vertical-align: middle;\n              transition: transform linear 100ms;\n\n              @include mask-image-internal-fill(\"common/content/bulletpoint-action-arrow.svg\");\n            }\n          }\n        }\n      }\n    }\n  }\n\n  .navigation {\n    user-select: none;\n    margin-top: 20px;\n    display: grid;\n    gap: 7px 18px;\n    grid-template-columns: 1fr 1fr;\n\n    .navigation-link {\n      &:hover {\n        border-color: var(--color-content-navigation-border-active);\n      }\n\n      &:active {\n        transform: translateY(1px);\n        box-shadow: 0 1px 1px 0 var(--color-content-navigation-shadow-active);\n      }\n    }\n\n    .navigation-item {\n      margin: 0;\n      padding: 0 0 3px;\n      overflow: hidden;\n\n      &::before {\n        display: none;\n      }\n\n      .navigation-link {\n        padding: 10px 14px 10px 20px;\n        text-decoration: none;\n        display: flex;\n        align-items: center;\n\n        &:hover {\n          .navigation-action {\n            text-decoration: underline;\n\n            &::after {\n              transform: translateX(1px);\n            }\n          }\n        }\n\n        &,\n        &:active {\n          .navigation-action {\n            &::after {\n              transform: translateX(0);\n            }\n          }\n        }\n\n        .navigation-text {\n          flex: 1;\n\n          &,\n          .navigation-title,\n          .navigation-label {\n            text-overflow: ellipsis;\n            white-space: nowrap;\n            overflow: hidden;\n            margin: 0;\n          }\n\n          .navigation-title,\n          .navigation-label {\n            display: block;\n          }\n\n          .navigation-title {\n            color: var(--color-black);\n            line-height: 20px;\n\n            @include font-size(14.5px);\n          }\n\n          .navigation-label {\n            color: var(--color-content-navigation-label-text);\n            line-height: 18px;\n            margin-top: 3px;\n\n            @include font-size(13.5px);\n          }\n        }\n\n        .navigation-action {\n          color: var(--color-accent-base);\n          margin-left: 8px;\n          flex: 0 0 auto;\n\n          @include font-size(13.5px);\n\n          &::after {\n            content: \"\";\n            background-color: var(--color-accent-base);\n            width: 11px;\n            height: 8px;\n            margin-top: -1px;\n            margin-left: 4px;\n            vertical-align: middle;\n            transition: transform linear 100ms;\n\n            @include mask-image-internal-fill(\"common/content/navigation-item-link-arrow.svg\");\n          }\n        }\n      }\n    }\n  }\n}\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (max-width: $screen-large-width-breakpoint) {\n  #content {\n    .panes {\n      .sidebar {\n        &.sidebar--right {\n          width: $content-sidebar-right-large-width;\n        }\n      }\n    }\n  }\n}\n\n@media screen and (max-width: $screen-medium-width-breakpoint) {\n  #content {\n    .panes {\n      .content {\n        &.content--padded {\n          padding-left: 28px;\n          padding-right: $wrapper-medium-padding-sides;\n        }\n      }\n\n      .sidebar {\n        &.sidebar--right {\n          display: none;\n        }\n      }\n\n      .sidebar-toggler {\n        margin-left: $wrapper-medium-padding-sides;\n      }\n    }\n\n    .bulletpoints {\n      column-gap: 16px;\n    }\n  }\n}\n\n@media screen and (max-width: $screen-small-width-breakpoint) {\n  #content {\n    .panes {\n      .content {\n        &.content--padded {\n          padding-left: $wrapper-small-padding-sides;\n          padding-right: $wrapper-small-padding-sides;\n        }\n      }\n\n      .sidebar {\n        &.sidebar--left {\n          background-color: var(--color-content-sidebar-left-background-small);\n          padding-top: 20px;\n          padding-bottom: 20px;\n          overflow: hidden auto;\n          position: fixed;\n          top: ($header-height + $header-border-size);\n          bottom: 0;\n          left: (-1 * ($content-sidebar-left-width + 10px));\n          z-index: 75;\n          transition: left ease 200ms;\n          box-shadow: 1px 0 5px 0 var(--color-content-sidebar-left-shadow-small);\n\n          &[data-visible=\"true\"] {\n            left: 0;\n          }\n\n          .sidebar-toggler-retract {\n            margin-top: 11px;\n            display: inline-block;\n          }\n\n          @include backdrop-filter(blur(4px) saturate(160%) contrast(45%) brightness(140%));\n        }\n\n        .nest {\n          &:last-of-type {\n            padding-bottom: 40px;\n          }\n        }\n      }\n\n      .sidebar-toggler {\n        margin-left: $wrapper-small-padding-sides;\n        display: block;\n      }\n    }\n\n    .bulletpoints {\n      row-gap: 14px;\n      grid-template-columns: 1fr 1fr;\n    }\n\n    .navigation {\n      row-gap: 5px;\n      grid-template-columns: 1fr;\n    }\n  }\n}\n\n@media screen and (max-width: $screen-tiny-width-breakpoint) {\n  #content {\n    .panes {\n      .content {\n        padding-top: ($header-height-outer + 22px);\n        padding-bottom: 74px;\n\n        &.content--padded {\n          padding-left: $wrapper-tiny-padding-sides;\n          padding-right: $wrapper-tiny-padding-sides;\n        }\n      }\n\n      .sidebar {\n        &.sidebar--left {\n          background-color: var(--color-content-sidebar-left-background-tiny);\n          visibility: hidden;\n          opacity: 0;\n          width: auto;\n          left: 0;\n          right: 0;\n          transition: opacity linear 150ms;\n          box-shadow: none;\n\n          &[data-visible=\"true\"] {\n            visibility: visible;\n            opacity: 1;\n          }\n\n          @include backdrop-filter(none);\n        }\n      }\n\n      .sidebar-toggler {\n        margin-left: $wrapper-tiny-padding-sides;\n      }\n    }\n\n    .bulletpoints {\n      row-gap: 12px;\n      grid-template-columns: 1fr;\n    }\n  }\n}\n\n@media screen and (max-width: $screen-lilliput-width-breakpoint) {\n  #content {\n    .panes {\n      .content {\n        &.content--padded {\n          padding-left: $wrapper-lilliput-padding-sides;\n          padding-right: $wrapper-lilliput-padding-sides;\n        }\n      }\n\n      .sidebar-toggler {\n        margin-left: $wrapper-lilliput-padding-sides;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/common/_fonts.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n// - Proxima Nova\n\n// Regular\n\n@include font-face-include(\"Chappe Proxima Nova Regular\", \"proxima_nova/proxima_nova_regular\", 400, normal);\n@include font-face-include(\"Chappe Proxima Nova Regular\", \"proxima_nova/proxima_nova_regular_italic\", 400, italic);\n\n\n// Semibold\n\n@include font-face-include(\"Chappe Proxima Nova Semibold\", \"proxima_nova/proxima_nova_semibold\", 600, normal);\n@include font-face-include(\"Chappe Proxima Nova Semibold\", \"proxima_nova/proxima_nova_semibold_italic\", 600, italic);\n\n\n// Bold\n\n@include font-face-include(\"Chappe Proxima Nova Bold\", \"proxima_nova/proxima_nova_bold\", 700, normal);\n@include font-face-include(\"Chappe Proxima Nova Bold\", \"proxima_nova/proxima_nova_bold_italic\", 700, italic);\n\n// - Hack\n\n// Regular\n\n@include font-face-include(\"Chappe Hack Regular\", \"hack/hack_regular\", 400, normal);\n\n// - Bindings\n\n// - Sans Serif\n\n.font-sans-regular {\n  font-family: \"Chappe Proxima Nova Regular\", sans-serif;\n}\n\n.font-sans-semibold {\n  font-family: \"Chappe Proxima Nova Semibold\", sans-serif;\n}\n\n.font-sans-bold {\n  font-family: \"Chappe Proxima Nova Bold\", sans-serif;\n}\n\n.font-code-regular {\n  font-family: \"Chappe Hack Regular\", sans-serif;\n}\n"
  },
  {
    "path": "src/stylesheets/common/_footer.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n#footer {\n  display: flex;\n  flex-direction: row;\n\n  .inner {\n    border-top: 1px solid var(--color-grey);\n    padding: 30px 0 40px;\n    flex: 1;\n  }\n\n  .wrapper {\n    .navigation,\n    .metadata,\n    .left,\n    .right {\n      height: 100%;\n      display: flex;\n      flex-direction: row;\n      align-items: center;\n    }\n\n    .left {\n      flex: 1;\n    }\n\n    .right {\n      flex: 0 0 auto;\n      padding-left: 12px;\n    }\n  }\n\n  .navigation {\n    .links .link .link-target,\n    .copyright {\n      letter-spacing: -.1px;\n\n      @include font-size(13.5px);\n    }\n\n    .links {\n      user-select: none;\n\n      .link {\n        margin-right: 20px;\n        display: inline-block;\n\n        &:last-of-type {\n          margin-right: 0;\n        }\n\n        .link-target {\n          color: var(--color-footer-link-target-text);\n\n          &:hover {\n            text-decoration: underline;\n          }\n        }\n      }\n    }\n\n    .copyright {\n      color: var(--color-footer-copyright-text);\n    }\n  }\n\n  .metadata {\n    margin-top: 38px;\n\n    .brand {\n      display: flex;\n      flex-direction: row;\n      align-items: center;\n\n      .logo {\n        height: 26px;\n\n        @include background-image-fill;\n      }\n\n      .separator {\n        background-color: var(--color-footer-brand-separator);\n        width: 1px;\n        height: 22px;\n        margin: -1px 20px 0;\n      }\n\n      .engine {\n        letter-spacing: -.11px;\n\n        @include font-size(14px);\n\n        &,\n        a {\n          color: var(--color-footer-engine-text);\n        }\n\n        a {\n          text-decoration: underline;\n        }\n      }\n    }\n\n    .status {\n      visibility: visible;\n      opacity: 1;\n      transition: opacity linear 100ms;\n\n      &[data-status=\"none\"] {\n        visibility: hidden;\n        opacity: 0;\n      }\n\n      &[data-status=\"none\"],\n      &[data-status=\"failure\"] {\n        .status-wrapped {\n          .status-text {\n            .status-state {\n              .status-state-variant {\n                &.status-state-variant--failure {\n                  display: block;\n                }\n              }\n            }\n          }\n        }\n      }\n\n      &[data-status=\"healthy\"] {\n        .status-wrapped {\n          .status-icon {\n            @include background-image-internal(\"common/footer/status-healthy.svg\");\n          }\n\n          .status-text {\n            .status-state {\n              color: var(--color-status-healthy-text);\n\n              .status-state-variant {\n                &.status-state-variant--healthy {\n                  display: block;\n                }\n              }\n            }\n          }\n        }\n      }\n\n      &[data-status=\"sick\"] {\n        .status-wrapped {\n          .status-icon {\n            @include background-image-internal(\"common/footer/status-sick.svg\");\n          }\n\n          .status-text {\n            .status-state {\n              color: var(--color-status-sick-text);\n\n              .status-state-variant {\n                &.status-state-variant--sick {\n                  display: block;\n                }\n              }\n            }\n          }\n        }\n      }\n\n      &[data-status=\"dead\"] {\n        .status-wrapped {\n          .status-icon {\n            @include background-image-internal(\"common/footer/status-dead.svg\");\n          }\n\n          .status-text {\n            .status-state {\n              color: var(--color-status-dead-text);\n\n              .status-state-variant {\n                &.status-state-variant--dead {\n                  display: block;\n                }\n              }\n            }\n          }\n        }\n      }\n\n      .status-wrapped {\n        border: 1px solid var(--color-footer-status-border-default);\n        user-select: none;\n        padding: 5px 30px 5px 18px;\n        border-radius: 1px;\n        display: flex;\n        flex-direction: row;\n        align-items: center;\n        transition: border-color linear 100ms;\n\n        &:hover {\n          border-color: var(--color-footer-status-border-hover);\n          cursor: pointer;\n        }\n\n        .status-icon {\n          width: 24px;\n          height: 24px;\n\n          @include background-image-internal-fill(\"common/footer/status-failure.svg\");\n        }\n\n        .status-text {\n          padding-left: 18px;\n\n          .status-state,\n          .status-time {\n            display: block;\n          }\n\n          .status-state {\n            color: var(--color-status-default-text);\n            letter-spacing: 0;\n            line-height: 20px;\n\n            @include font-size(13.5px);\n\n            .status-state-variant {\n              display: none;\n            }\n          }\n\n          .status-time {\n            color: var(--color-footer-status-time-text);\n            letter-spacing: -.1px;\n            line-height: 18px;\n\n            @include font-size(12.5px);\n          }\n        }\n      }\n    }\n  }\n}\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (max-width: $screen-medium-width-breakpoint) {\n  #footer {\n    .wrapper {\n      .navigation,\n      .metadata {\n        display: block;\n      }\n\n      .navigation {\n        .left,\n        .right {\n          text-align: center;\n          padding: 0;\n          display: block;\n        }\n\n        .right {\n          margin-top: 20px;\n        }\n\n        .links {\n          line-height: 19px;\n\n          .link {\n            margin: 0 8px;\n          }\n        }\n      }\n\n      .metadata {\n        margin-top: 28px;\n\n        .left,\n        .right {\n          justify-content: center;\n          padding: 0;\n        }\n\n        .right {\n          margin-top: 36px;\n        }\n      }\n    }\n  }\n}\n\n@media screen and (max-width: $screen-tiny-width-breakpoint) {\n  #footer {\n    .metadata {\n      .brand {\n        flex-direction: column;\n\n        .separator {\n          display: none;\n        }\n\n        .engine {\n          line-height: 18px;\n          text-align: center;\n          margin-top: 24px;\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/common/_header.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n#header {\n  background-color: var(--color-header-background);\n  border-bottom: $header-border-size solid var(--color-header-border);\n  height: $header-height;\n  position: fixed;\n  top: 0;\n  left: 0;\n  right: 0;\n  z-index: 100;\n  box-shadow: 0 2px 6px 0 var(--color-header-shadow);\n\n  @include backdrop-filter(blur(4px) saturate(160%) contrast(45%) brightness(140%));\n\n  .wrapper {\n    &,\n    .left,\n    .right {\n      height: 100%;\n      display: flex;\n      flex-direction: row;\n    }\n\n    .left,\n    .right {\n      align-items: center;\n    }\n\n    .left {\n      flex: 1;\n    }\n\n    .right {\n      flex: 0 0 auto;\n      padding-left: 12px;\n    }\n  }\n\n  .logo {\n    color: var(--color-black);\n    margin-right: 60px;\n\n    .logo-image {\n      height: 24px;\n\n      @include background-image-fill;\n\n      &.logo-image--full {\n        display: block;\n      }\n\n      &.logo-image--short {\n        display: none;\n      }\n    }\n  }\n\n  .menu,\n  .extras {\n    height: 100%;\n    display: flex;\n    align-items: center;\n    flex-direction: row;\n  }\n\n  .menu .menu-item,\n  .extras .extras-item {\n    user-select: none;\n    height: 100%;\n    padding: 0 2px;\n    display: flex;\n    align-items: center;\n    flex-direction: row;\n    position: relative;\n\n    &::after {\n      content: \"\";\n      background-color: var(--color-grey);\n      height: 0;\n      position: absolute;\n      left: 0;\n      right: 0;\n      bottom: -1px;\n      transition: none;\n    }\n  }\n\n  .menu .menu-item .menu-link,\n  .extras .extras-item .extras-link {\n    height: 100%;\n    display: flex;\n    align-items: center;\n  }\n\n  .menu .menu-item:hover,\n  .menu .menu-item.menu-item--active,\n  .extras .extras-item.extras-item--active {\n    &::after {\n      height: 3px;\n      transition: height linear 100ms;\n    }\n  }\n\n  .menu .menu-item.menu-item--active,\n  .extras .extras-item.extras-item--active {\n    &::after {\n      background-color: var(--color-accent-base);\n    }\n  }\n\n  .dropdown {\n    $dropdown-padding-edges: 8px;\n    $dropdown-link-padding-sides: 8px;\n\n    background-color: var(--color-white);\n    border: 1px solid var(--color-header-dropdown-border);\n    padding: ($header-height - 15px) $dropdown-padding-edges $dropdown-padding-edges;\n    display: none;\n    position: absolute;\n    z-index: 1;\n    top: 7px;\n    left: (-1 * ($dropdown-padding-edges + $dropdown-link-padding-sides));\n    box-shadow: 0 3px 5px 0 var(--color-header-dropdown-shadow);\n    border-radius: 3px;\n\n    .dropdown-link {\n      color: var(--color-black);\n      line-height: 28px;\n      letter-spacing: -.1px;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n      overflow: hidden;\n      margin-bottom: 2px;\n      padding: 0 32px 0 ($dropdown-link-padding-sides + 1px);\n      display: block;\n      border-radius: 3px;\n\n      @include font-size(13.5px);\n\n      &:hover,\n      &:active {\n        color: var(--color-white);\n      }\n\n      &:hover {\n        background-color: var(--color-header-dropdown-link-background-hover);\n\n        &:active {\n          transition: background-color linear 100ms;\n        }\n      }\n\n      &:active {\n        background-color: var(--color-header-dropdown-link-background-active);\n      }\n\n      &:last-of-type {\n        margin-bottom: 0;\n      }\n\n      &.dropdown-link--multiple {\n        line-height: 18px;\n        padding-top: 6px;\n        padding-bottom: 6px;\n\n        .dropdown-link-label {\n          margin-top: 1px;\n        }\n      }\n\n      &.dropdown-link--external {\n        padding-right: 42px;\n        position: relative;\n\n        &::after {\n          $icon-size: 12px;\n\n          content: \"\";\n          background-color: var(--color-black);\n          width: $icon-size;\n          height: $icon-size;\n          margin-top: (-1 * ($icon-size * 0.5));\n          opacity: .3;\n          position: absolute;\n          right: ($dropdown-link-padding-sides + 1px);\n          top: 50%;\n\n          @include mask-image-internal-fill(\"common/header/actions-dropdown-external.svg\");\n        }\n\n        &:hover,\n        &:active {\n          &::after {\n            background-color: var(--color-white);\n            opacity: 1;\n          }\n        }\n      }\n\n      .dropdown-link-title,\n      .dropdown-link-label {\n        display: block;\n      }\n\n      .dropdown-link-label {\n        opacity: .8;\n\n        @include font-size(13px);\n      }\n    }\n  }\n\n  .menu {\n    .menu-item {\n      margin-right: 28px;\n      position: relative;\n\n      &:hover {\n        .dropdown {\n          display: block;\n        }\n      }\n\n      &:last-of-type {\n        margin-right: 0;\n      }\n\n      .menu-link {\n        color: var(--color-black);\n\n        @include font-size(14px);\n\n        &.menu-link--dropdown {\n          position: relative;\n          z-index: 2;\n\n          &::after {\n            content: \"\";\n            background-color: var(--color-black);\n            width: 8px;\n            height: 5px;\n            margin-top: 1px;\n            margin-left: 5px;\n            opacity: .35;\n\n            @include mask-image-internal-fill(\"common/header/menu-arrow.svg\");\n          }\n        }\n      }\n    }\n  }\n\n  .extras {\n    .extras-item {\n      .extras-link {\n        color: var(--color-accent-base);\n        letter-spacing: -.09px;\n\n        @include font-size(13px);\n      }\n    }\n  }\n\n  .search {\n    background: var(--color-header-search-background-default);\n    border: 1px solid var(--color-header-search-border-default);\n    height: 32px;\n    margin-left: 18px;\n    padding: 0 10px 0 12px;\n    display: flex;\n    align-items: center;\n    user-select: none;\n    border-radius: 3px;\n    box-shadow: 0 1px 0 0 var(--color-header-search-shadow);\n    transition: border-color linear 100ms;\n\n    &:hover {\n      border-color: var(--color-header-search-border-hover);\n      cursor: pointer;\n    }\n\n    &:active {\n      border-color: var(--color-header-search-border-active);\n    }\n\n    .search-placeholder {\n      color: var(--color-header-search-placeholder-text);\n      letter-spacing: -.1px;\n\n      @include font-size(13px);\n\n      &::before {\n        content: \"\";\n        background-color: var(--color-blue-grey-dark);\n        width: 14px;\n        height: 14px;\n        margin-top: -2px;\n        margin-right: 10px;\n        vertical-align: middle;\n\n        @include mask-image-internal-fill(\"common/header/search-icon.svg\");\n      }\n    }\n\n    .search-shortcut {\n      background: var(--color-white);\n      border: 1px solid var(--color-header-search-shortcut-border);\n      color: var(--color-header-search-shortcut-text);\n      letter-spacing: -.2px;\n      text-align: center;\n      height: 20px;\n      line-height: 21px;\n      margin-left: 20px;\n      padding: 0 5px;\n      box-shadow: 0 1px 0 0 var(--color-header-search-shortcut-shadow);\n      border-radius: 3px;\n\n      @include font-size(12px);\n    }\n  }\n\n  .coloring {\n    $coloring-height: 16px;\n    $coloring-width: 32px;\n    $coloring-toggle-size: 24px;\n\n    background-color: var(--color-header-coloring-background-default);\n    width: $coloring-width;\n    height: $coloring-height;\n    margin: 0 32px;\n    cursor: pointer;\n    position: relative;\n    border-radius: ($coloring-height * 0.5);\n\n    &:hover {\n      .toggle {\n        border-color: var(--color-header-coloring-toggle-border-hover);\n        box-shadow: 0 1px 4px 0 var(--color-header-coloring-toggle-shadow-hover);\n      }\n    }\n\n    &:active {\n      .toggle {\n        border-color: var(--color-header-coloring-toggle-border-active);\n        box-shadow: 0 1px 1px 0 var(--color-header-coloring-toggle-shadow-active);\n      }\n    }\n\n    &[data-mode=\"light\"] {\n      .toggle {\n        margin-left: (-1 * ($coloring-toggle-size * 0.25));\n\n        &::before {\n          background-color: var(--color-header-coloring-toggle-icon-light-background);\n          width: 14px;\n          height: 14px;\n\n          @include mask-image-internal(\"common/header/coloring-mode-light-icon.svg\");\n        }\n      }\n    }\n\n    &[data-mode=\"dark\"] {\n      .toggle {\n        margin-left: ($coloring-width - (($coloring-toggle-size + 2px) - ($coloring-toggle-size * 0.25)));\n\n        &::before {\n          background-color: var(--color-header-coloring-toggle-icon-dark-background);\n          width: 12px;\n          height: 12px;\n\n          @include mask-image-internal(\"common/header/coloring-mode-dark-icon.svg\");\n        }\n      }\n    }\n\n    .toggle {\n      background-color: var(--color-white);\n      border: 1px solid var(--color-header-coloring-toggle-border-default);\n      height: $coloring-toggle-size;\n      width: $coloring-toggle-size;\n      margin-top: (-1 * (($coloring-toggle-size * 0.5) - ($coloring-height * 0.5) + 1px));\n      top: 0;\n      left: 0;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      position: absolute;\n      border-radius: 100%;\n      box-shadow: 0 1px 3px 0 var(--color-header-coloring-toggle-shadow-default);\n      transition: all linear 100ms;\n      transition-property: border-color, margin-left, box-shadow;\n\n      &::before {\n        content: \"\";\n\n        @include mask-image-fill;\n      }\n    }\n  }\n\n  .actions {\n    .action {\n      position: relative;\n\n      &:hover {\n        .dropdown {\n          display: block;\n        }\n\n        .action-button {\n          &.button {\n            background-color: transparent !important;\n            color: var(--color-black) !important;\n            box-shadow: none !important;\n            transition: none !important;\n          }\n\n          &::after {\n            background-color: var(--color-black);\n          }\n        }\n      }\n\n      .dropdown {\n        left: auto;\n        right: -5px;\n        top: -5px;\n      }\n\n      .action-button {\n        position: relative;\n        z-index: 2;\n\n        &:active {\n          transform: none;\n        }\n\n        &::after {\n          content: \"\";\n          background-color: var(--color-white);\n          width: 8px;\n          height: 5px;\n          margin-left: 7px;\n          margin-right: -3px;\n          vertical-align: middle;\n          opacity: .55;\n          transition: opacity linear 100ms;\n\n          @include mask-image-internal-fill(\"common/header/actions-arrow.svg\");\n        }\n      }\n    }\n  }\n}\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (max-width: $screen-medium-width-breakpoint) {\n  #header {\n    .logo {\n      margin-right: 42px;\n    }\n\n    .coloring {\n      margin: 0 20px;\n    }\n  }\n}\n\n@media screen and (max-width: $screen-small-width-breakpoint) {\n  #header {\n    .logo {\n      margin-right: 32px;\n    }\n\n    .menu {\n      .menu-item {\n        margin-right: 20px;\n\n        &:last-of-type {\n          margin-right: 0;\n        }\n      }\n    }\n\n    .search {\n      margin-left: 12px;\n      padding: 0 11px;\n\n      .search-placeholder-text,\n      .search-shortcut {\n        display: none;\n      }\n\n      .search-placeholder {\n        &::before {\n          margin-right: 0;\n        }\n      }\n    }\n\n    .coloring {\n      display: none;\n    }\n\n    .actions {\n      margin-left: 10px;\n    }\n  }\n}\n\n@media screen and (max-width: $screen-tiny-width-breakpoint) {\n  #header {\n    .logo {\n      margin-top: 1px;\n      margin-right: 28px;\n\n      .logo-image {\n        &.logo-image--full {\n          display: none;\n        }\n\n        &.logo-image--short {\n          display: block;\n        }\n      }\n    }\n\n    .search {\n      margin-left: 0;\n    }\n\n    .extras,\n    .actions {\n      display: none;\n    }\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/common/_highlight.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\npre {\n  code {\n    &[class*=\"language-\"] {\n      color: var(--color-highlight-main);\n\n      .namespace {\n        opacity: .7;\n      }\n\n      .token.entity,\n      .language-css .token.string,\n      .style .token.string {\n        color: var(--color-highlight-entity-light);\n        background: var(--color-highlight-entity-dark);\n      }\n\n      .token {\n        &.comment,\n        &.prolog,\n        &.cdata {\n          color: var(--color-highlight-comment);\n        }\n\n        &.punctuation {\n          color: var(--color-highlight-punctuation);\n        }\n\n        &.variable,\n        &.tag,\n        &.operator,\n        &.deleted {\n          color: var(--color-highlight-variable);\n        }\n\n        &.number,\n        &.boolean,\n        &.constant,\n        &.url {\n          color: var(--color-highlight-number);\n        }\n\n        &.class-name,\n        &.bold {\n          color: var(--color-highlight-class);\n        }\n\n        &.property,\n        &.string,\n        &.symbol,\n        &.attr-value,\n        &.inserted,\n        &.atrule {\n          color: var(--color-highlight-property);\n        }\n\n        &.regex,\n        &.important {\n          color: var(--color-highlight-regex);\n        }\n\n        &.function,\n        &.attr-name {\n          color: var(--color-highlight-function);\n        }\n\n        &.keyword,\n        &.selector,\n        &.italic,\n        &.char,\n        &.builtin {\n          color: var(--color-highlight-keyword);\n        }\n\n        &.doctype {\n          color: var(--color-highlight-doctype);\n        }\n\n        &.important,\n        &.bold {\n          font-weight: bold;\n        }\n\n        &.italic {\n          font-style: italic;\n        }\n\n        &.entity {\n          cursor: help;\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/stylesheets/common/_markdown.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n.markdown {\n  font-family: \"Chappe Proxima Nova Regular\", sans-serif;\n\n  * {\n    &:first-child {\n      margin-top: 0 !important;\n    }\n\n    &:last-child {\n      margin-bottom: 0 !important;\n    }\n  }\n\n  h1,\n  h2,\n  h3 {\n    font-family: \"Chappe Proxima Nova Bold\", sans-serif;\n  }\n\n  h4,\n  h5,\n  h6 {\n    font-family: \"Chappe Proxima Nova Semibold\", sans-serif;\n  }\n\n  h1,\n  h2,\n  h3,\n  h4,\n  h5,\n  h6 {\n    a {\n      font-family: inherit;\n      color: inherit;\n    }\n  }\n\n  h2,\n  h3,\n  h4,\n  h5,\n  h6 {\n    margin-top: 20px;\n    margin-bottom: 14px;\n  }\n\n  h3,\n  h4,\n  h5,\n  h6 {\n    color: var(--color-markdown-title-small-text);\n  }\n\n  h1,\n  h2 {\n    color: var(--color-black);\n  }\n\n  h1 {\n    margin-top: 42px;\n    margin-bottom: 20px;\n\n    @include font-size(20px);\n  }\n\n  h2 {\n    @include font-size(18px);\n  }\n\n  h3 {\n    @include font-size(17.5px);\n  }\n\n  h4 {\n    @include font-size(17px);\n  }\n\n  h5 {\n    @include font-size(16.5px);\n  }\n\n  h6 {\n    @include font-size(16px);\n  }\n\n  p,\n  ul,\n  ol,\n  blockquote,\n  .emphasis {\n    line-height: 22px;\n\n    @include font-size(14.5px);\n  }\n\n  p {\n    margin-bottom: .9em;\n  }\n\n  a {\n    color: var(--color-accent-base);\n\n    &:hover {\n      text-decoration: underline;\n    }\n  }\n\n  hr {\n    background-color: var(--color-grey-light);\n    border: 0 none;\n    height: 1px;\n    margin: 28px auto 30px;\n  }\n\n  img {\n    width: 100%;\n    margin: 16px auto;\n  }\n\n  em {\n    font-style: italic;\n  }\n\n  strong,\n  a,\n  table thead,\n  figure figcaption,\n  .embed .embed-caption {\n    font-family: \"Chappe Proxima Nova Semibold\", sans-serif;\n  }\n\n  ul,\n  ol {\n    list-style-type: none;\n    margin: 18px 0;\n\n    li {\n      line-height: 20px;\n      margin-bottom: 8px;\n      padding-left: 24px;\n      position: relative;\n\n      &:last-of-type {\n        margin-bottom: 0;\n      }\n\n      ul,\n      ol {\n        &,\n        &:first-child,\n        &:last-child {\n          margin-top: 12px !important;\n          margin-bottom: 18px !important;\n        }\n      }\n\n      ul {\n        li {\n          padding-left: 20px;\n\n          &::before {\n            background-color: var(--color-black);\n            width: 4px;\n            height: 4px;\n            margin-top: 1px;\n            border-radius: 0;\n          }\n        }\n      }\n    }\n  }\n\n  ul {\n    li {\n      &::before {\n        content: \"\";\n        background-color: var(--color-markdown-list-item-bullet);\n        width: 7px;\n        height: 7px;\n        border-radius: 100%;\n        position: absolute;\n        top: 7px;\n        left: 10px;\n      }\n    }\n  }\n\n  ol {\n    counter-reset: list-counter;\n\n    li {\n      counter-increment: list-counter;\n\n      &::before {\n        content: counter(list-counter) \".\";\n        text-align: right;\n        min-width: 19px;\n        position: absolute;\n        top: 0;\n        left: 0;\n      }\n    }\n  }\n\n  figure,\n  .embed {\n    margin: 36px auto 32px;\n  }\n\n  figure figcaption,\n  .embed .embed-caption {\n    color: var(--color-markdown-figure-text);\n    text-align: center;\n    line-height: 18px;\n    margin-top: 10px;\n    display: block;\n\n    @include font-size(14px);\n  }\n\n  figure {\n    text-align: center;\n    max-width: 70%;\n    display: block;\n\n    img {\n      border: 1px solid var(--color-markdown-figure-border);\n      width: auto;\n      max-width: 100%;\n      margin: 0 auto;\n      display: block;\n    }\n  }\n\n  code {\n    font-family: \"Chappe Hack Regular\", sans-serif;\n    font-size: .8em;\n    background-color: var(--color-markdown-code-inline-background);\n    border: 1px solid var(--color-markdown-code-inline-border);\n    color: var(--color-markdown-code-inline-text);\n    hyphens: auto;\n    word-break: break-all;\n    margin: 0 1px;\n    padding: 2px 6px;\n    display: inline;\n    border-radius: 2px;\n  }\n\n  pre {\n    margin: 22px auto;\n    position: relative;\n\n    &.copy {\n      &[data-copy-state=\"copied\"] {\n        .code-clipboard {\n          border-color: var(--color-markdown-code-block-clipboard-border-copied);\n\n          &::after {\n            background-color: var(--color-markdown-code-block-clipboard-icon-background-copied);\n            width: 16px;\n            height: 12px;\n\n            @include mask-image-internal(\"common/markdown/code-clipboard-copied.svg\");\n          }\n        }\n      }\n\n      &[data-copy-state=\"copied\"],\n      &:hover {\n        .code-clipboard {\n          visibility: visible;\n          opacity: 1;\n        }\n      }\n    }\n\n    .code-clipboard {\n      $code-clipboard-size: 30px;\n\n      background-color: var(--color-markdown-code-block-clipboard-background-default);\n      border: 1px solid var(--color-markdown-code-block-clipboard-border-default);\n      width: $code-clipboard-size;\n      height: $code-clipboard-size;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      visibility: hidden;\n      opacity: 0;\n      cursor: pointer;\n      position: absolute;\n      top: 8px;\n      right: 9px;\n      transition: all linear 75ms;\n      transition-property: background-color, border-color, opacity;\n      border-radius: 5px;\n\n      &:hover {\n        background-color: var(--color-markdown-code-block-clipboard-background-hover);\n        border-color: var(--color-markdown-code-block-clipboard-border-hover);\n      }\n\n      &:active {\n        background-color: var(--color-markdown-code-block-clipboard-background-active);\n        border-color: var(--color-markdown-code-block-clipboard-border-active);\n      }\n\n      &::after {\n        content: \"\";\n        background-color: var(--color-markdown-code-block-clipboard-icon-background-default);\n        width: 13px;\n        height: 15px;\n        transition: background-color linear 75ms;\n\n        @include mask-image-internal(\"common/markdown/code-clipboard-copy.svg\");\n      }\n    }\n\n    code {\n      background-color: var(--color-markdown-code-block-background);\n      border: 0 none;\n      color: var(--color-markdown-code-block-text);\n      line-height: 19px;\n      text-align: left;\n      white-space: pre;\n      word-spacing: normal;\n      word-break: normal;\n      overflow-wrap: normal;\n      tab-size: 4;\n      hyphens: none;\n      margin: 0;\n      padding: 15px 18px;\n      overflow: auto hidden;\n      display: block;\n    }\n\n    @include selection {\n      background-color: var(--color-markdown-code-selection);\n    }\n  }\n\n  table {\n    border-collapse: collapse;\n    text-align: left;\n    width: 100%;\n    margin: 26px 0 34px;\n\n    @include font-size(13px);\n\n    thead {\n      background-color: var(--color-markdown-table-head-background);\n      color: var(--color-markdown-table-head-text);\n      letter-spacing: .1px;\n      user-select: none;\n    }\n\n    tbody {\n      tr {\n        &:hover {\n          background-color: var(--color-markdown-table-body-row-background-hover);\n        }\n      }\n    }\n\n    th,\n    td {\n      border: 1px solid var(--color-markdown-table-cell-border);\n      line-height: 18px;\n      word-break: break-word; /* stylelint-disable-line declaration-property-value-keyword-no-deprecated */\n      hyphens: auto;\n      padding: 6px 10px 6px 16px;\n    }\n  }\n\n  blockquote,\n  .emphasis {\n    $emphasis-margin-top-bottom-base: 26px;\n\n    margin: $emphasis-margin-top-bottom-base 0;\n    padding: 14px 20px 14px 28px;\n    position: relative;\n    overflow: hidden;\n    border-radius: 2px;\n\n    & + blockquote,\n    & + .emphasis {\n      margin-top: ((-1 * $emphasis-margin-top-bottom-base) + 9px);\n    }\n\n    &::before {\n      content: \"\";\n      width: 4px;\n      position: absolute;\n      top: 0;\n      bottom: 0;\n      left: 0;\n    }\n  }\n\n  blockquote {\n    background-color: var(--color-markdown-blockquote-background);\n    color: var(--color-markdown-blockquote-text);\n    font-style: italic;\n\n    &::before {\n      background-color: var(--color-markdown-blockquote-border);\n    }\n  }\n\n  .emphasis {\n    &.emphasis--warning {\n      background-color: var(--color-markdown-emphasis-warning-background);\n\n      &::before {\n        background-color: var(--color-markdown-emphasis-warning-border);\n      }\n    }\n\n    &.emphasis--info {\n      background-color: var(--color-markdown-emphasis-info-background);\n\n      &::before {\n        background-color: var(--color-markdown-emphasis-info-border);\n      }\n    }\n\n    &.emphasis--notice {\n      background-color: var(--color-markdown-emphasis-notice-background);\n\n      &::before {\n        background-color: var(--color-markdown-emphasis-notice-border);\n      }\n    }\n  }\n\n  .embed {\n    &[data-loaded=\"true\"] {\n      .embed-wrap {\n        .embed-frame {\n          opacity: 1;\n          z-index: 2;\n        }\n\n        .embed-preview {\n          opacity: 0;\n\n          &::after {\n            display: none;\n          }\n        }\n      }\n    }\n\n    .embed-wrap {\n      height: 420px;\n      width: 100%;\n      max-width: 680px;\n      margin: 0 auto;\n      position: relative;\n\n      .embed-preview,\n      .embed-frame,\n      .embed-frame iframe {\n        background-color: var(--color-markdown-embed-wrap-background);\n      }\n\n      .embed-preview,\n      .embed-frame {\n        width: 100%;\n        height: 100%;\n        transition: opacity ease-in-out 600ms;\n      }\n\n      .embed-preview {\n        cursor: pointer;\n        opacity: 1;\n        position: relative;\n        z-index: 1;\n\n        @include background-image-fill($display: false);\n\n        &:hover {\n          &::after {\n            opacity: 1;\n          }\n        }\n\n        &:active {\n          &::after {\n            opacity: .95;\n          }\n        }\n\n        &::after {\n          $icon-size: 68px;\n\n          content: \"\";\n          width: $icon-size;\n          height: $icon-size;\n          position: absolute;\n          top: 50%;\n          left: 50%;\n          opacity: .9;\n          transition: opacity ease-in-out 300ms;\n          transform: translate(-50%, -50%);\n\n          @include background-image-internal-fill(\"common/markdown/embed-preview-play.svg\");\n        }\n      }\n\n      .embed-frame {\n        opacity: 0;\n        position: absolute;\n        top: 0;\n        left: 0;\n\n        iframe {\n          width: 100%;\n          height: 100%;\n        }\n      }\n    }\n  }\n\n  sup {\n    &:has([data-footnote-ref]) {\n      display: inline-block;\n      vertical-align: baseline;\n      font-size: smaller;\n      position: relative;\n      top: -0.4em;\n    }\n  }\n  \n  .footnotes {\n    ol {\n      list-style: none;\n\n      li {\n        counter-increment: footnote-counter;\n\n        &::before {\n          content: counter(footnote-counter) \".\";\n          top: 1px;\n        }\n      }\n    }\n  }\n}\n\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (max-width: $screen-small-width-breakpoint) {\n  .markdown {\n    figure {\n      max-width: 80%;\n    }\n\n    .embed {\n      .embed-wrap {\n        height: 380px;\n      }\n    }\n  }\n}\n\n@media screen and (max-width: $screen-tiny-width-breakpoint) {\n  .markdown {\n    figure {\n      max-width: none;\n    }\n\n    .embed {\n      .embed-wrap {\n        height: 280px;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/common/_search.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n#search {\n  display: none;\n  position: fixed;\n  inset: 0;\n  z-index: 200;\n\n  .spotlight,\n  &::after {\n    position: absolute;\n    inset: 0;\n  }\n\n  .spotlight {\n    $spotlight-padding-sides: 26px;\n    $spotlight-field-icon-size: 16px;\n    $spotlight-field-spinner-size: 12px;\n\n    padding-top: 90px;\n    z-index: 2;\n    animation-name: search-spotlight-animation;\n    animation-delay: .1s;\n    animation-duration: .2s;\n    animation-fill-mode: both;\n\n    &[data-has-results=\"false\"] {\n      .spotlight-results {\n        display: none;\n      }\n\n      .spotlight-legend {\n        background-color: var(--color-white);\n      }\n    }\n\n    &[data-index-loading=\"true\"] {\n      .spotlight-field {\n        &::after {\n          display: inline-block;\n        }\n\n        .spotlight-input {\n          padding-right: ($spotlight-padding-sides + $spotlight-field-spinner-size + 10px);\n        }\n      }\n    }\n\n    &[data-index-error=\"true\"] {\n      .spotlight-legend {\n        .spotlight-error {\n          display: block;\n        }\n      }\n    }\n\n    .spotlight-box {\n      background-color: var(--color-white);\n      width: 100%;\n      max-width: 600px;\n      margin: 0 auto;\n      overflow: hidden;\n      display: flex;\n      flex-direction: column;\n      border-radius: 6px;\n      box-shadow: 0 4px 6px 0 var(--color-search-spotlight-shadow);\n    }\n\n    .spotlight-field,\n    .spotlight-legend {\n      background-color: var(--color-search-spotlight-field-background);\n      flex: 0 0 auto;\n    }\n\n    .spotlight-field {\n      border-bottom: 1px solid var(--color-grey-dark);\n      height: 58px;\n      position: relative;\n\n      &::before,\n      &::after {\n        content: \"\";\n        position: absolute;\n        top: 50%;\n      }\n\n      &::before {\n        background-color: var(--color-blue-grey-dark);\n        width: $spotlight-field-icon-size;\n        height: $spotlight-field-icon-size;\n        margin-top: (-1 * ($spotlight-field-icon-size * 0.5));\n        left: $spotlight-padding-sides;\n\n        @include mask-image-internal-fill(\"common/search/field-icon.svg\");\n      }\n\n      &::after {\n        background: transparent;\n        border-color: var(--color-search-spotlight-field-spinner-border) var(--color-search-spotlight-field-spinner-border) transparent;\n        border-width: 2px;\n        border-style: solid;\n        width: $spotlight-field-spinner-size;\n        height: $spotlight-field-spinner-size;\n        margin-top: (-1 * (($spotlight-field-spinner-size * 0.5) + 2px));\n        right: $spotlight-padding-sides;\n        opacity: .75;\n        border-radius: 100%;\n        display: none;\n        animation-name: search-spotlight-field-spinner-animation;\n        animation-duration: 1s;\n        animation-timing-function: linear;\n        animation-fill-mode: both;\n        animation-iteration-count: infinite;\n      }\n\n      .spotlight-input {\n        background: transparent;\n        border: 0 none;\n        outline: 0 none;\n        color: var(--color-black);\n        letter-spacing: .1px;\n        padding-left: ($spotlight-padding-sides + $spotlight-field-icon-size + 14px);\n        height: 100%;\n        width: 100%;\n\n        @include font-size(15px);\n        @include appearance(none);\n\n        @include input-placeholder {\n          color: var(--color-search-spotlight-field-placeholder);\n          opacity: 1;\n        }\n\n        @include input-search {\n          @include appearance(none);\n\n          display: none;\n        }\n      }\n    }\n\n    .spotlight-results {\n      $spotlight-result-padding-scroller-top-bottom: 3px;\n\n      max-height: 380px;\n      padding: (12px - $spotlight-result-padding-scroller-top-bottom) $spotlight-padding-sides (20px - $spotlight-result-padding-scroller-top-bottom);\n      overflow: hidden auto;\n      flex: 1;\n\n      .spotlight-entries {\n        .spotlight-entry {\n          $spotlight-entry-padding-sides: 12px;\n          $spotlight-entry-padding-top-bottom: 9px;\n          $spotlight-entry-type-icon-size: 14px;\n\n          margin: 0 (-1 * $spotlight-entry-padding-sides);\n          padding-top: $spotlight-result-padding-scroller-top-bottom;\n          padding-bottom: $spotlight-result-padding-scroller-top-bottom;\n\n          &[data-selected=\"true\"] {\n            .spotlight-entry-link {\n              background-color: var(--color-search-spotlight-entry-link-background-default);\n              color: var(--color-white);\n\n              &:active {\n                background-color: var(--color-search-spotlight-entry-link-background-active);\n                transition: background-color linear 100ms;\n              }\n\n              &::after {\n                background-color: var(--color-white);\n                opacity: 1;\n              }\n\n              .spotlight-entry-path {\n                opacity: 1;\n              }\n\n              .spotlight-entry-separator {\n                background-color: var(--color-white);\n                opacity: .55;\n              }\n            }\n          }\n\n          &.spotlight-entry--page {\n            .spotlight-entry-link {\n              &::after {\n                @include mask-image-internal(\"common/search/result-type-page.svg\");\n              }\n            }\n          }\n\n          &.spotlight-entry--anchor {\n            .spotlight-entry-link {\n              &::after {\n                width: ($spotlight-entry-type-icon-size - 2px);\n                height: ($spotlight-entry-type-icon-size - 2px);\n\n                @include mask-image-internal(\"common/search/result-type-anchor.svg\");\n              }\n            }\n          }\n\n          &,\n          .spotlight-entry-link {\n            display: block;\n          }\n\n          .spotlight-entry-link {\n            color: var(--color-black);\n            letter-spacing: -.1px;\n            padding: $spotlight-entry-padding-top-bottom ($spotlight-entry-padding-sides + $spotlight-entry-type-icon-size + 1px) $spotlight-entry-padding-top-bottom $spotlight-entry-padding-sides;\n            position: relative;\n            border-radius: 3px;\n\n            &::after {\n              content: \"\";\n              background-color: var(--color-black);\n              width: $spotlight-entry-type-icon-size;\n              height: $spotlight-entry-type-icon-size;\n              opacity: .25;\n              position: absolute;\n              top: $spotlight-entry-padding-top-bottom;\n              right: $spotlight-entry-padding-sides;\n\n              @include mask-image-fill;\n            }\n\n            .spotlight-entry-path,\n            .spotlight-entry-title,\n            .spotlight-entry-preview {\n              text-overflow: ellipsis;\n              white-space: nowrap;\n              overflow: hidden;\n              display: block;\n            }\n\n            .spotlight-entry-path {\n              line-height: 14px;\n              opacity: .85;\n\n              @include font-size(12.5px);\n            }\n\n            .spotlight-entry-title {\n              line-height: 16px;\n              margin-top: 6px;\n\n              @include font-size(15px);\n            }\n\n            .spotlight-entry-preview {\n              line-height: 18px;\n              margin-top: 6px;\n              margin-bottom: -1px;\n              opacity: .8;\n\n              @include font-size(13.5px);\n            }\n\n            .spotlight-entry-separator {\n              background-color: var(--color-black);\n              vertical-align: middle;\n              width: 4px;\n              height: 8px;\n              margin: -1px 5px 0;\n              opacity: .35;\n              display: inline-block;\n\n              @include mask-image-internal-fill(\"common/search/result-separator.svg\");\n            }\n          }\n        }\n      }\n    }\n\n    .spotlight-legend {\n      user-select: none;\n      height: 32px;\n      padding: 0 $spotlight-padding-sides;\n      display: flex;\n      align-items: center;\n      flex-direction: row;\n\n      .spotlight-shortcuts {\n        display: flex;\n        align-items: center;\n        flex: 0 0 auto;\n\n        .spotlight-shortcut {\n          color: var(--color-search-spotlight-shortcut-text);\n          letter-spacing: .1px;\n          margin-top: -1px;\n          margin-right: 18px;\n          flex: 0 0 auto;\n\n          @include font-size(11px);\n\n          &:last-of-type {\n            margin-right: 0;\n          }\n\n          &.spotlight-shortcut--navigate {\n            &::before {\n              width: 10px;\n              height: 13px;\n\n              @include mask-image-internal(\"common/search/legend-icon-navigate.svg\");\n            }\n          }\n\n          &.spotlight-shortcut--go {\n            &::before {\n              width: 10px;\n              height: 10px;\n\n              @include mask-image-internal(\"common/search/legend-icon-go.svg\");\n            }\n          }\n\n          &.spotlight-shortcut--close {\n            &::before {\n              width: 14px;\n              height: 10px;\n\n              @include mask-image-internal(\"common/search/legend-icon-close.svg\");\n            }\n          }\n\n          &::before {\n            content: \"\";\n            background-color: var(--color-search-spotlight-shortcut-text);\n            vertical-align: middle;\n            margin-top: -2px;\n            margin-right: 4px;\n\n            @include mask-image-fill;\n          }\n        }\n      }\n\n      .spotlight-error {\n        color: var(--color-red);\n        text-align: right;\n        margin-top: -2px;\n        padding-left: 10px;\n        flex: 1;\n        display: none;\n\n        @include font-size(12px);\n      }\n    }\n  }\n\n  &::after {\n    content: \"\";\n    background-color: var(--color-search-lock-background);\n    z-index: 1;\n    animation-name: search-lock-animation;\n    animation-duration: .1s;\n    animation-fill-mode: both;\n  }\n}\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (max-width: $screen-medium-width-breakpoint) {\n  #search {\n    .spotlight {\n      padding-top: 76px;\n    }\n  }\n}\n\n@media screen and (max-width: $screen-tiny-width-breakpoint) {\n  #search {\n    .spotlight {\n      padding-top: $header-height;\n    }\n  }\n}\n\n// For screens below defined heights\n\n@media screen and (height <= 600px) {\n  #search {\n    .spotlight {\n      .spotlight-box {\n        height: 100%;\n        border-bottom-left-radius: 0;\n        border-bottom-right-radius: 0;\n      }\n\n      .spotlight-results {\n        max-height: none;\n      }\n    }\n  }\n}\n\n// - Keyframes\n\n@include keyframes-fix(search-lock-animation) {\n  0% {\n    opacity: 0;\n  }\n\n  100% {\n    opacity: 1;\n  }\n}\n\n@include keyframes-fix(search-spotlight-animation) {\n  0% {\n    opacity: 0;\n    transform: scale3d(.99, .99, .99) translate3d(0, 1%, 0);\n  }\n\n  50% {\n    opacity: 1;\n  }\n}\n\n@include keyframes-fix(search-spotlight-field-spinner-animation) {\n  0% {\n    transform: rotate(0deg);\n  }\n\n  100% {\n    transform: rotate(360deg);\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/common/_viewer.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n.viewer {\n  &[data-viewer-panes=\"2\"],\n  &[data-viewer-panes=\"3\"] {\n    #content {\n      .panes {\n        .content {\n          margin-left: $content-sidebar-left-width;\n        }\n      }\n    }\n\n    #footer {\n      &::before {\n        content: \"\";\n        background-color: var(--color-white);\n        width: $content-sidebar-left-width;\n        flex: 0 0 auto;\n      }\n\n      .inner {\n        border-left: 1px solid var(--color-viewer-panes-footer-border);\n      }\n    }\n  }\n\n  &[data-viewer-panes=\"3\"] {\n    #header {\n      .search {\n        background: var(--color-header-search-background-clear);\n        border-color: var(--color-header-search-border-clear-default);\n\n        &:hover {\n          border-color: var(--color-header-search-border-clear-hover);\n        }\n\n        &:active {\n          border-color: var(--color-header-search-border-clear-active);\n        }\n      }\n\n      .coloring {\n        background-color: var(--color-header-coloring-background-clear);\n      }\n    }\n  }\n}\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (max-width: $screen-medium-width-breakpoint) {\n  .viewer {\n    &[data-viewer-panes=\"3\"] {\n      #header {\n        .search {\n          background: var(--color-header-search-background-default);\n          border-color: var(--color-header-search-border-default);\n\n          &:hover {\n            border-color: var(--color-header-search-border-hover);\n          }\n\n          &:active {\n            border-color: var(--color-header-search-border-active);\n          }\n        }\n\n        .coloring {\n          background-color: var(--color-header-coloring-background-default);\n        }\n      }\n    }\n  }\n}\n\n@media screen and (max-width: $screen-small-width-breakpoint) {\n  .viewer {\n    &[data-viewer-panes=\"2\"],\n    &[data-viewer-panes=\"3\"] {\n      #content {\n        .panes {\n          .content {\n            margin-left: 0;\n          }\n        }\n      }\n\n      #footer {\n        &::before {\n          display: none;\n        }\n\n        .inner {\n          border-left: 0 none;\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/common/common.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_config\" with (\n  $inlining: $use-inline-images\n);\n\n@use \"_fonts\";\n@use \"_base\";\n@use \"_buttons\";\n@use \"_badges\";\n@use \"_markdown\";\n@use \"_code\";\n@use \"_highlight\";\n\n@use \"_header\";\n@use \"_search\";\n@use \"_content\";\n@use \"_footer\";\n\n@use \"_viewer\";\n@use \"_appearance\";"
  },
  {
    "path": "src/stylesheets/guides/_common.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n#guides {\n  .nest .nest-category .nest-icon,\n  .details .details-category .details-icon {\n    background-color: var(--color-accent-base);\n\n    @include mask-image-internal-fill(\"guides/common/category-icon-others.svg\");\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/guides/_content.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n#guides {\n  .details {\n    border-bottom: 1px solid var(--color-guides-details-border);\n    padding-bottom: 18px;\n\n    .details-upper,\n    .details-lower {\n      display: flex;\n      flex-direction: row;\n      align-items: center;\n    }\n\n    .details-upper .details-category,\n    .details-lower .details-title {\n      flex: 1;\n    }\n\n    .details-upper .details-feedback,\n    .details-lower .details-updated {\n      flex: 0 0 auto;\n      padding-left: 8px;\n\n      @include font-size(13.5px);\n    }\n\n    .details-upper .details-category,\n    .details-lower .details-updated {\n      color: var(--color-guides-details-inner-text);\n    }\n\n    .details-upper {\n      .details-category {\n        line-height: 18px;\n        margin-left: -2px;\n        overflow: visible;\n\n        @include font-size(16px);\n\n        .details-icon {\n          width: 19px;\n          height: 19px;\n          margin-top: -3px;\n          margin-right: 4px;\n          vertical-align: middle;\n\n          @include background-image-fill;\n        }\n      }\n\n      .details-feedback {\n        color: var(--color-accent-base);\n        user-select: none;\n\n        &:hover {\n          text-decoration: underline;\n        }\n      }\n    }\n\n    .details-lower {\n      margin-top: 10px;\n\n      .details-title {\n        color: var(--color-black);\n        line-height: 24px;\n\n        @include font-size(23px);\n      }\n    }\n  }\n\n  .article {\n    padding-top: 40px;\n  }\n}\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (max-width: $screen-small-width-breakpoint) {\n  #guides {\n    .details {\n      .details-upper {\n        padding-left: 48px;\n      }\n    }\n  }\n}\n\n@media screen and (max-width: $screen-tiny-width-breakpoint) {\n  #guides {\n    .details {\n      padding-left: 40px;\n      padding-right: 10px;\n\n      .details-upper .details-category,\n      .details-lower .details-title {\n        text-align: center;\n      }\n\n      .details-upper .details-feedback,\n      .details-lower .details-updated {\n        display: none;\n      }\n\n      .details-upper {\n        padding-left: 0;\n      }\n    }\n\n    .article {\n      padding-top: 30px;\n    }\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/guides/_sidebar_left.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n#guides {\n  .panes {\n    .sidebar {\n      &.sidebar--left {\n        .nest {\n          .nest-category {\n            .nest-icon {\n              @include background-image-fill;\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (max-width: $screen-small-width-breakpoint) {\n  #guides {\n    .panes {\n      .sidebar-toggler {\n        margin-top: 24px;\n      }\n    }\n  }\n}\n\n@media screen and (max-width: $screen-tiny-width-breakpoint) {\n  #guides {\n    .panes {\n      .sidebar-toggler {\n        margin-top: 30px;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/guides/guides.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_config\" with (\n  $inlining: $use-inline-images\n);\n\n@use \"_common\";\n@use \"_sidebar_left\";\n@use \"_content\";"
  },
  {
    "path": "src/stylesheets/home/home.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_config\" with (\n  $inlining: $use-inline-images\n);\n\n@use \"../_globals\" as *;\n\n#home {\n  text-align: center;\n  padding-top: 62px;\n  padding-bottom: 100px;\n\n  .illustration {\n    max-width: 560px;\n    width: 100%;\n    height: 280px;\n    margin: 48px auto -30px;\n\n    @include background-image-fill;\n  }\n\n  .actions {\n    $actions-button-margin-bottom: 7px;\n\n    margin-top: 30px;\n    margin-bottom: (-1 * $actions-button-margin-bottom);\n\n    .button {\n      margin: 0 5px $actions-button-margin-bottom;\n\n      &:hover {\n        &::after {\n          background-color: var(--color-white);\n        }\n      }\n\n      &::after {\n        content: \"\";\n        background-color: var(--color-accent-base);\n        width: 6px;\n        height: 9px;\n        margin-left: 17px;\n        margin-right: -7px;\n        flex: 0 0 auto;\n\n        @include mask-image-internal-fill(\"home/action-quickstart-icon.svg\");\n      }\n    }\n  }\n\n  .bulletpoints {\n    max-width: 940px;\n    margin: 74px auto 0;\n\n    .bulletpoint {\n      &.bulletpoint--quickstart {\n        &::before {\n          @include mask-image-internal(\"home/bulletpoint-icon-quickstart.svg\");\n        }\n      }\n\n      &.bulletpoint--guides {\n        &::before {\n          @include mask-image-internal(\"home/bulletpoint-icon-guides.svg\");\n        }\n      }\n\n      &.bulletpoint--references {\n        &::before {\n          @include mask-image-internal(\"home/bulletpoint-icon-references.svg\");\n        }\n      }\n    }\n  }\n\n  .support {\n    margin-top: 62px;\n    display: flex;\n    justify-content: center;\n\n    .support-wrap {\n      display: flex;\n      flex-direction: row;\n    }\n\n    .support-text {\n      text-align: left;\n\n      .support-question {\n        color: var(--color-black);\n        line-height: 20px;\n        margin-top: -2px;\n\n        @include font-size(15.5px);\n      }\n\n      .support-label {\n        color: var(--color-blue-grey);\n        line-height: 18px;\n        margin-top: 5px;\n\n        @include font-size(14.5px);\n      }\n    }\n\n    .support-action {\n      margin-left: 34px;\n\n      .button {\n        &:hover {\n          &::before {\n            opacity: 1;\n          }\n        }\n\n        &::before {\n          content: \"\";\n          background-color: var(--color-white);\n          width: 20px;\n          height: 18px;\n          margin-top: -1px;\n          margin-left: -2px;\n          margin-right: 12px;\n          opacity: .55;\n          transition: opacity linear 100ms;\n\n          @include mask-image-internal-fill(\"home/support-chat-icon.svg\");\n        }\n      }\n    }\n  }\n}\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (max-width: $screen-small-width-breakpoint) {\n  #home {\n    padding-top: 52px;\n\n    .illustration {\n      margin-bottom: 0;\n    }\n\n    .bulletpoints {\n      margin-top: 60px;\n    }\n  }\n}\n\n@media screen and (max-width: $screen-tiny-width-breakpoint) {\n  #home {\n    padding-top: 42px;\n    padding-bottom: 80px;\n\n    .illustration {\n      height: 200px;\n    }\n\n    .support {\n      .support-wrap {\n        display: block;\n\n        .support-text {\n          text-align: center;\n        }\n\n        .support-action {\n          margin-left: 0;\n          margin-top: 18px;\n        }\n      }\n    }\n  }\n}\n\n@media screen and (max-width: $screen-lilliput-width-breakpoint) {\n  #home {\n    .actions .button,\n    .support .support-action .button {\n      display: flex;\n    }\n\n    .actions {\n      .button {\n        margin-left: 0;\n        margin-right: 0;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/not_found/not_found.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_config\" with (\n  $inlining: $use-inline-images\n);\n\n@use \"../_globals\" as *;\n\n#not_found {\n  text-align: center;\n  padding-top: 62px;\n  padding-bottom: 86px;\n\n  .illustration {\n    max-width: 468px;\n    width: 100%;\n    height: 320px;\n    margin: 64px auto 0;\n\n    @include background-image-fill;\n  }\n\n  .actions {\n    margin-top: 30px;\n\n    .button {\n      &:hover {\n        &::before {\n          opacity: 1;\n        }\n      }\n\n      &::before {\n        content: \"\";\n        background-color: var(--color-white);\n        width: 20px;\n        height: 16px;\n        margin-top: -2px;\n        margin-left: -3px;\n        margin-right: 12px;\n        opacity: .55;\n        transition: opacity linear 100ms;\n\n        @include mask-image-internal-fill(\"not_found/action-home-icon.svg\");\n      }\n    }\n  }\n}\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (max-width: $screen-small-width-breakpoint) {\n  #not_found {\n    padding-top: 52px;\n  }\n}\n\n@media screen and (max-width: $screen-tiny-width-breakpoint) {\n  #not_found {\n    padding-top: 42px;\n    padding-bottom: 64px;\n\n    .illustration {\n      height: 260px;\n    }\n  }\n}\n\n@media screen and (max-width: $screen-lilliput-width-breakpoint) {\n  #not_found {\n    .actions {\n      .button {\n        display: flex;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/references/_content.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n$part-chunk-padding-sides: 24px;\n\n#references {\n  .content {\n    position: relative;\n\n    .sidebar {\n      &.sidebar--right {\n        position: absolute;\n        top: 0;\n        right: 0;\n        bottom: 0;\n        z-index: 0;\n      }\n    }\n  }\n\n  .details,\n  .introduction {\n    position: relative;\n    z-index: 2;\n  }\n\n  .details {\n    margin-bottom: 32px;\n\n    &.details--with-sidebar-right {\n      padding-left: $part-chunk-padding-sides;\n    }\n\n    .details-inner {\n      border-bottom: 1px solid var(--color-references-separator-content);\n      padding-bottom: 18px;\n      display: flex;\n      flex-direction: row;\n      align-items: center;\n\n      .details-title {\n        color: var(--color-black);\n        line-height: 22px;\n        margin: 0;\n        flex: 1;\n\n        @include font-size(21px);\n      }\n\n      .details-updated {\n        color: var(--color-references-details-updated-text);\n        flex: 0 0 auto;\n        padding-left: 8px;\n\n        @include font-size(13.5px);\n      }\n    }\n  }\n\n  .group,\n  .introduction,\n  .parts .specification {\n    padding: 0 $part-chunk-padding-sides;\n  }\n\n  .group,\n  .introduction,\n  .details.details--with-sidebar-right {\n    padding-right: ($content-sidebar-right-default-width + $part-chunk-padding-sides);\n  }\n\n  .group {\n    margin-bottom: 26px;\n\n    &,\n    .group-anchor {\n      color: var(--color-black);\n      line-height: 20px;\n\n      @include font-size(19px);\n    }\n\n    .group-anchor {\n      .group-origin {\n        color: var(--color-references-group-origin-text);\n        margin-left: 9px;\n      }\n    }\n  }\n\n  .introduction {\n    margin-bottom: 38px;\n  }\n\n  .parts {\n    position: relative;\n    z-index: 1;\n\n    &:last-of-type {\n      .part {\n        &:last-of-type {\n          margin-bottom: 0;\n          padding-bottom: 0;\n\n          &::before,\n          &::after {\n            display: none;\n          }\n        }\n      }\n    }\n\n    .part {\n      display: flex;\n      flex-direction: row;\n      margin-bottom: 28px;\n      padding-bottom: 32px;\n      position: relative;\n\n      &:last-of-type {\n        margin-bottom: 38px;\n      }\n\n      &.part--method-get,\n      &.part--method-head {\n        .specification {\n          .request-specification {\n            border-color: var(--color-method-get-head-light);\n\n            .request-target {\n              background-color: var(--color-method-get-head-light);\n\n              .request-target-method {\n                background-color: var(--color-method-get-head-dark);\n              }\n            }\n          }\n        }\n      }\n\n      &.part--method-post {\n        .specification {\n          .request-specification {\n            border-color: var(--color-method-post-light);\n\n            .request-target {\n              background-color: var(--color-method-post-light);\n\n              .request-target-method {\n                background-color: var(--color-method-post-dark);\n              }\n            }\n          }\n        }\n      }\n\n      &.part--method-put,\n      &.part--method-patch {\n        .specification {\n          .request-specification {\n            border-color: var(--color-method-put-patch-light);\n\n            .request-target {\n              background-color: var(--color-method-put-patch-light);\n\n              .request-target-method {\n                background-color: var(--color-method-put-patch-dark);\n              }\n            }\n          }\n        }\n      }\n\n      &.part--method-delete {\n        .specification {\n          .request-specification {\n            border-color: var(--color-method-delete-light);\n\n            .request-target {\n              background-color: var(--color-method-delete-light);\n\n              .request-target-method {\n                background-color: var(--color-method-delete-dark);\n              }\n            }\n          }\n        }\n      }\n\n      &::before,\n      &::after {\n        content: \"\";\n        height: 1px;\n        position: absolute;\n        bottom: 0;\n      }\n\n      &::before {\n        background-color: var(--color-references-separator-content);\n        left: $part-chunk-padding-sides;\n        right: $content-sidebar-right-default-width;\n      }\n\n      &::after {\n        background-color: var(--color-references-separator-sidebar);\n        width: $content-sidebar-right-default-width;\n        right: 0;\n      }\n\n      .specification {\n        $specification-max-width-outer-offset: ($content-sidebar-right-default-width + (2 * $part-chunk-padding-sides));\n\n        max-width: calc(100% - #{$specification-max-width-outer-offset});\n        flex: 1;\n\n        .request-title {\n          margin-bottom: 10px;\n\n          &,\n          .request-title-anchor {\n            color: var(--color-black);\n\n            @include font-size(17.5px);\n          }\n        }\n\n        .request-specification {\n          border: 1px solid var(--color-grey);\n          margin-top: 12px;\n          border-radius: 3px;\n\n          .request-target,\n          .request-format {\n            padding: 0 12px;\n          }\n\n          .request-target {\n            background-color: var(--color-grey);\n            height: 42px;\n            display: flex;\n            align-items: center;\n\n            .request-target-method,\n            .request-target-url {\n              line-height: 28px;\n            }\n\n            .request-target-method {\n              background-color: var(--color-black);\n              color: var(--color-white);\n              letter-spacing: -.1px;\n              user-select: none;\n              padding: 0 10px;\n              border-radius: 2px;\n              flex: 0 0 auto;\n\n              @include font-size(13px);\n            }\n\n            .request-target-url {\n              background: var(--color-references-request-target-url-background);\n              border: 1px solid var(--color-references-request-target-url-border-default);\n              margin-left: 12px;\n              padding: 0 11px 0 9px;\n              display: flex;\n              align-items: center;\n              cursor: pointer;\n              overflow: hidden;\n              border-radius: 2px;\n              flex: 1;\n              transition: border-color linear 75ms;\n\n              &[data-copy-state=\"copied\"] {\n                .request-target-copy {\n                  background-color: var(--color-green);\n                  width: 14px;\n                  height: 11px;\n                  margin-right: -1px;\n\n                  @include mask-image-internal(\"references/content/request-target-copied.svg\");\n                }\n              }\n\n              &:hover {\n                border-color: var(--color-references-request-target-url-border-hover);\n\n                .request-target-copy {\n                  opacity: .85;\n                }\n              }\n\n              &:active {\n                border-color: var(--color-references-request-target-url-border-active);\n\n                .request-target-copy {\n                  opacity: 1;\n                }\n              }\n\n              .request-target-copy {\n                content: \"\";\n                background-color: var(--color-black);\n                width: 11px;\n                height: 12px;\n                margin-left: 5px;\n                opacity: .7;\n                flex: 0 0 auto;\n                transition: opacity linear 100ms;\n\n                @include mask-image-internal-fill(\"references/content/request-target-copy.svg\");\n              }\n            }\n\n            .request-target-path {\n              color: var(--color-black);\n              text-overflow: ellipsis;\n              white-space: nowrap;\n\n              @include font-size(12px);\n\n              overflow: hidden;\n              flex: 1;\n            }\n          }\n\n          .request-format {\n            line-height: 18px;\n            padding-top: 14px;\n            padding-bottom: 14px;\n            overflow: hidden;\n\n            .markdown {\n              hyphens: auto;\n              word-break: break-word; /* stylelint-disable-line declaration-property-value-keyword-no-deprecated */\n\n              p {\n                line-height: 20px;\n              }\n\n              pre {\n                margin: 10px auto;\n              }\n\n              .emphasis {\n                $emphasis-margin-top-bottom: 12px;\n\n                margin: $emphasis-margin-top-bottom 0;\n                padding-left: 22px;\n\n                & + .emphasis {\n                  margin-top: ((-1 * $emphasis-margin-top-bottom) + 8px);\n                }\n              }\n            }\n\n            .request-format-title {\n              border-bottom: 1px solid var(--color-references-separator-content);\n              color: var(--color-black);\n              user-select: none;\n              margin-top: 18px;\n              margin-bottom: 11px;\n              padding-bottom: 6px;\n\n              @include font-size(15px);\n            }\n\n            .request-format-keys {\n              .request-format-key {\n                margin-bottom: 6px;\n\n                @include font-size(13.5px);\n\n                &:last-of-type {\n                  margin-bottom: 0;\n                }\n\n                .request-format-keys {\n                  border-left: 2px solid var(--color-references-request-format-keys-depth-border-two);\n                  margin: 7px 0;\n                  padding-left: 12px;\n\n                  &[data-depth] {\n                    border-left-color: var(--color-references-request-format-keys-depth-border-infinity);\n                  }\n\n                  &[data-depth=\"0\"] {\n                    border-left-color: var(--color-references-request-format-keys-depth-border-zero);\n                  }\n\n                  &[data-depth=\"1\"] {\n                    border-left-color: var(--color-references-request-format-keys-depth-border-one);\n                  }\n\n                  &[data-depth=\"2\"] {\n                    border-left-color: var(--color-references-request-format-keys-depth-border-two);\n                  }\n\n                  &[data-depth=\"3\"] {\n                    border-left-color: var(--color-references-request-format-keys-depth-border-three);\n                  }\n\n                  &[data-depth=\"4\"] {\n                    border-left-color: var(--color-references-request-format-keys-depth-border-four);\n                  }\n                }\n              }\n            }\n\n            .request-format-parts {\n              .request-format-type,\n              .request-format-required,\n              .request-format-optional {\n                margin-left: 8px;\n              }\n\n              .request-format-path,\n              .request-format-type,\n              .request-format-required,\n              .request-format-optional {\n                text-transform: lowercase;\n                flex: 0 0 auto;\n              }\n\n              .request-format-type {\n                color: var(--color-references-request-format-type-text);\n                user-select: none;\n                min-width: 58px;\n              }\n\n              .request-format-required,\n              .request-format-optional {\n                margin-left: 4px;\n              }\n\n              .request-format-required {\n                color: var(--color-references-request-format-required-text);\n              }\n\n              .request-format-optional {\n                color: var(--color-references-request-format-optional-text);\n              }\n\n              .request-format-head {\n                display: flex;\n                flex-direction: row;\n              }\n\n              .request-format-label {\n                color: var(--color-references-request-format-label-text);\n                hyphens: auto;\n                word-break: break-all;\n                margin-top: 1px;\n                flex: 1;\n\n                &.markdown {\n                  * {\n                    font-size: inherit;\n                    line-height: inherit;\n                  }\n\n                  code {\n                    padding: 1px 4px;\n\n                    @include font-size(11.5px);\n                  }\n\n                  p {\n                    margin-bottom: 5px;\n\n                    &:last-of-type {\n                      margin-bottom: 0;\n                    }\n                  }\n\n                  ul {\n                    line-height: 14px;\n                    margin-top: 6px !important;\n                    margin-bottom: 12px !important;\n                    padding-left: 5px !important;\n\n                    @include font-size(12px);\n\n                    li {\n                      padding-left: 15px;\n\n                      &::before {\n                        background-color: var(--color-black);\n                        width: 4px;\n                        height: 4px;\n                        top: 5px;\n                        left: 4px;\n                        border-radius: 0;\n                      }\n                    }\n                  }\n                }\n              }\n            }\n\n            .request-format-toggle-button {\n              color: var(--color-black);\n              background-color: var(--color-white);\n              border: 1px solid var(--color-references-request-format-toggle-button-border);\n              line-height: 25px;\n              white-space: nowrap;\n              height: auto;\n              margin-top: 7px;\n              margin-bottom: 5px;\n              padding: 0 10px;\n              cursor: pointer;\n              user-select: none;\n              display: inline-flex;\n              align-items: center;\n              border-radius: 2px;\n              box-shadow: 0 2px 1px 0 var(--color-references-request-format-toggle-button-shadow);\n\n              &:hover {\n                &::before {\n                  opacity: .85;\n                }\n              }\n\n              &:active {\n                box-shadow: none;\n              }\n\n              &::before {\n                content: \"\";\n                background-color: var(--color-black);\n                width: 8px;\n                height: 6px;\n                margin-right: 9px;\n                opacity: .55;\n                flex: 0 0 auto;\n                transition: all linear .15s;\n                transition-property: transform, opacity;\n\n                @include mask-image-internal-fill(\"references/content/request-format-toggle-button-arrow.svg\");\n              }\n\n              .request-format-toggle-label {\n                text-align: center;\n                text-overflow: ellipsis;\n                overflow: hidden;\n                flex: 1;\n              }\n\n              .request-format-toggle-count {\n                $toggle-count-footprint: 12px;\n\n                color: var(--color-references-request-format-toggle-count-text);\n                background-color: var(--color-references-request-format-toggle-count-background);\n                text-align: center;\n                line-height: $toggle-count-footprint;\n                min-width: $toggle-count-footprint;\n                margin-left: 8px;\n                padding: 2px;\n\n                @include font-size(10px);\n\n                flex: 0 0 auto;\n                border-radius: 100%;\n              }\n            }\n\n            .request-format-toggle-checkbox,\n            .request-format-toggle-checkbox:not(:checked) ~ .request-format-keys,\n            .request-format-toggle-checkbox:not(:checked) ~ .request-format-toggle-button .request-format-toggle-label--hide,\n            .request-format-toggle-checkbox:checked ~ .request-format-toggle-button .request-format-toggle-label--show {\n              display: none;\n            }\n\n            .request-format-toggle-checkbox:checked ~ .request-format-toggle-button {\n              &::before {\n                transform: rotate(-180deg);\n              }\n            }\n\n            .request-format-toggle-checkbox:checked ~ .request-format-keys {\n              animation-name: references-content-request-format-keys-animation;\n              animation-duration: .35s;\n              animation-fill-mode: both;\n            }\n          }\n        }\n      }\n\n      .examples {\n        width: $content-sidebar-right-default-width;\n        flex: 0 0 auto;\n\n        .examples-wrap {\n          padding: 34px 16px 0;\n        }\n\n        .examples-details {\n          .examples-detail {\n            margin-bottom: 7px;\n            display: flex;\n            flex-direction: row;\n            align-items: center;\n\n            &:last-of-type {\n              margin-bottom: 0;\n            }\n\n            @include selection {\n              background-color: var(--color-references-examples-selection);\n              color: var(--color-white);\n            }\n\n            .examples-detail-label {\n              min-width: 65px;\n              text-align: right;\n              flex: 0 0 auto;\n            }\n\n            .examples-detail-value {\n              color: var(--color-references-examples-detail-value-text);\n              line-height: 16px;\n              word-break: break-word; /* stylelint-disable-line declaration-property-value-keyword-no-deprecated */\n              margin-left: 12px;\n              flex: 1;\n\n              @include font-size(13.5px);\n\n              .badge {\n                margin-right: 5px;\n\n                &:last-of-type {\n                  margin-right: 0;\n                }\n              }\n\n              .examples-detail-segment {\n                color: var(--color-references-examples-detail-segment-text);\n              }\n\n              .examples-detail-parameter {\n                color: var(--color-references-examples-detail-parameter-text);\n              }\n            }\n          }\n        }\n\n        .code {\n          margin: 24px 0 0;\n        }\n      }\n    }\n  }\n}\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (max-width: $screen-large-width-breakpoint) {\n  #content {\n    .group,\n    .introduction,\n    .details.details--with-sidebar-right {\n      padding-right: ($content-sidebar-right-large-width + $part-chunk-padding-sides);\n    }\n\n    .parts {\n      .part {\n        .specification {\n          $specification-max-width-outer-offset: ($content-sidebar-right-large-width + (2 * $part-chunk-padding-sides));\n\n          max-width: calc(100% - #{$specification-max-width-outer-offset});\n        }\n\n        .examples {\n          width: $content-sidebar-right-large-width;\n        }\n\n        &::before {\n          right: $content-sidebar-right-large-width;\n        }\n\n        &::after {\n          width: $content-sidebar-right-large-width;\n        }\n      }\n    }\n  }\n}\n\n@media screen and (max-width: $screen-medium-width-breakpoint) {\n  #content {\n    .group,\n    .introduction,\n    .details.details--with-sidebar-right {\n      padding: 0 $part-chunk-padding-sides;\n    }\n\n    .parts {\n      .part {\n        .specification {\n          $specification-max-width-outer-offset: (2 * $part-chunk-padding-sides);\n\n          max-width: calc(100% - #{$specification-max-width-outer-offset});\n        }\n\n        &::after,\n        .examples {\n          display: none;\n        }\n\n        &::before {\n          right: $part-chunk-padding-sides;\n        }\n      }\n    }\n  }\n}\n\n@media screen and (max-width: $screen-small-width-breakpoint) {\n  #content {\n    .details {\n      .details-inner {\n        padding-left: 50px;\n      }\n    }\n\n    .group,\n    .introduction,\n    .details.details--with-sidebar-right,\n    .parts .specification {\n      padding: 0 $wrapper-small-padding-sides;\n    }\n\n    .parts {\n      .part {\n        &::before {\n          left: $wrapper-small-padding-sides;\n          right: $wrapper-small-padding-sides;\n        }\n\n        .specification {\n          $specification-max-width-outer-offset: (2 * $wrapper-small-padding-sides);\n\n          max-width: calc(100% - #{$specification-max-width-outer-offset});\n        }\n      }\n    }\n  }\n}\n\n@media screen and (max-width: $screen-tiny-width-breakpoint) {\n  #content {\n    .details {\n      .details-inner {\n        padding-left: 40px;\n        padding-right: 10px;\n\n        .details-title {\n          text-align: center;\n        }\n\n        .details-updated {\n          display: none;\n        }\n      }\n    }\n\n    .parts {\n      .part {\n        .specification {\n          .request-specification {\n            .request-format {\n              .request-format-toggle-button {\n                margin-bottom: 9px;\n                display: flex;\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\n// - Keyframes\n\n@include keyframes-fix(references-content-request-format-keys-animation) {\n  0% {\n    opacity: 0;\n  }\n\n  100% {\n    opacity: 1;\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/references/_sidebar_left.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_globals\" as *;\n\n#references {\n  .panes {\n    .sidebar {\n      &.sidebar--left {\n        .context .context-name .context-icon,\n        .nest .nest-category .nest-icon {\n          background-color: var(--color-accent-base);\n\n          @include mask-image-fill;\n        }\n\n        .nest {\n          .nest-category {\n            .nest-icon {\n              @include mask-image-internal(\"references/sidebar/category-icon-default.svg\");\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\n// - Media Queries\n\n// For screens below defined widths\n\n@media screen and (max-width: $screen-small-width-breakpoint) {\n  #references {\n    .panes {\n      .sidebar-toggler {\n        margin-top: 26px;\n      }\n    }\n  }\n}\n\n@media screen and (max-width: $screen-tiny-width-breakpoint) {\n  #references {\n    .panes {\n      .sidebar-toggler {\n        margin-top: 15px;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/stylesheets/references/references.scss",
    "content": "// chappe\n//\n// Copyright 2021, Crisp IM SAS\n// Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n@use \"../_config\" with (\n  $inlining: $use-inline-images\n);\n\n@use \"_sidebar_left\";\n@use \"_content\";"
  },
  {
    "path": "src/templates/__base.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\ninclude _mixins\n\nblock vars\n  - var page  = null;\n  - var panes = 1;\n\nmixin head-stylesheet(href)\n  link(\n    rel=\"stylesheet\",\n    href=`${href}.css?${REVISION}`,\n    type=\"text/css\"\n  )\n\nmixin head-javascript(src)\n  script(\n    src=`${src}.js?${REVISION}`\n  )\n\ndoctype html\n\nhtml(\n  lang=LOCALE.CODE,\n  dir=LOCALE.DIRECTION,\n  data-environment=ENVIRONMENT\n)\n  head\n    include _head_http\n    include _head_screen\n    include _head_theme\n    include _head_includes\n    include _head_favicon\n    include _head_metas\n\n    block head_metas\n\n    title\n      block title\n        | #{(SITE.identity.title || $_.COMMON.METAS.SITE_NAME)}\n\n    block alternates\n\n    block stylesheets\n      +head-stylesheet(\"/static/stylesheets/common/libs\")\n      +head-stylesheet(\"/static/stylesheets/common/common\")\n\n    block javascripts\n      +head-javascript(\"/static/javascripts/common/libs\")\n      +head-javascript(\"/static/javascripts/common/common\")\n\n  body.viewer.appearance(\n    data-viewer-panes=(panes || 1),\n    data-appearance=\"light\"\n  )\n    include _body_header\n    include _body_search\n\n    #content\n      block body_content\n\n    include _body_footer\n"
  },
  {
    "path": "src/templates/_body_footer.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n#footer\n  .inner\n    .wrapper\n      .navigation\n        .left\n          if SITE.links.footer.navigation.length > 0\n            ul.links\n              each link in SITE.links.footer.navigation\n                li.link\n                  a.link-target(\n                    href=link.target,\n                    rel=\"noopener\"\n                  )\n                    | #{link.label}\n\n        .right\n          span.copyright\n            | © #{DATE.YEAR}\n\n            if SITE.identity.copyright\n              |  #{SITE.identity.copyright}\n\n      .metadata\n        .left\n          .brand\n            if SITE.images.logos.footer\n              span.logo(\n                style=`background-image: url('/static/user/${SITE.images.logos.footer}'); width: ${SITE.dimensions.logos.footer.width}px;`\n              )\n\n              span.separator\n\n            span.engine\n              | #{$_.COMMON.FOOTER.ENGINE.GENERATED_BY}\n\n              +text-space\n\n              a.font-sans-semibold(\n                href=PACKAGE.homepage,\n                target=\"_blank\"\n              )\n                | Chappe\n\n              | , #{$_.COMMON.FOOTER.ENGINE.PROJECT_FROM}\n\n              +text-space\n\n              a.font-sans-semibold(\n                href=URLS.CRISP_WEB,\n                target=\"_blank\"\n              )\n                | Crisp\n\n              | .\n\n        .right\n          - var status_url = (SITE.urls.vigil || SITE.urls.crisp_status || null);\n\n          if status_url\n            .status(\n              data-status=\"none\"\n            )\n              a.status-wrapped(\n                href=status_url,\n                target=\"_blank\"\n              )\n                span.status-icon\n\n                span.status-text\n                  span.status-state.font-sans-semibold\n                    span.status-state-variant.status-state-variant--failure\n                      | #{$_.COMMON.FOOTER.STATUS.LABEL_FAILURE}\n\n                    span.status-state-variant.status-state-variant--healthy\n                      | #{$_.COMMON.FOOTER.STATUS.LABEL_HEALTHY}\n\n                    span.status-state-variant.status-state-variant--sick\n                      | #{$_.COMMON.FOOTER.STATUS.LABEL_SICK}\n\n                    span.status-state-variant.status-state-variant--dead\n                      | #{$_.COMMON.FOOTER.STATUS.LABEL_DEAD}\n\n                  span.status-time\n                    | #{$_.COMMON.FOOTER.STATUS.TIME_LAST}\n\n                    +text-space\n\n                    span.status-time-seconds\n                      | 0\n\n                    | s #{$_.COMMON.FOOTER.STATUS.TIME_AGO}.\n"
  },
  {
    "path": "src/templates/_body_header.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n#header\n  .wrapper\n    .left\n      a.logo.font-sans-bold(\n        href=\"/\"\n      )\n        if SITE.images.logos.header_full && SITE.images.logos.header_short\n          each variant in [\"full\", \"short\"]\n            span.logo-image(\n              class=`logo-image--${variant}`,\n              style=`background-image: url('/static/user/${SITE.images.logos['header_' + variant]}'); width: ${SITE.dimensions.logos['header_' + variant].width}px;`\n            )\n\n        else\n          | #{(SITE.identity.title || $_.COMMON.METAS.SITE_NAME)}\n\n      if SITE.links.header.navigation.length > 0\n        ul.menu\n          each item in SITE.links.header.navigation\n            li.menu-item(\n              class=((item.route[0] === page) ? \"menu-item--active\" : null)\n            )\n              - var item_href = `/${item.route.join(\"/\")}/`;\n\n              if (item.dropdown || []).length > 0\n                span.menu-link.menu-link--dropdown.font-sans-semibold\n                  | #{item.label}\n\n                .dropdown\n                  each subitem in item.dropdown\n                    a.dropdown-link(\n                      href=`${item_href}${subitem.route.join(\"/\")}/`\n                    )\n                      | #{subitem.label}\n\n              else\n                a.menu-link.font-sans-semibold(\n                  href=item_href\n                )\n                  | #{item.label}\n\n    .right\n      ul.extras\n        each extra in [\"changes\"]\n          li.extras-item(\n            class=((extra === page) ? \"extras-item--active\" : null)\n          )\n            a.extras-link(\n              href=`/${extra}/`\n            )\n              | #{$_.COMMON.HEADER.EXTRAS[extra.toUpperCase()]}\n\n      .search\n        span.search-placeholder\n          span.search-placeholder-text\n            | #{$_.COMMON.HEADER.SEARCH.PLACEHOLDER}\n\n        span.search-shortcut\n          | ⌘K\n\n      .coloring(\n        data-mode=\"light\"\n      )\n        span.toggle\n\n      if SITE.links.header.actions.length > 0\n        .actions\n          each action in SITE.links.header.actions\n            .action\n              .action-button.button.button--small.button--icon\n                span.button-text\n                  | #{action.label}\n\n              .dropdown\n                each subaction in action.dropdown\n                  a.dropdown-link.dropdown-link--multiple.dropdown-link--external(\n                    href=subaction.target,\n                    target=\"_blank\"\n                  )\n                    span.dropdown-link-title.font-sans-semibold\n                      | #{subaction.title}\n\n                    span.dropdown-link-label\n                      | #{subaction.subtitle}\n"
  },
  {
    "path": "src/templates/_body_search.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n#search\n  .spotlight(\n    data-has-results=\"false\"\n  )\n    .spotlight-box\n      .spotlight-field\n        input.spotlight-input.font-sans-regular(\n          name=\"search\",\n          type=\"search\",\n          placeholder=$_.COMMON.SEARCH.FIELD.INPUT_PLACEHOLDER\n        )\n\n      .spotlight-results\n        ul.spotlight-entries\n\n      .spotlight-legend\n        .spotlight-shortcuts\n          - var shortcuts = [[\"navigate\", $_.COMMON.SEARCH.LEGEND.SHORTCUT_NAVIGATE], [\"go\", $_.COMMON.SEARCH.LEGEND.SHORTCUT_GO], [\"close\", $_.COMMON.SEARCH.LEGEND.SHORTCUT_CLOSE]];\n\n          each shortcut in shortcuts\n            .spotlight-shortcut(\n              class=`spotlight-shortcut--${shortcut[0]}`\n            )\n              | #{shortcut[1]}\n\n        span.spotlight-error.text-ellipsis.font-sans-semibold\n          | #{$_.COMMON.SEARCH.LEGEND.ERROR_INDEX}\n"
  },
  {
    "path": "src/templates/_head_favicon.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\nif SITE.favicons.sizes\n  each href, size in SITE.favicons.sizes\n    link(\n      rel=\"icon\",\n      href=`/static/user/${href}`,\n      sizes=((size !== \"default\") ? size : null),\n      type=\"image/png\"\n    )\n\nif SITE.favicons.main\n  link(\n    rel=\"shortcut icon\",\n    href=`/static/user/${SITE.favicons.main}`,\n    type=\"image/x-icon\"\n  )\n"
  },
  {
    "path": "src/templates/_head_http.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\nmeta(\n  http-equiv=\"Content-Type\",\n  content=\"text/html; charset=utf-8\"\n)\n\nif (INDEXING === false || page === \"not_found\")\n  meta(\n    name=\"robots\",\n    content=\"noindex, nofollow\"\n  )\n"
  },
  {
    "path": "src/templates/_head_includes.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\nif SITE.tokens.crisp_website_id\n  - var crisp_chatbox_url = (SITE.overrides.crisp_chatbox_url || \"https://client.crisp.chat/l.js\");\n\n  script.\n    window.$crisp = [];\n    window.CRISP_WEBSITE_ID = \"#{SITE.tokens.crisp_website_id}\";\n\n    (function(){d=document;s=d.createElement(\"script\");s.src=\"#{crisp_chatbox_url}\";s.async=1;d.getElementsByTagName(\"head\")[0].appendChild(s);})();\n\n  if SITE.features.support !== true\n    script.\n      window.$crisp.push([\"do\", \"chat:hide\"]);\n\neach script_inline in SITE.includes.scripts.inline\n  script.\n    #{script_inline}\n\neach script_url in SITE.includes.scripts.urls\n  if typeof script_url === \"string\"\n    script(\n      src=script_url,\n      defer\n    )\n\n  else\n    script(\n      src=script_url.src,\n      async=script_url.async,\n      defer=script_url.defer\n    )\n\neach stylesheet_inline in SITE.includes.stylesheets.inline\n  style.\n    #{stylesheet_inline}\n\neach stylesheet_url in SITE.includes.stylesheets.urls\n  link(\n    rel=\"stylesheet\",\n    href=stylesheet_url,\n    type=\"text/css\"\n  )\n"
  },
  {
    "path": "src/templates/_head_metas.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\nmeta(\n  name=\"generator\",\n  content=`Chappe v${PACKAGE.version}`\n)\n\nmeta(\n  property=\"og:locale\",\n  content=LOCALE.CODE\n)\n\nmeta(\n  property=\"og:site_name\",\n  content=(SITE.identity.title || $_.COMMON.METAS.SITE_NAME)\n)\n\nmeta(\n  property=\"og:type\",\n  content=\"website\"\n)\n\nif SITE.images.metas.opengraph\n  - var opengraph_image = `${SITE.urls.base}/static/user/${SITE.images.metas.opengraph}`;\n\n  meta(\n    property=\"og:image\",\n    content=opengraph_image\n  )\n\n  meta(\n    name=\"twitter:card\",\n    content=\"summary_large_image\"\n  )\n\n  meta(\n    name=\"twitter:image:src\",\n    content=opengraph_image\n  )\n"
  },
  {
    "path": "src/templates/_head_screen.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\nmeta(\n  name=\"viewport\",\n  content=\"width=device-width, initial-scale=1\"\n)\n"
  },
  {
    "path": "src/templates/_head_theme.pug",
    "content": "//- chappe\n//-\n//- Copyright 2022, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\nstyle.\n  .appearance {\n    --color-accent-base: #{SITE.theme.accent.light.base};\n    --color-accent-active: #{SITE.theme.accent.light.active};\n  }\n\n  .appearance[data-appearance=\"dark\"] {\n    --color-accent-base: #{SITE.theme.accent.dark.base};\n    --color-accent-active: #{SITE.theme.accent.dark.active};\n  }\n"
  },
  {
    "path": "src/templates/_mixins.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n\nmixin text-space\n  | <span> </span>\n\n\nmixin icon-user-override(icon_class, icon_image)\n  span(\n    class=icon_class,\n    style=((icon_image !== null) ? (`-webkit-mask-image: url('/static/user/${icon_image}'); mask-image: url('/static/user/${icon_image}');`) : null)\n  )\n\n\nmixin sidebar-toggler\n  .sidebar-toggler(\n    data-toggled=\"false\"\n  )\n\n\nmixin head-metas-page(title, description, url_path)\n  meta(\n    property=\"og:url\",\n    content=`${SITE.urls.base}${url_path}`\n  )\n\n  meta(\n    property=\"og:title\",\n    content=title\n  )\n\n  meta(\n    property=\"og:description\",\n    content=description\n  )\n\n  meta(\n    name=\"description\",\n    content=description\n  )\n\n\nmixin nest(origin, category, parent_id)\n  .nest\n    .nest-category.font-sans-semibold(\n      class=((category.id === subpage) ? \"nest-category--active\" : null)\n    )\n      - var link_icon_category = (SITE.images.categories[origin][category.id] || null);\n      - var link_url_category  = (category.segments ? (`/${page}/${category.segments.join(\"/\")}/`) : null);\n\n      +icon-user-override(\"nest-icon\", link_icon_category)\n\n      if link_url_category\n        a.nest-category-link(\n          href=link_url_category\n        )\n          | #{category.title}\n\n      else\n        span.nest-category-link\n          | #{category.title}\n\n    .nest-navigate\n      each item in category.subtrees\n        - var is_slice = ((item.subtrees.length > 0) ? true : false);\n\n        .nest-navigate-level.nest-navigate-level--first(\n          data-expanded=(is_slice ? ((parent_id || \"\").startsWith(item.id) ? \"true\" : \"false\") : null)\n        )\n          - var link_url_item = (item.segments ? (`/${page}/${item.segments.join(\"/\")}/`) : `#${item.id}`);\n\n          if is_slice\n            .nest-navigate-link.nest-navigate-link--slice(\n              data-active=((item.id === subpage) ? \"true\" : null),\n              data-anchor=link_url_item\n            )\n              a.nest-navigate-slice.font-sans-semibold(\n                href=link_url_item\n              )\n                | #{item.title}\n\n              span.nest-navigate-toggle\n\n            .nest-navigate-level.nest-navigate-level--second.font-sans-regular\n              each slice in item.subtrees\n                - var link_url_slice = (slice.segments ? (`/${page}/${slice.segments.join(\"/\")}/`) : `#${slice.id}`);\n\n                +nest-navigate-link(link_url_slice, slice.id, slice.title, slice.badge, subpage, false)\n\n              if item.links\n                each link, index in item.links\n                  +nest-navigate-links-external(link, index, item)\n\n          else\n            +nest-navigate-link(link_url_item, item.id, item.title, item.badge, subpage, false)\n\n      if category.links\n        each link, index in category.links\n          .nest-navigate-level.nest-navigate-level--first\n            +nest-navigate-links-external(link, index, category)\n\n\nmixin nest-navigate-link(href, id, label, badge, id_active, is_external)\n  - var is_active = ((id === id_active) ? true : false);\n\n  a.nest-navigate-link(\n    href=href,\n    target=(is_external ? \"_blank\" : null),\n    class=(is_external ? \"nest-navigate-link--external font-sans-semibold\" : \"\"),\n    data-active=(is_active ? \"true\" : null),\n    data-starred=(label.startsWith(\"⭐\") ? \"true\" : null),\n    data-anchor=href\n  )\n    span.nest-navigate-link-text\n      | #{label}\n\n    if badge && badge[0]\n      span.nest-navigate-link-objects\n        span.badge.badge--small.font-sans-bold(\n          class=`badge--${badge[1] || \"black\"}`\n        )\n          | #{badge[0]}\n\n\nmixin nest-navigate-links-external(link, index, item)\n  +nest-navigate-link(link.url, `${item.id}_link_${index}`, link.name, null, null, true)\n\n\nmixin code(language, flows, request_options, response_options)\n  .code.copy(\n    data-was-loaded=\"false\",\n    data-has-request=\"false\"\n  )\n    input.code-data(\n      name=\"data\",\n      type=\"hidden\",\n      value=JSON.stringify(flows)\n    )\n\n    .code-header\n      .code-language\n        | #{language}\n\n      .code-actions\n        each action in [\"copy\"]\n          span.code-action(\n            class=`code-action--${action}${(action === \"copy\") ? \" copy-button\" : \"\"}`\n          )\n\n    .code-section.code-section--request\n      +code-metas($_.COMMON.CODE.META_NAME_REQUEST, request_options)\n      +code-content\n\n    .code-section.code-section--response\n      +code-metas($_.COMMON.CODE.META_NAME_RESPONSE, response_options)\n      +code-content\n\n\nmixin code-metas(label, options)\n  .code-metas\n    .code-meta-name.font-sans-semibold\n      | #{label}\n\n    .code-meta-picker\n      +code-select(\"group\", options, ((options || [])[0] || [])[0])\n\n    .code-meta-type\n\n\nmixin code-content\n  pre.code-content.font-code-regular\n    | #{$_.COMMON.CODE.CONTENT_LOADING}\n\n\nmixin code-select(name, options, selected)\n  select.font-sans-regular(\n    name=name,\n    autocomplete=\"off\"\n  )\n    each option in options\n      option(\n        value=option[0],\n        selected=((selected && option[0] === selected) ? \"\" : null)\n      )\n        | #{option[1]}\n"
  },
  {
    "path": "src/templates/changes/index.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\nextends ../__base\n\nblock vars\n  - var page = \"changes\";\n\n  //- Special: generate page title\n  - var page_title = `${$_.CHANGES.PAGE} | ${$_.CHANGES.NAVIGATE[(\"\" + changes.current).toUpperCase()] || changes.current}`;\n\nblock title\n  | #{page_title}\n\nblock append stylesheets\n  +head-stylesheet(\"/static/stylesheets/changes/changes\")\n\nblock head_metas\n  - var page_url = `/changes/${(changes.current !== \"latest\") ? `${changes.current}/` : \"\"}`;\n\n  //- Special: generate page description\n  - var page_description = ((changes.current === \"latest\") ? (SITE.texts.changes.titles.latest || $_.CHANGES.TITLE_LATEST) : `${SITE.texts.changes.titles.year || $_.CHANGES.TITLE_YEAR} ${changes.year}`);\n\n  +head-metas-page(page_title, page_description, page_url)\n\nblock append alternates\n  link(\n    rel=\"alternate\",\n    type=\"application/rss+xml\",\n    title=(SITE.texts.changes.titles.feed || $_.CHANGES.TITLE_FEED),\n    href=\"/changes.rss\"\n  )\n\nblock body_content\n  .wrapper\n    #changes\n      ul.navigate\n        - var navigate_years = [].concat([\"latest\"], years);\n\n        each navigate_year in navigate_years\n          li.navigate-item(\n            class=((navigate_year === changes.current) ? \"navigate-item--active\" : null)\n          )\n            a.navigate-link(\n              href=`/changes/${(navigate_year === \"latest\") ? \"\" : `${navigate_year}/`}`,\n              class=((typeof navigate_year === \"string\" || navigate_year === changes.current) ? \"font-sans-semibold\" : null)\n            )\n              if typeof navigate_year === \"string\"\n                | #{($_.CHANGES.NAVIGATE[navigate_year.toUpperCase()] || navigate_year)}\n\n              else\n                | #{navigate_year}\n\n      .content\n        .content-aside\n          h4.title.font-sans-bold\n            if changes.current === \"latest\"\n              | #{(SITE.texts.changes.titles.latest || $_.CHANGES.TITLE_LATEST)}\n\n            else\n              | #{(SITE.texts.changes.titles.year || $_.CHANGES.TITLE_YEAR)} #{changes.year}\n\n          if changes.current === \"latest\"\n            .notice.markdown\n              - var notice_text = (SITE.texts.changes.notice || $_.CHANGES.NOTICE);\n\n              != METHODS.marked(notice_text)\n\n          .timeline\n            each month_entry in changes.timeline\n              .month\n                h6.month-name.font-sans-semibold\n                  | #{($_.CHANGES.MONTHS[month_entry[0]] || month_entry[0])} #{changes.year}\n\n                ul.month-events\n                  each month_change in month_entry[1]\n                    li.month-event(\n                      class=((month_change.type === \"deprecation\") ? \"month-event--deprecated\" : null),\n                      data-group=month_change.group\n                    )\n                      - var group_color = (((month_change.type !== \"deprecation\") ? SITE.colors.changes.groups[month_change.group] : null) || null);\n\n                      .month-event-group\n                        | #{(SITE.texts.changes.groups[month_change.group] || month_change.group)}\n\n                      .month-event-text.markdown(\n                        style=((group_color !== null) ? `border-color: ${group_color};` : null)\n                      )\n                        != METHODS.marked(month_change.text)\n"
  },
  {
    "path": "src/templates/guides/_content.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n.content.content--padded\n  .content-wrap\n    .details\n      - var category_icon  = (SITE.images.categories.guides[guide.segments[0]] || null);\n      - var category_title = ((parent_titles.length > 0) ? parent_titles.join(\" | \") : guide.title);\n\n      .details-upper\n        h6.details-category.text-ellipsis.font-sans-semibold\n          +icon-user-override(\"details-icon\", category_icon)\n\n          | #{category_title}\n\n        if SITE.tokens.crisp_website_id && SITE.features.support === true\n          a.details-feedback.font-sans-semibold(\n            href=\"#crisp-chat-feedback\"\n          )\n            | #{$_.GUIDES.DETAILS.FEEDBACK}\n\n      .details-lower\n        h1.details-title.text-ellipsis.font-sans-bold\n          | #{((category_title !== guide.title) ? guide.title : $_.GUIDES.DETAILS.TITLE_INDEX)}\n\n        span.details-updated\n          | #{$_.GUIDES.DETAILS.UPDATED} #{guide.updated.toLocaleDateString(FORMAT.DATES.LOCALE_DATE_STRING.AREA, FORMAT.DATES.LOCALE_DATE_STRING.OPTIONS)}\n\n    .article.markdown\n      != METHODS.marked(guide.markdown)\n"
  },
  {
    "path": "src/templates/guides/_sidebar_left.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\ninclude ../_mixins\n\n.sidebar.sidebar--left\n  span.sidebar-toggler-retract\n\n  - var guides_base = ((guides[0] || {}).subtrees || []);\n\n  each category in guides_base\n    +nest(\"guides\", category, guide.id)\n\n+sidebar-toggler\n"
  },
  {
    "path": "src/templates/guides/index.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\nextends ../__base\n\nblock vars\n  - var page    = \"guides\";\n  - var subpage = guide.id;\n  - var panes   = 2;\n\n  //- Special: acquire all parent titles\n  - var parent_titles = [];\n  - var next_parent   = guide.parent;\n\n  while next_parent\n    if next_parent.id\n      - parent_titles.unshift(next_parent.title);\n\n    - next_parent = next_parent.parent;\n\n  //- Special: generate page title\n  - var page_title = guide.title;\n\n  if parent_titles.length > 0\n    - page_title += ` (${parent_titles.join(\" → \")})`;\n\nblock title\n  | #{page_title}\n\nblock append stylesheets\n  +head-stylesheet(\"/static/stylesheets/guides/guides\")\n\nblock head_metas\n  - var page_url = `/guides/${guide.segments.join(\"/\")}${(guide.segments.length > 0) ? \"/\" : \"\"}`;\n\n  //- Special: generate page description\n  - var page_description = METHODS.truncate_text(METHODS.remove_markdown(guide.markdown), FORMAT.DESCRIPTION.TRUNCATE);\n\n  +head-metas-page(page_title, page_description, page_url)\n\nblock body_content\n  #guides\n    .panes\n      include _sidebar_left\n      include _content\n"
  },
  {
    "path": "src/templates/home/index.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\nextends ../__base\n\nblock vars\n  - var page = \"home\";\n\n  //- Special: define bulletpoints\n  - var bulletpoints = [[\"quickstart\", SITE.bulletpoints.home.quickstart.description, SITE.bulletpoints.home.quickstart.actions], [\"guides\", SITE.bulletpoints.home.guides.description, SITE.bulletpoints.home.guides.actions], [\"references\", SITE.bulletpoints.home.references.description, SITE.bulletpoints.home.references.actions]];\n\nblock title\n  | #{(SITE.identity.title || $_.HOME.PAGE)}\n\nblock append stylesheets\n  +head-stylesheet(\"/static/stylesheets/home/home\")\n\nblock append javascripts\n  script(\n    type=\"application/ld+json\"\n  )\n    | {\n    |   \"@context\": \"https://schema.org\",\n    |   \"@type\": \"BreadcrumbList\",\n\n    |   \"itemListElement\": [\n\n    - var last_position = 0;\n\n    each bulletpoint, bulletpoint_index in bulletpoints\n      if bulletpoint[2] && bulletpoint[2].length > 0\n        each action, action_index in bulletpoint[2]\n          - var is_last = ((bulletpoint_index === (bulletpoints.length - 1)) && (action_index === (bulletpoint[2].length - 1)));\n\n          | {\n          |       \"@type\": \"ListItem\",\n          |       \"position\": #{(++last_position)},\n          |       \"name\": \"#{action.label}\",\n          |       \"item\": \"#{SITE.urls.base}/#{action.route.join('/')}/\"\n          |     }#{((is_last === true) ? \"\" : \", \")}\n\n    | ]\n    | }\n\nblock head_metas\n  +head-metas-page((SITE.identity.title || $_.HOME.PAGE), (SITE.texts.home.label || $_.HOME.LABEL), \"/\")\n\nblock body_content\n  .wrapper\n    #home\n      .main\n        h1.main-title.font-sans-bold\n          | #{(SITE.texts.home.title || $_.HOME.TITLE)}\n\n        p.main-label\n          | #{(SITE.texts.home.label || $_.HOME.LABEL)}\n\n      if SITE.actions.home.length > 0\n        .actions\n          each action in SITE.actions.home\n            a.button.button--large.button--reverse.button--icon.font-sans-semibold(\n              href=`/${action.route.join(\"/\")}/`\n            )\n              span.button-text\n                | #{action.label}\n\n      if SITE.images.illustrations.home\n        .illustration(\n          style=`background-image: url('/static/user/${SITE.images.illustrations.home}');`\n        )\n\n      ul.bulletpoints\n        each bulletpoint in bulletpoints\n          li.bulletpoint(\n            class=`bulletpoint--${bulletpoint[0]}`\n          )\n            h6.bulletpoint-title.font-sans-semibold\n              | #{$_.HOME.BULLETPOINTS[bulletpoint[0].toUpperCase()]}\n\n            if bulletpoint[1]\n              p.bulletpoint-label\n                | #{bulletpoint[1]}\n\n            if bulletpoint[2] && bulletpoint[2].length > 0\n              .bulletpoint-actions\n                each action in bulletpoint[2]\n                  a.bulletpoint-action.font-sans-semibold(\n                    href=`/${action.route.join(\"/\")}/`,\n                    class=((bulletpoint[2].length === 1) ? \"bulletpoint-action--single\" : null)\n                  )\n                    | #{action.label}\n\n      if SITE.tokens.crisp_website_id && SITE.features.support === true\n        .support\n          .support-wrap\n            .support-text\n              h6.support-question.font-sans-regular\n                | #{$_.HOME.SUPPORT.QUESTION}\n\n              p.support-label\n                | #{$_.HOME.SUPPORT.LABEL}\n\n            .support-action\n              a.button.button--icon(\n                href=\"#crisp-chat-open\"\n              )\n                span.button-text\n                  | #{$_.HOME.SUPPORT.ACTION}\n"
  },
  {
    "path": "src/templates/not_found/index.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\nextends ../__base\n\nblock vars\n  - var page = \"not_found\";\n\nblock title\n  | #{$_.NOT_FOUND.PAGE}\n\nblock append stylesheets\n  +head-stylesheet(\"/static/stylesheets/not_found/not_found\")\n\nblock body_content\n  .wrapper\n    #not_found\n      .main\n        h1.main-title.font-sans-bold\n          | #{$_.NOT_FOUND.TITLE}\n\n        p.main-label\n          span.main-label-line\n            | #{$_.NOT_FOUND.LABEL_FIRST}\n\n          span.main-label-line\n            | #{$_.NOT_FOUND.LABEL_SECOND}\n\n      .actions\n        a.button.button--icon(\n          href=\"/\"\n        )\n          span.button-text\n            | #{$_.NOT_FOUND.ACTION}\n\n      if SITE.images.illustrations.not_found\n        .illustration(\n          style=`background-image: url('/static/user/${SITE.images.illustrations.not_found}');`\n        )\n"
  },
  {
    "path": "src/templates/references/_blueprint.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\ninclude _mixins_common\ninclude _mixins_blueprint\n\ninclude _blueprint_sidebar_left\ninclude _blueprint_content\n"
  },
  {
    "path": "src/templates/references/_blueprint_content.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n.content\n  .content-wrap.content-wrap--full\n    +references-content-details(reference, true)\n\n    each entry in reference.data.content\n      case entry.element\n        when \"copy\"\n          +references-blueprint-introduction(entry.content)\n\n        when \"category\"\n          +references-blueprint-group(entry.content, entry.meta, reference.baseline)\n\n  .sidebar.sidebar--right\n"
  },
  {
    "path": "src/templates/references/_blueprint_sidebar_left.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\ninclude ../_mixins\n\n.sidebar.sidebar--left\n  span.sidebar-toggler-retract\n\n  +references-sidebar-context(reference)\n\n  each category in reference.categories\n    +nest(\"references\", category)\n\n+sidebar-toggler\n"
  },
  {
    "path": "src/templates/references/_markdown.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\ninclude _mixins_common\n\ninclude _markdown_sidebar_left\ninclude _markdown_content\n"
  },
  {
    "path": "src/templates/references/_markdown_content.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n.content.content--padded\n  .content-wrap.markdown\n    +references-content-details(reference)\n\n    != METHODS.marked(reference.data)\n"
  },
  {
    "path": "src/templates/references/_markdown_sidebar_left.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\ninclude ../_mixins\n\n.sidebar.sidebar--left\n  span.sidebar-toggler-retract\n\n  +references-sidebar-context(reference)\n\n  each category in reference.categories\n    +nest(\"references\", category)\n\n+sidebar-toggler\n"
  },
  {
    "path": "src/templates/references/_mixins_blueprint.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n\nmixin references-blueprint-group(content, meta, baseline)\n  .parts\n    - var id = \"\";\n\n    if meta.title\n      - id = METHODS.slug(meta.title.content);\n\n    each entry in content\n      if entry.element === \"resource\"\n        +references-blueprint-group-title(entry.meta, meta, id)\n\n        each part in entry.content\n          if part.element === \"transition\"\n            +references-blueprint-part(part.content, part.meta, part.attributes, reference.examples, baseline)\n\n\nmixin references-blueprint-group-title(meta, origin_meta, origin_id)\n  if meta.title\n    - var id = METHODS.slug(meta.title.content);\n\n    if origin_id\n      - id = `${origin_id}-${id}`;\n\n    h1.group.font-sans-bold(\n      id=id\n    )\n      a.group-anchor.title-group-anchor(\n        href=`#${id}`\n      )\n        span.title-anchor\n          | #{meta.title.content}\n\n        if origin_meta && origin_meta.title\n          span.group-origin.font-sans-regular\n            | #{$_.REFERENCES.BLUEPRINT.GROUP.ORIGIN_IN} #{origin_meta.title.content}\n\n\nmixin references-blueprint-introduction(content)\n  .introduction.markdown\n    != METHODS.marked(content)\n\n\nmixin references-blueprint-part(content, meta, attrs, examples, baseline)\n  - var id          = (meta.title ? METHODS.slug(meta.title.content) : \"\");\n  - var http_method = ((examples[id] || {}).method || \"none\");\n\n  .part(\n    class=`part--method-${http_method}`\n  )\n    .specification\n      if meta.title\n        h2.request-title.font-sans-bold(\n          id=id\n        )\n          a.request-title-anchor.title-anchor(\n            href=`#${id}`\n          )\n            | #{meta.title.content}\n\n      +references-blueprint-part-specification(id, content, meta, attrs, baseline, http_method)\n\n    +references-blueprint-part-examples(id, examples, baseline)\n\n\nmixin references-blueprint-part-specification(id, content, meta, attrs, baseline, http_method)\n  - var data_source = (([\"head\", \"get\"].includes(http_method) === true) ? \"response\" : \"request\");\n\n  .request-specification\n    .request-target\n      .request-target-method.font-sans-semibold\n        | #{http_method.toUpperCase()}\n\n      .request-target-url.copy.copy-button\n        span.request-target-path.copy-value.font-code-regular\n          | #{(attrs.href ? `${baseline.host.path}${attrs.href.content}` : \"--\")}\n\n        span.request-target-copy\n\n    .request-format\n      //- Collapse deeper for data coming from response (due to nesting in 'data')\n      - var collapse_depth = ((data_source === \"response\") ? 1 : 0);\n\n      each entry in content\n        if entry.element === \"copy\"\n          .markdown\n            != METHODS.marked(entry.content)\n\n      if attrs.hrefVariables && attrs.hrefVariables.content\n        .request-format-title.font-sans-bold\n          | #{$_.REFERENCES.BLUEPRINT.SPECIFICATION.REQUEST_FORMAT_TITLES.URI_PARAMETERS}\n\n        +references-blueprint-request-format-keys(id, http_method, 0, attrs.hrefVariables.content, collapse_depth, false)\n\n      if attrs.data && attrs.data.content && attrs.data.content.content\n        .request-format-title.font-sans-bold\n          | #{$_.REFERENCES.BLUEPRINT.SPECIFICATION.REQUEST_FORMAT_TITLES[`DATA_${data_source.toUpperCase()}`]}\n\n        +references-blueprint-request-format-keys(id, http_method, 0, attrs.data.content.content, collapse_depth, true)\n\n\nmixin references-blueprint-part-examples(id, examples, baseline)\n  - var example = (examples[id] || null);\n\n  if example\n    .examples\n      .examples-wrap\n        ul.examples-details\n          li.examples-detail\n            span.examples-detail-label\n              span.badge.badge--white.font-sans-semibold\n                | #{(example.method || \"none\").toUpperCase()}\n\n            span.examples-detail-value\n              | #{baseline.host.url}\n\n              each part in example.url.parts\n                span(\n                  class=`examples-detail-${part.type}`\n                )\n                  | #{part.value}\n\n          if example.tiers.length > 0\n            li.examples-detail\n              span.examples-detail-label\n                span.badge.badge--white.font-sans-semibold\n                  | #{$_.REFERENCES.BLUEPRINT.EXAMPLES.DETAILS.TIERS}\n\n              span.examples-detail-value\n                each tier in example.tiers\n                  span.badge.badge--special-value\n                    | #{tier}\n\n          if example.scopes.length > 0\n            li.examples-detail\n              span.examples-detail-label\n                span.badge.badge--white.font-sans-semibold\n                  | #{$_.REFERENCES.BLUEPRINT.EXAMPLES.DETAILS.SCOPES}\n\n              span.examples-detail-value\n                each scope in example.scopes\n                  span.badge.badge--special-value\n                    | #{scope}\n\n        if example.flows\n          - var flow_keys = Object.keys(example.flows);\n\n          if flow_keys.length > 0\n            - var first_flow       = example.flows[flow_keys[0]];\n\n            - var request_options  = flow_keys.map(function(key) { return [key, example.flows[key].request.name] });\n            - var response_options = first_flow.responses.map(function(response, index) { return [(\"\" + index), response.name] });\n\n            +code(\"HTTP\", example.flows, request_options, response_options)\n\n\nmixin references-blueprint-request-format-keys(id, http_method, depth, content, collapse_depth, with_children)\n  //- Generate keys tree from API Blueprint 'data' object\n  .request-format-keys(\n    data-depth=depth\n  )\n    each data_level in content\n      if data_level.element === \"member\"\n        - var key = {};\n\n        //- Push main data\n        - key.key  = data_level.content.key.content;\n        - key.type = data_level.content.value.element;\n\n        //- Parse label?\n        if data_level.meta && data_level.meta.description\n          - key.label = data_level.meta.description.content;\n\n        //- Begin array content type?\n        if data_level.content.value.element === \"array\" && data_level.content.value.content\n          - key.type += `[${((data_level.content.value.content[0] || {}).element || \"\")}`;\n\n        //- Parse enumeration members?\n        - var enumeration_attributes = data_level.content.value.attributes;\n\n        if data_level.content.value.element === \"array\" && data_level.content.value.content\n          //- Flatten enumeration members from array (only for array[enum[*]])\n          - enumeration_attributes = (data_level.content.value.content[0] || {}).attributes;\n\n        if enumeration_attributes && enumeration_attributes.enumerations && enumeration_attributes.enumerations.content && enumeration_attributes.enumerations.content.length > 0\n          - key.type += `[${enumeration_attributes.enumerations.content[0].element}]`;\n\n          //- Append all enumeration member values to label (using Markdown)\n          - var members = \"\";\n\n          each enumeration in enumeration_attributes.enumerations.content\n            if enumeration.content !== undefined\n              - members += `* \\`${enumeration.content}\\``;\n\n              if enumeration.meta && enumeration.meta.description && enumeration.meta.description.content\n                - members += ` ${enumeration.meta.description.content}`;\n\n              - members += \"\\n\";\n\n          if members\n            - key.label = `${key.label || \"\"}\\n\\n**${$_.REFERENCES.BLUEPRINT.SPECIFICATION.REQUEST_FORMAT_MEMBERS}**\\n${members.trim()}`;\n\n        //- End array content type?\n        if data_level.content.value.element === \"array\" && data_level.content.value.content\n          - key.type += \"]\";\n\n        //- Parse required and optional attributes?\n        if data_level.attributes && data_level.attributes.typeAttributes && data_level.attributes.typeAttributes.content\n          each type_attribute in data_level.attributes.typeAttributes.content\n            if !key.attribute\n              - key.attribute = type_attribute.content;\n\n        //- Parse children? (if children parsing is enabled)\n        if with_children === true && data_level.content.value.content\n          //- Unwind array? (first item only for array[object])\n          if data_level.content.value.element === \"array\"\n            - key.children = (data_level.content.value.content[0] || {}).content;\n          else\n            - key.children = data_level.content.value.content;\n\n        .request-format-key\n          +references-blueprint-request-format-parts(key.key, key.type, key.label, key.attribute)\n\n          if key.children && key.children.length > 0\n            if with_children === true && depth >= collapse_depth\n              +references-blueprint-request-format-toggle(id, http_method, key.key, key.children.length, depth)\n\n            +references-blueprint-request-format-keys(id, http_method, (depth + 1), key.children, collapse_depth, with_children)\n\n\nmixin references-blueprint-request-format-parts(key_name, type, label, attribute)\n  .request-format-parts\n    .request-format-head\n      .request-format-path.font-sans-semibold\n        | #{key_name}\n\n      .request-format-type\n        | #{(type || \"?\")}\n\n        case attribute\n          when \"required\"\n            span.request-format-required.font-sans-semibold\n              | #{$_.REFERENCES.BLUEPRINT.SPECIFICATION.REQUEST_FORMAT_REQUIRED}\n\n          when \"optional\"\n            span.request-format-optional\n              | #{$_.REFERENCES.BLUEPRINT.SPECIFICATION.REQUEST_FORMAT_OPTIONAL}\n\n    if label\n      .request-format-label.markdown\n        != METHODS.marked(label)\n\n\nmixin references-blueprint-request-format-toggle(id, http_method, key_name, children_count, depth)\n  - var toggle_parts = [http_method, id, `z${depth}`, key_name];\n  - var toggle_id    = `toggle_${toggle_parts.join(\"-\").toLowerCase()}`;\n\n  input.request-format-toggle-checkbox(\n    type=\"checkbox\",\n    id=toggle_id,\n    name=toggle_id\n  )\n\n  label.request-format-toggle-button.button.button--small(\n    for=toggle_id\n  )\n    span.request-format-toggle-label.request-format-toggle-label--show\n      | #{$_.REFERENCES.BLUEPRINT.SPECIFICATION.REQUEST_FORMAT_TOGGLE_SHOW}\n\n    span.request-format-toggle-label.request-format-toggle-label--hide\n      | #{$_.REFERENCES.BLUEPRINT.SPECIFICATION.REQUEST_FORMAT_TOGGLE_HIDE}\n\n    span.request-format-toggle-count.font-sans-semibold\n      | #{children_count}\n"
  },
  {
    "path": "src/templates/references/_mixins_common.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\n\nmixin references-sidebar-context(reference)\n  - var context_icon = (SITE.images.categories.references[reference.segments[0]] || null);\n\n  .context\n    .context-name.font-sans-semibold\n      +icon-user-override(\"context-icon\", context_icon)\n\n      | #{reference.title}\n\n\nmixin references-content-details(reference, has_sidebar_right)\n  .details(\n    class=(has_sidebar_right ? \"details--with-sidebar-right\" : null)\n  )\n    .details-inner\n      h1.details-title.text-ellipsis.font-sans-bold\n        | #{reference.title}\n\n      span.details-updated\n        | #{$_.REFERENCES.MARKDOWN.DETAILS.UPDATED} #{reference.updated.toLocaleDateString(FORMAT.DATES.LOCALE_DATE_STRING.AREA, FORMAT.DATES.LOCALE_DATE_STRING.OPTIONS)}\n"
  },
  {
    "path": "src/templates/references/index.pug",
    "content": "//- chappe\n//-\n//- Copyright 2021, Crisp IM SAS\n//- Author: Valerian Saliou <valerian@valeriansaliou.name>\n\nextends ../__base\n\nblock vars\n  - var page    = \"references\";\n  - var subpage = reference.id;\n  - var panes   = ((reference.type === \"API Blueprint\") ? 3 : 2);\n\nblock title\n  | #{reference.title}\n\nblock append stylesheets\n  +head-stylesheet(\"/static/stylesheets/references/references\")\n\nblock head_metas\n  - var page_url = `/references/${reference.segments.join(\"/\")}${(reference.segments.length > 0) ? \"/\" : \"\"}`;\n\n  //- Special: generate page description\n  - var page_description = ((typeof reference.data === \"string\") ? METHODS.truncate_text(METHODS.remove_markdown(reference.data), FORMAT.DESCRIPTION.TRUNCATE) : reference.title);\n\n  +head-metas-page(reference.title, page_description, page_url)\n\nblock body_content\n  #references\n    .panes\n      case reference.type\n        when \"API Blueprint\"\n          include _blueprint\n\n        when \"Markdown\"\n          include _markdown\n"
  }
]