diff --git a/.github/workflows/check-code-embedding.yml b/.github/workflows/check-code-embedding.yml
new file mode 100644
index 0000000..ff00d62
--- /dev/null
+++ b/.github/workflows/check-code-embedding.yml
@@ -0,0 +1,37 @@
+name: Check Code Embedding
+
+on:
+ pull_request:
+
+jobs:
+ build-embedded-code:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v5
+ with:
+ submodules: 'recursive'
+
+ - uses: actions/setup-java@v5
+ with:
+ java-version: 8
+ distribution: zulu
+
+ - run: ./gradlew :buildAll
+
+ check-embedded-samples:
+ # TODO:2025-12-16:julia.evseeva: Remove the `if: false` when the issue
+ # will be fixed https://github.com/SpineEventEngine/embed-code/issues/66
+ if: false
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v5
+ with:
+ submodules: 'recursive'
+
+ - uses: actions/setup-java@v5
+ with:
+ java-version: 11
+ distribution: zulu
+
+ - name: Check Embedding
+ run: ./gradlew :checkSamples
diff --git a/.gitignore b/.gitignore
index f360e34..c766456 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,18 @@
-# The result of Jekyll build
-_site
-.sass-cache
-.jekyll-metadata
+# Hugo cache files
+/site/resources
+public
-# Intermediate directory used by `embed-code` Jekyll subcommand
-.fragments
+# Cache folder for Hugo Modules
+/site/_vendor
+
+# Node modules
+node_modules
+
+# Used to control concurrency between multiple Hugo instances
+/site/.hugo_build.lock
+
+# Needed for navigation/intellisense help inside code editors
+jsconfig.json
# IntelliJ IDEA modules and interim config files
*.iml
@@ -18,3 +26,16 @@ _site
!.idea/misc.xml
!.idea/codeStyles/
!.idea/copyright/
+
+# The `embed-code` temporary directory
+_code/build
+
+# Local Java version for this project
+.java-version
+
+# Gradle interim configs
+.gradle/
+
+# Gradle build files
+build/
+generated/
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..7e1663a
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/404.html b/404.html
deleted file mode 100644
index c472b4e..0000000
--- a/404.html
+++ /dev/null
@@ -1,24 +0,0 @@
----
-layout: default
----
-
-
-
-
-
404
-
-
Page not found :(
-
The requested page could not be found.
-
diff --git a/Gemfile b/Gemfile
deleted file mode 100644
index 21d7a66..0000000
--- a/Gemfile
+++ /dev/null
@@ -1,9 +0,0 @@
-source "https://rubygems.org"
-gem "jekyll"
-gem "github-pages"
-
-# kramdown v2 ships without the gfm parser by default. If you're using
-# kramdown v1, comment out this line.
-gem "kramdown-parser-gfm"
-
-gem 'embed-code', :git => 'https://github.com/SpineEventEngine/embed-code', :group => :jekyll_plugins
diff --git a/Gemfile.lock b/Gemfile.lock
deleted file mode 100644
index c038077..0000000
--- a/Gemfile.lock
+++ /dev/null
@@ -1,292 +0,0 @@
-GIT
- remote: https://github.com/SpineEventEngine/embed-code
- revision: 169a3e0dcf42fd8cf4565768fe62410c07b8a892
- specs:
- embed-code (0.1.1)
-
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (6.0.4.4)
- concurrent-ruby (~> 1.0, >= 1.0.2)
- i18n (>= 0.7, < 2)
- minitest (~> 5.1)
- tzinfo (~> 1.1)
- zeitwerk (~> 2.2, >= 2.2.2)
- addressable (2.8.0)
- public_suffix (>= 2.0.2, < 5.0)
- coffee-script (2.4.1)
- coffee-script-source
- execjs
- coffee-script-source (1.11.1)
- colorator (1.1.0)
- commonmarker (0.23.9)
- concurrent-ruby (1.1.9)
- dnsruby (1.61.9)
- simpleidn (~> 0.1)
- em-websocket (0.5.3)
- eventmachine (>= 0.12.9)
- http_parser.rb (~> 0)
- ethon (0.15.0)
- ffi (>= 1.15.0)
- eventmachine (1.2.7)
- execjs (2.8.1)
- faraday (1.9.3)
- faraday-em_http (~> 1.0)
- faraday-em_synchrony (~> 1.0)
- faraday-excon (~> 1.1)
- faraday-httpclient (~> 1.0)
- faraday-multipart (~> 1.0)
- faraday-net_http (~> 1.0)
- faraday-net_http_persistent (~> 1.0)
- faraday-patron (~> 1.0)
- faraday-rack (~> 1.0)
- faraday-retry (~> 1.0)
- ruby2_keywords (>= 0.0.4)
- faraday-em_http (1.0.0)
- faraday-em_synchrony (1.0.0)
- faraday-excon (1.1.0)
- faraday-httpclient (1.0.1)
- faraday-multipart (1.0.3)
- multipart-post (>= 1.2, < 3)
- faraday-net_http (1.0.1)
- faraday-net_http_persistent (1.2.0)
- faraday-patron (1.0.0)
- faraday-rack (1.0.0)
- faraday-retry (1.0.3)
- ffi (1.15.5)
- forwardable-extended (2.6.0)
- gemoji (3.0.1)
- github-pages (228)
- github-pages-health-check (= 1.17.9)
- jekyll (= 3.9.3)
- jekyll-avatar (= 0.7.0)
- jekyll-coffeescript (= 1.1.1)
- jekyll-commonmark-ghpages (= 0.4.0)
- jekyll-default-layout (= 0.1.4)
- jekyll-feed (= 0.15.1)
- jekyll-gist (= 1.5.0)
- jekyll-github-metadata (= 2.13.0)
- jekyll-include-cache (= 0.2.1)
- jekyll-mentions (= 1.6.0)
- jekyll-optional-front-matter (= 0.3.2)
- jekyll-paginate (= 1.1.0)
- jekyll-readme-index (= 0.3.0)
- jekyll-redirect-from (= 0.16.0)
- jekyll-relative-links (= 0.6.1)
- jekyll-remote-theme (= 0.4.3)
- jekyll-sass-converter (= 1.5.2)
- jekyll-seo-tag (= 2.8.0)
- jekyll-sitemap (= 1.4.0)
- jekyll-swiss (= 1.0.0)
- jekyll-theme-architect (= 0.2.0)
- jekyll-theme-cayman (= 0.2.0)
- jekyll-theme-dinky (= 0.2.0)
- jekyll-theme-hacker (= 0.2.0)
- jekyll-theme-leap-day (= 0.2.0)
- jekyll-theme-merlot (= 0.2.0)
- jekyll-theme-midnight (= 0.2.0)
- jekyll-theme-minimal (= 0.2.0)
- jekyll-theme-modernist (= 0.2.0)
- jekyll-theme-primer (= 0.6.0)
- jekyll-theme-slate (= 0.2.0)
- jekyll-theme-tactile (= 0.2.0)
- jekyll-theme-time-machine (= 0.2.0)
- jekyll-titles-from-headings (= 0.5.3)
- jemoji (= 0.12.0)
- kramdown (= 2.3.2)
- kramdown-parser-gfm (= 1.1.0)
- liquid (= 4.0.4)
- mercenary (~> 0.3)
- minima (= 2.5.1)
- nokogiri (>= 1.13.6, < 2.0)
- rouge (= 3.26.0)
- terminal-table (~> 1.4)
- github-pages-health-check (1.17.9)
- addressable (~> 2.3)
- dnsruby (~> 1.60)
- octokit (~> 4.0)
- public_suffix (>= 3.0, < 5.0)
- typhoeus (~> 1.3)
- html-pipeline (2.14.0)
- activesupport (>= 2)
- nokogiri (>= 1.4)
- http_parser.rb (0.8.0)
- i18n (0.9.5)
- concurrent-ruby (~> 1.0)
- jekyll (3.9.3)
- addressable (~> 2.4)
- colorator (~> 1.0)
- em-websocket (~> 0.5)
- i18n (>= 0.7, < 2)
- jekyll-sass-converter (~> 1.0)
- jekyll-watch (~> 2.0)
- kramdown (>= 1.17, < 3)
- liquid (~> 4.0)
- mercenary (~> 0.3.3)
- pathutil (~> 0.9)
- rouge (>= 1.7, < 4)
- safe_yaml (~> 1.0)
- jekyll-avatar (0.7.0)
- jekyll (>= 3.0, < 5.0)
- jekyll-coffeescript (1.1.1)
- coffee-script (~> 2.2)
- coffee-script-source (~> 1.11.1)
- jekyll-commonmark (1.4.0)
- commonmarker (~> 0.22)
- jekyll-commonmark-ghpages (0.4.0)
- commonmarker (~> 0.23.7)
- jekyll (~> 3.9.0)
- jekyll-commonmark (~> 1.4.0)
- rouge (>= 2.0, < 5.0)
- jekyll-default-layout (0.1.4)
- jekyll (~> 3.0)
- jekyll-feed (0.15.1)
- jekyll (>= 3.7, < 5.0)
- jekyll-gist (1.5.0)
- octokit (~> 4.2)
- jekyll-github-metadata (2.13.0)
- jekyll (>= 3.4, < 5.0)
- octokit (~> 4.0, != 4.4.0)
- jekyll-include-cache (0.2.1)
- jekyll (>= 3.7, < 5.0)
- jekyll-mentions (1.6.0)
- html-pipeline (~> 2.3)
- jekyll (>= 3.7, < 5.0)
- jekyll-optional-front-matter (0.3.2)
- jekyll (>= 3.0, < 5.0)
- jekyll-paginate (1.1.0)
- jekyll-readme-index (0.3.0)
- jekyll (>= 3.0, < 5.0)
- jekyll-redirect-from (0.16.0)
- jekyll (>= 3.3, < 5.0)
- jekyll-relative-links (0.6.1)
- jekyll (>= 3.3, < 5.0)
- jekyll-remote-theme (0.4.3)
- addressable (~> 2.0)
- jekyll (>= 3.5, < 5.0)
- jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
- rubyzip (>= 1.3.0, < 3.0)
- jekyll-sass-converter (1.5.2)
- sass (~> 3.4)
- jekyll-seo-tag (2.8.0)
- jekyll (>= 3.8, < 5.0)
- jekyll-sitemap (1.4.0)
- jekyll (>= 3.7, < 5.0)
- jekyll-swiss (1.0.0)
- jekyll-theme-architect (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-cayman (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-dinky (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-hacker (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-leap-day (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-merlot (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-midnight (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-minimal (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-modernist (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-primer (0.6.0)
- jekyll (> 3.5, < 5.0)
- jekyll-github-metadata (~> 2.9)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-slate (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-tactile (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-time-machine (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-titles-from-headings (0.5.3)
- jekyll (>= 3.3, < 5.0)
- jekyll-watch (2.2.1)
- listen (~> 3.0)
- jemoji (0.12.0)
- gemoji (~> 3.0)
- html-pipeline (~> 2.2)
- jekyll (>= 3.0, < 5.0)
- kramdown (2.3.2)
- rexml
- kramdown-parser-gfm (1.1.0)
- kramdown (~> 2.0)
- liquid (4.0.4)
- listen (3.7.1)
- rb-fsevent (~> 0.10, >= 0.10.3)
- rb-inotify (~> 0.9, >= 0.9.10)
- mercenary (0.3.6)
- mini_portile2 (2.8.2)
- minima (2.5.1)
- jekyll (>= 3.5, < 5.0)
- jekyll-feed (~> 0.9)
- jekyll-seo-tag (~> 2.1)
- minitest (5.15.0)
- multipart-post (2.1.1)
- nokogiri (1.15.1)
- mini_portile2 (~> 2.8.2)
- racc (~> 1.4)
- octokit (4.22.0)
- faraday (>= 0.9)
- sawyer (~> 0.8.0, >= 0.5.3)
- pathutil (0.16.2)
- forwardable-extended (~> 2.6)
- public_suffix (4.0.6)
- racc (1.6.0)
- rb-fsevent (0.11.0)
- rb-inotify (0.10.1)
- ffi (~> 1.0)
- rexml (3.2.5)
- rouge (3.26.0)
- ruby2_keywords (0.0.5)
- rubyzip (2.3.2)
- safe_yaml (1.0.5)
- sass (3.7.4)
- sass-listen (~> 4.0.0)
- sass-listen (4.0.0)
- rb-fsevent (~> 0.9, >= 0.9.4)
- rb-inotify (~> 0.9, >= 0.9.7)
- sawyer (0.8.2)
- addressable (>= 2.3.5)
- faraday (> 0.8, < 2.0)
- simpleidn (0.2.1)
- unf (~> 0.1.4)
- terminal-table (1.8.0)
- unicode-display_width (~> 1.1, >= 1.1.1)
- thread_safe (0.3.6)
- typhoeus (1.4.0)
- ethon (>= 0.9.0)
- tzinfo (1.2.9)
- thread_safe (~> 0.1)
- unf (0.1.4)
- unf_ext
- unf_ext (0.0.8)
- unicode-display_width (1.8.0)
- zeitwerk (2.5.3)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- embed-code!
- github-pages
- jekyll
- kramdown-parser-gfm
-
-BUNDLED WITH
- 2.4.13
diff --git a/README.md b/README.md
index e98d8bc..26ec8f8 100644
--- a/README.md
+++ b/README.md
@@ -2,57 +2,175 @@
This repository serves for three purposes:
-1. The [Wiki](https://github.com/SpineEventEngine/documentation/wiki) provides documentation for developers contributing to the framework, Commiters.
+1. The [Wiki][spine-wiki] provides documentation for developers contributing to the framework, Commiters.
2. Gathering issues improving the documentation of the framework and tasks on writing articles at spine.io and other web resources.
-3. Storing documentation files that are added as a submodule to the [spine.io](https://github.com/SpineEventEngine/spine.io) repository.
+3. Storing documentation files that are added as a Hugo module to the [spine.io][spine-repo] repository.
-We have repository for the code of [spine.io](https://spine.io) site. Issues there are for improving the site features.
+We have repository for the code of [spine.io](https://spine.io) site.
+Issues there are for improving the site features.
-Tasks for the content of the [spine.io](https://spine.io) site should belong to issues of _this_ repository.
+Tasks for the content of the [spine.io](https://spine.io) site should belong
+to issues of _this_ repository.
-This repository is made to be self-sustainable in terms of editing. A fully-fledged Jekyll site
-has been set up for it. All the contents and links are working as intended. It allows making
-changes more convenient for authors. To make it all possible this repository has its
-own `docs` folder — otherwise all links would be broken.
+This repository is made to be self-sustainable in terms of editing.
+A fully-fledged Hugo site has been set up for it. All the contents and links
+are working as intended. It allows making changes more convenient for authors.
-# Prerequisites
+The code samples used on the site and in the framework documentation are added
+using the [`embed-code`][embed-code] tool (Go version).
-1. Install Ruby.
-2. Install the `bundler` tool.
-3. Install the project dependencies by running `bundle install`.
+The code resides under the `_code` directory. For instructions on embedding the code
+into the pages, please see the [`_code/EMBEDDING.md`](_code/EMBEDDING.md) file.
-Now you should be able to run the documentation locally.
+## Prerequisites
-# Running the documentation locally
+1. Install [Java JDK] version `11` to build the site.
+2. Install [Go][go] at least version `1.12`.
+3. Install [Node.js][nodejs]. Its version should be `18+`.
+4. Install [Hugo Extended][hugo-quick-start] at least version `v0.145` or higher.
+5. Get access to the [`site-commons`][site-commons] repository from the admins
+ to be able to download the theme.
+6. Make sure [SSH][site-commons-ssh] is configured correctly and the passphrase
+ is stored in the keychain.
+7. Install project dependencies from the `site` directory by running `npm install`.
+
+## Running the documentation locally
+
+The project has two directories:
+
+* `docs` – contains the documentation files along with all the necessary
+ JS and CSS files. This directory will be added to
+ `SpineEventEngine/SpineEventEngine.github.io` as a Hugo Module.
+* `site` – contains the HTML and CSS files needed only to run the
+ documentation locally.
To build and launch the site on the local server:
+
+```shell
+./gradlew :runSite
```
-bundle exec jekyll serve
+
+To build the site without running the server:
+
+```shell
+./gradlew :buildSite
```
-# Code samples
+Another way to run the site locally is to follow these steps:
+
+1. Navigate to the `site` folder.
+2. Start the local server with this command:
+
+ ```shell
+ hugo server
+ ```
+
+If you receive a `permission denied` message, but you are sure that you have
+all the rights to the [required repositories](#prerequisites), try clearing
+the cache and run the `hugo serve` again:
+
+```shell
+hugo mod clean --all
+```
+
+## Theme updates
+
+This project uses several components from the [`site-commons`][site-commons]
+Hugo theme.
+
+Please note that if you update a theme with any critical changes, it must also
+be updated in the main `spine.io` site repository.
+
+To get theme updates:
+
+1. Navigate to the `site` directory.
+2. Clean the module cache:
+
+ ```shell
+ hugo mod clean
+ ```
+
+3. Update the Hugo Modules:
+
+ ```shell
+ hugo mod get -u
+ ```
+
+4. Commit and push changes from `go.mod` and `go.sum` files.
+ In the `go.sum` file keep only two last records to avoid file cluttering.
+
+## Code samples
The code samples used in the framework documentation are added using
-the [`embed-code`](https://github.com/SpineEventEngine/embed-code) Jekyll subcommand.
+the [`embed-code`][embed-code] Go subcommand.
+
+The code resides under the `_code` directory. For instructions on embedding
+the code into the pages, please see the [`EMBEDDING.md`](./_code/EMBEDDING.md) file.
+
+Please note that the following part of the script requires an ARM-based Mac.
+
+To embed the code samples, run:
+
+```shell
+./gradlew :embedCode
+```
+
+To verify that the source code samples embedded into the pages are up-to-date, run:
+
+```shell
+./gradlew :checkSamples
+```
+
+## Product release
-The code resides under the `_code` directory. For instructions on embedding the code into the pages,
-please see the [`EMBEDDING.md`](https://github.com/SpineEventEngine/spine.io/blob/master/_code/EMBEDDING.md) file.
+To release a new version of Spine documentation, see the [SPINE_RELEASE.md](SPINE_RELEASE.md).
-# Authoring
+## Authoring
For instructions on adding the content to the documentation, please see
-the [`AUTHORING.md`](https://github.com/SpineEventEngine/spine.io/blob/master/AUTHORING.md) file.
+the [`AUTHORING.md`][authoring-guide] file.
+
+## Styles and assets
+
+The documentation related styles are placed inside `docs/assets/scss`.
+The `docs` directory will be automatically added to the main site using Hugo Modules.
+
+There are two main import files:
+
+* `docs-common.scss` — contains the common styles that are also necessary for
+ the `SpineEventEngine/SpineEventEngine.github.io` repository. To not duplicate
+ global variables and layout styles, import the file in the main repository
+ _at the top_ of the `main.scss`
+* `docs.scss` — contains styles responsible for the appearance of the documentation.
+ Should be imported into the `main.scss` of the `spine.io` site as well.
+
+Styles needed only for running the documentation locally are located
+in `site/assets/scss`. They will not be available on `spine.io`.
+
+## Troubleshooting
+
+1. If you are getting the terminal `prompts disabled error` when trying to get
+ theme updates, make sure you have allowed 2FA to do its job. Also if you have
+ authentication issues during submodules update. You can resolve it with this
+ command:
-# Styles and assets
+ ```shell
+ git config --global --add url."git@github.com:".insteadOf "https://github.com/"
+ ```
-Styles were placed inside the `_sass` folder. And were separated into two import files:
-- `docs.scss` — contains styles responsible for the appearance of the documentation.
-Should be imported into the main spine.io site.
-- `docs-common.scss` — contains styles that should only be used to build this site.
-They are responsible for the common site layout and should not be imported into
-the main spine.io site to not override or duplicate styles there.
+2. If you are getting the `unknown revision upgrade` error when trying to get
+ theme updates, clean the module cache:
-The `_css`, `_favicons`, and `_fonts` have been configured not to be built in the main repository.
+ ```shell
+ hugo mod clean --all
+ ```
-The images should be placed inside `img/docs` folder to be automatically included in the main site
-and avoid accidental overrides for files with the same name.
+[go]: https://go.dev/doc/install
+[nodejs]: https://nodejs.org/en/download/current
+[hugo-quick-start]: https://gohugo.io/getting-started/quick-start/#step-1-install-hugo
+[site-commons]: https://github.com/TeamDev-Ltd/site-commons
+[site-commons-ssh]: https://github.com/TeamDev-Ltd/site-commons/tree/master?tab=readme-ov-file#configure-go-to-use-ssh-for-github
+[embed-code]: https://github.com/SpineEventEngine/embed-code/tree/embed-code-go
+[authoring-guide]: https://github.com/SpineEventEngine/SpineEventEngine.github.io/blob/master/AUTHORING.md
+[spine-repo]: https://github.com/SpineEventEngine/SpineEventEngine.github.io
+[spine-wiki]: https://github.com/SpineEventEngine/documentation/wiki
diff --git a/SPINE_RELEASE.md b/SPINE_RELEASE.md
new file mode 100644
index 0000000..ed6b626
--- /dev/null
+++ b/SPINE_RELEASE.md
@@ -0,0 +1,168 @@
+Release new version of the documentation
+========
+
+**Table of Contents**
+* [Release major version](#release-major-version)
+ * [Release steps](#release-steps)
+ * [URLs](#urls)
+
+## Release major version
+
+This site supports documentation versioning. All links within the
+documentation are rendered automatically.
+
+The versions are managed in the `docs/data/versions.yml` file.
+
+```yml
+- short: "1"
+ full: 1.9.0
+ path: docs
+ is_main: true
+ switcher:
+ visible: false
+ item_visible: false
+- short: "2"
+ full: 2.0.0
+ path: docs/2
+ switcher:
+ visible: false
+ item_visible: false
+```
+
+Where:
+
+* `short` shows the major version number, which will be used primarily for the link generation.
+* `full` the full version that will be visible in the version switcher.
+* `path` will be used when generating links inside a documentation version.
+* `is_main` the optional flag that marks the current main version.
+* `switcher`:
+ * `visible` specifies whether the version switcher will be visible on the page.
+ * `item_visible` specifies whether the version will be available in the switcher dropdown.
+
+The `switcher` component is under development.
+
+The documentation content should be created under the `content//` directory.
+Note that the current main version is also placed under its `version` directory.
+
+```
+content
+└── docs
+ ├── 1
+ │ ├── client-libs
+ │ ├── quick-start
+ │ └── _index.md
+ └── 2
+ ├── client-libs
+ ├── quick-start
+ └── _index.md
+```
+
+Also, each version should have its own `sidenav` config inside
+the `data/docs/` directory.
+
+```
+data
+└── docs
+ ├── 1
+ │ └── sidenav.yml
+ └── 2
+ └── sidenav.yml
+```
+
+### Release steps
+
+1. Create a new directory for the documentation inside the `content/docs//`.
+2. Create the `sidenav.yml` inside `data/docs//` directory.
+3. Update the `data/versions.yml` config.
+
+ For example, if you release the version `2` as the main version,
+ the config should be updated as follows:
+
+ ```yml
+ - short: "1"
+ full: 1.9.0
+ path: docs # Change the path from `docs` to `docs/1`.
+ is_main: true # Change the flag to `false` or delete the line.
+ switcher:
+ visible: false
+ item_visible: false
+ - short: "2"
+ full: 2.0.0-eap # Change the version to `2.0.0` if needed.
+ path: docs/2 # Change the path from `docs/2` to `docs`.
+ is_main: false # Set `is_main` as `true`.
+ switcher:
+ visible: false
+ item_visible: false
+ ```
+
+4. Update the `site/config/_default/hugo.toml` to make the version `2` as main.
+
+ - Navigate to the end of the file to the section "Version control config".
+ - Find the `path` parameters and update as follows:
+ ```
+ path = '{/docs/1}' -> path = '{/docs/2}'
+ path = '{/docs/1/**}' -> path = '{/docs/2/**}'
+ ```
+
+ Now the version `2` will be available at the `/docs/` URL and
+ will be opened by default.
+
+5. Commit and push the new version release.
+
+Get updates into the main website:
+
+1. Go to the [`SpineEventEngine.github.io`](https://github.com/SpineEventEngine/SpineEventEngine.github.io)
+ repository.
+2. Get theme updates `hugo mod get -u github.com/SpineEventEngine/documentation/docs`.
+3. Update the `config/_default/hugo.toml` to make the version `2` as main.
+
+### URLs
+
+All URLs inside `content/docs//` are automatically managed
+according to the current documentation version.
+
+Use links in documentation as follows, without including a version:
+
+`[Requirements](docs/guides/requirements/)`.
+
+Please note:
+
+* All internal links **must not** start with a slash.
+* Each link should **end with a slash** to prevent unnecessary redirects.
+
+The link above will be automatically rendered as:
+
+- `"/docs/guides/requirements/"` – for main version.
+- `"/docs/2/guides/requirements/"` – for the version `2`.
+- `"/docs/3/guides/requirements/"` – for the version `3`.
+
+To render the current documentation full version inside API URL,
+use `{{% version %}}` shortcode:
+
+```markdown
+[CoreJvm]({{% version %}})
+
+[CoreJvm {{% version %}}]({{% get-site-data "spine.core_jvm_repo" %}}/index.html)
+```
+
+Will be rendered as:
+
+```html
+CoreJvm
+
+CoreJvm 1.9.0
+```
+
+Where:
+
+* {{% get-site-data "spine.core_jvm_repo" %}} will apply the `core_jvm_repo`
+ from the `data/spine.yml` file.
+* {{% version %}} adds the full version of the current page -> `1.9.0`, or `2.0.0`.
+
+To provide a specific version for example in FAQ or Release Notes, use:
+
+```markdown
+{{% version "1" %}}
+```
+
+It will always render the latest “full” version of `1`, for example now it is `1.9.0`.
diff --git a/_code/EMBEDDING.md b/_code/EMBEDDING.md
new file mode 100644
index 0000000..6d1c46e
--- /dev/null
+++ b/_code/EMBEDDING.md
@@ -0,0 +1,126 @@
+Embedding code
+======
+
+We use the [embed-code][embed-code-repo] tool (Go version) to embed code snippets
+to markdown pages. Take a look on its [README][embed-code-readme] file to know
+how it works.
+
+## Prerequisites
+
+### Obtain `embed-code` executable
+
+Firstly, we need to download the last available executable of `embed-code`.
+Originally, this tool was written as a plugin for Jekyll, but now it is also
+available as a binary executable written in Go.
+
+1. Open [embed-code][embed-code-repo] repository.
+2. Switch to the `embed-code-go` branch.
+3. Go to the `embed-code-go/bin` and download an executable suitable for you OS.
+4. Put it in the `_code` directory of this repository.
+
+### Download code snippets
+
+The `_code` directory contains the source code, which is embedded into the pages
+of the spine.io documentation:
+
+* `examples` — contains examples selected from the repositories under `spine-examples`
+ organization. These repositories are added to this project as Git submodules.
+* `samples` — smaller pieces of code embedded to the site.
+
+To get the latest version of the code snippets, update the submodules:
+
+```shell
+git submodule update --remote
+```
+
+### Config files
+
+As for now, there is the `_code/config-v1.yml` config file for Spine v1.
+
+## Usage patterns
+
+First, update the code snippets, and then migrate the updated snippets
+to the documentation files using the tool. The most important points here are:
+
+1. Choose the correct config file (or create / modify one).
+2. Check out the correct branch in the repository with snippets.
+3. Make sure the used Spine version in snippets matches your expectations.
+4. After code embedding, please always review the changes made to `.md`
+ or `.html` files.
+
+### How to update an existing code snippet?
+
+1. Update the snippet in the appropriate repository.
+2. Make sure it builds successfully.
+3. Go to the `SpineEventEngine/documentation` project.
+4. Navigate to the `_code` directory.
+5. Execute the binary based on your operating system and architecture:
+ `./embed-code- -config-path="config-of-your-choice.yml" -mode="embed"`.
+
+ For example:
+ ```shell
+ ./embed-code-macos -config-path="config-v1.yml" -mode="embed"
+ ```
+
+### Adding a new example project
+
+1. Make sure the project you're going to add has a top-level `buildAll` Gradle task.
+
+ See the build script of [Hello Example](https://github.com/spine-examples/hello/blob/master/build.gradle)
+ for the declaration of such a task. This task must be present in both single-
+ and multi-module Gradle example projects that are going to be used for
+ embedding into this site.
+
+ See the declaration of `buildAll` task for more details.
+
+2. Add the example code as a submodule for this project:
+
+ ```bash
+ git submodule add https://github.com/spine-examples/ _code/examples/
+ ```
+ Please make sure the new submodule goes under the `_code/examples` directory, as shown in
+ the command line template above.
+
+3. Include the build of the added project into the [`settings.gradle.kts`](settings.gradle.kts)
+ file.
+4. Insert code embedding directives where needed in the `docs/content/` folder.
+5. Navigate to the `_code` directory.
+6. Execute: `./embed-code -config-path="config-of-your-choice.yml" -mode="embed"`.
+
+ For example:
+ ```shell
+ ./embed-code-macos -config-path="config-v1.yml" -mode="embed"
+ ```
+
+### Adding a new small piece
+
+1. Add the code under `_code/samples/src` directory.
+2. Make sure tests for the new code pass.
+3. Add the new piece using the [`embed-code` guide][embed-code-readme].
+4. Include the build of the added project into the [`settings.gradle.kts`](settings.gradle.kts)
+ file `includeBuild("./_code/samples")`.
+
+### How to remove a code snippet?
+
+1. Remove the snippet in the appropriate repository.
+2. Remove its code embeddings from the markdown files.
+3. Make sure there are no embeddings of the removed snippet anymore:
+ `./embed-code- -config-path="config-of-your-choice.yml" -mode="check"`.
+
+ For example:
+ ```shell
+ ./embed-code-macos -config-path="config-v1.yml" -mode="check"
+ ```
+
+## Troubleshooting
+
+If you encounter an error indicating that the executable file cannot be run,
+it likely does not have execution permissions. To grant execution rights
+on Linux or macOS, run the following command from the `_code` directory:
+
+```shell
+chmod +x embed-code-macos
+```
+
+[embed-code-repo]: https://github.com/SpineEventEngine/embed-code
+[embed-code-readme]: https://github.com/SpineEventEngine/embed-code/blob/embed-code-go/embed-code-go/README.md
diff --git a/_code/config-v1.yml b/_code/config-v1.yml
new file mode 100644
index 0000000..4f7184c
--- /dev/null
+++ b/_code/config-v1.yml
@@ -0,0 +1,5 @@
+code-path: "../_code"
+docs-path: "../docs/content/docs/1"
+embed-mappings:
+ - code-path: "../_code"
+ docs-path: "../docs/content/docs/1"
diff --git a/_code/embed-code-macos b/_code/embed-code-macos
new file mode 100755
index 0000000..b19f982
Binary files /dev/null and b/_code/embed-code-macos differ
diff --git a/_code/embed-code-macos-x86_64 b/_code/embed-code-macos-x86_64
new file mode 100755
index 0000000..9627656
Binary files /dev/null and b/_code/embed-code-macos-x86_64 differ
diff --git a/_code/embed-code-ubuntu b/_code/embed-code-ubuntu
new file mode 100755
index 0000000..7379805
Binary files /dev/null and b/_code/embed-code-ubuntu differ
diff --git a/_code/examples/airport b/_code/examples/airport
index 7312595..f17c82e 160000
--- a/_code/examples/airport
+++ b/_code/examples/airport
@@ -1 +1 @@
-Subproject commit 731259550588540239bcdd2d4d1ea409e95b556c
+Subproject commit f17c82ef405bd20f271101d143e0738537d631cc
diff --git a/_code/examples/blog b/_code/examples/blog
index 2055008..e74a84f 160000
--- a/_code/examples/blog
+++ b/_code/examples/blog
@@ -1 +1 @@
-Subproject commit 20550087d1f4a08e6efd01fb2191f2a7d0602e66
+Subproject commit e74a84f397e2b4ee3d8dc103ed043ccfc45eccce
diff --git a/_code/examples/hello b/_code/examples/hello
index 5cb4c9a..ed78883 160000
--- a/_code/examples/hello
+++ b/_code/examples/hello
@@ -1 +1 @@
-Subproject commit 5cb4c9a5f238c93d4de2d07ee877eb936a7c1be6
+Subproject commit ed788833f4b9f10f2e6850e5e3829d1301813129
diff --git a/_code/examples/kanban b/_code/examples/kanban
index d8de57c..06ed081 160000
--- a/_code/examples/kanban
+++ b/_code/examples/kanban
@@ -1 +1 @@
-Subproject commit d8de57caa7372febdefd63f656420fd10a60866f
+Subproject commit 06ed081cb091f35f3c97045caa8a3873589aa2d2
diff --git a/_code/examples/todo-list b/_code/examples/todo-list
index 2d026d5..f9dcc2d 160000
--- a/_code/examples/todo-list
+++ b/_code/examples/todo-list
@@ -1 +1 @@
-Subproject commit 2d026d5c1d56af4c24a0201618ec4ab69145e66c
+Subproject commit f9dcc2d510f3f6d9fbaf2b4a04607108c7a99537
diff --git a/_code/samples/build.gradle b/_code/samples/build.gradle
new file mode 100644
index 0000000..8b18b57
--- /dev/null
+++ b/_code/samples/build.gradle
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+plugins {
+ id "io.spine.tools.gradle.bootstrap" version "1.7.0"
+}
+
+spine.enableJava().server()
+
+ext {
+ junitVersion = '5.7.0'
+}
+
+dependencies {
+ testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
+ testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
+}
+
+test {
+ useJUnitPlatform {
+ includeEngines 'junit-jupiter'
+ }
+}
+
+project.ext {
+ // The root for the generated source code.
+ generatedRootDir = "$projectDir/generated"
+
+ // Directories for the code generated by the Protobuf Compiler and Spine plugins
+ // for the Compiler.
+ generatedJavaDir = "$generatedRootDir/main/java"
+ generatedTestJavaDir = "$generatedRootDir/test/java"
+
+ // Directories for the Spine-specific code generated by the Spine Model Compiler,
+ // for example, rejections.
+ generatedSpineDir = "$generatedRootDir/main/spine"
+ generatedTestSpineDir = "$generatedRootDir/test/spine"
+}
+
+apply plugin: 'idea'
+
+idea {
+ module {
+ // Add generated code directories to the source code of the module in IntelliJ IDEA.
+ generatedSourceDirs += file(generatedJavaDir)
+ generatedSourceDirs += file(generatedSpineDir)
+ testSourceDirs += file(generatedTestJavaDir)
+ testSourceDirs += file(generatedTestSpineDir)
+ }
+}
+
+/**
+ * The task for composite builds to include this project and depend on this top-level task.
+ */
+task buildAll(type: GradleBuild) {
+ tasks = [ 'build' ]
+}
diff --git a/_code/samples/gradle/wrapper/gradle-wrapper.jar b/_code/samples/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/_code/samples/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/_code/samples/gradle/wrapper/gradle-wrapper.properties b/_code/samples/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..4d9ca16
--- /dev/null
+++ b/_code/samples/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/_code/samples/gradlew b/_code/samples/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/_code/samples/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/_code/samples/gradlew.bat b/_code/samples/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/_code/samples/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/_code/samples/settings.gradle b/_code/samples/settings.gradle
new file mode 100644
index 0000000..55ac0f4
--- /dev/null
+++ b/_code/samples/settings.gradle
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+rootProject.name = "samples"
diff --git a/_code/samples/src/main/java/io/spine/site/home/server/NanoPmContext.java b/_code/samples/src/main/java/io/spine/site/home/server/NanoPmContext.java
new file mode 100644
index 0000000..6bdcb96
--- /dev/null
+++ b/_code/samples/src/main/java/io/spine/site/home/server/NanoPmContext.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.site.home.server;
+
+import io.spine.server.BoundedContext;
+import io.spine.server.BoundedContextBuilder;
+
+/**
+ * Defines the Mini PM context.
+ */
+public final class NanoPmContext {
+
+ static final String NAME = "Nano PM";
+
+ /** Prevents instantiation of this utility class. */
+ private NanoPmContext() {
+ }
+
+ /** Creates a builder for single-tenant context filled in with entity types. */
+ public static BoundedContextBuilder newBuilder() {
+ return BoundedContext.singleTenant(NAME)
+ .add(TaskAggregate.class)
+ .add(TaskProjection.class);
+ }
+}
diff --git a/_code/samples/src/main/java/io/spine/site/home/server/TaskAggregate.java b/_code/samples/src/main/java/io/spine/site/home/server/TaskAggregate.java
new file mode 100644
index 0000000..152f55f
--- /dev/null
+++ b/_code/samples/src/main/java/io/spine/site/home/server/TaskAggregate.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.site.home.server;
+
+import io.spine.core.CommandContext;
+import io.spine.site.home.Task;
+import io.spine.site.home.TaskId;
+import io.spine.site.home.command.AssignTask;
+import io.spine.site.home.command.CompleteTask;
+import io.spine.site.home.command.CreateTask;
+import io.spine.site.home.event.TaskAssigned;
+import io.spine.site.home.event.TaskCompleted;
+import io.spine.site.home.event.TaskCreated;
+import io.spine.server.aggregate.Aggregate;
+import io.spine.server.aggregate.Apply;
+import io.spine.server.command.Assign;
+
+final class TaskAggregate extends Aggregate {
+
+ @Assign
+ TaskCreated handle(CreateTask cmd, CommandContext ctx) {
+ return TaskCreated.newBuilder()
+ .setTask(cmd.getId())
+ .setName(cmd.getName())
+ .setDescription(cmd.getDescription())
+ .setOwner(ctx.getActorContext().getActor())
+ .vBuild(); // validate the event
+ }
+
+ @Apply
+ private void event(TaskCreated e) {
+ builder().setName(e.getName())
+ .setDescription(e.getDescription())
+ .setOwner(e.getOwner());
+ }
+
+ @Assign
+ TaskCompleted handle(CompleteTask cmd) {
+ return TaskCompleted.newBuilder()
+ .setTask(cmd.getTask())
+ .vBuild();
+ }
+
+ @Apply
+ private void event(TaskCompleted e) {
+ setArchived(true);
+ }
+
+ @Assign
+ TaskAssigned handle(AssignTask cmd) {
+ return TaskAssigned.newBuilder()
+ .setAssignee(cmd.getAssignee())
+ .vBuild();
+ }
+
+ @Apply
+ private void event(TaskAssigned e) {
+ builder().setAssignee(e.getAssignee());
+ }
+}
diff --git a/_code/samples/src/main/java/io/spine/site/home/server/TaskProjection.java b/_code/samples/src/main/java/io/spine/site/home/server/TaskProjection.java
new file mode 100644
index 0000000..03752f9
--- /dev/null
+++ b/_code/samples/src/main/java/io/spine/site/home/server/TaskProjection.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.site.home.server;
+
+import io.spine.core.EventContext;
+import io.spine.core.Subscribe;
+import io.spine.core.UserId;
+import io.spine.site.home.TaskId;
+import io.spine.site.home.TaskItem;
+import io.spine.site.home.event.TaskAssigned;
+import io.spine.site.home.event.TaskCompleted;
+import io.spine.site.home.event.TaskCreated;
+import io.spine.server.projection.Projection;
+
+final class TaskProjection extends Projection {
+
+ @Subscribe
+ void on(TaskCreated e) {
+ builder().setName(e.getName())
+ .setDescription(e.getDescription())
+ .setOwner(toPersonName(e.getOwner()));
+ }
+
+ @Subscribe
+ void on(TaskAssigned e) {
+ builder().setAssignee(toPersonName(e.getAssignee()));
+ }
+
+ @Subscribe
+ void on(TaskCompleted e, EventContext ctx) {
+ builder().setWhenDone(ctx.getTimestamp());
+ }
+
+ /**
+ * Returns always the same mock name because this code is not going to be shown
+ * at the site pages.
+ */
+ private static String toPersonName(@SuppressWarnings("unused") UserId user) {
+ return "John Doe";
+ }
+}
diff --git a/_code/samples/src/main/java/io/spine/site/home/server/package-info.java b/_code/samples/src/main/java/io/spine/site/home/server/package-info.java
new file mode 100644
index 0000000..13658d0
--- /dev/null
+++ b/_code/samples/src/main/java/io/spine/site/home/server/package-info.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * This package contains server-side implementation of the Mini PM example which is used
+ * at the site home page.
+ */
+@CheckReturnValue
+@ParametersAreNonnullByDefault
+@BoundedContext(NanoPmContext.NAME)
+package io.spine.site.home.server;
+
+import com.google.errorprone.annotations.CheckReturnValue;
+import io.spine.core.BoundedContext;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/_code/samples/src/main/proto/spine/example/events.proto b/_code/samples/src/main/proto/spine/example/events.proto
new file mode 100644
index 0000000..ace437e
--- /dev/null
+++ b/_code/samples/src/main/proto/spine/example/events.proto
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+syntax = "proto3";
+
+package spine.example;
+
+import "spine/options.proto";
+
+option (type_url_prefix) = "type.spine.io";
+option java_package = "io.spine.example";
+option java_outer_classname = "EventsProto";
+option java_multiple_files = true;
+
+// #docregion ProjectCreated
+message ProjectCreated {
+
+}
+// #enddocregion ProjectCreated
diff --git a/_code/samples/src/main/proto/spine/site/home/commands.proto b/_code/samples/src/main/proto/spine/site/home/commands.proto
new file mode 100644
index 0000000..bd14cd4
--- /dev/null
+++ b/_code/samples/src/main/proto/spine/site/home/commands.proto
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+syntax = "proto3";
+
+package spine.site.home;
+
+import "spine/options.proto";
+
+option (type_url_prefix) = "type.spine.io";
+option java_package = "io.spine.site.home.command";
+option java_outer_classname = "CommandsProto";
+option java_multiple_files = true;
+
+import "spine/site/home/identifiers.proto";
+
+// Create a new task.
+message CreateTask {
+ TaskId id = 1; // assumed `required`
+ string name = 2 [(required) = true];
+ string description = 3;
+}
+
+// Assign a task on a user.
+message AssignTask {
+ TaskId task = 1;
+ spine.core.UserId assignee = 2 [(required) = true];
+}
+
+// Mark a task as completed.
+message CompleteTask {
+ TaskId task = 1;
+}
diff --git a/_code/samples/src/main/proto/spine/site/home/events.proto b/_code/samples/src/main/proto/spine/site/home/events.proto
new file mode 100644
index 0000000..379e1c7
--- /dev/null
+++ b/_code/samples/src/main/proto/spine/site/home/events.proto
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+syntax = "proto3";
+
+package spine.site.home;
+
+import "spine/options.proto";
+
+option (type_url_prefix) = "type.spine.io";
+option java_package = "io.spine.site.home.event";
+option java_outer_classname = "CommandsProto";
+option java_multiple_files = true;
+
+import "spine/site/home/identifiers.proto";
+
+// A new task has been created.
+message TaskCreated {
+ TaskId task = 1 [(required) = true];
+ string name = 2 [(required) = true];
+ string description = 3;
+ spine.core.UserId owner = 4 [(required) = true];
+}
+
+// A task was assigned to a user.
+message TaskAssigned {
+ TaskId task = 1 [(required) = true];
+ spine.core.UserId assignee = 2 [(required) = true];
+}
+
+// A task was marked as completed.
+message TaskCompleted {
+ TaskId task = 1 [(required) = true];
+}
diff --git a/_code/samples/src/main/proto/spine/site/home/identifiers.proto b/_code/samples/src/main/proto/spine/site/home/identifiers.proto
new file mode 100644
index 0000000..fee4bea
--- /dev/null
+++ b/_code/samples/src/main/proto/spine/site/home/identifiers.proto
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+syntax = "proto3";
+
+package spine.site.home;
+
+import "spine/options.proto";
+
+option (type_url_prefix) = "type.spine.io";
+option java_package = "io.spine.site.home";
+option java_outer_classname = "IdentifiersProto";
+option java_multiple_files = true;
+
+import public "spine/core/user_id.proto";
+
+message TaskId {
+ string uuid = 1;
+}
diff --git a/_code/samples/src/main/proto/spine/site/home/task.proto b/_code/samples/src/main/proto/spine/site/home/task.proto
new file mode 100644
index 0000000..32aa1da
--- /dev/null
+++ b/_code/samples/src/main/proto/spine/site/home/task.proto
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+syntax = "proto3";
+
+package spine.site.home;
+
+import "spine/options.proto";
+
+option (type_url_prefix) = "type.spine.io";
+option java_package = "io.spine.site.home";
+option java_outer_classname = "TaskProto";
+option java_multiple_files = true;
+
+import "google/protobuf/timestamp.proto";
+import "spine/site/home/identifiers.proto";
+
+// A task which can be assigned to a user.
+message Task {
+ option (entity).kind = AGGREGATE;
+ TaskId id = 1; // assumed `required`
+ string name = 2 [(required) = true];
+ string description = 3;
+ spine.core.UserId owner = 4 [(required) = true];
+ spine.core.UserId assignee = 5;
+}
+
+// An item in a task list displayed in UI.
+message TaskItem {
+ option (entity).kind = PROJECTION;
+ TaskId task = 1;
+ string name = 2 [(required) = true];
+ string description = 3;
+ string owner = 4 [(required) = true];
+ string assignee = 5;
+ google.protobuf.Timestamp when_done = 6;
+}
diff --git a/_code/samples/src/test/java/io/spine/example/CompiledProtobuf.java b/_code/samples/src/test/java/io/spine/example/CompiledProtobuf.java
new file mode 100644
index 0000000..591de70
--- /dev/null
+++ b/_code/samples/src/test/java/io/spine/example/CompiledProtobuf.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package io.spine.example;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static com.google.common.truth.Truth.assertThat;
+
+@DisplayName("Project should contain Protobuf types:")
+class CompiledProtobuf {
+
+ @Test
+ @DisplayName("ProjectCreated")
+ void projectCreated() {
+ assertThat(ProjectCreated.class)
+ .isNotNull();
+ }
+}
diff --git a/_code/samples/src/test/java/io/spine/site/home/server/TaskCreationTest.java b/_code/samples/src/test/java/io/spine/site/home/server/TaskCreationTest.java
new file mode 100644
index 0000000..5b7d085
--- /dev/null
+++ b/_code/samples/src/test/java/io/spine/site/home/server/TaskCreationTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.site.home.server;
+
+import io.spine.server.BoundedContextBuilder;
+import io.spine.site.home.Task;
+import io.spine.site.home.TaskId;
+import io.spine.site.home.TaskItem;
+import io.spine.site.home.command.CreateTask;
+import io.spine.site.home.event.TaskCreated;
+import io.spine.testing.server.blackbox.ContextAwareTest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static io.spine.testing.TestValues.randomString;
+
+@DisplayName("Handling `CreateTask` command should")
+public class TaskCreationTest extends ContextAwareTest {
+
+ private TaskId task;
+ private String name;
+ private String description;
+
+ @Override
+ protected BoundedContextBuilder contextBuilder() {
+ return NanoPmContext.newBuilder();
+ }
+
+ private CreateTask generateCommand() {
+ task = TaskId.generate();
+ name = randomString();
+ description = randomString();
+ return CreateTask.newBuilder()
+ .setId(task)
+ .setName(name)
+ .setDescription(description)
+ .vBuild();
+ }
+
+ @BeforeEach
+ void postCommand() {
+ CreateTask cmd = generateCommand();
+ context().receivesCommand(cmd);
+ }
+
+ @Test
+ @DisplayName("generate `TaskCreated` event")
+ void eventGenerated() {
+ TaskCreated expected = expectedEvent();
+ context().assertEvent(TaskCreated.class)
+ .comparingExpectedFieldsOnly()
+ .isEqualTo(expected);
+ }
+
+ private TaskCreated expectedEvent() {
+ return TaskCreated.newBuilder()
+ .setTask(task)
+ .setName(name)
+ .setDescription(description)
+ .buildPartial();
+ }
+
+ @Test
+ @DisplayName("create a `Task`")
+ void aggregateCreation() {
+ Task expected = expectedAggregateState();
+ context().assertEntityWithState(task, Task.class)
+ .hasStateThat()
+ .comparingExpectedFieldsOnly()
+ .isEqualTo(expected);
+ }
+
+ private Task expectedAggregateState() {
+ return Task.newBuilder()
+ .setId(task)
+ .setName(name)
+ .setDescription(description)
+ .buildPartial();
+ }
+
+ @Test
+ @DisplayName("create a `TaskItem`")
+ void projectionCreation() {
+ TaskItem expected = expectedProjectionState();
+ context().assertEntityWithState(task, TaskItem.class)
+ .hasStateThat()
+ .comparingExpectedFieldsOnly()
+ .isEqualTo(expected);
+ }
+
+ private TaskItem expectedProjectionState() {
+ return TaskItem.newBuilder()
+ .setName(name)
+ .setDescription(description)
+ .buildPartial();
+ }
+}
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index e5285ff..0000000
--- a/_config.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-title: Spine Event Engine documentation
-description: CQRS/ES framework for modern could applications
-baseurl: ""
-github_username: SpineEventEngine
-
-core_java_repo: https://github.com/SpineEventEngine/core-java
-web_repo: https://github.com/SpineEventEngine/web
-dart_repo: https://github.com/SpineEventEngine/dart
-
-base_api_doc: https://spine.io/base-libraries/dokka-reference
-core_api_doc: https://spine.io/core-jvm/dokka-reference
-web_api_doc: https://spine.io/web/dokka-reference
-js_api_doc: https://spine.io/web/reference/client-js
-dart_api_doc: https://spine.io/dart/reference
-
-# Conversion
-markdown: kramdown
-highlighter: rouge
-excerpt_separator:
-
-sass:
- style: compressed
-
-kramdown:
- input: GFM
- hard_wrap: false
- parse_block_html: true
-
-exclude:
- - .github/
- - .idea/
- - .saas-cache/
- - Gemfile
- - Gemfile.lock
- - LICENSE
- - README.md
-
-include:
- - _fonts
- - _css
- - _favicons
- - _libs
-
-embed_code:
- code_root: ./_code
- documentation_root: ./docs
- code_includes: ["**/*.proto", "**/*.java", "**/*.kt", "**/*.dart", "**/*.js", "**/*.html", "**/*.gradle", "**/*.gradle.kts"]
diff --git a/_css/code-dark-theme.css b/_css/code-dark-theme.css
deleted file mode 100644
index 136c357..0000000
--- a/_css/code-dark-theme.css
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * Syntax highlighting styles
- */
-.highlight pre { background: #2b2b2b; }
-.highlighter-rouge .highlight { background: #2b2b2b; }
-.highlight code { color: #a9b7c6; font-weight: 500; } /* common font color */
-.highlight .c { color: #998; font-style: italic; }
-.highlight .err { color: #a61717; background-color: #e3d2d2; }
-.highlight .k { color: #D97317; font-weight: bold; }
-.highlight .o { font-weight: bold; } /* punctuation */
-.highlight .cm { color: #998; font-style: italic; }
-.highlight .cp { color: #999; font-weight: bold; }
-.highlight .c1 { color: #808080; } /* comment */
-.highlight .cs { color: #999; font-weight: bold; font-style: italic; }
-.highlight .gd { color: #000; background-color: #fdd; }
-.highlight .gd .x { color: #000; background-color: #faa; }
-.highlight .ge { font-style: italic; }
-.highlight .gr { color: #a00; }
-.highlight .gh { color: #999; }
-.highlight .gi { color: #000; background-color: #dfd; }
-.highlight .gi .x { color: #000; background-color: #afa; }
-.highlight .go { color: #888; }
-.highlight .gp { color: #555; }
-.highlight .gs { font-weight: bold; }
-.highlight .gu { color: #aaa; }
-.highlight .gt { color: #a00; }
-.highlight .kc { color: #D97317; font-weight: bold; } /* boolean */
-.highlight .kd { color: #D97317; font-weight: bold; } /* keywords */
-.highlight .kn { color: #D97317; font-weight: bold; } /* keywords */
-.highlight .kp { color: #D97317; font-weight: bold; }
-.highlight .kr { color: #D97317; font-weight: bold; }
-.highlight .kt { color: #D97317; font-weight: bold; }
-.highlight .m { color: #099; }
-.highlight .s { color: #6A8759; } /* string */
-.highlight .na { color: #a9b7c6; }
-.highlight .nb { color: #0086B3; }
-.highlight .nc { color: #a9b7c6; }
-.highlight .nd { color: #bbb529; font-weight: bold; }
-.highlight .no { color: #A680B5; } /* constants */
-.highlight .ni { color: #800080; }
-.highlight .ne { color: #900; font-weight: bold; }
-.highlight .nf { color: #FCC46C; font-weight: bold; }
-.highlight .nt { color: #000080; }
-.highlight .nv { color: #008080; }
-.highlight .ow { font-weight: bold; }
-.highlight .w { color: #bbb; }
-.highlight .mf { color: #099; }
-.highlight .mh { color: #099; }
-.highlight .mi { color: #6896BB; } /* values */
-.highlight .mo { color: #099; }
-.highlight .sb { color: #6A8759; }
-.highlight .sc { color: #6A8759; }
-.highlight .sd { color: #6A8759; }
-.highlight .s2 { color: #6A8759; }
-.highlight .se { color: #6A8759; }
-.highlight .sh { color: #6A8759; }
-.highlight .si { color: #6A8759; }
-.highlight .sx { color: #6A8759; }
-.highlight .sr { color: #6A8759; }
-.highlight .s1 { color: #6A8759; } /* string */
-.highlight .ss { color: #990073; }
-.highlight .bp { color: #999; }
-.highlight .vc { color: #008080; }
-.highlight .vg { color: #008080; }
-.highlight .vi { color: #008080; }
-.highlight .il { color: #099; }
-
-/* Removes keyword style for the word after `return`. */
-.highlight .k + .nf { color: #a9b7c6; font-weight: 500; }
diff --git a/_css/style-docs.scss b/_css/style-docs.scss
deleted file mode 100644
index e37336d..0000000
--- a/_css/style-docs.scss
+++ /dev/null
@@ -1,6 +0,0 @@
----
-# Front matter comment to ensure Jekyll properly reads the file.
----
-
-@import "docs-common";
-@import "docs";
diff --git a/_layouts/base.html b/_layouts/base.html
deleted file mode 100644
index 3862c3f..0000000
--- a/_layouts/base.html
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
-
-
-
-
-
- Spine / {{ page.title }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ content }}
-
-
-
-
-
-
-
-
-
-
-
- {% if page.customjs %}
-
- {% endif %}
-
-
diff --git a/_layouts/default.html b/_layouts/default.html
deleted file mode 100644
index 3797865..0000000
--- a/_layouts/default.html
+++ /dev/null
@@ -1,5 +0,0 @@
----
-layout: base
----
-
-{{ content }}
diff --git a/_layouts/docs.html b/_layouts/docs.html
deleted file mode 100644
index ea584ec..0000000
--- a/_layouts/docs.html
+++ /dev/null
@@ -1,15 +0,0 @@
----
-title: docs
-layout: default
-bodyclass: docs
----
-
-
-
-
-
- {{ content }}
-
-
-
-
diff --git a/_layouts/full-screen.html b/_layouts/full-screen.html
deleted file mode 100644
index 3797865..0000000
--- a/_layouts/full-screen.html
+++ /dev/null
@@ -1,5 +0,0 @@
----
-layout: base
----
-
-{{ content }}
diff --git a/_libs/js.cookie.min.js b/_libs/js.cookie.min.js
deleted file mode 100644
index 90a7672..0000000
--- a/_libs/js.cookie.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! js-cookie v3.0.1 | MIT */
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self,function(){var n=e.Cookies,o=e.Cookies=t();o.noConflict=function(){return e.Cookies=n,o}}())}(this,(function(){"use strict";function e(e){for(var t=1;t("runSite") {
+ commandLine("./_script/hugo-serve")
+}
+
+/**
+ * Builds the site without starting the server.
+ */
+task("buildSite") {
+ commandLine("./_script/hugo-build")
+}
+
+/**
+ * Embeds the code samples into pages of the site.
+ */
+task("embedCode") {
+ commandLine("./_script/embed-code")
+}
+
+/**
+ * Verifies that the source code samples embedded into the pages are up-to-date.
+ */
+task("checkSamples") {
+ commandLine("./_script/check-samples")
+}
+
+/**
+ * Builds all included projects via depending on the top-level "buildAll" tasks
+ * declared in these projects.
+ *
+ * @see https://discuss.gradle.org/t/defining-a-composite-build-only-to-build-all-subprojects/25070/6
+ * @see https://github.com/AlexMAS/gradle-composite-build-example
+ * @see https://docs.gradle.org/current/userguide/composite_builds.html
+ */
+tasks.register("buildAll") {
+ dependsOn(gradle.includedBuilds.map { it.task(":buildAll") })
+}
diff --git a/docs/assets/js/bootstrap.js b/docs/assets/js/bootstrap.js
new file mode 100644
index 0000000..89909c4
--- /dev/null
+++ b/docs/assets/js/bootstrap.js
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import 'bootstrap/js/dist/collapse';
+import 'bootstrap/js/dist/dropdown';
+import 'bootstrap/js/dist/tooltip';
diff --git a/docs/assets/js/docs-main.js b/docs/assets/js/docs-main.js
new file mode 100644
index 0000000..6e4bab5
--- /dev/null
+++ b/docs/assets/js/docs-main.js
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Theme JS from the `TeamDev-Ltd/site-commons`.
+/* TODO:2025-12-03:julia.evseeva: Enable when the icon position will be approved. */
+/*import 'js/components/copy-code.js'*/
+
+import {interactiveToc} from 'js/docs/interactive-toc';
+import {setElementMaxHeight} from 'js/docs/element-max-height';
+import {initSidenav} from 'js/docs/sidenav';
+import {initCodeTheme} from 'js/docs/code-theme';
+import {setupAnchorClick} from 'js/docs/anchor-icon';
+
+$(function() {
+ initCodeTheme();
+ setElementMaxHeight();
+
+ if ($('body').hasClass('docs')) {
+ interactiveToc();
+ initSidenav();
+ setupAnchorClick();
+ }
+});
diff --git a/docs/assets/js/docs/anchor-icon.js b/docs/assets/js/docs/anchor-icon.js
new file mode 100644
index 0000000..6702397
--- /dev/null
+++ b/docs/assets/js/docs/anchor-icon.js
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+'use strict';
+
+import {copyToClipboard} from "js/components/copy-to-clipboard";
+
+/**
+ * Manages the anchor icon click.
+ *
+ *
Also, copies the `href` to clipboard.
+ */
+export function setupAnchorClick() {
+ const anchorIconClass = 'anchor-icon';
+ const $anchorLinks = $('a[href^="#"]');
+
+ $anchorLinks.on('click', function() {
+ const $this = $(this);
+ const anchor = $this.attr('href');
+
+ if ($this.hasClass(anchorIconClass)) {
+ window.location.hash = anchor;
+ copyToClipboard(window.location.href);
+ }
+ });
+}
diff --git a/tools/architecture-diagram.js b/docs/assets/js/docs/architecture-diagram.js
similarity index 79%
rename from tools/architecture-diagram.js
rename to docs/assets/js/docs/architecture-diagram.js
index 021cdba..e68c3e6 100644
--- a/tools/architecture-diagram.js
+++ b/docs/assets/js/docs/architecture-diagram.js
@@ -1,7 +1,33 @@
+/*
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
/**
* This is a JavaScript file which backs the Spine architecture diagram.
*
- * Please see `/docs/introduction/architecture.md` for usage.
+ * Please see `/docs/1/introduction/architecture.md` for usage.
*/
$(
@@ -11,11 +37,13 @@ $(
* Binds the handlers to the events in this HTML page.
*/
function () {
+ const baseURL = BASE_URL;
+
+ console.log(`baseurl ${baseURL}`)
/**
* CSS classes used as selectors to manipulate the elements of SVG diagram.
*/
-
const endUserClass = "end-user";
const boxCaptionClass = "box-caption";
const arrowCaptionClass = "arrow-caption";
@@ -203,7 +231,7 @@ $(
})
.click(function () {
- document.location.href = url;
+ document.location.href = baseURL + url;
});
$(".g-caption-bounded-context" + selector)
@@ -247,7 +275,7 @@ $(
})
.click(function () {
- document.location.href = url;
+ document.location.href = baseURL + url;
});
@@ -285,7 +313,7 @@ $(
})
.click(function () {
- document.location.href = url;
+ document.location.href = baseURL + url;
});
}
@@ -326,28 +354,27 @@ $(
// Link items to the corresponding pages.
// Boxes:
- makeClickable(".aggregate", "/docs/introduction/concepts#aggregate");
- makeClickable(".bounded-context", "/docs/introduction/concepts#bounded-context");
- makeClickable(".pm", "/docs/introduction/concepts#process-manager");
- makeClickable(".projection", "/docs/introduction/concepts#projection");
- makeClickable(".aggregate-repo", "/docs/introduction/concepts#repository");
- makeClickable(".pm-repo", "/docs/introduction/concepts#repository");
- makeClickable(".projection-repo", "/docs/introduction/concepts#repository");
- makeClickable(".command-bus", "/docs/introduction/concepts#command-bus");
- makeClickable(".event-bus", "/docs/introduction/concepts#event-bus");
- makeClickable(".aggregate-mirror", "/docs/introduction/concepts#aggregate-mirror");
- makeClickable(".command-store", "/docs/introduction/concepts#command-store");
- makeClickable(".event-store", "/docs/introduction/concepts#event-store");
- makeClickable(".command-service", "/docs/introduction/concepts#command-service");
- makeClickable(".query-service", "/docs/introduction/concepts#query-service");
- makeClickable(".subscription-service", "/docs/introduction/concepts#subscription-service");
- makeClickable(".stand", "/docs/introduction/concepts#stand");
+ makeClickable(".aggregate", "docs/introduction/concepts#aggregate");
+ makeClickable(".bounded-context", "docs/introduction/concepts#bounded-context");
+ makeClickable(".pm", "docs/introduction/concepts#process-manager");
+ makeClickable(".projection", "docs/introduction/concepts#projection");
+ makeClickable(".aggregate-repo", "docs/introduction/concepts#repository");
+ makeClickable(".pm-repo", "docs/introduction/concepts#repository");
+ makeClickable(".projection-repo", "docs/introduction/concepts#repository");
+ makeClickable(".command-bus", "docs/introduction/concepts#command-bus");
+ makeClickable(".event-bus", "docs/introduction/concepts#event-bus");
+ makeClickable(".aggregate-mirror", "docs/introduction/concepts#aggregate-mirror");
+ makeClickable(".command-store", "docs/introduction/concepts#command-store");
+ makeClickable(".event-store", "docs/introduction/concepts#event-store");
+ makeClickable(".command-service", "docs/introduction/concepts#command-service");
+ makeClickable(".query-service", "docs/introduction/concepts#query-service");
+ makeClickable(".subscription-service", "docs/introduction/concepts#subscription-service");
+ makeClickable(".stand", "docs/introduction/concepts#stand");
// Arrows:
-
- makeClickable(".ui-command-service", "/docs/introduction/concepts#command");
- makeClickable(".command-service-ui", "/docs/introduction/concepts#acknowledgement");
- makeClickable(".event-bus-aggregate-repo", "/docs/introduction/concepts#event");
- makeClickable(".integration-events", "/docs/introduction/concepts#integration-event");
+ makeClickable(".ui-command-service", "docs/introduction/concepts#command");
+ makeClickable(".command-service-ui", "docs/introduction/concepts#acknowledgement");
+ makeClickable(".event-bus-aggregate-repo", "docs/introduction/concepts#event");
+ makeClickable(".integration-events", "docs/introduction/concepts#integration-event");
}
);
diff --git a/docs/assets/js/docs/code-theme.js b/docs/assets/js/docs/code-theme.js
new file mode 100644
index 0000000..b3ddb91
--- /dev/null
+++ b/docs/assets/js/docs/code-theme.js
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+'use strict';
+
+import * as params from '@params';
+
+/**
+ * Changes the code theme styles and body class on the toggle click.
+ *
+ *
The selected theme will be saved in cookies and the user will be able
+ * to navigate between pages without selecting the theme again.
+ *
+ *
The stylesheet is applied in `layouts/_partials/head/code-theme.html`.
+ */
+export function initCodeTheme() {
+ const $body = $('body');
+ const $themeCSS = $('#code-theme-css');
+ const $themeToggle = $('#code-theme-toggle');
+ const $codeBlock = $('div.highlight');
+ const themeClass = {
+ dark: 'dark-code-theme',
+ light: 'light-code-theme'
+ }
+ const toggleClass = {
+ toggle: 'code-theme-toggle',
+ dark: 'dark',
+ light: 'light'
+ }
+ const defaultTheme = params.defaultCodeTheme || 'dark';
+ let theme = Cookies.get('code-theme') || defaultTheme;
+
+ setTheme(theme);
+ createToggleIcon();
+
+ /**
+ * Creates the theme toggle icon under each code block.
+ */
+ function createToggleIcon() {
+ if (!$codeBlock) return;
+
+ $codeBlock.each(function () {
+ const icon = $(``);
+ icon.tooltip('enable');
+ $(this).append(icon);
+ });
+ }
+
+ /**
+ * Updates the theme on the toggle icon click.
+ */
+ $(document).on('click', `.${toggleClass.toggle}`, function () {
+ const currentHref = $themeCSS.attr('href');
+ const lightHref = $themeCSS.data('light');
+ const isLight = currentHref === lightHref;
+ const newTheme = isLight ? 'dark' : 'light';
+
+ setTheme(newTheme);
+ Cookies.set('code-theme', newTheme);
+ });
+
+ /**
+ * Sets the provided theme as the body class and changes the CSS URL
+ * to the corresponding one.
+ *
+ * @param {string} theme the code theme to be set
+ */
+ function setTheme(theme) {
+ $themeCSS.attr('href', $themeCSS.data(theme));
+ $body.removeClass(`${themeClass.dark} ${themeClass.light}`)
+ .addClass(themeClass[theme]);
+ $themeToggle.removeClass(`${toggleClass.dark} ${toggleClass.light}`)
+ .addClass(toggleClass[theme]);
+ }
+}
diff --git a/docs/assets/js/docs/element-max-height.js b/docs/assets/js/docs/element-max-height.js
new file mode 100644
index 0000000..be7d12c
--- /dev/null
+++ b/docs/assets/js/docs/element-max-height.js
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+'use strict';
+
+/**
+ * Sets the `max-height` property to an element with the `.set-max-height` class.
+ *
+ *
The `max-height` value is calculated depending on the current window
+ * height and will be updated on window resize.
+ */
+export function setElementMaxHeight() {
+ const $element = $('.set-max-height');
+ const $navbar = $('#header');
+ const navbarHeight = $navbar.innerHeight();
+ let scrollTop = 0;
+
+ if (!$element) return;
+
+ updateMaxHeight();
+
+ $(window).on('load resize scroll', function () {
+ scrollTop = $(window).scrollTop();
+ updateMaxHeight();
+ });
+
+ /**
+ * Sets the `max-height` value to the element
+ * to be sure that it always fits on the page.
+ */
+ function updateMaxHeight() {
+ $element.each(function() {
+ const $this = $(this);
+ const maxHeight = calculateMaxHeight($this);
+
+ $(this).css({
+ 'overflow': 'auto',
+ 'max-height': maxHeight
+ });
+ });
+ }
+
+ /**
+ * Calculates the possible element `max-height` based on the window
+ * and navigation heights.
+ *
+ * @param $element the element whose max-height needs to be calculated
+ * @return {number} maxHeight the value of the maximum possible height
+ */
+ function calculateMaxHeight($element) {
+ const windowHeight = $(window).height();
+ const elementTopPosition = getTopOffset($element);
+ const elementBottomMargin = 20;
+ const maxHeight = windowHeight - elementTopPosition - elementBottomMargin;
+
+ return maxHeight;
+ }
+
+ /**
+ * Returns the provided element top position relative to the window.
+ *
+ *
If the element is sticky, returns the CSS `top` value, otherwise
+ * calculates the top position. It is useful when the hero section
+ * is present on the page.
+ *
+ * @param $element the element whose position needs to be calculated
+ * @return {number} the top position of the element
+ */
+ function getTopOffset($element) {
+ const stickyTop = parseInt($element.css('top'), 10) || navbarHeight || 0;
+ const isSticky = $element[0].getBoundingClientRect().top <= stickyTop;
+ let topPosition;
+
+ if (isSticky) {
+ topPosition = stickyTop;
+ } else {
+ topPosition = $element.offset().top - scrollTop;
+ }
+
+ return topPosition;
+ }
+}
diff --git a/docs/assets/js/docs/interactive-toc.js b/docs/assets/js/docs/interactive-toc.js
new file mode 100644
index 0000000..b28d79f
--- /dev/null
+++ b/docs/assets/js/docs/interactive-toc.js
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+'use strict';
+
+/**
+ * Makes the Hugo TOC items interactive.
+ *
+ *
During scrolling, adds the `current` class to the anchor, the heading
+ * of which is now visible on the page.
+ *
+ *
Note, that the TOC element should be wrapped into the `div` with
+ * the `interactive-toc` class. For example:
+ * ```
+ *
Prevents the default scroll to avoid active item overriding.
+ */
+ $tocItems.on('click', function (e) {
+ e.preventDefault();
+ const $current = $(this);
+ const anchor = $current.attr('href');
+
+ disableScroll = true;
+ $tocItems.removeClass(currentClass);
+ $current.addClass(currentClass);
+ window.location.hash = anchor;
+
+ $('html, body').animate(
+ {scrollTop: $(anchor).offset().top - 80},
+ 300,
+ function () {
+ disableScroll = false;
+ }
+ );
+ });
+
+ /**
+ * Marks the current TOC item with the `currentClass`.
+ */
+ function markCurrentTocItem() {
+ if (!anchors) return;
+ $tocItems.removeClass(currentClass);
+ const $active = getCurrentTocItem().addClass(currentClass);
+ scrollToActiveTocItem($active.get(0));
+ }
+
+ /**
+ * Returns the TOC item, heading of which is now visible on the page.
+ *
+ * @returns {jQuery|HTMLElement} the current TOC item
+ */
+ function getCurrentTocItem() {
+ const scrollPosition = window.pageYOffset;
+ const windowIncrement = 0.14;
+ let currentAnchor = null;
+ anchors.each(function() {
+ let headingPosition = getHeading(this).position().top;
+ if (headingPosition < scrollPosition + window.innerHeight * windowIncrement) {
+ currentAnchor = this;
+ return;
+ }
+ })
+ return getTocItem(currentAnchor);
+ }
+
+ /**
+ * Returns the TOC item corresponding to the provided `anchor`.
+ *
+ * @param {String} anchor the anchor of the visible heading
+ * @returns {*|jQuery|HTMLElement} the TOC item corresponding to the `anchor`
+ */
+ function getTocItem(anchor) {
+ return $('#TableOfContents a[href=\"' + anchor + '\"]');
+ }
+
+ /**
+ * Returns the heading element corresponding to the provided `anchor`.
+ *
+ * @param {String} anchor the anchor of the heading
+ * @returns {*|jQuery|HTMLElement} the heading element
+ */
+ function getHeading(anchor) {
+ return $(':header[id=' + anchor.substring(1) + ']');
+ }
+
+ /**
+ * Returns the list of available anchors on the page.
+ *
+ * @returns {Array} the list of anchors
+ */
+ function getAnchors() {
+ if (!anchors) {
+ anchors = $tocItems.map(function() {
+ return $(this).attr("href");
+ });
+ }
+ return anchors;
+ }
+
+ /**
+ * Scrolls the TOC container to the active item.
+ *
+ * @param {DOM node} activeElement the active element in the TOC
+ */
+ function scrollToActiveTocItem(activeElement) {
+ if (!activeElement) return;
+
+ if ($interactiveToc && $interactiveToc[0].contains(activeElement) && !isElementInView(activeElement)) {
+ if (activeElement !== lastActiveElement) {
+ activeElement.scrollIntoView({
+ block: 'nearest',
+ inline: 'nearest',
+ behavior: 'smooth'
+ });
+ lastActiveElement = activeElement;
+ }
+ }
+ }
+
+ /**
+ * Checks if the element is in view.
+ *
+ *
Helps to avoid scroll jumping at the top of the page.
+ *
+ * @param element the element that should be in the view
+ * @return {boolean}
+ */
+ function isElementInView(element) {
+ const rect = element.getBoundingClientRect();
+ return rect.top >= 0 && rect.bottom <= window.innerHeight;
+ }
+}
diff --git a/docs/assets/js/docs/sidenav.js b/docs/assets/js/docs/sidenav.js
new file mode 100644
index 0000000..a22e49d
--- /dev/null
+++ b/docs/assets/js/docs/sidenav.js
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Toggles the sidenav on mobile devices.
+ */
+export function initSidenav() {
+ const $body = $('body');
+ const $mobileSidenavToggle = $('#mobile-sidenav-toggle');
+ const $mobileSidenavCloseBtn = $('#close-mobile-sidenav');
+ const mobileSidenavOpenedClass = 'mobile-sidenav-opened';
+
+ $(window).on('resize', function() {
+ hideMobileSidenavOnResize();
+ });
+
+ /**
+ * Shows the mobile sidenav panel on the toggle click.
+ */
+ $mobileSidenavToggle.click(function () {
+ $body.addClass(mobileSidenavOpenedClass);
+ });
+
+ /**
+ * Closes the mobile sidenav panel on the close button click.
+ */
+ $mobileSidenavCloseBtn.click(function () {
+ $body.removeClass(mobileSidenavOpenedClass);
+ });
+
+ /**
+ * Hides the mobile sidenav on window resizing.
+ */
+ function hideMobileSidenavOnResize() {
+ const tabletWidth = 880;
+ const mobileWindow = $(window).width() <= tabletWidth;
+
+ if (!mobileWindow) {
+ $body.removeClass(mobileSidenavOpenedClass);
+ }
+ }
+}
diff --git a/docs/assets/scss/docs-common.scss b/docs/assets/scss/docs-common.scss
new file mode 100644
index 0000000..cbc7e47
--- /dev/null
+++ b/docs/assets/scss/docs-common.scss
@@ -0,0 +1,36 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+@import "fonts";
+
+@import "docs-common/reset";
+@import "docs-common/config";
+@import "docs-common/mixins";
+@import "docs-common/layout";
+@import "docs-common/code";
+@import "docs-common/snackbar";
+@import "docs-common/tooltip";
+@import "docs-common/anchor-icon";
diff --git a/docs/assets/scss/docs-common/_anchor-icon.scss b/docs/assets/scss/docs-common/_anchor-icon.scss
new file mode 100644
index 0000000..bb63372
--- /dev/null
+++ b/docs/assets/scss/docs-common/_anchor-icon.scss
@@ -0,0 +1,41 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+$icon-color: #1a96de;
+$icon-opacity: .38;
+$icon-size-s: 24px;
+
+// Common theme styles from the `TeamDev-Ltd/site-commons`.
+@import "../anchor-icon";
+
+.anchor-icon {
+ color: $icon-color !important;
+ font-weight: 700;
+
+ &:after {
+ margin-bottom: 2px;
+ }
+}
diff --git a/docs/assets/scss/docs-common/_config.scss b/docs/assets/scss/docs-common/_config.scss
new file mode 100755
index 0000000..30b15f9
--- /dev/null
+++ b/docs/assets/scss/docs-common/_config.scss
@@ -0,0 +1,65 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Layout
+$layout-max-width: 1260px;
+$doc-sidenav-mobile-breakpoint: 880px;
+
+// Line heights
+$article-line-height: 1.74;
+
+// General dimensions and spacing
+$header-height: 68px;
+
+// Z-index
+// Usage: z-index: map_get($z-index, 'color-selector');
+$z-index: (
+ 'go-top-button' : 999,
+ 'color-selector' : 999,
+ 'header' : 1000,
+ 'snackbar' : 1000,
+ 'nav-mobile' : 1001,
+ 'side-nav-mobile' : 1001,
+ 'redirect-screen' : 1001,
+ 'hamburger-menu' : 9999,
+ 'search-mobile' : 10000
+);
+
+// Border-radius
+$border-radius-s : 2px;
+$border-radius : 3px;
+$border-radius-m : 4px;
+$border-radius-l : 5px;
+
+// Icon sizes
+$icon-size--xs : 16px;
+$icon-size--s : 24px;
+$icon-size--m : 32px;
+$icon-size--l : 40px;
+$icon-size--xl : 48px;
+
+// Inputs
+$input-border-radius: $border-radius;
diff --git a/docs/assets/scss/docs-common/_layout.scss b/docs/assets/scss/docs-common/_layout.scss
new file mode 100644
index 0000000..cce0d27
--- /dev/null
+++ b/docs/assets/scss/docs-common/_layout.scss
@@ -0,0 +1,81 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+:root {
+ --text-color: #3a3a3a;
+}
+
+html {
+ scroll-padding-top: $header-height;
+}
+
+html,
+body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ color: var(--text-color);
+ font-family: $main-font;
+ -webkit-font-smoothing: antialiased;
+}
+
+.wrapper {
+ min-height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+}
+
+.main {
+ flex-grow: 1;
+}
+
+.footer {
+ flex-shrink: 0;
+}
+
+$layout-padding-medium: 40px;
+$layout-padding-tablet: 32px;
+$layout-padding-phone: 20px;
+
+.content-holder {
+ max-width: $layout-max-width;
+
+ @include breakpoint(md-desktop) {
+ padding-right: $layout-padding-medium;
+ padding-left: $layout-padding-medium;
+ }
+
+ @include breakpoint(tablet) {
+ padding-right: $layout-padding-tablet;
+ padding-left: $layout-padding-tablet;
+ }
+
+ @include breakpoint(md-phone) {
+ padding-right: $layout-padding-phone;
+ padding-left: $layout-padding-phone;
+ }
+}
diff --git a/docs/assets/scss/docs-common/_mixins.scss b/docs/assets/scss/docs-common/_mixins.scss
new file mode 100644
index 0000000..c4e29a8
--- /dev/null
+++ b/docs/assets/scss/docs-common/_mixins.scss
@@ -0,0 +1,84 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+$breakpoints: (
+ sm-phone: 320px,
+ md-phone: 480px,
+ xmd-phone: 576px,
+ lg-phone: 640px,
+ tablet: 768px,
+ md-tablet: 860px,
+ lg-tablet: 960px,
+ sm-desktop: 991px,
+ desktop: 1024px,
+ md-desktop: 1280px,
+ lg-desktop: 1440px
+);
+
+// Usage: `@include breakpoint(desktop)`
+@mixin breakpoint($deviceWidth) {
+ @if map-has-key($breakpoints, $deviceWidth) {
+ $value: map-get($breakpoints, $deviceWidth);
+
+ @media screen and (max-width: $value) {
+ @content;
+ }
+ } @else {
+ @media screen and (max-width: $deviceWidth) {
+ @content;
+ }
+ }
+}
+
+@mixin breakpoint-min($deviceWidth) {
+ @if map-has-key($breakpoints, $deviceWidth) {
+ $value: map-get($breakpoints, $deviceWidth);
+
+ @media screen and (min-width: $value + 1) {
+ @content;
+ }
+ } @else {
+ @media screen and (min-width: $deviceWidth + 1) {
+ @content;
+ }
+ }
+}
+
+// Usage: `@include breakpoint-min-and-max(md-tablet, desktop) {}`
+@mixin breakpoint-min-and-max($minWidth, $maxWidth) {
+ @if map-has-key($breakpoints, $minWidth) and map-has-key($breakpoints, $maxWidth) {
+ $minValue: map-get($breakpoints, $minWidth);
+ $maxValue: map-get($breakpoints, $maxWidth);
+
+ @media (min-width: $minValue + 1) and (max-width: $maxValue) {
+ @content;
+ }
+ } @else {
+ @media (min-width: $minWidth + 1) and (max-width: $maxWidth) {
+ @content;
+ }
+ }
+}
diff --git a/docs/assets/scss/docs-common/_reset.scss b/docs/assets/scss/docs-common/_reset.scss
new file mode 100755
index 0000000..3617b72
--- /dev/null
+++ b/docs/assets/scss/docs-common/_reset.scss
@@ -0,0 +1,96 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, figure,
+a, abbr, acronym, address, big, cite,
+del, dfn, em, font, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+ border: 0;
+ font-family: inherit;
+ font-size: 100%;
+ font-style: inherit;
+ font-weight: inherit;
+ margin: 0;
+ outline: 0;
+ padding: 0;
+ vertical-align: baseline;
+}
+
+:focus {
+ outline: 0;
+}
+
+body {
+ background: #ffffff;
+ line-height: 1;
+}
+
+ol,
+ul {
+ list-style: none;
+}
+
+table {
+ border-collapse: separate;
+ border-spacing: 0;
+}
+
+caption,
+th,
+td {
+ font-weight: normal;
+ text-align: left;
+}
+
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
+ content: "";
+}
+
+blockquote,
+q {
+ quotes: "" "";
+}
+
+a img {
+ border: 0;
+}
+
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+
+a,
+a:hover {
+ text-decoration: none;
+}
diff --git a/docs/assets/scss/docs-common/_snackbar.scss b/docs/assets/scss/docs-common/_snackbar.scss
new file mode 100644
index 0000000..a8eb3e4
--- /dev/null
+++ b/docs/assets/scss/docs-common/_snackbar.scss
@@ -0,0 +1,30 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+$dark-color: #232526;
+
+// Common theme styles from the `TeamDev-Ltd/site-commons`.
+@import "../snackbar";
diff --git a/docs/assets/scss/docs-common/_tooltip.scss b/docs/assets/scss/docs-common/_tooltip.scss
new file mode 100644
index 0000000..47b8195
--- /dev/null
+++ b/docs/assets/scss/docs-common/_tooltip.scss
@@ -0,0 +1,38 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Overrides styles of the Bootstrap tooltip. */
+
+.tooltip {
+ --bs-tooltip-padding-y: .5rem;
+ --bs-tooltip-padding-x: .7rem;
+ --bs-tooltip-bg: #232526;
+ --bs-tooltip-opacity: .87;
+ --bs-tooltip-font-size: 12px;
+ --bs-tooltip-border-radius: 5px;
+
+ line-height: 1.4;
+}
diff --git a/docs/assets/scss/docs-common/code/_code-block.scss b/docs/assets/scss/docs-common/code/_code-block.scss
new file mode 100644
index 0000000..4fe4d6e
--- /dev/null
+++ b/docs/assets/scss/docs-common/code/_code-block.scss
@@ -0,0 +1,86 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ Extends the `_code.scss` styles.
+
+ The `.code-block` wrapper is available when using the `{{< highlight >}}`
+ shortcode with the file name parameter or with using the `code-tabs`.
+*/
+.code-block {
+ $code-block-background: var(--code-block-background);
+ $header-name-font-size: 12px;
+ $header-bg-color: var(--code-block-background);
+ $header-divider-color: var(--code-block-header-divider-color);
+
+ margin-bottom: 40px;
+ background-color: $code-block-background;
+ border-radius: $code-block-border-radius;
+
+ .code-block-header {
+ position: relative;
+ display: flex;
+ align-items: center;
+ background-color: $header-bg-color;
+ border-radius: $code-block-border-radius $code-block-border-radius 0 0;
+ border-bottom: 1px solid $header-divider-color;
+
+ .file-name {
+ color: var(--code-block-text-color);
+ font-size: $header-name-font-size;
+ font-weight: bold;
+ line-height: 1;
+ padding: 14px $code-block-padding;
+ }
+
+ .copy-code-to-clipboard-icon {
+ background-color: unset;
+ top: 0;
+ }
+ }
+
+ .highlight {
+ margin-bottom: 0;
+
+ pre {
+ border-radius: 0 0 $code-block-border-radius $code-block-border-radius;
+ }
+
+ table tr td {
+ &:first-child {
+ pre {
+ border-top-left-radius: 0;
+ }
+ }
+
+ &:last-child {
+ pre {
+ border-top-right-radius: 0;
+ }
+ }
+ }
+ }
+}
diff --git a/docs/assets/scss/docs-common/code/_code-copy-icon.scss b/docs/assets/scss/docs-common/code/_code-copy-icon.scss
new file mode 100644
index 0000000..c388f2a
--- /dev/null
+++ b/docs/assets/scss/docs-common/code/_code-copy-icon.scss
@@ -0,0 +1,96 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// The icon is added to the DOM in `assets/js/components/docs/copy-code.js`.
+.copy-code-to-clipboard-icon {
+ $icon-container-size: 40px;
+ $icon-size: 16px;
+ $icon-color: var(--copy-code-icon-color);
+ $icon-color-hover: var(--copy-code-icon-hover-color);
+ $icon-bg-color: var(--code-block-background);
+
+ position: absolute;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: $icon-container-size;
+ height: $icon-container-size;
+ font-size: $icon-size;
+ color: $icon-color;
+ background-color: $icon-bg-color;
+ border-radius: $code-block-border-radius;
+ top: 0;
+ right: 0;
+ transition: color .2s ease-in-out;
+ cursor: pointer;
+
+ &:hover {
+ color: $icon-color-hover
+ }
+
+ $tooltip-top-position: -16px;
+ $tooltip-right-position: -5px;
+
+ + .copy-code-tooltip {
+ position: absolute;
+ visibility: hidden;
+ font-family: $main-font;
+ top: $tooltip-top-position;
+ right: $tooltip-right-position;
+ font-size: 12px;
+ background: rgba(0, 0, 0, .9);
+ color: white;
+ padding: 4px 6px;
+ border-radius: 3px;
+
+ &.show {
+ visibility: visible;
+ animation: tooltipFadeIn .5s, tooltipFadeOut .5s 2.5s;
+ }
+ }
+
+ @keyframes tooltipFadeIn {
+ 0% {
+ top: 0;
+ opacity: 0;
+ }
+ 100% {
+ top: $tooltip-top-position;
+ opacity: 1;
+ }
+ }
+
+ @keyframes tooltipFadeOut {
+ 0% {
+ top: $tooltip-top-position;
+ opacity: 1;
+ }
+ 100% {
+ top: 0;
+ opacity: 0;
+ }
+ }
+}
diff --git a/docs/assets/scss/docs-common/code/_code-tabs.scss b/docs/assets/scss/docs-common/code/_code-tabs.scss
new file mode 100644
index 0000000..581ca92
--- /dev/null
+++ b/docs/assets/scss/docs-common/code/_code-tabs.scss
@@ -0,0 +1,120 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * The tabs are managed by `assets/js/components/docs/code-tabs.js`.
+ * The layout of tabs depends on the `assets/scss/components/code/_code-block.scss`.
+ */
+.code-tabs {
+ $code-tab-color: var(--code-tab-color);
+ $code-tab-active-color: var(--code-tab-active-color);
+ $code-tab-indicator-color: var(--code-tab-indicator-color);
+ $code-tab-font: var(--main-font);
+ $code-tab-padding: 14px 16px;
+ $border-radius: 3px;
+ $indicator-line-height: 3px;
+ $indicator-line-border-radius: $indicator-line-height/2;
+
+ margin: 20px 0 32px;
+
+ .tabs {
+ position: relative;
+ display: flex;
+
+ .indicator {
+ content: '';
+ position: absolute;
+ display: block;
+ bottom: -1px;
+ min-width: 0;
+ width: 0;
+ height: $indicator-line-height;
+ background: $code-tab-indicator-color;
+ border-radius: $indicator-line-border-radius;
+ will-change: left, width;
+ transition: left .3s ease, width .3s ease;
+ }
+
+ .tab {
+ position: relative;
+ display: inline-block;
+ padding: $code-tab-padding;
+ font-family: $code-tab-font;
+ color: $code-tab-color;
+ font-weight: bold;
+ font-size: 12px;
+ line-height: 1;
+ transition: color .2s ease-in-out;
+ cursor: pointer;
+
+ &.active {
+ color: $code-tab-active-color;
+
+ &.show-static-indicator {
+ &:after {
+ content: '';
+ position: absolute;
+ display: block;
+ left: 0;
+ bottom: -1px;
+ width: 100%;
+ height: $indicator-line-height;
+ background: red;
+ }
+ }
+ }
+ }
+
+ &.one-tab {
+ .indicator {
+ display: none;
+ }
+ }
+ }
+
+ .code-tab-content {
+ p {
+ padding-top: 0 !important;
+ margin-bottom: 8px;
+ }
+ }
+}
+
+.code-tab-content {
+ display: none;
+
+ &.active {
+ display: block;
+ }
+
+ &.inline.active {
+ display: inline-block;
+ }
+
+ p:first-child {
+ padding-top: 0 !important;
+ }
+}
diff --git a/docs/assets/scss/docs-common/code/_code-theme-toggle.scss b/docs/assets/scss/docs-common/code/_code-theme-toggle.scss
new file mode 100644
index 0000000..81f445e
--- /dev/null
+++ b/docs/assets/scss/docs-common/code/_code-theme-toggle.scss
@@ -0,0 +1,64 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.highlight {
+ position: relative;
+}
+
+.light-code-theme {
+ .code-theme-toggle {
+ opacity: .26;
+ }
+}
+
+.code-theme-toggle {
+ $toggle-size: 24px;
+ $icon-size: 18px;
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ top: calc((#{$toggle-size} + 2px) * -1);
+ right: 0;
+ width: $toggle-size;
+ height: $toggle-size;
+ opacity: .38;
+ transition: opacity .2s ease-in-out;
+ cursor: pointer;
+
+ &:before {
+ content: '';
+ position: absolute;
+ background: url('img/icons/brightness.svg') no-repeat center/cover;
+ width: $icon-size;
+ height: $icon-size;
+ }
+
+ &:hover {
+ opacity: .54;
+ }
+}
diff --git a/docs/assets/scss/docs-common/code/_code-with-label.scss b/docs/assets/scss/docs-common/code/_code-with-label.scss
new file mode 100644
index 0000000..bc9fe77
--- /dev/null
+++ b/docs/assets/scss/docs-common/code/_code-with-label.scss
@@ -0,0 +1,40 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.code-with-label {
+ .highlight {
+ margin-bottom: 0 !important;
+
+ pre {
+ border-radius: $code-block-border-radius !important;
+ margin: 0 !important;
+
+ code {
+ padding: $code-block-padding !important;
+ }
+ }
+ }
+}
diff --git a/docs/assets/scss/docs-common/code/_code.scss b/docs/assets/scss/docs-common/code/_code.scss
new file mode 100644
index 0000000..91cfd1a
--- /dev/null
+++ b/docs/assets/scss/docs-common/code/_code.scss
@@ -0,0 +1,198 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+:root {
+ --code-background: #ffffff;
+ --code-text-color: inherit;
+ --code-padding: 0;
+}
+
+.dark-code-theme {
+ --code-block-background: #2b2b2b;
+ --code-block-text-color: #a9b7c6;
+ --code-block-box-shadow: unset;
+ --code-block-header-divider-color: rgba(255, 255, 255, .08);
+ --code-block-highlighted-line-bg: #2c3035;
+ --copy-code-icon-color: #{$gray-600};
+ --copy-code-icon-hover-color: rgba(255, 255, 255, .8);
+ --code-tab-color: #{$gray-600};
+ --code-tab-active-color: #f8f8f2;
+ --code-tab-indicator-color: #{$gray-700};
+}
+
+.light-code-theme {
+ --code-block-background: #ffffff;
+ --code-block-text-color: var(--text-color);
+ --code-block-box-shadow: 0 3px 12px 0 rgba(35, 37, 38, .1);
+ --code-block-header-divider-color: rgba(0, 0, 0, .07);
+ --code-block-highlighted-line-bg: #e9f0e9;
+ --copy-code-icon-color: #{$gray-600};
+ --copy-code-icon-hover-color: #{$gray-700};
+ --code-tab-color: #{$gray-600};
+ --code-tab-active-color: var(--text-color);
+ --code-tab-indicator-color: $main-brand-color;
+}
+
+$mono-font-family: $main-mono-font;
+$code-block-font-size: 13px;
+$code-block-line-height: 1.6;
+$code-block-border-radius: 6px;
+$code-block-padding: 16px;
+
+pre,
+code {
+ font-family: $mono-font-family;
+ color: var(--code-text-color);
+}
+
+pre {
+ overflow-x: auto;
+ margin: 0;
+}
+
+code {
+ font-size: 95%;
+ background-color: var(--code-background);
+ padding: var(--code-padding);
+ margin: 0;
+ font-variant-ligatures: none;
+}
+
+/*
+ The default Hugo Highlight.
+ See the usage here https://gohugo.io/content-management/syntax-highlighting/.
+
+ For better customization enable classes in the project `config`:
+ ```
+ [markup.highlight]
+ noClasses = false
+ ```
+*/
+.highlight {
+ position: relative;
+
+ pre {
+ border-radius: $code-block-border-radius;
+ box-shadow: var(--code-block-box-shadow);
+ }
+
+ // The first `div` that wraps `` or `
`.
+ // When classes are enabled the `div` has the `chroma` class.
+ // Needed to make the code scrollable if the line numbers are enabled.
+ > div {
+ overflow-x: auto;
+ }
+
+ code {
+ display: table;
+ width: 100%;
+ padding: $code-block-padding;
+ font-size: $code-block-font-size;
+ line-height: $code-block-line-height;
+ background-color: var(--code-block-background);
+ color: var(--code-block-text-color);
+ border-radius: unset;
+
+ // Fixes iOS font sizing inside code blocks.
+ // Font size is affected while using flexbox for `` tag
+ // inside code on Safari/Chrome iOS.
+ text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+ -moz-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+ }
+
+ // Makes highlighted line full-width.
+ .hl {
+ margin: 0 -#{$code-block-padding};
+ padding: 0 $code-block-padding;
+ }
+
+ // Hugo layout with numbered code lines.
+ // The first column is the numbers, the second is the code.
+ // The layout works if `{linenos=table} near the code backticks is specified.
+ table {
+ &.lntable {
+ width: 100%;
+ }
+
+ tr {
+ td {
+ &:first-child {
+ user-select: none;
+
+ pre {
+ overflow: hidden;
+ border-radius: $code-block-border-radius 0 0 $code-block-border-radius;
+
+ code {
+ padding-right: 0;
+ padding-left: 16px;
+
+ span {
+ // Aligns numbers with the highlighted code line.
+ display: flex;
+ }
+ }
+ }
+ }
+
+ &:last-child {
+ // Prevents the first column from expanding arbitrarily.
+ width: 100%;
+
+ pre {
+ border-radius: 0 $code-block-border-radius $code-block-border-radius 0;
+
+ code {
+ padding-left: 0;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+.opaque-hl-lines {
+ .highlight {
+ code {
+ .hl {
+ background: none;
+ }
+
+ .line:not(.hl) {
+ opacity: .3;
+ }
+ }
+ }
+}
+
+.markdown {
+ .highlight {
+ margin-bottom: 40px;
+ }
+}
diff --git a/docs/assets/scss/docs-common/code/_index.scss b/docs/assets/scss/docs-common/code/_index.scss
new file mode 100644
index 0000000..c45e8cc
--- /dev/null
+++ b/docs/assets/scss/docs-common/code/_index.scss
@@ -0,0 +1,32 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+@import "code";
+@import "code-block";
+@import "code-tabs";
+@import "code-copy-icon";
+@import "code-theme-toggle";
+@import "code-with-label";
diff --git a/docs/assets/scss/docs.scss b/docs/assets/scss/docs.scss
new file mode 100644
index 0000000..4ab6660
--- /dev/null
+++ b/docs/assets/scss/docs.scss
@@ -0,0 +1,42 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+@import "docs/base/colors";
+@import "docs/base/mixins";
+
+@import "docs/modules/layout";
+@import "docs/modules/article-container";
+@import "docs/modules/article-text";
+@import "docs/modules/interactive-toc";
+@import "docs/modules/sidenav";
+@import "docs/modules/next-prev-nav";
+@import "docs/modules/diagram";
+@import "docs/modules/note-block";
+@import "docs/modules/docs-category-card";
+@import "docs/modules/book-card";
+@import "docs/modules/person-card";
+
+@import "docs/pages/resources";
diff --git a/docs/assets/scss/docs/base/_colors.scss b/docs/assets/scss/docs/base/_colors.scss
new file mode 100644
index 0000000..060782d
--- /dev/null
+++ b/docs/assets/scss/docs/base/_colors.scss
@@ -0,0 +1,48 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Brand Colors
+$main-brand-color: #1a96de;
+$second-brand-color: #116db4;
+$body-light-gray-color: #f6f8fA;
+$note-bg-color: #e0f2ff;
+$warning-color: #deba32;
+
+// General Colors
+$black: #2e2e2e;
+$text-color: #3a3a3a;
+
+// Grays
+$gray-100: rgba(black, .12);
+$gray-200: rgba(black, .26);
+$gray-300: rgba(black, .38);
+$gray-500: rgba(black, .54);
+$gray-600: rgba(black, .6);
+$gray-700: rgba(black, .7);
+
+// Links
+$link-color: $main-brand-color;
+$link-hover-color: darken($link-color, 10%);
diff --git a/docs/assets/scss/docs/base/_mixins.scss b/docs/assets/scss/docs/base/_mixins.scss
new file mode 100644
index 0000000..62ba17c
--- /dev/null
+++ b/docs/assets/scss/docs/base/_mixins.scss
@@ -0,0 +1,80 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ The close icon.
+ */
+@mixin close-icon {
+ display: inline-block;
+ width: $icon-size--s;
+ height: $icon-size--s;
+ background: url('img/icons/close.svg') no-repeat center/cover;
+ opacity: .26;
+ transition: all .3s ease-in-out;
+}
+
+/*
+ Ordered and unordered list styles.
+ Usage: `@include list();`
+ */
+@mixin list($item-line-height: 1.6) {
+ ol, ul {
+ margin: 0 0 32px 0;
+ line-height: $item-line-height;
+
+ @include breakpoint(tablet) {
+ margin-bottom: 24px;
+ }
+
+ li {
+ margin-left: 18px;
+ margin-top: .6em;
+ padding-left: 6px; // Adds additional space between bullet and text.
+
+ ol, ul {
+ margin-bottom: 32px;
+
+ li {
+ margin-top: .45em;
+ }
+ }
+ }
+ }
+
+ ul {
+ list-style: disc;
+
+ li {
+ ul {
+ list-style: circle;
+ }
+ }
+ }
+
+ ol {
+ list-style: decimal;
+ }
+}
diff --git a/docs/assets/scss/docs/modules/_article-container.scss b/docs/assets/scss/docs/modules/_article-container.scss
new file mode 100644
index 0000000..2bd01c6
--- /dev/null
+++ b/docs/assets/scss/docs/modules/_article-container.scss
@@ -0,0 +1,87 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+$article-border-color: #e6ecf1;
+$article-container-border: 1px solid $article-border-color;
+$article-side-padding: 56px;
+$article-container-padding: 32px $article-side-padding 40px;
+$article-container-border-radius: 3px;
+$article-container-min-height: 460px;
+
+.article-container {
+ display: flex;
+ flex-direction: column;
+ background-color: white;
+ border: $article-container-border;
+ border-radius: $article-container-border-radius;
+ padding: $article-container-padding;
+ min-width: 0; // Fixes a bug when `pre` code breaks flex element.
+ min-height: $article-container-min-height;
+
+ @include breakpoint(lg-phone) {
+ padding: 0;
+ border: none;
+ }
+
+ // Full-width code block inside articles.
+ // Extends styles provided in `scss/docs-common/code/_index.scss`.
+ --code-block-padding: #{$article-side-padding};
+
+ @include breakpoint(lg-phone) {
+ --code-block-padding: 32px;
+ }
+
+ @include breakpoint(md-phone) {
+ --code-block-padding: 20px;
+ }
+
+ .highlight {
+ pre {
+ border-radius: 0;
+ margin-right: calc(var(--code-block-padding) * -1);
+ margin-left: calc(var(--code-block-padding) * -1);
+
+ code {
+ padding-right: var(--code-block-padding);
+ padding-left: var(--code-block-padding);
+ }
+ }
+ }
+
+ li {
+ .highlight {
+ pre {
+ border-radius: $code-block-border-radius;
+ margin-left: unset;
+ margin-right: unset;
+
+ code {
+ padding: $code-block-padding;
+ }
+ }
+ }
+ }
+}
diff --git a/_sass/base/_article-text.scss b/docs/assets/scss/docs/modules/_article-text.scss
similarity index 54%
rename from _sass/base/_article-text.scss
rename to docs/assets/scss/docs/modules/_article-text.scss
index 1fa28c1..0944597 100644
--- a/_sass/base/_article-text.scss
+++ b/docs/assets/scss/docs/modules/_article-text.scss
@@ -1,13 +1,37 @@
-$article-line-height: 1.74;
-
-/* Please be careful when renaming this class.
-It is used in the DocSearch configuration in the admin panel. */
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Please be careful when renaming this class.
+// It is used in the DocSearch configuration in the admin panel.
.article-text {
- a {
+ a:not(.anchor-icon) {
text-decoration: none;
- color: $main-brand-color;
+ color: $link-color;
font-weight: 400;
- @include transition(all .3s ease-in-out);
+ transition: all .3s ease-in-out;
&:hover,
&:focus {
@@ -15,8 +39,9 @@ It is used in the DocSearch configuration in the admin panel. */
text-decoration: underline;
}
- &.external {
+ &.external-link {
white-space: nowrap;
+
&:after {
content: '\2192';
font-weight: 300;
@@ -27,10 +52,8 @@ It is used in the DocSearch configuration in the admin panel. */
line-height: 6px;
text-indent: -11px;
opacity: .6;
- -webkit-transform: rotate(-45deg);
- -ms-transform: rotate(-45deg);
transform: rotate(-45deg);
- @include transition(opacity .16s ease-in);
+ transition: opacity .16s ease-in;
vertical-align: super;
font-size: smaller;
text-decoration: none;
@@ -49,17 +72,21 @@ It is used in the DocSearch configuration in the admin panel. */
padding: 0;
}
+ p {
+ margin-bottom: 16px;
+ }
+
p, ol, ul {
font-family: $main-serif-font;
font-size: $article-font-size;
line-height: $article-line-height;
text-align: justify;
- @media (max-width: $tablet) {
+ @include breakpoint(tablet) {
font-size: $font-size--s;
}
- @media (max-width: $phone-medium) {
+ @include breakpoint(md-phone) {
text-align: left;
}
}
@@ -68,8 +95,7 @@ It is used in the DocSearch configuration in the admin panel. */
margin-top: -4px;
}
- .lead + h2,
- .lead + div + h2 {
+ .lead + h2 {
padding-top: 0;
}
@@ -80,34 +106,30 @@ It is used in the DocSearch configuration in the admin panel. */
}
}
- h1 + h2,
- h1 + div + h2 {
+ h1 + h2 {
padding-top: 0;
}
ul + h2,
- ol + h2,
- ul + div + h2,
- ol + div + h2 {
+ ol + h2 {
padding-top: 16px;
}
- // `div` is an invisible element that is created by Anchor JS library.
- h2 + div + h3 {
+ h2 + h3 {
padding-top: 4px;
}
- h3 + div + h4 {
+ h3 + h4 {
margin-top: 12px;
}
- ol + div + h3,
- ul + div + h3 {
+ ol + h3,
+ ul + h3 {
padding-top: 0;
}
- ol + div + h4,
- ul + div + h4 {
+ ol + h4,
+ ul + h4 {
margin-top: 0;
}
@@ -118,31 +140,66 @@ It is used in the DocSearch configuration in the admin panel. */
padding-bottom: 8px;
}
- .img-small {
- img {
- width: 70%;
+ // Use anchors in markdown as an image style modifier.
+ // ``
+ img[src$="#medium"],
+ img[src$="#small"] {
+ margin: 0 auto;
+ display: block;
+
+ @include breakpoint(lg-phone) {
+ max-width: 100%;
}
}
- .img-caption {
- font-family: $main-font;
- font-size: 14px;
- color: $gray-500;
- margin-bottom: 32px;
+ img[src$="#medium"] {
+ max-width: 85%;
+ }
+
+ img[src$="#small"] {
+ max-width: 70%;
+ }
+
+ .image-caption {
+ $caption-color: rgba(0, 0, 0, .54);
+ $caption-size: 15px;
+
+ text-align: center;
+ font-size: $caption-size !important;
+ line-height: 1.5 !important;
+ color: $caption-color;
+ margin-top: 0;
+
+ @include breakpoint-min(lg-phone) {
+ margin-bottom: 32px;
+ }
+
+ &.left {
+ text-align: left;
+ }
+
+ a {
+ text-decoration: underline;
+ color: $caption-color;
+
+ &:hover {
+ color: $text-color;
+ }
+ }
}
@include list($item-line-height: $article-line-height);
- /* Section headlines */
+ // Section headlines
h1 {
font-size: $font-size--xxxl;
font-weight: 300;
color: rgba(black, .4);
line-height: 1.2;
- margin-bottom: 32px;
padding-top: 16px;
+ margin-bottom: 32px;
- @media (max-width: $phone-xlarge) {
+ @include breakpoint(lg-phone) {
margin-bottom: 16px;
}
}
@@ -152,8 +209,8 @@ It is used in the DocSearch configuration in the admin panel. */
font-weight: 700;
color: $black;
line-height: 1.3;
- margin-bottom: 12px;
padding-top: 32px;
+ margin-bottom: 12px;
&.top {
padding-top: 8px;
@@ -188,46 +245,6 @@ It is used in the DocSearch configuration in the admin panel. */
color: $black;
}
- pre {
- margin: 0;
-
- @media (max-width: $tablet) {
- margin: 5px 0;
- }
- }
-
- .lead {
- font-family: $main-font;
- font-size: 18px;
- font-weight: 300;
- letter-spacing: .2px;
- padding-top: 8px;
- margin-bottom: 32px;
- }
-
- .note,
- .warning {
- padding: 12px 24px 13px;
- color: $black;
- border-radius: $border-radius-s;
- margin-bottom: 24px;
-
- code {
- background-color: transparent;
- padding: 0;
- }
- }
-
- .note {
- background-color: $note-bg-color;
- border-left: 4px solid $main-brand-color;
- }
-
- .warning {
- background-color: #fffde6;
- border-left: 4px solid $warning-color;
- }
-
.highlight {
margin: 8px 0 40px;
}
@@ -278,7 +295,7 @@ It is used in the DocSearch configuration in the admin panel. */
left: 0;
width: $icon-size--s;
height: $icon-size--s;
- background: url('../../img/docs/icons/quote.svg') no-repeat center/cover;
+ background: url('img/icons/quote.svg') no-repeat center/cover;
}
}
}
diff --git a/docs/assets/scss/docs/modules/_book-card.scss b/docs/assets/scss/docs/modules/_book-card.scss
new file mode 100644
index 0000000..4998fdb
--- /dev/null
+++ b/docs/assets/scss/docs/modules/_book-card.scss
@@ -0,0 +1,125 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.book-card {
+ $book-mobile-breakpoint: xmd-phone;
+
+ position: relative;
+ padding-bottom: 32px;
+
+ @include breakpoint-min($book-mobile-breakpoint) {
+ min-height: 240px;
+ padding-bottom: 48px;
+ }
+
+ $book-size: 142px;
+ $book-size-mobile: 60px;
+ $book-shadow:
+ 0 1px 0 0 #d0d0d0,
+ 0 10px 0 -4px #dedede,
+ 0 11px 0 -4px #d0d0d0,
+ 0 11px 20px -4px rgba(black, .16);
+
+ .book {
+ position: absolute;
+ width: $book-size;
+ box-shadow: $book-shadow;
+
+ &:after {
+ content: '';
+ position: absolute;
+ display: block;
+ width: 1px;
+ height: 6px;
+ background: #d0d0d0;
+ bottom: -7px;
+ left: 2px;
+ float: left;
+ transform: skewX(40deg);
+ }
+
+ @include breakpoint($book-mobile-breakpoint) {
+ width: $book-size-mobile;
+ }
+
+ img {
+ padding: 0;
+ }
+ }
+
+ .book-info {
+ padding-left: $book-size + 32px;
+
+ @include breakpoint($book-mobile-breakpoint) {
+ padding-left: $book-size-mobile + 20px;
+ }
+
+ .book-title {
+ white-space: normal;
+
+ &:after {
+ display: none;
+ }
+
+ &:hover {
+ text-decoration: none;
+
+ h3 {
+ color: $link-color;
+ }
+ }
+
+ .book-subtitle {
+ font-family: $main-font;
+ font-size: 15px;
+ color: $text-color;
+ margin-top: -4px;
+ margin-bottom: 8px;
+ }
+ }
+
+ .author {
+ font-size: 15px;
+ color: $gray-500;
+ margin-bottom: 8px;
+ }
+
+ .description {
+ @include breakpoint(tablet) {
+ text-align: left;
+ }
+ }
+
+ h3 {
+ padding-top: 4px;
+ margin-bottom: 6px;
+
+ @include breakpoint($book-mobile-breakpoint) {
+ padding-top: 0;
+ }
+ }
+ }
+}
diff --git a/docs/assets/scss/docs/modules/_diagram.scss b/docs/assets/scss/docs/modules/_diagram.scss
new file mode 100644
index 0000000..9668f0a
--- /dev/null
+++ b/docs/assets/scss/docs/modules/_diagram.scss
@@ -0,0 +1,114 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.diagram-box {
+ margin: 24px 0 32px;
+}
+
+// The diagram visibility controls by `js/docs/architecture-diagram.js`.
+#spine-architecture-diagram {
+ visibility: hidden;
+}
+
+.architecture-link {
+ cursor: pointer;
+ color: $link-color;
+ border-bottom:1px dotted $link-color;
+
+ &:hover {
+ color: $link-hover-color;
+ }
+}
+
+.full-screen-preview {
+ font-family: $main-font;
+
+ .preview-header {
+ padding: 24px 24px 16px;
+ border-bottom: 1px solid rgba(0, 0, 0, .12);
+
+ .preview-title {
+ font-size: 1.25rem;
+ font-weight: 700;
+ line-height: 1.4;
+ color: $black;
+ padding: 0;
+ margin: 0;
+ }
+
+ .preview-subtitle {
+ font-size: 15px;
+ color: $text-color;
+ line-height: 1.6;
+ margin: 8px 0 16px;
+ max-width: 500px;
+ }
+
+ .close-btn-wrapper {
+ float: right;
+
+ .close-btn {
+ display: block;
+ cursor: pointer;
+ line-height: 0;
+ padding: 1rem;
+ margin: -14px -1rem -1rem auto; // `-14px` is used to align the icon with the title text.
+
+ .close-icon {
+ @include close-icon;
+ }
+
+ &:hover {
+ .close-icon {
+ opacity: .87;
+ }
+ }
+ }
+ }
+ }
+
+ .preview-body {
+ padding: 24px 24px 56px;
+
+ svg {
+ display: block;
+ margin: 0 auto;
+ max-width: 1424px;
+ }
+ }
+}
+
+p.full-screen-link {
+ margin-bottom: 24px;
+
+ i {
+ margin-right: 8px;
+ }
+
+ span {
+ font-size: 15px;
+ }
+}
diff --git a/docs/assets/scss/docs/modules/_docs-category-card.scss b/docs/assets/scss/docs/modules/_docs-category-card.scss
new file mode 100644
index 0000000..a71cae5
--- /dev/null
+++ b/docs/assets/scss/docs/modules/_docs-category-card.scss
@@ -0,0 +1,98 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.docs-card-container {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(200px, 1fr));
+ gap: 24px;
+ margin-bottom: 24px;
+
+ @include breakpoint(lg-phone) {
+ grid-template-columns: 1fr;
+ }
+
+ a:hover {
+ text-decoration: none;
+ }
+}
+
+.docs-category-card {
+ $card-padding: 20px;
+ $card-border-radius: 6px;
+ $card-shadow:
+ 0 -1px 1px 0 rgba(0, 0, 0, .07),
+ 0 2px 4px 0 rgba(35, 37, 38, .12);
+ $card-hover-shadow: 0 6px 12px rgba(0, 0, 0, .12);
+
+ display: flex;
+ padding: $card-padding;
+ box-shadow: $card-shadow;
+ border-radius: $card-border-radius;
+
+ i {
+ font-size: 24px;
+ margin-right: 20px;
+ color: $main-brand-color;
+ }
+
+ &:hover {
+ text-decoration: none !important;
+ color: inherit;
+ box-shadow: $card-hover-shadow;
+
+ i {
+ color: $main-brand-color;
+ }
+
+ .card-content {
+ .title {
+ color: $link-color;
+ }
+ }
+ }
+
+ .card-content {
+ .title,
+ .description {
+ font-family: $main-font;
+ color: $text-color;
+ text-align: left;
+ line-height: 1.5;
+ }
+
+ .title {
+ font-weight: 700;
+ font-size: 16px;
+ margin-bottom: 4px;
+ }
+
+ .description {
+ font-weight: 400;
+ font-size: 14px;
+ margin: 0;
+ }
+ }
+}
diff --git a/docs/assets/scss/docs/modules/_interactive-toc.scss b/docs/assets/scss/docs/modules/_interactive-toc.scss
new file mode 100644
index 0000000..6138936
--- /dev/null
+++ b/docs/assets/scss/docs/modules/_interactive-toc.scss
@@ -0,0 +1,105 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ The TOC can be with the left indicator line or without it.
+ Manages by adding the corresponding class.
+*/
+
+.interactive-toc {
+ $toc-top-offset: 24px;
+ $toc-item-font-size: 12px;
+ $toc-item-color: $gray-700;
+ $toc-item-line-height: 1.5;
+ $toc-item-active-color: $main-brand-color;
+ $toc-item-bottom-space: 12px;
+ $toc-indicator-width: 1px;
+ $toc-indicator-line-color: rgba(0, 0, 0, .12);
+ $toc-indicator-color: $main-brand-color;
+
+ padding-top: $toc-top-offset + 8px;
+
+ #TableOfContents {
+ margin-bottom: 0;
+ padding-left: 12px;
+
+ ul {
+ list-style: none;
+ margin-bottom: $toc-item-bottom-space;
+
+ li {
+ font-size: $toc-item-font-size;
+ margin-bottom: $toc-item-bottom-space;
+ line-height: $toc-item-line-height;
+
+ a {
+ text-decoration: none;
+ color: $toc-item-color;
+ font-weight: 400;
+ padding-right: 6px;
+
+ &:hover {
+ color: $toc-item-active-color;
+ }
+
+ &.current {
+ color: $toc-item-active-color;
+ font-weight: 600;
+ }
+ }
+
+ code {
+ background-color: transparent;
+ }
+
+ ul {
+ margin: $toc-item-bottom-space 0 0 $toc-item-bottom-space;
+ }
+ }
+ }
+ }
+
+ .toc-indicator {
+ display: none;
+ }
+
+ &.with-indicator-line {
+ position: relative;
+
+ #TableOfContents {
+ border-left: $toc-indicator-width solid $toc-indicator-line-color;
+ padding-left: 16px;
+ }
+
+ .toc-indicator {
+ position: absolute;
+ left: 0;
+ width: $toc-indicator-width;
+ background: $toc-indicator-color;
+ transition: top .3s ease;
+ }
+ }
+}
diff --git a/docs/assets/scss/docs/modules/_layout.scss b/docs/assets/scss/docs/modules/_layout.scss
new file mode 100644
index 0000000..264f823
--- /dev/null
+++ b/docs/assets/scss/docs/modules/_layout.scss
@@ -0,0 +1,116 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+$doc-sidenav-desktop-width: 200px;
+$doc-sidenav-tablet-width: 200px;
+$doc-toc-width: 220px;
+$doc-sidenav-background: $gray-100;
+
+.docs {
+ background-color: $body-light-gray-color;
+
+ @include breakpoint(lg-phone) {
+ background-color: white;
+ }
+
+ .page-content {
+ display: flex;
+ justify-content: center;
+ flex: 1 1 0;
+ padding: 0;
+ margin-top: 0;
+
+ &.with-colored-sidenav {
+ @include breakpoint-min($doc-sidenav-mobile-breakpoint) {
+ background: linear-gradient(90deg, $doc-sidenav-background 0%, white 50%);
+ }
+
+ .content-col {
+ background-color: white;
+ }
+ }
+ }
+
+ .content-holder {
+ width: 100%;
+ }
+
+ .content-columns {
+ padding: 0 15px;
+
+ @include breakpoint($doc-sidenav-mobile-breakpoint) {
+ padding: 0;
+ }
+
+ .doc-sidenav-col,
+ .toc-col {
+ padding: 0 0 24px;
+ }
+
+ .content-col {
+ display: flex;
+ flex-direction: column;
+ padding: 24px 15px 48px;
+
+ @include breakpoint($doc-sidenav-mobile-breakpoint) {
+ padding: 16px 0 48px;
+ }
+ }
+
+ .article-container {
+ flex-grow: 1;
+ }
+
+ .sticky-col {
+ position: sticky;
+ align-self: start;
+ top: $header-height;
+ }
+ }
+
+ .three-column {
+ position: relative;
+ display: grid;
+ grid-template-columns: $doc-sidenav-desktop-width minmax(0, 1fr) $doc-toc-width;
+ height: 100%;
+
+ @include breakpoint(desktop) {
+ grid-template-columns: $doc-sidenav-tablet-width minmax(0, 1fr);
+
+ .toc-col {
+ display: none;
+ }
+ }
+
+ @include breakpoint($doc-sidenav-mobile-breakpoint) {
+ display: block;
+
+ .doc-sidenav-col {
+ display: none;
+ }
+ }
+ }
+}
diff --git a/docs/assets/scss/docs/modules/_next-prev-nav.scss b/docs/assets/scss/docs/modules/_next-prev-nav.scss
new file mode 100644
index 0000000..0d59958
--- /dev/null
+++ b/docs/assets/scss/docs/modules/_next-prev-nav.scss
@@ -0,0 +1,84 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.next-prev-nav {
+ $space-between: 16px;
+ $label-offset: 24px;
+ $font-family: $main-font;
+ $icon-offset: 8px;
+
+ display: flex;
+ padding: 32px 0 16px;
+ margin-top: auto;
+
+ .item {
+ flex: 1 1 auto;
+ width: 50%;
+ margin: 0;
+ padding: 0;
+ }
+
+ .previous {
+ padding-right: $space-between/2;
+ }
+
+ .next {
+ padding-left: $space-between/2;
+ text-align: right;
+ }
+
+ .arrow-link {
+ display: flex;
+ align-items: center;
+
+ &:after,
+ &:before {
+ content: '';
+ display: none;
+ border: solid $main-brand-color;
+ border-width: 0 2px 2px 0;
+ padding: 3px;
+ }
+
+ &.prev {
+ &:before {
+ display: block;
+ transform: rotate(135deg);
+ margin-right: $icon-offset;
+ }
+ }
+
+ &.next {
+ justify-content: flex-end;
+
+ &:after {
+ display: block;
+ transform: rotate(-45deg);
+ margin-left: $icon-offset;
+ }
+ }
+ }
+}
diff --git a/docs/assets/scss/docs/modules/_note-block.scss b/docs/assets/scss/docs/modules/_note-block.scss
new file mode 100644
index 0000000..b9d47c0
--- /dev/null
+++ b/docs/assets/scss/docs/modules/_note-block.scss
@@ -0,0 +1,63 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.note-block {
+ padding: 12px 24px 13px;
+ color: $black;
+ border-radius: $border-radius-s;
+ margin-bottom: 24px;
+
+ &.note {
+ background-color: $note-bg-color;
+ border-left: 4px solid $main-brand-color;
+ }
+
+ &.warning {
+ background-color: #fffde6;
+ border-left: 4px solid $warning-color;
+ }
+
+ &.lead {
+ padding: 8px 0 0 0;
+ margin-bottom: 32px;
+
+ p {
+ font-family: $main-font;
+ font-size: 18px;
+ font-weight: 300;
+ letter-spacing: .2px;
+ }
+ }
+
+ code {
+ background-color: transparent;
+ padding: 0;
+ }
+
+ p:last-child {
+ margin-bottom: 0;
+ }
+}
diff --git a/docs/assets/scss/docs/modules/_person-card.scss b/docs/assets/scss/docs/modules/_person-card.scss
new file mode 100644
index 0000000..2f708cb
--- /dev/null
+++ b/docs/assets/scss/docs/modules/_person-card.scss
@@ -0,0 +1,92 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.person-card {
+ $avatar-size: 100px;
+ $avatar-size-mobile: 60px;
+
+ position: relative;
+ padding-bottom: 40px;
+
+ .avatar {
+ position: absolute;
+ padding: 0;
+ width: $avatar-size;
+ height: $avatar-size;
+ border-radius: 50%;
+
+ @include breakpoint(xmd-phone) {
+ width: $avatar-size-mobile;
+ height: $avatar-size-mobile;
+ }
+ }
+
+ .person-info {
+ padding-left: $avatar-size + 20px;
+
+ @include breakpoint(xmd-phone) {
+ padding-left: $avatar-size-mobile + 20px;
+ }
+
+ .social-networks {
+ padding-bottom: 14px;
+
+ a {
+ padding-right: 12px;
+
+ &:after {
+ display: none;
+ }
+
+ &:hover {
+ text-decoration: none;
+
+ i {
+ color: $gray-500;
+ }
+ }
+ }
+
+ i {
+ font-size: 18px;
+ color: $gray-200;
+
+ &.fa-twitter {
+ font-size: 20px;
+ color: #8a8a8a;
+
+ &:before {
+ content: '\1d54f';
+ }
+ }
+ }
+ }
+
+ h3 {
+ padding-top: 4px;
+ }
+ }
+}
diff --git a/docs/assets/scss/docs/modules/_sidenav.scss b/docs/assets/scss/docs/modules/_sidenav.scss
new file mode 100755
index 0000000..501dcd0
--- /dev/null
+++ b/docs/assets/scss/docs/modules/_sidenav.scss
@@ -0,0 +1,252 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+$sidenav-top-offset: 24px;
+$sidenav-link-top-level-font-size: 14px;
+$sidenav-link-font-size: 13px;
+$sidenav-link-color: rgba(0, 0, 0, .54);
+$sidenav-link-color-active: $black;
+$sidenav-link-chevron-border-size: 1px solid rgba(0, 0, 0, .4);
+$sidenav-link-chevron-border-size-active: 1px solid rgba(0, 0, 0, .7);
+$sidenav-link-chevron-size: 6px;
+$sidenav-first-level-left-padding: 24px;
+$sidenav-second-level-left-padding: calc(#{$sidenav-first-level-left-padding} + 16px);
+$sidenav-third-level-left-padding: calc(#{$sidenav-second-level-left-padding} + 16px);
+$sticky-docs-sidenav-width: $doc-sidenav-desktop-width + $sidenav-first-level-left-padding;
+$divider-color: rgba(0, 0, 0, .08);
+
+$sidenav-holder-padding: 32px 24px 32px 16px;
+$sidenav-title-left-padding: 22px;
+
+.doc-sidenav {
+ padding-top: $sidenav-top-offset;
+ margin-left: -$sidenav-first-level-left-padding;
+ width: $sticky-docs-sidenav-width;
+ list-style: none;
+
+ @include breakpoint(sm-desktop) {
+ margin: 0;
+ width: 100%;
+ max-height: none !important; // Unsets calculated max-height for the sticky element on mobile.
+ }
+
+ li {
+ list-style: none;
+ line-height: 22px;
+ }
+
+ .sidenav-link {
+ position: relative;
+ display: block;
+ text-decoration: none;
+ font-size: $sidenav-link-top-level-font-size;
+ padding: 8px $sidenav-first-level-left-padding;
+ color: $sidenav-link-color;
+ font-weight: 400;
+ cursor: pointer;
+
+ @include breakpoint(sm-desktop) {
+ padding: 12px $sidenav-first-level-left-padding;
+ }
+
+ // Tree title with chevron.
+ &.tree-title {
+ color: $sidenav-link-color-active;
+
+ &:before {
+ content: '';
+ position: absolute;
+ display: inline-block;
+ border-right: $sidenav-link-chevron-border-size-active;
+ border-bottom: $sidenav-link-chevron-border-size-active;
+ width: $sidenav-link-chevron-size;
+ height: $sidenav-link-chevron-size;
+ top: 14px;
+ left: 8px;
+ transform: rotate(45deg);
+ transition: all .3s ease-in-out;
+
+ @include breakpoint(sm-desktop) {
+ top: 18px;
+ }
+ }
+
+ &.collapsed {
+ color: $sidenav-link-color;
+
+ &:before {
+ border-right: $sidenav-link-chevron-border-size;
+ border-bottom: $sidenav-link-chevron-border-size;
+ top: 15px;
+ transform: rotate(-45deg);
+
+ @include breakpoint(sm-desktop) {
+ top: 19px;
+ }
+ }
+ }
+ }
+
+ &:hover {
+ background-color: rgba(0, 0, 0, .03);
+ text-decoration: none;
+ border-radius: $border-radius-s;
+ }
+
+ &.active {
+ color: $sidenav-link-color-active;
+ font-weight: 600;
+ }
+ }
+
+ .subnav {
+ .sidenav-link {
+ font-size: $sidenav-link-font-size;
+ padding-left: $sidenav-second-level-left-padding;
+
+ // Tree title with chevron
+ &.tree-title {
+ &:before {
+ left: calc(#{$sidenav-first-level-left-padding} - 3px);
+ }
+ }
+ }
+
+ .subnav {
+ position: relative;
+
+ // Side nav vertical progress bar
+ &:before {
+ content: '';
+ position: absolute;
+ background: $divider-color;
+ display: block;
+ left: $sidenav-second-level-left-padding;
+ top: 13px;
+ bottom: 13px;
+ width: 2px;
+ }
+
+ .sidenav-link {
+ padding-left: $sidenav-third-level-left-padding;
+
+ &.active {
+ &:before {
+ content: '';
+ position: absolute;
+ background: rgba(0, 0, 0, .6);
+ display: block;
+ width: 2px;
+ height: 20px;
+ top: 9px;
+ left: $sidenav-second-level-left-padding;
+ }
+ }
+ }
+ }
+ }
+}
+
+.mobile-sidenav-header {
+ display: none;
+
+ @include breakpoint($doc-sidenav-mobile-breakpoint) {
+ display: flex;
+ justify-content: flex-end;
+
+ .icon-wrapper {
+ position: fixed;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ top: 16px;
+ right: 16px;
+ width: 48px;
+ height: 48px;
+ background-color: rgba(255, 255, 255, .9);
+ border-radius: 50%;
+ cursor: pointer;
+ transition: background-color .2s ease-in-out;
+ z-index: 1;
+
+ &:hover {
+ background-color: rgba(0, 0, 0, .03);
+ }
+
+ .close-icon {
+ @include close-icon;
+ }
+ }
+ }
+}
+
+.mobile-sidenav-holder {
+ @include breakpoint($doc-sidenav-mobile-breakpoint) {
+ display: none;
+ position: fixed !important;
+ background-color: white;
+ top: 0 !important;
+ left: 0;
+ width: 100%;
+ height: 100vh;
+ padding: $sidenav-holder-padding !important;
+ overflow-x: auto;
+ transition: all .4s ease-in-out;
+ z-index: map_get($z-index, 'side-nav-mobile');
+ }
+}
+
+.mobile-sidenav-opened {
+ @include breakpoint($doc-sidenav-mobile-breakpoint) {
+ overflow: hidden;
+ }
+
+ .mobile-sidenav-holder {
+ display: block !important;
+ }
+}
+
+#mobile-sidenav-toggle {
+ display: none;
+
+ @include breakpoint($doc-sidenav-mobile-breakpoint) {
+ display: block;
+ padding: 0 15px;
+ cursor: pointer;
+ color: $main-brand-color;
+ font-size: 15px;
+ margin: 24px 0 8px;
+
+ @include breakpoint(lg-phone) {
+ padding: 0;
+ }
+
+ i {
+ margin-right: 8px;
+ font-size: 16px;
+ }
+ }
+}
diff --git a/docs/assets/scss/docs/pages/_resources.scss b/docs/assets/scss/docs/pages/_resources.scss
new file mode 100644
index 0000000..9f6f48a
--- /dev/null
+++ b/docs/assets/scss/docs/pages/_resources.scss
@@ -0,0 +1,35 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.resources {
+ h1 {
+ margin-bottom: 6px;
+ }
+
+ h1 + p {
+ margin-bottom: 48px;
+ }
+}
diff --git a/docs/assets/scss/fonts/_index.scss b/docs/assets/scss/fonts/_index.scss
new file mode 100644
index 0000000..44600bb
--- /dev/null
+++ b/docs/assets/scss/fonts/_index.scss
@@ -0,0 +1,46 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Common theme styles from the `TeamDev-Ltd/site-commons`.
+@import "../fonts/icon-font";
+
+@import "roboto";
+@import "pt-serif";
+
+$main-font: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+$main-mono-font: 'Roboto Mono', monospace;
+$main-serif-font: 'PT Serif', serif;
+
+$font-size--primary : 16px;
+$font-size--xxs : .8rem;
+$font-size--xs : 1rem;
+$font-size--s : 1.15rem;
+$article-font-size : 1.1rem;
+$font-size--m : 1.65rem;
+$font-size--l : 1.85rem;
+$font-size--xl : 2rem;
+$font-size--xxl : 2.5rem;
+$font-size--xxxl : 2.625rem;
diff --git a/docs/assets/scss/fonts/_pt-serif.scss b/docs/assets/scss/fonts/_pt-serif.scss
new file mode 100644
index 0000000..eb82503
--- /dev/null
+++ b/docs/assets/scss/fonts/_pt-serif.scss
@@ -0,0 +1,33 @@
+$pt-serif-font-path: "fonts/pt-serif/" !default;
+
+@font-face {
+ font-family: "PT Serif";
+ src: url("#{$pt-serif-font-path}PTSerif-Regular.ttf") format("truetype");
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: "PT Serif";
+ src: url("#{$pt-serif-font-path}PTSerif-Italic.ttf") format("truetype");
+ font-weight: 400;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: "PT Serif";
+ src: url("#{$pt-serif-font-path}PTSerif-Bold.ttf") format("truetype");
+ font-weight: 700;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: "PT Serif";
+ src: url("#{$pt-serif-font-path}PTSerif-BoldItalic.ttf") format("truetype");
+ font-weight: 700;
+ font-style: italic;
+ font-display: swap;
+}
diff --git a/docs/assets/scss/fonts/_roboto.scss b/docs/assets/scss/fonts/_roboto.scss
new file mode 100644
index 0000000..d95fa17
--- /dev/null
+++ b/docs/assets/scss/fonts/_roboto.scss
@@ -0,0 +1,105 @@
+$roboto-font-path: "fonts/roboto/" !default;
+$roboto-mono-font-path: "fonts/robotomono/" !default;
+
+@font-face {
+ font-family: "Roboto";
+ src: local(Roboto Thin), url('#{$roboto-font-path}Roboto-Thin.eot');
+ src: url("#{$roboto-font-path}Roboto-Thin.eot?#iefix") format('embedded-opentype'),
+ url("#{$roboto-font-path}Roboto-Thin.woff2") format("woff2"),
+ url("#{$roboto-font-path}Roboto-Thin.woff") format("woff"),
+ url("#{$roboto-font-path}Roboto-Thin.ttf") format("truetype");
+
+ font-weight: 200;
+}
+
+@font-face {
+ font-family: "Roboto";
+ src: local(Roboto Light), url('#{$roboto-font-path}Roboto-Light.eot');
+ src: url("#{$roboto-font-path}Roboto-Light.eot?#iefix") format('embedded-opentype'),
+ url("#{$roboto-font-path}Roboto-Light.woff2") format("woff2"),
+ url("#{$roboto-font-path}Roboto-Light.woff") format("woff"),
+ url("#{$roboto-font-path}Roboto-Light.ttf") format("truetype");
+ font-weight: 300;
+}
+
+@font-face {
+ font-family: "Roboto";
+ src: local(Roboto Regular), url('#{$roboto-font-path}Roboto-Regular.eot');
+ src: url("#{$roboto-font-path}Roboto-Regular.eot?#iefix") format('embedded-opentype'),
+ url("#{$roboto-font-path}Roboto-Regular.woff2") format("woff2"),
+ url("#{$roboto-font-path}Roboto-Regular.woff") format("woff"),
+ url("#{$roboto-font-path}Roboto-Regular.ttf") format("truetype");
+ font-weight: 400;
+}
+
+@font-face {
+ font-family: "Roboto";
+ src: url('#{$roboto-font-path}Roboto-Medium.eot');
+ src: url("#{$roboto-font-path}Roboto-Medium.eot?#iefix") format('embedded-opentype'),
+ url("#{$roboto-font-path}Roboto-Medium.woff2") format("woff2"),
+ url("#{$roboto-font-path}Roboto-Medium.woff") format("woff"),
+ url("#{$roboto-font-path}Roboto-Medium.ttf") format("truetype");
+ font-weight: 500;
+}
+
+@font-face {
+ font-family: "Roboto";
+ src: url('#{$roboto-font-path}Roboto-Bold.eot');
+ src: url("#{$roboto-font-path}Roboto-Bold.eot?#iefix") format('embedded-opentype'),
+ url("#{$roboto-font-path}Roboto-Bold.woff2") format("woff2"),
+ url("#{$roboto-font-path}Roboto-Bold.woff") format("woff"),
+ url("#{$roboto-font-path}Roboto-Bold.ttf") format("truetype");
+ font-weight: 700;
+}
+
+@font-face {
+ font-family: 'Roboto Mono';
+ font-style: normal;
+ font-weight: 300;
+ src: url('#{$roboto-mono-font-path}roboto-mono-v5-latin-300.eot'); /* IE9 Compat Modes */
+ src: local('Roboto Mono Light'), local('RobotoMono-Light'),
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-300.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-300.woff2') format('woff2'), /* Super Modern Browsers */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-300.woff') format('woff'), /* Modern Browsers */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-300.ttf') format('truetype'), /* Safari, Android, iOS */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-300.svg#RobotoMono') format('svg'); /* Legacy iOS */
+}
+
+@font-face {
+ font-family: 'Roboto Mono';
+ font-style: normal;
+ font-weight: 400;
+ src: url('#{$roboto-mono-font-path}roboto-mono-v5-latin-regular.eot'); /* IE9 Compat Modes */
+ src: local('Roboto Mono'), local('RobotoMono-Regular'),
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-regular.woff') format('woff'), /* Modern Browsers */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-regular.svg#RobotoMono') format('svg'); /* Legacy iOS */
+}
+
+@font-face {
+ font-family: 'Roboto Mono';
+ font-style: normal;
+ font-weight: 500;
+ src: url('#{$roboto-font-path}roboto-mono-v5-latin-500.eot'); /* IE9 Compat Modes */
+ src: local('Roboto Mono Medium'), local('RobotoMono-Medium'),
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-500.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-500.woff2') format('woff2'), /* Super Modern Browsers */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-500.woff') format('woff'), /* Modern Browsers */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-500.ttf') format('truetype'), /* Safari, Android, iOS */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-500.svg#RobotoMono') format('svg'); /* Legacy iOS */
+}
+
+@font-face {
+ font-family: 'Roboto Mono';
+ font-style: normal;
+ font-weight: 700;
+ src: url('#{$roboto-mono-font-path}roboto-mono-v5-latin-700.eot'); /* IE9 Compat Modes */
+ src: local('Roboto Mono Bold'), local('RobotoMono-Bold'),
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-700.woff2') format('woff2'), /* Super Modern Browsers */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-700.woff') format('woff'), /* Modern Browsers */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */
+ url('#{$roboto-mono-font-path}roboto-mono-v5-latin-700.svg#RobotoMono') format('svg'); /* Legacy iOS */
+}
diff --git a/docs/client-libs/dart.md b/docs/client-libs/dart.md
deleted file mode 100644
index 11dd6e8..0000000
--- a/docs/client-libs/dart.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Dart Client Library
-headline: Documentation
-bodyclass: docs
-layout: docs
----
-# Dart Client Library
- * [Reference documentation]({{site.dart_api_doc}}/client/index.html)
- * [Source code]({{site.dart_repo}}/tree/master/client)
-
diff --git a/docs/client-libs/index.md b/docs/client-libs/index.md
deleted file mode 100644
index af8d07d..0000000
--- a/docs/client-libs/index.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Java Client Library
-headline: Documentation
-bodyclass: docs
-layout: docs
----
-# Java Client Library
-
- * [Reference documentation]({{site.core_api_doc}}/client)
- * [Source code]({{site.core_java_repo}}/tree/master/client)
diff --git a/docs/client-libs/js.md b/docs/client-libs/js.md
deleted file mode 100644
index 9b42fe8..0000000
--- a/docs/client-libs/js.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: JavaScript Client Library
-headline: Documentation
-bodyclass: docs
-layout: docs
----
-# JavaScript Client Library
-
- * [Reference documentation]({{site.js_api_doc}}/index.html)
- * [Source code]({{site.web_repo}}/tree/master/client-js)
-
diff --git a/docs/config/_default/hugo.toml b/docs/config/_default/hugo.toml
new file mode 100644
index 0000000..8648819
--- /dev/null
+++ b/docs/config/_default/hugo.toml
@@ -0,0 +1,6 @@
+[params]
+ defaultCodeTheme = 'dark'
+
+# The third-party libraries from the `site-commons`.
+[params.libs.js_cookie]
+ enabled = true
diff --git a/docs/content/docs/1/_index.md b/docs/content/docs/1/_index.md
new file mode 100644
index 0000000..6fcdd2e
--- /dev/null
+++ b/docs/content/docs/1/_index.md
@@ -0,0 +1,37 @@
+---
+title: Documentation
+headline: Documentation
+---
+
+# Welcome
+
+{{% note-block class="lead" %}}
+Welcome to the Spine developer documentation. This page gives
+the overview of the documentation sections.
+{{% /note-block %}}
+
+## [Quick Start](docs/quick-start/)
+In this section you can learn what it's like to develop with Spine by going
+through the code of the [Hello World]({{% get-site-data "spine.examples" %}}/hello/)
+example.
+
+## [Introduction](docs/introduction/)
+This section gives an overview of the development process, the architecture of
+the Spine-based application, information on DDD concepts implemented by the
+framework and how framework users deal with them.
+
+## [Guides](docs/guides/validation)
+This section provides detailed instructions on the framework use.
+
+## [Client Libraries](docs/client-libs/)
+This section provides language-specific guides for building client-side applications.
+
+## [API Reference](docs/reference/)
+This sections provides links to the generated documentation.
+
+## [Examples](docs/examples/)
+This page is the entry point for learning from the code of
+the [example applications]({{% get-site-data "spine.examples" %}}).
+
+## [DDD Resources](docs/resources/)
+A brief selection of learning materials we recommend to the colleagues in DDD.
diff --git a/docs/content/docs/1/client-libs/_index.md b/docs/content/docs/1/client-libs/_index.md
new file mode 100755
index 0000000..66e4f26
--- /dev/null
+++ b/docs/content/docs/1/client-libs/_index.md
@@ -0,0 +1,9 @@
+---
+title: Java Client Library
+headline: Documentation
+---
+
+# Java Client Library
+
+ * [Reference documentation]({{% get-site-data "spine.core_api_doc" %}}/client/)
+ * [Source code]({{% get-site-data "spine.core_jvm_repo" %}}/tree/master/client/)
diff --git a/docs/content/docs/1/client-libs/dart.md b/docs/content/docs/1/client-libs/dart.md
new file mode 100755
index 0000000..f43d31c
--- /dev/null
+++ b/docs/content/docs/1/client-libs/dart.md
@@ -0,0 +1,8 @@
+---
+title: Dart Client Library
+headline: Documentation
+---
+
+# Dart Client Library
+ * [Reference documentation]({{% get-site-data "spine.dart_api_doc" %}}/client/)
+ * [Source code]({{% get-site-data "spine.dart_repo" %}}/tree/master/client/)
diff --git a/docs/content/docs/1/client-libs/js.md b/docs/content/docs/1/client-libs/js.md
new file mode 100755
index 0000000..21dbf0b
--- /dev/null
+++ b/docs/content/docs/1/client-libs/js.md
@@ -0,0 +1,9 @@
+---
+title: JavaScript Client Library
+headline: Documentation
+---
+
+# JavaScript Client Library
+
+ * [Reference documentation]({{% get-site-data "spine.js_api_doc" %}})
+ * [Source code]({{% get-site-data "spine.web_repo" %}}/tree/master/client-js/)
diff --git a/docs/content/docs/1/examples/_index.md b/docs/content/docs/1/examples/_index.md
new file mode 100755
index 0000000..bc7063f
--- /dev/null
+++ b/docs/content/docs/1/examples/_index.md
@@ -0,0 +1,34 @@
+---
+title: Examples
+headline: Spine Examples
+---
+
+{{% note-block class="lead" %}}
+Examples are available through the [Spine Examples]({{% get-site-data "spine.examples" %}})
+GitHub organization.
+{{% /note-block %}}
+
+Please see the selected list of the examples with the descriptions below.
+
+## Java
+
+- [Hello World]({{% get-site-data "spine.examples" %}}/hello/) — a minimal
+ client-server solution described in the [Quick Start](docs/quick-start/) guide.
+- [Blog]({{% get-site-data "spine.examples" %}}/blog/) — shows server-side
+ functionality of a simple blog.
+- [Kanban]({{% get-site-data "spine.examples" %}}/kanban/) — shows orchestrating
+ Aggregates using Process Managers.
+- [Airport]({{% get-site-data "spine.examples" %}}/airport/) — integrating
+ a third-party systems using a Bounded Context. This example accompanies
+ the [“Integration with a Third Party”](docs/guides/integration/) guide.
+- [To-Do List]({{% get-site-data "spine.examples" %}}/todo-list/) — a simple
+ task management system with multiple client applications.
+ If you want to see a bigger picture of a Spine-based solution,
+ have a look at this example.
+
+## JavaScript
+
+- [Simple HTML/JS To-Do List client]({{% get-site-data "spine.examples" %}}/todo-list/tree/master/client/html-js/)
+ — a client app with very basic features.
+- [To-Do List client on Angular]({{% get-site-data "spine.examples" %}}/todo-list/tree/master/client/angular/)
+ — a more featured client built with Angular 10.
diff --git a/docs/guides/index.md b/docs/content/docs/1/guides/_index.md
old mode 100644
new mode 100755
similarity index 61%
rename from docs/guides/index.md
rename to docs/content/docs/1/guides/_index.md
index 3865f4a..ebafd06
--- a/docs/guides/index.md
+++ b/docs/content/docs/1/guides/_index.md
@@ -1,6 +1,4 @@
---
title: Guides
headline: Documentation
-bodyclass: docs
-layout: docs
---
diff --git a/docs/guides/gradle.md b/docs/content/docs/1/guides/gradle.md
old mode 100644
new mode 100755
similarity index 90%
rename from docs/guides/gradle.md
rename to docs/content/docs/1/guides/gradle.md
index fced23d..4d3dbba
--- a/docs/guides/gradle.md
+++ b/docs/content/docs/1/guides/gradle.md
@@ -1,8 +1,6 @@
---
title: Gradle configuration
headline: Documentation
-bodyclass: docs
-layout: docs
---
This guide will lead you through setting up a simple Spine-based project with a Java backend and
@@ -20,7 +18,7 @@ The minimal Gradle configuration you will need to start a new project is:
```groovy
plugins {
- id("io.spine.tools.gradle.bootstrap").version("1.7.0")
+ id("io.spine.tools.gradle.bootstrap").version("1.9.0")
}
```
@@ -28,7 +26,7 @@ Place the config into your root `build.gradle` file and execute a Gradle build.
the Spine Bootstrap plugin to your project.
You can also find this declaration on the [Gradle Plugin Portal](https://plugins.gradle.org/plugin/io.spine.tools.gradle.bootstrap),
-or on our [Getting Started page]({{site.baseurl}}/docs/quick-start/).
+or on our [Getting Started page](docs/quick-start/).
## Spine Bootstrap plugin
@@ -82,7 +80,7 @@ spine.enableJava {
}
```
-
+{{% note-block class="note" %}}
Note the use of the `protobuf` configuration. This tells our tools that the Protobuf definitions
in the subproject `model` must be converted into Java code in the current subproject.
@@ -90,7 +88,7 @@ Alternatively, if, for instance, the upstream project already contains code gene
and no additional codegen is required, the `api`/`implementation` configurations should be used. See
[this Gradle doc](https://docs.gradle.org/current/userguide/dependency_management_for_java_projects.html)
for more info.
-
+{{% /note-block %}}
### Java web server
@@ -142,7 +140,7 @@ language and responsibility boundaries.
If your server should be deployed as a whole, use a single `web-server` for all the contexts. If you
would like to deploy different contexts separately, declare a specific `web-server` subprojects
-for each of those contexts. See [this guide]({{site.baseurl}}/docs/guides/integration)
+for each of those contexts. See [this guide](docs/guides/integration/)
on the principles of integrating separate Bounded Contexts and third-party systems in Spine.
## Verbose configuration
@@ -160,8 +158,8 @@ Spine Model Compiler is a Gradle plugin which executes all the code generation r
Gradle tasks as well as the `modelCompiler { }` extension, which allows you to configure those
tasks.
-See the API reference for the list of the [declared tasks]({{site.base_api_doc}}/plugin-base/plugin-base/io.spine.tools.gradle/-model-compiler-task-name)
-and the [codegen configuration options]({{site.base_api_doc}}/model-compiler/model-compiler/io.spine.tools.gradle.compiler/-extension)
+See the API reference for the list of the [declared tasks]({{% get-site-data "spine.base_api_doc" %}}/plugin-base/plugin-base/io.spine.tools.gradle/-model-compiler-task-name/)
+and the [codegen configuration options]({{% get-site-data "spine.base_api_doc" %}}/model-compiler/model-compiler/io.spine.tools.gradle.compiler/-extension/)
### ProtoJS Plugin
@@ -170,5 +168,5 @@ The plugin adds the `generateJsonParsers` task, which appends generated JS files
Protobuf messages out of plain JS objects.
The plugin also provides the `protoJs { }` extension, which allows you to configure JS code
-generation. See the [API reference]({{site.base_api_doc}}/proto-js-plugin/proto-js-plugin/io.spine.js.gradle/-extension)
+generation. See the [API reference]({{% get-site-data "spine.base_api_doc" %}}/proto-js-plugin/proto-js-plugin/io.spine.js.gradle/-extension/)
for more info.
diff --git a/docs/guides/integration.md b/docs/content/docs/1/guides/integration.md
old mode 100644
new mode 100755
similarity index 83%
rename from docs/guides/integration.md
rename to docs/content/docs/1/guides/integration.md
index df35504..7ab6209
--- a/docs/guides/integration.md
+++ b/docs/content/docs/1/guides/integration.md
@@ -1,17 +1,15 @@
---
title: Integrating with a third party
headline: Documentation
-bodyclass: docs
-layout: docs
---
# Integration with a third party
-When developing an [Event-based]({{ site.baseurl }}/docs/introduction/concepts#event) system, it
+When developing an [Event-based](docs/introduction/concepts#event) system, it
is often tricky to integrate it with other software, be it a third party or a legacy system.
In this article, we will explore the strategies of integrating third-party systems with your
-Spine-based [Bounded Context]({{ site.baseurl }}/docs/introduction/concepts#bounded-context).
+Spine-based [Bounded Context](docs/introduction/concepts#bounded-context).
Note that we think of a third-party system like of yet another Bounded Context with its own
language. The terms “Bounded Context” and “System” are used interchangeably throughout the article.
@@ -40,15 +38,15 @@ to accept the language of the upstream and instead builds an intermediate model
land". The *ACL* translates the language of the upstream into the native language of the downstream
Context.
-
+{{% note-block class="note" %}}
This list is not exhaustive. The ["Domain-Driven Design" book](https://dddcommunity.org/book/evans_2003/)
offers a few more strategies, all worth considering. However, in this article, we are going to
describe the listed three patterns, because they are the most commonly used.
-
+{{% /note-block %}}
## The Domain
-
+
For the sake of an example, let’s consider airport management software. An airport is a complex
system which relies on many people and much software working together. Let’s consider the system
@@ -57,16 +55,15 @@ integrates with the software responsible for **Security Checks**, **Airplane Sup
**Weather**. All of these systems are independent of **Takeoffs and Landings** as well as of each
other. Thus, each of them can be treated as a third party.
-
+{{% note-block class="note" %}}
**Disclaimer.** The domain of an airport was chosen for being an "easy" example, familiar to many
readers. The system reflects a general impression of an airport and should not be treated as
an accurate representation.
-
+{{% /note-block %}}
## Customer/Supplier Contexts
-{: .img-small}
-
+
The **Takeoffs and Landings** system must know whether an *Aircraft* is ready for the *Flight*.
This decision requires data on the supplies, which are provided for the *Aircraft*. To obtain this
@@ -85,11 +82,11 @@ Note that those Events are specifically tailored to be consumed by our system, a
have to set up an elaborate Anticorruption Layer. However, a simple adapter is still required to
parse and validate domain Events, which we then publish to a Bounded Context, implemented in Spine.
-
+
The Event Consumer, as depicted above, implements the Event transformation logic. In order to
establish this communication channel, the **Airplane Supplies** system declares a [gRPC](https://grpc.io/)
-service. In [`supplies_service.proto`](https://github.com/spine-examples/airport/blob/master/airplane-supplies/src/main/proto/spine/example/airport/supplies/supplies_service.proto):
+service. In [`supplies_service.proto`]({{% get-site-data "spine.examples" %}}/airport/blob/master/airplane-supplies/src/main/proto/spine/example/airport/supplies/supplies_service.proto):
@@ -164,7 +161,7 @@ public void onNext(SuppliesEvent event) {
context.emittedEvent(eventMessage, actorContext);
}
```
-The [`AircraftAggregate`](https://github.com/spine-examples/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/AircraftAggregate.java)
+The [`AircraftAggregate`]({{% get-site-data "spine.examples" %}}/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/AircraftAggregate.java)
reacts on those events. Note that all the events published through `ThirdPartyContext` are always
`external`, so should be the subscriber and reactor methods.
@@ -182,7 +179,7 @@ AircraftPreparedForFlight on(@External PreflightCheckComplete event) {
```
## Conformist
-
+
**Weather** is an essential aspect of flying a plane, especially at low altitudes. The **Weather**
Context wraps the data received from a meteorological station. This is a true third party to our
@@ -190,15 +187,15 @@ system, as our organization, the airport, does not own it. Nearly all the detail
update are important to **Takeoffs and Landings**. The **Weather** Context forces
**Takeoffs and Landings** to conform to its domain model.
-
+
The schema of the conformist relation looks somewhat like the Customer/Supplier schema. Similar to
the Customer/Supplier, **Takeoffs and Landings** Context is downstream from another Context, in this
case from **Weather**. Unlike the Customer/Supplier, **Weather** does not provide a specific
Event Producer, which would adapt **Weather** Events to the needs of **Takeoffs and Landings**.
Also, the Event Consumer on the **Takeoffs and Landings** side is rather thin and devoid of logic.
-The Consumer consists of two parts: [`WeatherUpdateClient`](https://github.com/spine-examples/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/weather/WeatherUpdateClient.java)
-and [`WeatherUpdateEndpoint`](https://github.com/spine-examples/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/weather/WeatherUpdateEndpoint.java).
+The Consumer consists of two parts: [`WeatherUpdateClient`]({{% get-site-data "spine.examples" %}}/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/weather/WeatherUpdateClient.java)
+and [`WeatherUpdateEndpoint`]({{% get-site-data "spine.examples" %}}/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/weather/WeatherUpdateEndpoint.java).
The client polls the pull-style API of the **Weather** system.
on(@External TemperatureChanged event) {
```
## Anticorruption Layer
-
+
**Security Checks** Context has a rich model of its own. The system happens not to use domain Events
at all. The **Security Checks** software, used in our airport, must go through a complex audit and
@@ -285,7 +282,7 @@ Interaction with legacy software with known technical issues can be established
an Anticorruption Layer. This pattern suggests that we cope with the problems, imposed by the legacy
system, outside our domain model.
-
+
The Anticorruption Layer (ACL) acts as an interpreter from the language of **Security Checks**
Context into the language of **Takeoffs and Landings** Context. The ACL takes care of polling data
@@ -297,12 +294,12 @@ a large monolithic legacy system (a.k.a. a Big Ball of Mud) into Bounded Context
an ACL prevents the new "clean" Contexts from merging back into the Mud. If you are looking for
a way to add functionality to a complex legacy system without increasing the technical debt, look
no further. The Anticorruption Layer between **Takeoffs and Landings** and **Security Checks**
-is composed of a [polling client](https://github.com/spine-examples/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/passengers/PassengerClient.java),
-which performs all the technical work of obtaining and validating data, and a [Process Manager]({{ site.baseurl }}/docs/introduction/concepts#process-manager)
-for the [Boarding process](https://github.com/spine-examples/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/passengers/BoardingProcman.java).
+is composed of a [polling client]({{% get-site-data "spine.examples" %}}/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/passengers/PassengerClient.java),
+which performs all the technical work of obtaining and validating data, and a [Process Manager](docs/introduction/concepts#process-manager)
+for the [Boarding process]({{% get-site-data "spine.examples" %}}/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/passengers/BoardingProcman.java).
The **Security Checks** API provides data for each passenger independently. The client polls
the data and publishes many intermediate `PassengerBoarded` or `PassengerDeniedBoarding` external
-events via [`ThirdPartyContext`]({{site.core_api_doc}}/server/server/io.spine.server.integration/-third-party-context):
+events via [`ThirdPartyContext`]({{% get-site-data "spine.core_api_doc" %}}/server/server/io.spine.server.integration/-third-party-context/):
@@ -330,9 +327,9 @@ private void emitIfStatusKnown(TsaPassenger tsaPassenger) {
}
}
```
-The [Process Manager](https://github.com/spine-examples/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/passengers/BoardingProcman.java)
+The [Process Manager]({{% get-site-data "spine.examples" %}}/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/passengers/BoardingProcman.java)
accumulates the Events and, once the whole *Flight* is boarded, emits a `BoardingComplete` event,
-which is later consumed by the [*Flight* Aggregate](https://github.com/spine-examples/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/FlightAggregate.java).
+which is later consumed by the [*Flight* Aggregate]({{% get-site-data "spine.examples" %}}/airport/blob/master/takeoffs-and-landings/src/main/java/io/spine/example/airport/tl/FlightAggregate.java).
@@ -360,4 +357,4 @@ your current system to work in an event-driven manner. A correct integration str
isolate and perfect your own domain language while on the work of many external systems. Read more
about Bounded Contexts and their interactions in the "Domain-Driven Design" book by Eric Evans.
-The full version of the source code used in this article could be found in the [Airport Example repository](https://github.com/spine-examples/airport).
+The full version of the source code used in this article could be found in the [Airport Example repository]({{% get-site-data "spine.examples" %}}/airport).
diff --git a/docs/guides/rejections.md b/docs/content/docs/1/guides/rejections.md
old mode 100644
new mode 100755
similarity index 87%
rename from docs/guides/rejections.md
rename to docs/content/docs/1/guides/rejections.md
index e2d8913..0926718
--- a/docs/guides/rejections.md
+++ b/docs/content/docs/1/guides/rejections.md
@@ -1,12 +1,11 @@
---
title: Working with Rejections
headline: Documentation
-bodyclass: docs
-layout: docs
---
+
# Working with Rejections
-Compared with regular Events, [Rejections]({{ site.baseurl }}/docs/introduction/concepts#rejection)
+Compared with regular Events, [Rejections](docs/introduction/concepts#rejection)
are defined differently. Here is the summary of the differences:
1. `java_multiple_files` file option must be set to `false`
@@ -23,6 +22,3 @@ are defined differently. Here is the summary of the differences:
declared. Usually the outer class names are named using the name with the suffix `Proto`.
We want the name to end with `Rejections` so that it is clearly visible what is inside
this class.
-
-
-
diff --git a/docs/guides/start-new-project.md b/docs/content/docs/1/guides/start-new-project.md
old mode 100644
new mode 100755
similarity index 68%
rename from docs/guides/start-new-project.md
rename to docs/content/docs/1/guides/start-new-project.md
index e6ffb3b..18a5868
--- a/docs/guides/start-new-project.md
+++ b/docs/content/docs/1/guides/start-new-project.md
@@ -1,15 +1,15 @@
---
title: Starting a new project
headline: Documentation
-bodyclass: docs
-layout: docs
---
# Starting a new project
-
Starting off a new project goes smoother when you have an efficient
+{{% note-block class="lead" %}}
+Starting off a new project goes smoother when you have an efficient
and straightforward development flow to rely on. In this guide, we describe how we usually start
-and develop new projects.
+and develop new projects.
+{{% /note-block %}}
While the key stages of the development process are described in the [Introduction][introduction]
section, this guide provides more hands-on details on the steps that we follow while working
@@ -19,8 +19,10 @@ on a project.
In short, please follow the next steps to have a consistent and joyful development flow.
-
Each step and sub-step below results in a separate Pull Request
-adding its artifacts to the repository.
+{{% note-block class="note" %}}
+Each step and sub-step below results in a separate Pull Request
+adding its artifacts to the repository.
+{{% /note-block %}}
1. Conduct [EventStorming][EventStorming] to gather domain knowledge.
Digitize the Artifact and store it in the code repository.
@@ -65,13 +67,15 @@ the same “language” fast. It is important for engineers to avoid using techn
The results of the EventStorming (all the stickies) are captured as the Artifact and stored
as a part of the project documentation.
-
We store the EventStorming Artifact electronically as images under the project root
+{{% note-block class="note" %}}
+We store the EventStorming Artifact electronically as images under the project root
in the `/docs/eventstorming/` folder. If the session is performed offline, the photos
of the EventStorming board are stored in the repository. In case of an online session,
-the screenshots of the board are stored.
+the screenshots of the board are stored.
+{{% /note-block %}}
-
-
An example of the EventStorming board
+
+{{% image-caption %}}An example of the EventStorming board.{{% /image-caption %}}
After the session, a dedicated person creates a Pull Request with the Artifact, and the team reviews
it once again. This first EventStorming is usually addressed as a "Big Picture" and gives
@@ -89,12 +93,14 @@ While the temptation to dive into the development of everything right away may b
we recommend limiting the development scope down to only one
[Bounded Context][BoundedContext-concept].
-
We follow the rule: "Eat an elephant one bite at a time".
+{{% note-block class="note" %}}
+We follow the rule: "Eat an elephant one bite at a time".
+{{% /note-block %}}
You may need another EventStorming session to go into more detail.
-
-
An example of a Bounded Context
+
+{{% image-caption %}}An example of a Bounded Context.{{% /image-caption %}}
## Shaping the language
@@ -105,8 +111,10 @@ artifacts of the project. During this step we define Protobuf messages that mold
The results of these efforts are the `.proto` files [grouped][project-structure] under a specific
package in the `proto` folder.
-
If you are new to Protobuf, please see the [Naming Conventions][naming-conventions]
-section for how to name things in the proto code.
+{{% note-block class="note" %}}
+If you are new to Protobuf, please see the [Naming Conventions][naming-conventions]
+section for how to name things in the proto code.
+{{% /note-block %}}
While writing the protos, make sure to document **all** messages. It's time to unleash
your technical writing skills and lay the project's ground-standing foundation.
@@ -118,17 +126,21 @@ Here you may want to introduce some domain-level validation logic. Check out the
We put this step aside because in [Reactive DDD][ReactiveDDD] entities reference each other using
the typed [identifiers][identifier-concept].
-
Consider following the [Vaughn Vernon][VaughnVernon]’s rule on Aggregates from
+{{% note-block class="note" %}}
+Consider following the [Vaughn Vernon][VaughnVernon]’s rule on Aggregates from
the “Effective Aggregate Design Part II” that is applicable to **any** entity:
-“Reference other Aggregates by Identity”
+“Reference other Aggregates by Identity”
+{{% /note-block %}}
We recommend using message-based identifiers over simple types to make the API strongly pronounced
and type-safe. To make things obvious, consider putting the IDs of the context into the file named
[`identifiers.proto`][identifiers-proto]. This file will be imported when defining events,
commands, entity states, and other types of the selected context.
-
Please consult with the Naming Conventions [guide][identifiers-naming] for our
-recommendations on naming the identifier types.
+{{% note-block class="note" %}}
+Please consult with the Naming Conventions [guide][identifiers-naming] for our
+recommendations on naming the identifier types.
+{{% /note-block %}}
When the ID types are defined, please create a Pull Request so that the team can review
and polish the code of this important development step.
@@ -180,7 +192,7 @@ The definitions of entity states are [gathered][entity-state-proto] in a file na
a business model thing. E.g. for a `Task` aggregate, the definitions would be defined in
a `task.proto` file.
-As with the other steps, create a Pull Request to review the entity states with the team.
+As with the other steps, create a Pull Request to review the entity states with the team.
### Adding behavior
@@ -225,36 +237,36 @@ a context and split it into scenarios. Make sure each of the development steps r
in a separate Pull Request with dedicated artifacts in the source code repository.
Opt for smaller, fine-graded Pull Requests instead of cluttered and complicated ones.
-[introduction]: {{ site.baseurl }}/docs/introduction/ "Check the Introduction"
-[project-structure]: {{ site.baseurl }}/docs/introduction/project-structure#example "Check out the Example Project structure"
-[naming-conventions]: {{ site.baseurl }}/docs/introduction/naming-conventions "Check out the Naming Conventions"
-[validation-guide]: {{ site.baseurl }}/docs/guides/validation "Learn more about the Validation"
-[BoundedContext-concept]: {{ site.baseurl }}/docs/introduction/concepts#bounded-context "Check out the Bounded Context definition"
-[aggregate-concept]: {{ site.baseurl }}/docs/introduction/concepts#aggregate "Check out the Aggregate definition"
-[process-manager-concept]: {{ site.baseurl }}/docs/introduction/concepts#process-manager "Check out the Process Manager definition"
-[signals]: {{ site.baseurl }}/docs/introduction/#getting-domain-knowledge "Learn more about Signals"
-[testing]: {{ site.baseurl }}/docs/introduction/#testing "Learn more about Testing"
+[introduction]: docs/introduction/ "Check the Introduction"
+[project-structure]: docs/introduction/project-structure#example "Check out the Example Project structure"
+[naming-conventions]: docs/introduction/naming-conventions/ "Check out the Naming Conventions"
+[validation-guide]: docs/guides/validation/ "Learn more about the Validation"
+[BoundedContext-concept]: docs/introduction/concepts#bounded-context "Check out the Bounded Context definition"
+[aggregate-concept]: docs/introduction/concepts#aggregate "Check out the Aggregate definition"
+[process-manager-concept]: docs/introduction/concepts#process-manager "Check out the Process Manager definition"
+[signals]: docs/introduction/#getting-domain-knowledge "Learn more about Signals"
+[testing]: docs/introduction/#testing "Learn more about Testing"
-[identifier-concept]: {{ site.baseurl }}/docs/introduction/concepts#identifier "Learn more about Identifiers"
-[identifiers-proto]: {{ site.baseurl }}/docs/introduction/naming-conventions#identifiersproto "Learn more about Identifiers proto structure"
-[identifiers-naming]: {{ site.baseurl }}/docs/introduction/naming-conventions#identifiers "Learn more about Identifiers naming conventions"
+[identifier-concept]: docs/introduction/concepts#identifier "Learn more about Identifiers"
+[identifiers-proto]: docs/introduction/naming-conventions#identifiersproto "Learn more about Identifiers proto structure"
+[identifiers-naming]: docs/introduction/naming-conventions#identifiers "Learn more about Identifiers naming conventions"
-[event-concept]: {{ site.baseurl }}/docs/introduction/concepts#event "Learn more about Events"
-[events-proto]: {{ site.baseurl }}/docs/introduction/naming-conventions#eventsproto "Learn more about Events proto structure"
+[event-concept]: docs/introduction/concepts#event "Learn more about Events"
+[events-proto]: docs/introduction/naming-conventions#eventsproto "Learn more about Events proto structure"
-[command-concept]: {{ site.baseurl }}/docs/introduction/concepts#command "Learn more about Commands"
-[commands-proto]: {{ site.baseurl }}/docs/introduction/naming-conventions#commandsproto "Learn more about Commands proto structure"
+[command-concept]: docs/introduction/concepts#command "Learn more about Commands"
+[commands-proto]: docs/introduction/naming-conventions#commandsproto "Learn more about Commands proto structure"
-[rejection-concept]: {{ site.baseurl }}/docs/introduction/concepts#rejection "Learn more about Rejections"
-[rejections-proto]: {{ site.baseurl }}/docs/introduction/naming-conventions#rejectionsproto "Learn more about Rejections proto structure"
-[rejections-guide]: {{ site.baseurl }}/docs/guides/rejections "Learn more about Rejections"
+[rejection-concept]: docs/introduction/concepts#rejection "Learn more about Rejections"
+[rejections-proto]: docs/introduction/naming-conventions#rejectionsproto "Learn more about Rejections proto structure"
+[rejections-guide]: docs/guides/rejections/ "Learn more about Rejections"
-[entities]: {{ site.baseurl }}/docs/introduction/#entities "See more examples on entities"
-[entity-concept]: {{ site.baseurl }}/docs/introduction/concepts#entities "Learn more about Entities"
-[entity-state-naming]: {{ site.baseurl }}/docs/introduction/naming-conventions#entity-states-1 "Learn more about Entity states"
-[entity-state-proto]: {{ site.baseurl }}/docs/introduction/naming-conventions#entity-states "Learn more about Entity states proto structure"
+[entities]: docs/introduction/#entities "See more examples on entities"
+[entity-concept]: docs/introduction/concepts#entities "Learn more about Entities"
+[entity-state-naming]: docs/introduction/naming-conventions#entity-states-1 "Learn more about Entity states"
+[entity-state-proto]: docs/introduction/naming-conventions#entity-states "Learn more about Entity states proto structure"
[EventStorming]: https://eventstorming.com "Learn more about EventStorming"
[UbiquitousLanguage]: https://martinfowler.com/bliki/UbiquitousLanguage.html "Learn more about the Ubiquitous Language"
[ReactiveDDD]: https://www.infoq.com/presentations/reactive-ddd/ "Check out the Reactive DDD presentation"
-[VaughnVernon]: https://vaughnvernon.co/
+[VaughnVernon]: https://vaughnvernon.com/
diff --git a/docs/guides/validation.md b/docs/content/docs/1/guides/validation.md
old mode 100644
new mode 100755
similarity index 96%
rename from docs/guides/validation.md
rename to docs/content/docs/1/guides/validation.md
index a2b3ee8..03ee16a
--- a/docs/guides/validation.md
+++ b/docs/content/docs/1/guides/validation.md
@@ -1,15 +1,16 @@
---
title: Validation User Guide
headline: Documentation
-bodyclass: docs
-layout: docs
---
+
# Validation User Guide
-
Building a good domain model requires more than just defining data structures.
+{{% note-block class="lead" %}}
+Building a good domain model requires more than just defining data structures.
One of the commodities required for describing domain specifics is making sure that the data
is correct. This guide will walk you through the API the Validation Library which helps achieving
-this goal.
+this goal.
+{{% /note-block %}}
All of the validation features described here are currently supported in the Java environment.
Many are supported in Dart as well. For more info, see the description of individual features
@@ -20,10 +21,12 @@ Spine uses Protobuf for defining data structures of the domain models. The const
that define correctness of data are also defined at this level using custom Protobuf options
[offered](#validation-options) by the Validation Library.
-
In order to use validation features, you don't need to understand how custom
+{{% note-block class="note" %}}
+In order to use validation features, you don't need to understand how custom
options work. Those who are interested in the details of this advanced feature of Protobuf,
please see the [Protobuf Guide](https://developers.google.com/protocol-buffers/docs/proto3#custom_options)
-for details.
+for details.
+{{% /note-block %}}
Here are simple steps in adding validation to the data model:
1. The programmer adds validation constraints to the Protobuf types of the model.
@@ -85,13 +88,14 @@ at the level of a corresponding message type.
When modelling a domain, we often come up to certain data points which cannot be skipped. Those are
represented by required fields of an entity state, a Command, an Event, etc.
-
+{{% note-block class="note" %}}
Protobuf 2 used to have a native support for required fields.
However, from the serialization perspective, that proved to be
a [design mistake](https://stackoverflow.com/a/31814967/3183076). If a required field was missing,
the message could not be serialized and sent over the wire. Also, it is often too easy to add a new
required field, thereby breaking backwards-compatibility of the message type. In Protobuf 3 all
-the fields are optional.
+the fields are optional.
+{{% /note-block %}}
In the Validation Library, we've revived the concept of required fields, but on a different level.
The difference to the Protobuf 2 way is that out required fields do not affect the serialization
@@ -201,9 +205,9 @@ message UserIdentity {
In this case one of the fields `UserIdentity.email`, `UserIdentity.google`,
and `UserIdentity.twitter` must be set.
-
+{{% note-block class="warning" %}}
`(is_required)` option is not yet supported in Dart.
-
+{{% /note-block %}}
In some other cases, a field may be either required or not, depending on the value of another field.
Consider an example of an online store item:
@@ -273,7 +277,7 @@ the field declaring the option and second for the name of the field targeted by
### When `(required)` is implicit
-When defining the domain [Commands]({{site.baseurl}}/docs/introduction/naming-conventions#commandsproto)
+When defining the domain [Commands](docs/introduction/naming-conventions#commandsproto)
or entity states, we have found to be convenient that the first field of the respective Message is the identifier.
Therefore, by convention, Spine treats the first fields of such objects as their IDs:
@@ -313,7 +317,7 @@ In this case, the `ChangeProfilePicture.id` field is the first in the declaratio
therefore it is implicitly required. By default, the framework will use it in command routing,
as an identifier of the entity handling this command.
-This convention does not apply to [Events]({{site.baseurl}}/docs/introduction/naming-conventions#eventsproto).
+This convention does not apply to [Events](docs/introduction/naming-conventions#eventsproto).
Unlike Commands, event routing is typically specific to the use case. For example, `UserView` projection
may require a user ID to handle events, whereas the `ProfilePictureGallery` projection might use
a different routing approach, such as grouping by a user group or an email domain associated with a user.
@@ -356,9 +360,9 @@ User user = User
When applied to a `repeated` or a `map` field, each item (value of a `map`) is validated.
-
+{{% note-block class="warning" %}}
`(validate)` option is not yet supported in Dart.
-
+{{% /note-block %}}
#### Invalid fields
@@ -434,12 +438,12 @@ mathematically, there is no hard upper limit which a field value can reach.
Usage of the double dot separator ("`..`") between the bounds is mandatory.
-
+{{% note-block class="note" %}}
In some languages, Protobuf unsigned integers are represented by signed language primitives.
For example, in Java, a `uint64` is represented with a `long`. If a value of a field in Java will
overflow into `long` negatives, it will be considered a negative by the validation library. Keep
that in mind when defining lower bounds.
-
+{{% /note-block %}}
## Regular expressions
@@ -550,9 +554,9 @@ Validate.checkValidChange(old, changed);
`Validate.checkValidChange()` throws a `ValidationException` if the constraint is violated.
-
+{{% note-block class="warning" %}}
In Dart, there is no support for this feature.
-
+{{% /note-block %}}
Many fields of an entity are immutable. They may be set once in the life of the entity and then
should never be changed. The `(set_once)` constraint is checked automatically for entity states upon
@@ -640,13 +644,13 @@ validation should be declared. However, note that if the `Url` type declares any
own, all of it is discarded and only the "substitute" rules from the `UserPictureConstraint` are
used.
-
+{{% note-block class="warning" %}}
External constraints are not yet supported in Dart.
-
+{{% /note-block %}}
-
+{{% note-block class="note" %}}
Mind performance considerations when declaring external constraints. It is expected that the number
of such constrains in the whole project is not large, significantly smaller than the number of
normal constraints. This mechanism is not designed to override validation rules of an entire library
of Protobuf definitions, merely a small amount of local patches.
-
+{{% /note-block %}}
diff --git a/docs/introduction/index.md b/docs/content/docs/1/introduction/_index.md
similarity index 85%
rename from docs/introduction/index.md
rename to docs/content/docs/1/introduction/_index.md
index 78d350a..b78abd5 100644
--- a/docs/introduction/index.md
+++ b/docs/content/docs/1/introduction/_index.md
@@ -1,13 +1,14 @@
---
title: Development Process
headline: Documentation
-bodyclass: docs
-layout: docs
---
+
# Development Process
-
Building a solution based on Spine Event Engine framework is an iterative process
-which consists of the stages described in this document.
+{{% note-block class="lead" %}}
+Building a solution based on Spine Event Engine framework is an iterative process
+which consists of the stages described in this document.
+{{% /note-block %}}
## Getting domain knowledge
@@ -15,16 +16,16 @@ The purpose of this step is to find out what we're going to build and why.
Consider using [EventStorming](https://eventstorming.com) or another domain discovery
approach for grasping the knowledge from the experts.
-Most likely that the solution would have several [Bounded Contexts](concepts#bounded-context).
+Most likely that the solution would have several [Bounded Contexts](docs/introduction/concepts#bounded-context).
For each context developers need to define:
* Signals
- - [Events](concepts#event)
- - [Commands](concepts#command)
- - [Rejections](concepts#rejection)
+ - [Events](docs/introduction/concepts#event)
+ - [Commands](docs/introduction/concepts#command)
+ - [Rejections](docs/introduction/concepts#rejection)
* Entities
- - [Aggregates](concepts#aggregate)
- - [Process Managers](concepts#process-manager)
- - [Projections](concepts#projection).
+ - [Aggregates](docs/introduction/concepts#aggregate)
+ - [Process Managers](docs/introduction/concepts#process-manager)
+ - [Projections](docs/introduction/concepts#projection).
It is likely that some of the bits of this picture would change during the process.
But the whole team, including domain experts, need to have complete understanding of how the
@@ -46,7 +47,7 @@ It would be natural to start implementing the context which initiates the busine
Implementation starts from defining data types of the selected context as Protobuf messages.
-The first step is to define entity [IDs](concepts#identifier). For example:
+The first step is to define entity [IDs](docs/introduction/concepts#identifier). For example:
```proto
// The identifier for a task.
message TaskId {
@@ -87,7 +88,7 @@ message Task {
}
```
-[Value Objects]({{site.baseurl}}/docs/introduction/concepts#value-objects) are added when they
+[Value Objects](docs/introduction/concepts#value-objects) are added when they
are needed to describe entities or messages like commands or events.
### Adding business logic
@@ -165,7 +166,7 @@ BoundedContext tasksContext = BoundedContext.multiTenant("Tasks")
```
This wires repositories into the message delivery mechanism of the corresponding
-[Buses]({{site.baseurl}}/docs/introduction/concepts#message-buses).
+[Buses](docs/introduction/concepts#message-buses).
#### Testing
Implementation of the Bounded Context is tested using the messaging paradigm.
@@ -215,7 +216,7 @@ assertEntity.hasStateThat()
### Configuring Server Environment
For information on configuring server environment of a Spine-based application, please
-see the reference documentation of the [`ServerEnvironment`]({{site.core_api_doc}}/server/server/io.spine.server/-server-environment)
+see the reference documentation of the [`ServerEnvironment`]({{% get-site-data "spine.core_api_doc" %}}/server/server/io.spine.server/-server-environment/)
class.
### Assembling Application
@@ -231,9 +232,9 @@ Server server = Server.atPort(portNumber)
server.start();
```
-This exposes [`CommandService`]({{site.baseurl}}/docs/introduction/concepts#command-service),
-[`QueryService`]({{site.baseurl}}/docs/introduction/concepts#query-service), and
-[`SubscriptionService`]({{site.baseurl}}/docs/introduction/concepts#subscription-service) to client-side connections.
+This exposes [`CommandService`](docs/introduction/concepts#command-service),
+[`QueryService`](docs/introduction/concepts#query-service), and
+[`SubscriptionService`](docs/introduction/concepts#subscription-service) to client-side connections.
## Repeating the cycle
@@ -252,4 +253,4 @@ they are defied in the `.proto` files, and the Model Compiler finishes its job.
Client applications can also subscribe to events generated by Bounded Contexts at the backend.
For more information on the client-side development, please refer to
-the [Client Libraries]({{site.baseurl}}/docs/client-libs/) section.
+the [Client Libraries](docs/client-libs/) section.
diff --git a/docs/introduction/architecture.md b/docs/content/docs/1/introduction/architecture.md
similarity index 64%
rename from docs/introduction/architecture.md
rename to docs/content/docs/1/introduction/architecture.md
index 4b2d0ea..fdff314 100644
--- a/docs/introduction/architecture.md
+++ b/docs/content/docs/1/introduction/architecture.md
@@ -1,13 +1,11 @@
---
title: Architecture
headline: Documentation
-bodyclass: docs
-layout: docs
-customjs: /tools/architecture-diagram.js
+customjs: js/docs/architecture-diagram.js
---
+
# Application Architecture
-
A Spine-based application consists of several Bounded Contexts. Client applications interact
with the server-side via `CommandService`, `QueryService`, and `SubscriptionService`.
@@ -16,17 +14,15 @@ of a cloud application. When developing with Spine, you will be interacting
with only some of them, which
are not shaded on the diagram. The rest is handled by the framework.
-
Click on a component to navigate to its definition from
-the [Concepts]({{site.baseurl}}/docs/introduction/concepts) page.
+Click on a component to navigate to its definition from the
+[Concepts](docs/introduction/concepts/) page.
-
diff --git a/docs/introduction/concepts.md b/docs/content/docs/1/introduction/concepts.md
old mode 100644
new mode 100755
similarity index 90%
rename from docs/introduction/concepts.md
rename to docs/content/docs/1/introduction/concepts.md
index d64fdfd..a36c562
--- a/docs/introduction/concepts.md
+++ b/docs/content/docs/1/introduction/concepts.md
@@ -1,18 +1,19 @@
---
title: Concepts
headline: Documentation
-bodyclass: docs
-layout: docs
---
+
# Concepts
-
This document provides terminology used in the framework and its documentation.
+{{% note-block class="lead" %}}
+This document provides terminology used in the framework and its documentation.
You'll find most of the terms familiar from Domain-Driven Design, CQRS, Event Sourcing or Enterprise
Integration patterns.
We give brief descriptions for those who are new to these concepts and to tell
how they are implemented in our framework.
-Terms extending the industry-set terminology are designated as such.
+Terms extending the industry-set terminology are designated as such.
+{{% /note-block %}}
## Messaging
@@ -47,7 +48,7 @@ In Spine, rejections are defined as Protobuf messages in the file which names en
`rejections.proto`.
For detailed instructions on defining rejections, please refer to
-[“Working with Rejections”]({{site.baseurl}}/docs/guides/rejections) guide.
+[“Working with Rejections”](docs/guides/rejections/) guide.
### Acknowledgement
@@ -104,10 +105,12 @@ Event Reactor is an object which usually produces one or more events in response
event. Unlike [Event Subscriber](#event-subscriber), which always consumes events, a reacting object
generates events in response to changes in the domain.
-
In some cases, Event Reactor may ignore the event, returning `Nothing`.
+{{% note-block class="note" %}}
+In some cases, Event Reactor may ignore the event, returning `Nothing`.
It usually happens when a method returns one of the
- [`Either`]({{site.core_api_doc}}/server/server/io.spine.server.tuple/-either) types, with `Nothing` as
- one of the possible options: `EitherOf2`.
+ [`Either`]({{% get-site-data "spine.core_api_doc" %}}/server/server/io.spine.server.tuple/-either/) types, with `Nothing` as
+ one of the possible options: `EitherOf2`.
+{{% /note-block %}}
## Value Objects
@@ -135,8 +138,10 @@ The framework supports the following types of identifiers:
Examples of entity IDs used by the framework: `CommandId`, `EventId`, `UserId`, `TenantId`.
-
We highly recommend using message-based IDs to make your API strongly pronounced
- and type-safe.
+{{% note-block class="note" %}}
+We highly recommend using message-based IDs to make your API strongly pronounced
+ and type-safe.
+{{% /note-block %}}
### Aggregate
@@ -181,9 +186,9 @@ Repository encapsulates storage, retrieval, and search of Entities as if it were
a collection of objects. It isolates domain objects from the details of the database access code.
The applications you develop using Spine usually have the following types of repositories:
-* [`AggregateRepository`]({{site.core_api_doc}}/server/server/io.spine.server.aggregate/-aggregate-repository),
-* [`ProcessManagerRepository`]({{site.core_api_doc}}/server/server/io.spine.server.procman/-process-manager),
-* [`ProjectionRepository`]({{site.core_api_doc}}/server/server/io.spine.server.projection/-projection-repository).
+* [`AggregateRepository`]({{% get-site-data "spine.core_api_doc" %}}/server/server/io.spine.server.aggregate/-aggregate-repository/),
+* [`ProcessManagerRepository`]({{% get-site-data "spine.core_api_doc" %}}/server/server/io.spine.server.procman/-process-manager/),
+* [`ProjectionRepository`]({{% get-site-data "spine.core_api_doc" %}}/server/server/io.spine.server.projection/-projection-repository/).
### Snapshot
diff --git a/docs/content/docs/1/introduction/diagrams/_index.md b/docs/content/docs/1/introduction/diagrams/_index.md
new file mode 100644
index 0000000..0b15626
--- /dev/null
+++ b/docs/content/docs/1/introduction/diagrams/_index.md
@@ -0,0 +1,4 @@
+---
+build:
+ render: never
+---
diff --git a/docs/introduction/diagrams/spine-architecture-diagram-full-screen.html b/docs/content/docs/1/introduction/diagrams/spine-architecture-diagram-full-screen.html
similarity index 54%
rename from docs/introduction/diagrams/spine-architecture-diagram-full-screen.html
rename to docs/content/docs/1/introduction/diagrams/spine-architecture-diagram-full-screen.html
index 9ae5675..c61ba75 100644
--- a/docs/introduction/diagrams/spine-architecture-diagram-full-screen.html
+++ b/docs/content/docs/1/introduction/diagrams/spine-architecture-diagram-full-screen.html
@@ -1,25 +1,26 @@
---
-layout: full-screen
title: Application Architecture
-customjs: /tools/architecture-diagram.js
+layout: full-screen
+customjs: js/docs/architecture-diagram.js
---
diff --git a/docs/introduction/diagrams/spine-architecture-diagram.svg b/docs/content/docs/1/introduction/diagrams/spine-architecture-diagram.svg
similarity index 100%
rename from docs/introduction/diagrams/spine-architecture-diagram.svg
rename to docs/content/docs/1/introduction/diagrams/spine-architecture-diagram.svg
diff --git a/docs/introduction/naming-conventions.md b/docs/content/docs/1/introduction/naming-conventions.md
old mode 100644
new mode 100755
similarity index 85%
rename from docs/introduction/naming-conventions.md
rename to docs/content/docs/1/introduction/naming-conventions.md
index 1563c01..040f2e4
--- a/docs/introduction/naming-conventions.md
+++ b/docs/content/docs/1/introduction/naming-conventions.md
@@ -1,14 +1,15 @@
---
title: Naming Conventions
headline: Documentation
-bodyclass: docs
-layout: docs
---
+
# Naming Conventions
-
This document covers naming conventions for the code. Some of these conventions are
+{{% note-block class="lead" %}}
+This document covers naming conventions for the code. Some of these conventions are
used by the framework and code generation, and as such are required. Others are our recommendations
-that we find useful for making the code easier to understand.
+that we find useful for making the code easier to understand.
+{{% /note-block %}}
## Proto files
@@ -117,10 +118,12 @@ Identifiers are usually defined after the name of the entity with the `Id` suffi
You will find such naming pattern in the framework API. For example, `EventId`, `CommandId`,
`UserId`, `TenantId`, and others.
-
This convention is not a requirement. We find `Id` suffix short yet meaningful for
- building a rich type-safe API. You can also select another convention that fits your domain
- best. Please note that future version of the framework tools will use the `Id` suffix of the
- types for code scaffolding and improving intelligence of code generation.
+{{% note-block class="note" %}}
+This convention is not a requirement. We find `Id` suffix short yet meaningful for
+building a rich type-safe API. You can also select another convention that fits your domain
+best. Please note that future version of the framework tools will use the `Id` suffix of the
+types for code scaffolding and improving intelligence of code generation.
+{{% /note-block %}}
While the identifier type is usually defined with the `Id` suffix, we do not recommend following
the same strategy for the proto field names. Naming fields as `id` or adding the `_id` suffix
@@ -129,90 +132,95 @@ is usually excessive because the identifier type already has the `Id` suffix.
Instead, we name the fields by their respective type reference, so `user_id` becomes
`user` and `project_id` becomes `project`.
-
+{{< /code-with-label >}}
+
+{{< /docs-card-container >}}
The only exception from the suggestion is when the ID is a part of the root aggregate state,
or a command that creates the aggregate directly.
-
+{{< /code-with-label >}}
+
+{{< /docs-card-container >}}
### `repeated` and `map` fields
We recommend naming `repeated` and `map` fields using singular nouns as such a naming appears
to be closer to the language we speak. It also provides easier to use generated code.
-
This convention contradicts with the official
+{{% note-block class="note" %}}
+This convention contradicts with the official
[Protobuf Style Guide](https://developers.google.com/protocol-buffers/docs/style#repeated_fields
"Protocol Buffers Style Guide") which suggests naming `map` and `repeated` fields after plural
nouns. Knowing this, we still recommend singular because of the following.
The code generated for a `repeated` and `map` field named after a singular noun is closer to
real English. For the code related to the Domain-Driven Design this is far more important than
-the consistency with the style guide.
+the consistency with the style guide.
+{{% /note-block %}}
So when defining `repeated` and `map` fields use:
-
+{{< /code-with-label >}}
+
+{{< /docs-card-container >}}
### Commands
@@ -286,7 +294,7 @@ which seem hierarchical for convenience. When it comes to placing source code fi
in a project, there is usually nesting formed by the directories in a file system.
Spine framework uses this notion of “nesting” for marking multiple packages of a server-side code
-[belonging to a Bounded Context]({{site.core_api_doc}}/core/core/io.spine.core/-bounded-context)
+[belonging to a Bounded Context]({{% get-site-data "spine.core_api_doc" %}}/core/core/io.spine.core/-bounded-context/)
easier. But this is a convenience feature, not a requirement.
Please see our recommendations for organizing generated and handcrafted code in sections below.
@@ -340,8 +348,9 @@ These classes are used in the `throws` clause of command handling methods.
The arrangement with message classes nested under `Rejections` class and top-level `Throwable`s
is required to avoid name clashes while keeping these generated classes under the same package.
-
For details on rejections usage, refer to
- [Defining Rejections Guide]({{site.baseurl}}/docs/guides/rejections).
+{{% note-block class="note" %}}
+For details on rejections usage, refer to [Defining Rejections Guide](docs/guides/rejections/).
+{{% /note-block %}}
#### Server-side code
@@ -368,8 +377,10 @@ The suffix helps for observing together with other entities in a package.
For process managers it may be enough to have the `Process` suffix dropping `Manager`
which frequently worked for us too. Other options for suffixes are `Pm` or `Procman`.
-
It would be a good idea to decide on such suffix as a team standard before you
- start coding.
+{{% note-block class="note" %}}
+It would be a good idea to decide on such suffix as a team standard before you
+ start coding.
+{{% /note-block %}}
#### Repositories
diff --git a/docs/introduction/prior-art.md b/docs/content/docs/1/introduction/prior-art.md
old mode 100644
new mode 100755
similarity index 99%
rename from docs/introduction/prior-art.md
rename to docs/content/docs/1/introduction/prior-art.md
index d69f598..2165f5c
--- a/docs/introduction/prior-art.md
+++ b/docs/content/docs/1/introduction/prior-art.md
@@ -1,8 +1,6 @@
---
title: Prior Art
headline: Prior Art
-bodyclass: docs
-layout: docs
---
# Prior Art
diff --git a/docs/introduction/project-structure.md b/docs/content/docs/1/introduction/project-structure.md
old mode 100644
new mode 100755
similarity index 87%
rename from docs/introduction/project-structure.md
rename to docs/content/docs/1/introduction/project-structure.md
index 37c4231..6a5ce48
--- a/docs/introduction/project-structure.md
+++ b/docs/content/docs/1/introduction/project-structure.md
@@ -1,17 +1,18 @@
---
title: Project Structure
headline: Documentation
-bodyclass: docs
-layout: docs
---
+
# Project Structure
-
This document describes standard structure of a Spine-based project.
-It assumes that you are familiar with Gradle.
+{{% note-block class="lead" %}}
+This document describes standard structure of a Spine-based project.
+It assumes that you are familiar with [Gradle](https://gradle.org).
+{{% /note-block %}}
-
Spine uses Gradle for project model definition and as the build tool.
+Spine uses Gradle for project model definition and as the build tool.
It follows the standard structure of the Gradle project with extensions related to
-the code generation done by Protobuf Compiler and Spine Model Compiler.
+the code generation done by Protobuf Compiler and Spine Model Compiler.
## Handcrafted code
@@ -45,7 +46,7 @@ By not including the generated code into the version control we minimise the “
for developer eyes when a model changes. So, if you are using Git, for example, consider adding
the following line to your `.gitignore` file:
-```
+```text
**/generated/**
```
@@ -53,7 +54,7 @@ for developer eyes when a model changes. So, if you are using Git, for example,
Here's how a typical project structure would look like:
-```
+```text
myproject/
gradle/
module-one/
@@ -88,5 +89,4 @@ myproject/
gradlew
gradlew.bat
settings.gradle
-```
-
+```
diff --git a/docs/content/docs/1/introduction/rules.md b/docs/content/docs/1/introduction/rules.md
new file mode 100755
index 0000000..db77175
--- /dev/null
+++ b/docs/content/docs/1/introduction/rules.md
@@ -0,0 +1,18 @@
+---
+title: Rules
+headline: Documentation
+---
+
+# Rules
+
+Here are the ground rules the framework is built upon:
+
+1. An update to a business model _is_ an event.
+
+2. Entities are changed in response to events.
+
+3. A command has one and _only one_ handler.
+
+4. A command _must_ result in an event, a rejection, or other commands.
+
+5. Events are _always_ appended. Never deleted or edited.
diff --git a/docs/quick-start/index.md b/docs/content/docs/1/quick-start/_index.md
old mode 100644
new mode 100755
similarity index 91%
rename from docs/quick-start/index.md
rename to docs/content/docs/1/quick-start/_index.md
index 3d34b5b..6b410cc
--- a/docs/quick-start/index.md
+++ b/docs/content/docs/1/quick-start/_index.md
@@ -1,18 +1,16 @@
---
title: Getting Started in Java
headline: Documentation
-bodyclass: docs
-layout: docs
-next_btn:
- page: Development Process Overview
---
# Getting started with Spine in Java
-
This guide will walk you through a minimal client-server application in Java
-which handles one command to print some text on behalf of the current computer user. The document
-goes through already written code which is quite simple. So, it won't take long.
-
+{{% note-block class="lead" %}}
+This guide will walk you through a minimal client-server application in Java
+which handles one command to print some text on behalf of the current computer
+user. The document goes through already written code which is quite simple. So,
+it won't take long.
+{{% /note-block %}}
## What we'll do
@@ -20,13 +18,13 @@ We'll go through the example which shows a Bounded Context called “Hello”.
The context has one `ProcessManager`, which handles the `Print` command
sent from the client-side code to the server-side code hosting the context.
We'll go through the production code of the example suite, and through the code which
-tests the Hello context.
+tests the Hello context.
## What you'll need
-1. JDK version 8 or higher.
-2. Git.
-3. The source code of the [Hello World](https://github.com/spine-examples/hello) example.
+1. JDK version 8 or higher.
+2. Git.
+3. The source code of the [Hello World]({{% get-site-data "spine.examples" %}}/hello) example.
```bash
git clone git@github.com:spine-examples/hello.git
@@ -49,7 +47,7 @@ gradlew.bat :sayHello
This would build and execute the example.
The process should finish with the output which looks like this:
-```
+```bat
> Task :sayHello
Dec 18, 2020 5:32:11 PM io.spine.base.Environment setCurrentType
INFO: `Environment` set to `io.spine.base.Production`.
@@ -74,10 +72,12 @@ from the `Client`.
The text in between brackets is the name of the current computer user. The name was passed as
the argument of the `Print` command.
-
We opted to show a `ProcessManager` — instead of an `Aggregate` — because
+{{% note-block class="note" %}}
+We opted to show a `ProcessManager` — instead of an `Aggregate` — because
the console output is similar to an “External System”. Dealing with things like
that is the job of Process Managers. We also want to highlight the importance of using
-this architectural pattern.
+this architectural pattern.
+{{% /note-block %}}
The output that follows is the logging produced by the `Client` class as it receives the `Printed`
event from the server.
@@ -101,19 +101,20 @@ The root of the project contains the following files:
* **`build.gradle`** — the project configuration. We'll review this file later
[in details](#adding-spine-to-a-gradle-project).
-
The root directory also contains “invisible” files, names of which start with
+{{% note-block class="note" %}}
+The root directory also contains “invisible” files, names of which start with
the dot (e.g. `.gitattributes` and `.travis.yml`).
These files configure Git and CI systems we use. They are not directly related to the subject
of the example and this guide. If you're interested in this level of details,
please look into the code and comments in these files.
-
+{{% /note-block %}}
Here are the directories of interest in the project root:
- * `gradle` — this directory contains the code of Gradle Wrapper and two Gradle scripts
+ * **`gradle`** — this directory contains the code of Gradle Wrapper and two Gradle scripts
used in the [project configuration](#other-project-configuration).
* **`generated`** — this directory contains the code generated by Protobuf Compiler and
Spine Model Compiler. This directory and code it contains is created automatically
- when a domain model changes. This directory is excluded from version control.
+ when a domain model changes. This directory is _excluded_ from version control.
* **`src`** — contains the handcrafted source code.
Let's see how the source code is structured.
@@ -135,19 +136,21 @@ The production code consists of two parts allocated by sub-directories:
A real project would have these parts in separate modules or projects. We put it all
together for the sake of simplicity.
-Now, let's review the code in details, starting with how to add Spine to a Gradle project.
+Now, let's review the code in details, starting with how to add Spine to a
+Gradle project.
## Adding Spine to a Gradle project
-Let's open `build.gradle` from the root of the project. The simplest and recommended way for
-adding Spine dependencies to a project is the Bootstrap plugin:
+Let's open `build.gradle` from the root of the project. The simplest and
+recommended way for adding Spine dependencies to a project is the
+Bootstrap plugin:
```gradle
plugins {
- id("io.spine.tools.gradle.bootstrap").version("1.7.0")
+ id("io.spine.tools.gradle.bootstrap").version("1.9.0")
}
```
@@ -162,10 +165,11 @@ spine.enableJava().server()
This enables Java in the module and adds necessary dependencies and configurations.
-
Calling `spine.enableJava().server()` adds both server- and client-side dependencies.
+{{% note-block class="note" %}}
+Calling `spine.enableJava().server()` adds both server- and client-side dependencies.
This way a module of a Bounded Context “A” may be a client for a Bounded Context “B”.
Client-side applications or modules should call: `spine.enableJava().client()`.
-
+{{% /note-block %}}
### Other project configuration
@@ -191,22 +195,27 @@ the following files:
* **`commands.proto`** — this file defines the `Print` command.
-
By convention, commands are defined in a file with the `commands` suffix
- in its name. It can be, for example, `order_commands.proto` or just `commands.proto`
- like in our example.
+{{% note-block class="note" %}}
+By convention, commands are defined in a file with the `commands` suffix
+in its name. It can be, for example, `order_commands.proto` or just `commands.proto`
+like in our example.
+{{% /note-block %}}
* **`events.proto`** — this file defines the `Printed` event.
-
Similarly to commands, events are defined in proto files having the `events`
- suffix in their names.
+{{% note-block class="note" %}}
+Similarly to commands, events are defined in proto files having the `events`
+suffix in their names.
+{{% /note-block %}}
These two files define signals used by the Hello context. There's also data of the `Console`
Process Manager, which is defined in the package **`server`** in the file **`console.proto`**.
-
+{{% note-block class="note" %}}
We arrange the sub-package `server` to highlight the fact that this is server-only data. It is not
a convention used by the framework. We find the clarity of this infix useful when creating
-cloud applications. So, we share it as a recommendation in this example.
+cloud applications. So, we share it as a recommendation in this example.
+{{% /note-block %}}
Let's review the context data definition in details.
@@ -270,11 +279,13 @@ Protobuf Compiler for this `.proto` file:
option java_outer_classname = "CommandsProto";
```
-
Outer classes are used by Protobuf implementation internally.
+{{% note-block class="note" %}}
+Outer classes are used by Protobuf implementation internally.
When the `java_outer_classname` option is omitted, Protobuf Compiler would calculate the Java
class name taking the name of the corresponding `.proto` file.
We recommend setting the name directly to make it straight. This also avoids possible name clashes
-with the handcrafted code.
+with the handcrafted code.
+{{% /note-block %}}
The next standard option instructs the Protobuf Compiler to put each generated Java type into
a separate file. This way it would be easier to analyze dependencies of the code which uses these
@@ -311,9 +322,10 @@ The second field is marked as `(required)` using the custom option imported in
the `spine/options.proto` file above. This command does not make much sense if there is no text
to print.
-
In Protobuf a data type is either a **`message`** (we can send it) or an **`enum`**.
-If you're new to this language, you may want to look at the [Proto3 Language Guide][proto3-guide].
-
+{{% note-block class="note" %}}
+In Protobuf a data type is either a **`message`** (we can send it) or an **`enum`**.
+If you're new to this language, you may want to look at the [Proto3 Language Guide][proto3-guide].
+{{% /note-block %}}
Now, let's see how to define events.
@@ -367,14 +379,14 @@ message Printed {
The event tells which text was printed for a user. Both of the fields are marked as `(required)`
because the event does not make much sense if one of them is empty.
-
+{{% note-block class="note" %}}
Unlike for commands, the framework does not assume that the first event field is always
populated. This is so because default routing rules for commands and events are different.
When an event is produced by some entity, it remembers the ID of this producer entity.
By default, the framework uses the producer ID to route events to their target entities —
if they have identifiers of the same type. If the type of producer ID does not match one of the
target entity, then event fields are analyzed. It is also possible to set custom routing rules.
-
+{{% /note-block %}}
Now, let's see the server-side data of the Hello context.
@@ -503,10 +515,12 @@ return Printed.newBuilder()
The `vBuild()` call validates and builds the message. This method is generated by Spine Model
Compiler. For instructions on adding validation attributes to your model please see
-[Validation User Guide]({{site.baseurl}}/docs/guides/validation).
+[Validation User Guide](docs/guides/validation/).
-
After the event is generated, it is posted to the `EventBus` and delivered to
-subscribers automatically. You don't need to write any code for this.
+{{% note-block class="note" %}}
+After the event is generated, it is posted to the `EventBus` and delivered to
+subscribers automatically. You don't need to write any code for this.
+{{% /note-block %}}
Now, let's see how the `Console` Process Manager is exposed to the outer world so that it can
receive commands.
@@ -535,9 +549,11 @@ the `@BoundedContext` annotation:
package io.spine.helloworld.server.hello;
```
-
The framework assumes that all entity classes belonging to this and nested packages
+{{% note-block class="note" %}}
+The framework assumes that all entity classes belonging to this and nested packages
belong to the Bounded Context with the name specified in the argument of the annotation.
-This arrangement is needed for routing events.
+This arrangement is needed for routing events.
+{{% /note-block %}}
The second thing the `HelloContext` does is creating a Builder for the Bounded Context:
@@ -554,10 +570,12 @@ public static BoundedContextBuilder newBuilder() {
The context we create is single-tenant. It contains one entity type which we pass to the builder.
-
If an entity uses default routing rules for the incoming events and commands,
+{{% note-block class="note" %}}
+If an entity uses default routing rules for the incoming events and commands,
its type can be added to `BoundedContextBuilder` directly. If custom routing rules are needed,
they are specified by a custom `Repository` class. In this case, an instance of such `Repository`
-is passed to `BoundedContextBuilder` instead of the entity type managed by this `Repository`.
+is passed to `BoundedContextBuilder` instead of the entity type managed by this `Repository`.
+{{% /note-block %}}
Once we assembled the Bounded Context, let's test it.
@@ -565,9 +583,11 @@ Once we assembled the Bounded Context, let's test it.
Let's open the `HelloContextTest` suite. It is based on JUnit 5 and `spine-testutil-server` library.
-
We already added JUnit dependency when defining the Gradle project.
+{{% note-block class="note" %}}
+We already added JUnit dependency when defining the Gradle project.
The `testImplementation` dependency for `spine-testutil-server` is automatically added when
-you enable Spine in your project using `spine.enableJava().server()`. So, we're good to go testing.
+you enable Spine in your project using `spine.enableJava().server()`. So, we're good to go testing.
+{{% /note-block %}}
The class of the test suite extends the abstract base called `ContextAwareTest`:
@@ -687,7 +707,7 @@ Let's open the `Server` class of our example application suite. The static initi
the class configures the server environment:
```java
static {
@@ -705,7 +725,7 @@ the settings that are normally used for testing:
end=" }">
```java
private static void configureEnvironment() {
- ServerEnvironment.when(Production.class)
+ ServerEnvironment.when(DefaultMode.class)
.use(InMemoryStorageFactory.newInstance())
.use(Delivery.localAsync())
.use(InMemoryTransportFactory.newInstance());
@@ -735,10 +755,12 @@ public Server(String serverName) {
The constructor accepts the name which is used for connecting clients. This name
is passed to the `inProcess()` factory method of the `io.spine.server.Server` class.
-
In-process gRPC communications are normally used for testing.
+{{% note-block class="note" %}}
+In-process gRPC communications are normally used for testing.
This example uses in-process client/server arrangement in the production code for
the sake of simplicity. A real-world application would use a `Server` instance exposed
-via a TCP/IP port.
+via a TCP/IP port.
+{{% /note-block %}}
Once we have the `Server.Builder` instance returned by the `inProcess()` method,
we add the Hello Context via its builder to the constructed `Server` instance.
@@ -764,10 +786,12 @@ public final class Client {
private final io.spine.client.Client client;
```
-
The `io.spine:spine-client` library is provided
+{{% note-block class="note" %}}
+The `io.spine:spine-client` library is provided
to the example application project as a transitive dependency of
the `io.spine:spine-server` library, which is added to the project when you do
-`spine.enableJava().server()` in your Gradle project.
+`spine.enableJava().server()` in your Gradle project.
+{{% /note-block %}}
Then, the `Client` class declares a field for keeping subscriptions to the results of a command
execution. We'll see how this field is used in a minute.
diff --git a/docs/content/docs/1/reference/index.md b/docs/content/docs/1/reference/index.md
new file mode 100755
index 0000000..364e81a
--- /dev/null
+++ b/docs/content/docs/1/reference/index.md
@@ -0,0 +1,25 @@
+---
+title: API Reference
+headline: Documentation
+---
+
+# API Reference
+
+## Java
+
+- [Server]({{% get-site-data "spine.core_api_doc" %}}/server/)
+
+- [Client]({{% get-site-data "spine.core_api_doc" %}}/client/)
+
+- [Spine Web API]({{% get-site-data "spine.web_api_doc" %}}/web/).
+This reference defines the API for communicating with a Spine-based backend using web requests.
+
+- [Web API implementation on Firebase]({{% get-site-data "spine.web_api_doc" %}}/firebase-web/).
+This reference is on the implementation of the Spine Web API using the Firebase Realtime Database
+to deliver data to web clients.
+
+## JavaScript
+- [JavaScript Client]({{% get-site-data "spine.js_api_doc" %}})
+
+## Dart
+- [Dart Client]({{% get-site-data "spine.dart_api_doc" %}}/client/)
diff --git a/docs/content/docs/1/resources/_index.md b/docs/content/docs/1/resources/_index.md
new file mode 100644
index 0000000..9e6c762
--- /dev/null
+++ b/docs/content/docs/1/resources/_index.md
@@ -0,0 +1,57 @@
+---
+title: Resources
+headline: DDD Resources
+bodyclass: docs resources
+---
+
+# Resources on Domain-Driven Design
+
+{{% note-block class="lead" %}}
+A brief selection of learning materials we recommend to the colleagues in DDD.
+{{% /note-block %}}
+
+{{< docs-card-container >}}
+
+{{< docs-category-card
+ icon="fas fa-book"
+ title="Books"
+ url="docs/resources/books/" >}}
+The concepts and practical guidance.
+{{< /docs-category-card >}}
+
+{{< docs-category-card
+ icon="far fa-user-friends"
+ title="People"
+ url="docs/resources/people/" >}}
+The creators and drivers of the DDD ideas.
+{{< /docs-category-card >}}
+
+{{< docs-category-card
+ icon="far fa-comment-lines"
+ title="Blogs"
+ url="docs/resources/blogs/" >}}
+The ideas unfolding in real time.
+{{< /docs-category-card >}}
+
+{{< docs-category-card
+ icon="far fa-window"
+ title="Sites"
+ url="docs/resources/sites/" >}}
+Learning hubs.
+{{< /docs-category-card >}}
+
+{{< docs-category-card
+ icon="fas fa-users"
+ title="Communities"
+ url="docs/resources/communities/" >}}
+Places to discuss DDD with other practitioners.
+{{< /docs-category-card >}}
+
+{{< docs-category-card
+ icon="far fa-code"
+ title="Libraries and Frameworks"
+ url="docs/resources/libraries/" >}}
+Other tools that help with DDD in code.
+{{< /docs-category-card >}}
+
+{{< /docs-card-container >}}
diff --git a/docs/resources/blogs.md b/docs/content/docs/1/resources/blogs.md
old mode 100644
new mode 100755
similarity index 83%
rename from docs/resources/blogs.md
rename to docs/content/docs/1/resources/blogs.md
index e9e2e60..25cc43d
--- a/docs/resources/blogs.md
+++ b/docs/content/docs/1/resources/blogs.md
@@ -2,12 +2,13 @@
title: Blogs
headline: DDD Resources
bodyclass: docs resources
-layout: docs
---
# Blogs
-
The ideas unfolding in real time
+{{% note-block class="lead" %}}
+The ideas unfolding in real time
+{{% /note-block %}}
- [Domain Driven Design Weekly](http://dddweekly.com/).
The periodical digest of articles and materials on DDD by Nick Chamberlain.
diff --git a/docs/content/docs/1/resources/books.md b/docs/content/docs/1/resources/books.md
new file mode 100644
index 0000000..d7d1477
--- /dev/null
+++ b/docs/content/docs/1/resources/books.md
@@ -0,0 +1,61 @@
+---
+title: Books
+headline: DDD Resources
+bodyclass: docs resources
+---
+
+# Books
+
+{{% note-block class="lead" %}}
+Key works to get familiar with the approach.
+{{% /note-block %}}
+
+{{< book-card
+ image="ddd-design.png"
+ title="Domain-Driven Design"
+ subtitle="Tackling Complexity in the Heart of Software"
+ author="Eric Evans"
+ book_url="https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215" >}}
+The Big Blue Book which lay the basics of Domain-Driven Design methodology.
+It provides a broad framework for making design decisions and a vocabulary for
+discussing domain design.
+{{< /book-card >}}
+
+{{< book-card
+ image="ddd-design-reference.png"
+ title="Domain-Driven Design Reference"
+ author="Eric Evans"
+ book_url="https://www.amazon.com/Domain-Driven-Design-Reference-Definitions-Summaries/dp/1457501198" >}}
+This book provides a brief overview of the DDD methodology.
+It also defines the terms, which were coined in this topic since 2004.
+{{< /book-card >}}
+
+{{< book-card
+ image="implementing-ddd.png"
+ title="Implementing Domain-Driven Design"
+ author="Vaughn Vernon"
+ book_url="https://www.informit.com/store/implementing-domain-driven-design-9780133039894" >}}
+The Big Red Book provides practical guidance on how to apply DDD. Building on
+Eric Evans’ seminal book, Domain-Driven Design, the author presents practical
+DDD techniques through examples from familiar domains.
+{{< /book-card >}}
+
+{{< book-card
+ image="ddd-distilled.png"
+ title="Domain-Driven Design Distilled"
+ author="Vaughn Vernon"
+ book_url="https://www.informit.com/store/domain-driven-design-distilled-9780134434988" >}}
+If you are new to Domain-Driven Design, this book and the Domain-Driven Design
+Reference by Eric Evans are the best way to get into the topic.
+{{< /book-card >}}
+
+{{< book-card
+ image="event-storming.png"
+ title="Introducing EventStorming"
+ subtitle="An act of deliberate collective learning"
+ author="Alberto Brandolini"
+ book_url="https://www.eventstorming.com/book/" >}}
+The deepest tutorial and explanation about EventStorming, straight from the
+inventor. It provides guidance on leveraging the potential of this collaborative
+brainstorming technique used to identify domain events.
+{{< /book-card >}}
diff --git a/docs/resources/communities.md b/docs/content/docs/1/resources/communities.md
old mode 100644
new mode 100755
similarity index 90%
rename from docs/resources/communities.md
rename to docs/content/docs/1/resources/communities.md
index d3533f4..bc75718
--- a/docs/resources/communities.md
+++ b/docs/content/docs/1/resources/communities.md
@@ -2,12 +2,13 @@
title: Communities
headline: DDD Resources
bodyclass: docs resources
-layout: docs
---
# Communities
-
Places to discuss DDD with other practitioners
+{{% note-block class="lead" %}}
+Places to discuss DDD with other practitioners
+{{% /note-block %}}
- [Virtual DDD](https://virtualddd.com/) (EN)
A community-driven online meetup for people who want to get more in-depth knowledge of DDD
@@ -23,4 +24,3 @@ Our own community for developers and engineers who look for the new ways of solv
- [DDDevotion](https://t.me/dddevotion) (RU)
A community, dedicated to DDD, engineering practices, and technical excellence.
-
diff --git a/docs/resources/libraries.md b/docs/content/docs/1/resources/libraries.md
old mode 100644
new mode 100755
similarity index 83%
rename from docs/resources/libraries.md
rename to docs/content/docs/1/resources/libraries.md
index fd2fa74..f4f649a
--- a/docs/resources/libraries.md
+++ b/docs/content/docs/1/resources/libraries.md
@@ -2,12 +2,13 @@
title: Libraries and Frameworks
headline: DDD Resources
bodyclass: docs resources
-layout: docs
---
# Libraries and Frameworks
-
Other tools that help with DDD in code
+{{% note-block class="lead" %}}
+Other tools that help with DDD in code
+{{% /note-block %}}
- [Axon](https://axoniq.io/).
Open source framework for event-driven microservices and domain-driven design.
diff --git a/docs/content/docs/1/resources/people.md b/docs/content/docs/1/resources/people.md
new file mode 100644
index 0000000..99f4175
--- /dev/null
+++ b/docs/content/docs/1/resources/people.md
@@ -0,0 +1,58 @@
+---
+title: People
+headline: DDD Resources
+bodyclass: docs resources
+---
+
+# People
+
+{{% note-block class="lead" %}}
+The creators and drivers of the DDD ideas.
+{{% /note-block %}}
+
+{{< person-card
+ photo="eric-evans.png"
+ name="Eric Evans"
+ socials=`[
+ {"url":"https://domainlanguage.com/", "icon":"far fa-globe"},
+ {"url":"https://x.com/ericevans0", "icon":"fab fa-twitter"}
+ ]`>}}
+The author of the DDD methodology. Domain linguist. The author
+of “Domain-Driven Design: Tackling Complexity in Software”.
+{{< /person-card >}}
+
+{{< person-card
+ photo="vaughn-vernon.png"
+ name="Vaughn Vernon"
+ socials=`[
+ {"url":"https://github.com/VaughnVernon", "icon":"fab fa-github"},
+ {"url":"https://x.com/VaughnVernon", "icon":"fab fa-twitter"}
+ ]`>}}
+Domain Model Whisperer. The author of “Implementing Domain-Driven Design”
+and “Domain-Driven Design Distilled”. Founder of [Vlingo Platform](https://vlingo.io/).
+{{< /person-card >}}
+
+{{< person-card
+ photo="greg-young.png"
+ name="Greg Young"
+ socials=`[
+ {"url":"https://github.com/gregoryyoung", "icon":"fab fa-github"},
+ {"url":"https://x.com/gregyoung", "icon":"fab fa-twitter"}
+ ]`>}}
+Creator of CQRS and the driver of Event Sourcing.
+Founder of the [Event Store](https://eventstore.com/).
+{{< /person-card >}}
+
+{{< person-card
+ photo="alberto-brandolini.png"
+ name="Alberto Brandolini"
+ socials=`[
+ {"url":"https://github.com/ziobrando", "icon":"fab fa-github"},
+ {"url":"https://x.com/ziobrando", "icon":"fab fa-twitter"}
+ ]`>}}
+The inventor of #EventStorming. Practicing engineer and a coach. The author of
+Introducing “EventStorming – An act of deliberate collective learning”.
+{{< /person-card >}}
+
+And [many more](https://dddeurope.com/2020/speakers/) advancing the approach
+every day.
diff --git a/docs/resources/sites.md b/docs/content/docs/1/resources/sites.md
old mode 100644
new mode 100755
similarity index 91%
rename from docs/resources/sites.md
rename to docs/content/docs/1/resources/sites.md
index 1daf474..2f33d71
--- a/docs/resources/sites.md
+++ b/docs/content/docs/1/resources/sites.md
@@ -2,12 +2,13 @@
title: Sites
headline: DDD Resources
bodyclass: docs resources
-layout: docs
---
# Sites
-
To-Do List
- — a simple task management system with multiple client applications. If you want to see
- a bigger picture of a Spine-based solution, have a look at this example.
The Big Blue Book which lay the basics of Domain-Driven Design methodology.
- It provides a broad framework for making design decisions and a vocabulary for
- discussing domain design.
The Big Red Book provides practical guidance on how to apply DDD. Building on Eric
- Evans’ seminal book, Domain-Driven Design, the author presents practical DDD techniques
- through examples from familiar domains.
The deepest tutorial and explanation about EventStorming, straight from the inventor.
- It provides guidance on leveraging the potential of this collaborative brainstorming
- technique used to identify domain events.
The inventor of #EventStorming. Practicing engineer and a coach. The author of
- Introducing “EventStorming – An act of deliberate collective learning”.
Read a guide on adding the content to the documentation.
-
This page is only available on this site and will not be rendered on the
- spine.io
-
-
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 0000000..5d89773
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+rootProject.name = "spine.io"
+
+includeBuild("./_code/samples")
+
+includeBuild("./_code/examples/airport")
+includeBuild("./_code/examples/hello")
diff --git a/site/.browserslistrc b/site/.browserslistrc
new file mode 100644
index 0000000..b4e0f56
--- /dev/null
+++ b/site/.browserslistrc
@@ -0,0 +1,2 @@
+> 0.5%
+last 2 versions
diff --git a/site/assets/scss/docs/_home.scss b/site/assets/scss/docs/_home.scss
new file mode 100644
index 0000000..3f5ad7b
--- /dev/null
+++ b/site/assets/scss/docs/_home.scss
@@ -0,0 +1,64 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.home {
+ background-color: $body-light-gray-color;
+ font-family: $main-font;
+
+ .container {
+ background-color: white;
+ border: 1px solid #e6ecf1;
+ max-width: 760px;
+ padding: 48px 56px;
+
+ @include breakpoint(md-phone) {
+ padding: 24px;
+ }
+
+ h1 {
+ font-size: $font-size--xxxl;
+ font-weight: 300;
+ color: rgba(0, 0, 0, .4);
+ line-height: 1.2;
+ margin-bottom: 32px;
+ padding-top: 16px;
+
+ @include breakpoint(md-phone) {
+ font-size: $font-size--xl;
+ }
+ }
+
+ a {
+ color: $main-brand-color;
+ }
+
+ .subtle {
+ color: rgba(0, 0, 0, .46);
+ margin-top: 40px;
+ font-size: 14px;
+ }
+ }
+}
diff --git a/site/assets/scss/docs/_layout.scss b/site/assets/scss/docs/_layout.scss
new file mode 100644
index 0000000..88a7704
--- /dev/null
+++ b/site/assets/scss/docs/_layout.scss
@@ -0,0 +1,37 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.page-content {
+ .three-column {
+ .sticky-col {
+ top: 0;
+ }
+
+ .content-col {
+ padding-bottom: 20px;
+ }
+ }
+}
diff --git a/site/assets/scss/main.scss b/site/assets/scss/main.scss
new file mode 100644
index 0000000..f687a3b
--- /dev/null
+++ b/site/assets/scss/main.scss
@@ -0,0 +1,36 @@
+/*!
+ * Copyright 2025, TeamDev. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+@import "node_modules/bootstrap/scss/bootstrap";
+
+@import "docs-common";
+@import "docs";
+
+// Styles that should only be used to build this site.
+// They should not be imported into the main spine.io site
+// to not override or duplicate styles there.
+@import "docs/home";
+@import "docs/layout";
diff --git a/site/config/_default/hugo.toml b/site/config/_default/hugo.toml
new file mode 100644
index 0000000..fa0944e
--- /dev/null
+++ b/site/config/_default/hugo.toml
@@ -0,0 +1,48 @@
+baseURL = 'https://spine.io/'
+languageCode = 'en-us'
+title = 'Documentation preview'
+disableKinds = ['taxonomy', 'term']
+
+[module]
+ # First theme has higher priority than later ones.
+ [[module.imports]]
+ path = '../../docs'
+ [[module.imports]]
+ path = 'github.com/TeamDev-Ltd/site-commons'
+
+# Enables HTML rendering in markdown files.
+[markup]
+ [markup.goldmark]
+ [markup.goldmark.renderer]
+ unsafe = true
+ [markup.highlight]
+ noClasses = false
+
+# Version control config.
+# To change the current main version, update only the `path` parameter.
+# Read more in the `SPINE_RELEASE.md` guide.
+#
+# The same settings should be copied to the `SpineEventEngine/SpineEventEngine.github.io`
+# project due to the issue with the cascade arrays:
+# https://github.com/gohugoio/hugo/issues/13869
+[[cascade]]
+ [cascade.params]
+ url = 'docs/'
+ [cascade.target]
+ # The path to the `_index.md` of the current main version.
+ path = '{/docs/1}'
+ kind = 'section'
+[[cascade]]
+ [cascade.params]
+ url = 'docs/:sections[2:]/'
+ [cascade.target]
+ # The path to the current main version.
+ path = '{/docs/1/**}'
+ kind = 'section'
+[[cascade]]
+ [cascade.params]
+ url = 'docs/:sections[2:]/:contentbasename/'
+ [cascade.target]
+ # The path to the current main version.
+ path = '{/docs/1/**}'
+ kind = 'page'
diff --git a/site/content/_index.md b/site/content/_index.md
new file mode 100644
index 0000000..a48fb5a
--- /dev/null
+++ b/site/content/_index.md
@@ -0,0 +1,14 @@
+---
+title: Documentation preview
+---
+
+# Welcome to Spine Documentation
+
+Go to the [documentation section](docs/).
+
+Read a [guide][authoring] on adding the content to the documentation.
+
+
The page is only available on this site and will not be
+rendered on the spine.io.
- * ```
- *
- * In markdown, all tags should be left-aligned. This way, the blocks of code
- * will not be broken:
- * ```
- *
- *
- * Any Java content here
- *
- *
- * Any Kotlin content here
- *
- *
- * ```
- *
- * If you don't need to display the tabs, but only need to show a specific paragraph of text
- * or change the title depending on the language, just use this:
- * ```
- *
- * # Getting started with Spine in Java
- *
- *
- * # Getting started with Spine in Kotlin
- *
- * ```
- *
- * To change only some of the words in a sentence, use the `` tag with the `.inline` class:
- * ```
- * A minimal client-server application in
- * Java
- * Kotlin
- * which handles one command to print some text...
- * ```
- */
-
-'use strict';
-
-$(
- function() {
- const defaultLang = 'java'
- const cookieCodeLang = 'codeLang';
- const tabLangAttr = 'lang';
- const $codeTabs = $('.code-tabs');
- const $codeTabContent = $('.code-tab-content');
-
- addTabSwitcher();
- addTabContentClass();
- initCodeLangSwitcher();
-
- /**
- * Adds a tab switcher to the DOM.
- */
- function addTabSwitcher() {
- $codeTabs.each(function() {
- const tabBlock = $(this);
- createTab(tabBlock, createTabContainer(tabBlock));
- });
- }
-
- /**
- * Creates a container for tabs.
- *
- * Also, creates the `indicator` line which is used to underline the selected tab.
- *
- * @param tabBlock a block that contains tabs.
- * @return {jQuery|HTMLElement} tabContainer a container for tabs.
- */
- function createTabContainer(tabBlock) {
- const tabContainer = $('
');
- tabBlock.prepend(tabContainer);
- return tabContainer;
- }
-
- /**
- * Creates a tab inside the container for each `code-tab-content` element.
- *
- * Adds the corresponding class to the tab that was provided in the `lang` attribute.
- *
- * @param tabBlock a block that contains tabs.
- * @param tabContainer a container for tabs.
- */
- function createTab(tabBlock, tabContainer) {
- const tabContent = tabBlock.find($codeTabContent);
- tabContent.each(function () {
- const lang = getLang($(this));
- if (lang) {
- const item = $(`
${lang.langName}
`);
- tabContainer.append(item);
- }
- });
- }
-
- /**
- * Adds the class to the `code-tab-content` element from the `lang` attribute.
- */
- function addTabContentClass() {
- $codeTabContent.each(function () {
- const lang = getLang($(this));
- if (lang) {
- $(this).addClass(lang.langClass);
- }
- });
- }
-
- /**
- * Gets the language value from the `lang` attribute of the element.
- *
- * And generates a valid class name based on the value of the attribute.
- *
- * @param el an element that has `lang` attribute.
- * @return {{langName, langClass}}
- */
- function getLang(el) {
- const lang = el.attr(tabLangAttr);
- if (typeof lang !== 'undefined' && lang !== false ) {
- const langName = lang;
- let langClass = lang.replace(/\s+/g, '-').toLowerCase();
- langClass = langClass.replace(/[.,+\/#!$%\^&\*;:{}=\_`~()]/g,'-');
- return {langName, langClass}
- }
- }
-
- /**
- * Inits a code language switcher.
- *
- * By default, sets the primary code language to the `cookie`.
- * On a tab click switches between code languages.
- */
- function initCodeLangSwitcher() {
- const primaryLang = defaultLang;
- const cookieValue = Cookies.get(cookieCodeLang);
-
- if (cookieValue == null) {
- setCodeLang(primaryLang);
- } else {
- setCodeLang(cookieValue);
- }
-
- $('.tab').click(function() {
- const lang = $(this).attr('class').split(' ')[1];
- if (typeof lang !== 'undefined' && lang !== false) {
- setCodeLang(lang);
- }
- });
- }
-
- /**
- * Sets the chosen code language to the `cookie` and adds corresponding
- * CSS classes to the selected tab and content element.
- *
- * Sets the width and left position of the selected tab to the indicator line.
- *
- * The CSS file is located at `_sass/modules/_code-tabs.scss`.
- *
- * @param codeLang a selected code language.
- */
- function setCodeLang(codeLang) {
- const $selectedTab = $('.tab.' + codeLang);
- Cookies.set(cookieCodeLang, codeLang);
- $('.tab').removeClass('selected');
- $selectedTab.addClass('selected');
- $codeTabContent.removeClass('show');
- $('.code-tab-content.' + codeLang).addClass('show');
-
- if ($selectedTab.length) {
- const leftPosition = $selectedTab.position().left
- + parseInt($selectedTab.css('marginLeft'), 10);
- $('.tabs .indicator').css({
- width: $selectedTab.outerWidth(),
- left: leftPosition
- });
- }
- }
- }
-);