├── .formatter.exs ├── .gitattributes ├── .github └── workflows │ ├── artifacts.yml │ ├── ci.yml │ ├── release.yml │ └── release_pre_built │ └── action.yml ├── .gitignore ├── CHANGELOG.md ├── Cheatsheet.cheatmd ├── LICENSE ├── README.md ├── assets ├── .eslintignore ├── .eslintrc.js ├── README.md ├── build │ └── build.js ├── css │ ├── _epub.css │ ├── _html.css │ ├── autocomplete.css │ ├── content │ │ ├── admonition.css │ │ ├── bottom-actions.css │ │ ├── cheatsheet.css │ │ ├── code.css │ │ ├── epub-admonition.css │ │ ├── footer.css │ │ ├── functions.css │ │ ├── general.css │ │ └── summary.css │ ├── copy-button.css │ ├── custom-props │ │ ├── _elixir.css │ │ ├── _erlang.css │ │ ├── common.css │ │ ├── theme-dark.css │ │ └── theme-light.css │ ├── entry │ │ ├── epub-elixir.css │ │ ├── epub-erlang.css │ │ ├── html-elixir.css │ │ └── html-erlang.css │ ├── focus.css │ ├── icons.css │ ├── keyboard-shortcuts.css │ ├── layout.css │ ├── makeup.css │ ├── modal.css │ ├── preview.css │ ├── print-cheatsheet.css │ ├── print.css │ ├── quick-switch.css │ ├── screen-reader.css │ ├── search-bar.css │ ├── search.css │ ├── settings.css │ ├── sidebar.css │ ├── tabset.css │ ├── toast.css │ └── tooltips.css ├── fonts │ ├── RemixIconCollection.remixicon │ └── remixicon.woff2 ├── js │ ├── autocomplete │ │ ├── autocomplete-list.js │ │ └── suggestions.js │ ├── constants.js │ ├── content.js │ ├── copy-button.js │ ├── entry │ │ ├── epub.js │ │ ├── html.js │ │ └── inline_html.js │ ├── globals.js │ ├── handlebars │ │ ├── helpers.js │ │ └── templates │ │ │ ├── autocomplete-suggestions.handlebars │ │ │ ├── copy-button.html │ │ │ ├── modal-layout.html │ │ │ ├── quick-switch-modal-body.html │ │ │ ├── search-results.handlebars │ │ │ ├── settings-modal-body.handlebars │ │ │ ├── tooltip-body.handlebars │ │ │ └── versions-dropdown.handlebars │ ├── helpers.js │ ├── keyboard-shortcuts.js │ ├── makeup.js │ ├── modal.js │ ├── preview.js │ ├── quick-switch.js │ ├── search-bar.js │ ├── search-page.js │ ├── settings-store.js │ ├── settings.js │ ├── sidebar │ │ ├── constants.js │ │ ├── sidebar-drawer.js │ │ ├── sidebar-list.js │ │ └── sidebar-version-select.js │ ├── swup.js │ ├── tabsets.js │ ├── theme.js │ ├── toast.js │ └── tooltips │ │ ├── hint-page.js │ │ ├── hints.js │ │ └── tooltips.js ├── karma.conf.js ├── package-lock.json ├── package.json └── test │ ├── .eslintrc │ ├── autocomplete │ └── suggestions.spec.js │ ├── helpers.spec.js │ └── tooltips │ └── hints.spec.js ├── bin └── ex_doc ├── formatters ├── epub │ ├── dist │ │ ├── epub-4WIP524F.js │ │ ├── epub-elixir-FNUUKFP7.css │ │ └── epub-erlang-JBFPMY6T.css │ └── metainfo │ │ ├── com.apple.ibooks.display-options.xml │ │ └── container.xml └── html │ └── dist │ ├── html-Y223O6DN.js │ ├── html-elixir-KV3YOVJ3.css │ ├── html-erlang-DQDXQC7W.css │ ├── inline_html-4XT25SPW.js │ ├── lato-all-400-normal-MNITWADU.woff │ ├── lato-all-700-normal-XMT5XFBS.woff │ ├── lato-latin-400-normal-W7754I4D.woff2 │ ├── lato-latin-700-normal-2XVSBPG4.woff2 │ ├── lato-latin-ext-400-normal-N27NCBWW.woff2 │ ├── lato-latin-ext-700-normal-Q2L5DVMW.woff2 │ └── remixicon-QPNJX265.woff2 ├── lib ├── ex_doc.ex ├── ex_doc │ ├── application.ex │ ├── autolink.ex │ ├── cli.ex │ ├── config.ex │ ├── doc_ast.ex │ ├── formatter │ │ ├── epub.ex │ │ ├── epub │ │ │ ├── assets.ex │ │ │ ├── templates.ex │ │ │ └── templates │ │ │ │ ├── content_template.eex │ │ │ │ ├── extra_template.eex │ │ │ │ ├── head_template.eex │ │ │ │ ├── media-types.txt │ │ │ │ ├── module_template.eex │ │ │ │ ├── nav_grouped_item_template.eex │ │ │ │ ├── nav_template.eex │ │ │ │ ├── title_template.eex │ │ │ │ └── toc_item_template.eex │ │ ├── html.ex │ │ └── html │ │ │ ├── assets.ex │ │ │ ├── search_data.ex │ │ │ ├── templates.ex │ │ │ └── templates │ │ │ ├── api_reference_entry_template.eex │ │ │ ├── api_reference_template.eex │ │ │ ├── detail_template.eex │ │ │ ├── extra_template.eex │ │ │ ├── footer_template.eex │ │ │ ├── head_template.eex │ │ │ ├── module_template.eex │ │ │ ├── not_found_template.eex │ │ │ ├── redirect_template.eex │ │ │ ├── search_template.eex │ │ │ ├── sidebar_template.eex │ │ │ └── summary_template.eex │ ├── group_matcher.ex │ ├── language.ex │ ├── language │ │ ├── elixir.ex │ │ ├── erlang.ex │ │ └── source.ex │ ├── markdown.ex │ ├── markdown │ │ └── earmark.ex │ ├── nodes.ex │ ├── refs.ex │ ├── retriever.ex │ ├── shell_lexer.ex │ └── utils.ex └── mix │ └── tasks │ └── docs.ex ├── mix.exs ├── mix.lock └── test ├── ex_doc ├── cli_test.exs ├── config_test.exs ├── doc_ast_test.exs ├── formatter │ ├── epub │ │ └── templates_test.exs │ ├── epub_test.exs │ ├── html │ │ ├── erlang_test.exs │ │ ├── search_data_test.exs │ │ └── templates_test.exs │ └── html_test.exs ├── group_matcher_test.exs ├── language │ ├── elixir_test.exs │ └── erlang_test.exs ├── markdown │ └── earmark_test.exs ├── refs_test.exs ├── retriever │ ├── elixir_test.exs │ └── erlang_test.exs ├── retriever_test.exs └── utils_test.exs ├── ex_doc_test.exs ├── examples └── admonition.md ├── fixtures ├── ExtraPage.md ├── ExtraPageWithSettextHeader.md ├── LICENSE ├── LivebookFile.livemd ├── PlainText.txt ├── PlainTextFiles.md ├── README.md ├── behaviour.ex ├── callbacks_no_docs.ex ├── cheatsheets.cheatmd ├── common_nesting_prefix.ex ├── compiled_with_docs.ex ├── compiled_without_docs.ex ├── duplicate_headings.ex ├── elixir.png ├── overlapping_defaults.ex ├── protocol.ex ├── random_error.ex ├── task_with_docs.ex ├── types_and_specs.ex ├── umbrella │ ├── apps │ │ ├── bar │ │ │ ├── lib │ │ │ │ └── bar.ex │ │ │ └── mix.exs │ │ └── foo │ │ │ ├── lib │ │ │ └── foo.ex │ │ │ └── mix.exs │ └── mix.exs └── warnings.ex ├── mix └── tasks │ └── docs_test.exs ├── prerelease.sh ├── support └── with_without_module_doc.ex └── test_helper.exs /.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | inputs: ["{mix,.formatter}.exs", "{bin,lib,test}/**/*.{ex,exs}"] 3 | ] 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /formatters/epub/dist/* -diff 2 | /formatters/html/dist/* -diff 3 | -------------------------------------------------------------------------------- /.github/workflows/artifacts.yml: -------------------------------------------------------------------------------- 1 | name: Artifacts 2 | on: 3 | workflow_run: 4 | workflows: ["CI"] 5 | types: 6 | - completed 7 | branches-ignore: 8 | - main 9 | 10 | permissions: 11 | pull-requests: write 12 | 13 | jobs: 14 | notify: 15 | runs-on: ubuntu-latest 16 | if: ${{ github.event.workflow_run.event == 'pull_request' }} 17 | steps: 18 | - uses: actions/github-script@v7 19 | with: 20 | script: | 21 | const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ 22 | owner: context.repo.owner, 23 | repo: context.repo.repo, 24 | run_id: context.payload.workflow_run.id 25 | }); 26 | 27 | if (artifacts.data.total_count !== 1) { 28 | throw new Error('Expected one artifact') 29 | } 30 | 31 | const artifact = artifacts.data.artifacts[0]; 32 | const download = await github.rest.actions.downloadArtifact({ 33 | owner: context.repo.owner, 34 | repo: context.repo.repo, 35 | artifact_id: artifact.id, 36 | archive_format: 'zip' 37 | }); 38 | 39 | const fs = require('fs'); 40 | fs.writeFileSync('artifact.zip', Buffer.from(download.data)); 41 | require('child_process').execSync('unzip artifact.zip'); 42 | const ghInfo = JSON.parse(fs.readFileSync('gh.json', 'utf8')); 43 | const pullNumber = ghInfo.number; 44 | 45 | const artifactUrl = `${context.payload.workflow_run.html_url}/artifacts/${artifact.id}`; 46 | const commentBody = `\n📦 Docs artifacts are ready: ${artifactUrl}`; 47 | 48 | const comments = await github.rest.issues.listComments({ 49 | owner: context.repo.owner, 50 | repo: context.repo.repo, 51 | issue_number: pullNumber 52 | }); 53 | 54 | const botComment = comments.data.find(comment => 55 | comment.user.type === 'Bot' && 56 | comment.body.includes('') 57 | ); 58 | 59 | if (botComment) { 60 | await github.rest.issues.updateComment({ 61 | owner: context.repo.owner, 62 | repo: context.repo.repo, 63 | comment_id: botComment.id, 64 | body: commentBody 65 | }); 66 | } else { 67 | await github.rest.issues.createComment({ 68 | owner: context.repo.owner, 69 | repo: context.repo.repo, 70 | issue_number: pullNumber, 71 | body: commentBody 72 | }); 73 | } 74 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [pull_request, push] 4 | 5 | env: 6 | MIX_ENV: test 7 | 8 | jobs: 9 | assets: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | include: 14 | - elixir: "1.17" 15 | otp: "27" 16 | node: 18.x 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | # Setup Node 21 | - uses: actions/setup-node@v3 22 | with: 23 | node-version: ${{matrix.node}} 24 | 25 | - name: Cache npm dependencies 26 | uses: actions/cache@v3 27 | with: 28 | path: ~/.npm 29 | key: ${{ runner.os }}-node-${{ hashFiles('asssets/package-lock.json') }} 30 | 31 | - run: npm ci --prefix assets 32 | - run: npm run build --prefix assets 33 | 34 | # Setup Elixir 35 | - uses: erlef/setup-beam@v1 36 | with: 37 | otp-version: ${{ matrix.otp }} 38 | elixir-version: ${{ matrix.elixir }} 39 | 40 | # Generate and upload artifacts 41 | - name: Generate docs 42 | run: test/prerelease.sh 43 | 44 | - name: Attach docs metadata 45 | run: | 46 | echo "{\"number\":${{ github.event.number }}}" > test/tmp/contents/doc/gh.json 47 | 48 | - name: Upload docs 49 | uses: actions/upload-artifact@v4 50 | id: docs-upload 51 | with: 52 | name: docs 53 | path: test/tmp/contents/doc/ 54 | 55 | # Test JS 56 | - run: npm run lint --prefix assets 57 | - name: npm run test --prefix assets 58 | run: | 59 | sudo apt-get install xvfb 60 | xvfb-run --auto-servernum npm run test --prefix assets 61 | env: 62 | CI: true 63 | 64 | # Push updated assets if all good 65 | - name: Push updated assets 66 | if: github.ref_name == 'main' 67 | uses: stefanzweifel/git-auto-commit-action@v4 68 | with: 69 | commit_message: Update assets 70 | file_pattern: formatters 71 | 72 | elixir: 73 | runs-on: ubuntu-latest 74 | strategy: 75 | fail-fast: false 76 | matrix: 77 | include: 78 | # Test very old Elixir and Erlang 79 | - elixir: "1.15" 80 | otp: "25" 81 | # Test Erlang without -doc attribute support 82 | - elixir: "1.16" 83 | otp: "26" 84 | # Test with initial Erlang doc attribute support 85 | - elixir: "1.17" 86 | otp: "27" 87 | - elixir: "1.18" 88 | otp: "27" 89 | lint: true 90 | steps: 91 | - uses: actions/checkout@v3 92 | 93 | - uses: erlef/setup-beam@v1 94 | with: 95 | otp-version: ${{ matrix.otp }} 96 | elixir-version: ${{ matrix.elixir }} 97 | 98 | - run: mix deps.get 99 | 100 | - run: mix compile --warnings-as-errors 101 | if: ${{ matrix.lint }} 102 | 103 | - run: mix test 104 | 105 | - run: mix deps.unlock --check-unused 106 | if: ${{ matrix.lint }} 107 | 108 | - run: mix format --check-formatted 109 | if: ${{ matrix.lint }} 110 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | create_release: 13 | continue-on-error: true 14 | runs-on: ubuntu-22.04 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | steps: 18 | - name: Create release 19 | run: | 20 | echo "Creating release..." 21 | gh release create \ 22 | --repo ${{ github.repository }} \ 23 | --title ${{ github.ref_name }} \ 24 | ${{ github.ref_name }} 25 | 26 | release_pre_built: 27 | needs: create_release 28 | strategy: 29 | fail-fast: true 30 | matrix: 31 | include: 32 | - otp: 25 33 | otp_version: "25.3.2.12" 34 | elixir_version: "1.16.2" 35 | - otp: 26 36 | otp_version: "26.2.5" 37 | elixir_version: "1.16.2" 38 | - otp: 27 39 | otp_version: "27.2" 40 | elixir_version: "1.18.2" 41 | 42 | runs-on: ubuntu-22.04 43 | steps: 44 | - uses: actions/checkout@v4 45 | with: 46 | fetch-depth: 50 47 | - uses: ./.github/workflows/release_pre_built 48 | with: 49 | otp_version: ${{ matrix.otp_version }} 50 | otp: ${{ matrix.otp }} 51 | elixir_version: ${{ matrix.elixir_version }} 52 | 53 | - name: Upload Pre-built 54 | env: 55 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | run: | 57 | gh release upload --clobber "${{ github.ref_name }}" \ 58 | ex_doc_otp_${{ matrix.otp }} \ 59 | ex-doc-otp-${{ matrix.otp }}.sha{1,256}sum \ 60 | -------------------------------------------------------------------------------- /.github/workflows/release_pre_built/action.yml: -------------------------------------------------------------------------------- 1 | name: "Release pre built" 2 | description: "Builds ex_doc scripts" 3 | inputs: 4 | otp: 5 | description: "The major OTP version" 6 | otp_version: 7 | description: "The exact OTP version (major.minor[.patch])" 8 | elixir_version: 9 | description: "The exact Elixir version (major.minor[.patch])" 10 | runs: 11 | using: "composite" 12 | steps: 13 | - uses: erlef/setup-beam@v1.16.0 14 | with: 15 | otp-version: ${{ inputs.otp_version }} 16 | elixir-version: ${{ inputs.elixir_version }} 17 | - name: Build ex_doc 18 | shell: bash 19 | run: | 20 | mix deps.get 21 | mix escript.build 22 | mv ex_doc ex_doc_otp_${{ inputs.otp }} 23 | shasum -a 1 ex_doc_otp_${{ inputs.otp }} > ex-doc-otp-${{ inputs.otp }}.sha1sum 24 | shasum -a 256 ex_doc_otp_${{ inputs.otp }} > ex-doc-otp-${{ inputs.otp }}.sha256sum 25 | echo "$PWD/bin" >> $GITHUB_PATH 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | ex_doc-*.tar 24 | 25 | node_modules/ 26 | /test/fixtures/umbrella/_build/ 27 | /test/tmp/ 28 | /tmp/ 29 | /npm-debug.log 30 | 31 | # Ignore artifacts from non-production builds 32 | formatters/epub/dist/epub.js 33 | formatters/html/dist/html.js 34 | 35 | # Ignore escript when built 36 | /ex_doc 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012 Plataformatec 2 | Copyright 2021 The Elixir Team 3 | https://github.com/elixir-lang/ex_doc/ 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ========================================================================== 18 | 19 | Dependencies are externally maintained and have their own licenses; we 20 | recommend you read them as their terms may differ from the terms above. 21 | -------------------------------------------------------------------------------- /assets/.eslintignore: -------------------------------------------------------------------------------- 1 | # Ignore build artifacts 2 | formatters/* 3 | doc/* 4 | test/* 5 | 6 | # Ignore JavaScript files in dependencies 7 | deps/* 8 | -------------------------------------------------------------------------------- /assets/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true 5 | }, 6 | extends: 'standard', 7 | overrides: [ 8 | ], 9 | parserOptions: { 10 | ecmaVersion: 'latest', 11 | sourceType: 'module' 12 | }, 13 | rules: { 14 | 'no-new': 0, 15 | 'no-path-concat': 0, 16 | 'no-throw-literal': 0, 17 | 'no-useless-escape': 0, 18 | 'object-curly-spacing': 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | # Assets 2 | 3 | All asset sources for `ExDoc` live in this directory. The built, ready-to-use versions are found in `formatters/{html,epub}/dist`. 4 | 5 | To work on these assets you need to have [Node.js] and [npm] installed. (npm is usually installed along with Node.js.) The build process is currently tested in Node 18 LTS. 6 | 7 | Assets are built with [esbuild], which, along with the JavaScript linter and test-runner, is set as a dependency in the assets `package.json` and installed via [npm]: 8 | 9 | ```bash 10 | $ npm install --prefix assets 11 | ``` 12 | 13 | ## `npm run` scripts 14 | 15 | The following scripts are available from the root folder of the project. 16 | 17 | ### `build` 18 | 19 | ```bash 20 | $ npm run --prefix assets build 21 | ``` 22 | 23 | Build a complete production bundle, including JavaScript and CSS. 24 | 25 | (Note that this is not required to be manually run when generating docs: if you run `mix build` at the `ExDoc` root after changing your assets, the assets will be recompiled and fresh docs with your changes will be generated.) 26 | 27 | ### `build:watch` 28 | 29 | ```bash 30 | $ npm run --prefix assets build:watch 31 | ``` 32 | 33 | Run the `build` command with watch mode set, providing for automatic assets rebuilds on every asset file change. 34 | 35 | Additionally, in watch mode, the docs are built after every asset rebuild, meaning the only action required to check results after changing asset sources is to refresh/reload the browser or EPUB reader. 36 | 37 | ### `lint` 38 | 39 | ```bash 40 | $ npm run --prefix assets lint 41 | ``` 42 | 43 | Lint all JavaScript files using [ESLint]. 44 | 45 | ### `lint:fix` 46 | 47 | ```bash 48 | $ npm run --prefix assets lint:fix 49 | ``` 50 | 51 | Lint and automatically fix all JavaScript files using [ESLint]. 52 | 53 | ### `test` 54 | 55 | ```bash 56 | $ npm run --prefix assets test 57 | ``` 58 | 59 | Run all the available JavaScript tests using [Karma]. 60 | 61 | 62 | [esbuild]: https://esbuild.github.io 63 | [Node.js]: https://nodejs.org/ 64 | [npm]: https://www.npmjs.com/ 65 | [ESLint]: https://eslint.org/ 66 | [Karma]: https://karma-runner.github.io/ 67 | -------------------------------------------------------------------------------- /assets/build/build.js: -------------------------------------------------------------------------------- 1 | const path = require('node:path') 2 | const process = require('node:process') 3 | const cp = require('node:child_process') 4 | const esbuild = require('esbuild') 5 | const fsExtra = require('fs-extra') 6 | const fs = require('node:fs/promises') 7 | const handlebars = require('handlebars') 8 | const util = require('node:util') 9 | 10 | const exec = util.promisify(cp.exec) 11 | 12 | const watchMode = Boolean(process.env.npm_config_watch) 13 | 14 | /** @type {import('esbuild').BuildOptions[]} */ 15 | const formatters = [ 16 | { 17 | formatter: 'epub', 18 | outdir: path.resolve('../formatters/epub/dist'), 19 | entryPoints: [ 20 | 'js/entry/epub.js', 21 | 'css/entry/epub-elixir.css', 22 | 'css/entry/epub-erlang.css' 23 | ] 24 | }, 25 | { 26 | formatter: 'html', 27 | outdir: path.resolve('../formatters/html/dist'), 28 | entryPoints: [ 29 | 'js/entry/inline_html.js', 30 | 'js/entry/html.js', 31 | 'css/entry/html-elixir.css', 32 | 'css/entry/html-erlang.css' 33 | ], 34 | loader: { 35 | '.woff2': 'file', 36 | // TODO: Remove when @fontsource/* removes legacy .woff 37 | '.woff': 'file' 38 | } 39 | } 40 | ] 41 | 42 | Promise.all(formatters.map(async ({formatter, ...options}) => { 43 | // Clean outdir. 44 | await fsExtra.emptyDir(options.outdir) 45 | 46 | await esbuild.build({ 47 | entryNames: watchMode ? '[name]-dev' : '[name]-[hash]', 48 | bundle: true, 49 | minify: !watchMode, 50 | logLevel: watchMode ? 'warning' : 'info', 51 | watch: watchMode, 52 | ...options, 53 | plugins: [{ 54 | name: 'ex_doc', 55 | setup (build) { 56 | // Pre-compile handlebars templates. 57 | build.onLoad({ 58 | filter: /\.handlebars$/ 59 | }, async ({ path: filename }) => { 60 | try { 61 | const source = await fs.readFile(filename, 'utf-8') 62 | const template = handlebars.precompile(source) 63 | const contents = [ 64 | "import * as Handlebars from 'handlebars/runtime'", 65 | "import '../helpers'", 66 | `export default Handlebars.template(${template})` 67 | ].join('\n') 68 | return { contents } 69 | } catch (error) { 70 | return { errors: [{ text: error.message }] } 71 | } 72 | }) 73 | 74 | // Load html templates. 75 | build.onLoad({ 76 | filter: /\.html$/ 77 | }, async ({ path: filename }) => { 78 | try { 79 | const source = await fs.readFile(filename, 'utf-8') 80 | // Remove newlines and leading whitespace. 81 | // Shouldn't have any effect on content. 82 | const compressed = source.replace(/\n\s*/g, '') 83 | const contents = `export default ${JSON.stringify(compressed)}` 84 | return { contents } 85 | } catch (error) { 86 | return { errors: [{ text: error.message }] } 87 | } 88 | }) 89 | 90 | // Generate docs with new assets (watch mode only). 91 | if (watchMode) { 92 | build.onEnd(async result => { 93 | if (result.errors.length) return 94 | console.log(`${formatter} assets built`) 95 | await exec(`mix docs --formatter ${formatter}`, {cwd: '../'}) 96 | console.log(`${formatter} docs built`) 97 | }) 98 | } 99 | } 100 | }] 101 | }) 102 | })).catch((error) => { 103 | console.error(error) 104 | process.exit(1) 105 | }) 106 | -------------------------------------------------------------------------------- /assets/css/_epub.css: -------------------------------------------------------------------------------- 1 | @import 'custom-props/common.css'; 2 | @import 'custom-props/theme-light.css'; 3 | 4 | @import 'content/epub-admonition.css'; 5 | @import 'content/code.css'; 6 | @import 'content/functions.css'; 7 | @import 'screen-reader.css'; 8 | @import 'makeup.css'; 9 | 10 | body { 11 | display: block; 12 | font-size: 1em; 13 | line-height: 1.2; 14 | padding-left: 0; 15 | padding-right: 0; 16 | margin: 0 5pt; 17 | } 18 | 19 | nav > ol { 20 | list-style-type: square; 21 | } 22 | 23 | nav > ol ol { 24 | list-style-type: disc; 25 | } 26 | 27 | .title-container { 28 | text-align: center; 29 | } 30 | 31 | img[src*="#gh-dark-mode-only"] { 32 | display: none; 33 | } 34 | -------------------------------------------------------------------------------- /assets/css/_html.css: -------------------------------------------------------------------------------- 1 | @import "@fontsource/lato/400.css"; 2 | @import "@fontsource/lato/700.css"; 3 | 4 | @import "custom-props/common.css"; 5 | @import "custom-props/theme-light.css"; 6 | @import "custom-props/theme-dark.css"; 7 | 8 | @import "modern-normalize/modern-normalize.css"; 9 | 10 | @import "icons.css"; 11 | @import "layout.css"; 12 | @import "sidebar.css"; 13 | @import "search-bar.css"; 14 | @import "focus.css"; 15 | @import "content/general.css"; 16 | @import "content/admonition.css"; 17 | @import "content/summary.css"; 18 | @import "content/code.css"; 19 | @import "content/functions.css"; 20 | @import "content/footer.css"; 21 | @import "content/bottom-actions.css"; 22 | @import "content/cheatsheet.css"; 23 | @import "search.css"; 24 | @import "modal.css"; 25 | @import "keyboard-shortcuts.css"; 26 | @import "quick-switch.css"; 27 | @import "autocomplete.css"; 28 | @import "tooltips.css"; 29 | @import "copy-button.css"; 30 | @import "settings.css"; 31 | @import "toast.css"; 32 | @import "screen-reader.css"; 33 | @import "print.css"; 34 | @import "print-cheatsheet.css"; 35 | @import "makeup.css"; 36 | @import "tabset.css"; 37 | @import "preview.css"; 38 | 39 | body:not(.dark) .content-inner img[src*="#gh-dark-mode-only"], 40 | body.dark .content-inner img[src*="#gh-light-mode-only"] { 41 | display: none; 42 | } 43 | -------------------------------------------------------------------------------- /assets/css/content/admonition.css: -------------------------------------------------------------------------------- 1 | .content-inner section.admonition { 2 | border-radius: var(--borderRadius-base); 3 | border-left: 0; 4 | } 5 | 6 | .content-inner section.admonition.warning { 7 | background-color: var(--warningBackground); 8 | } 9 | 10 | .content-inner section.admonition.error { 11 | background-color: var(--errorBackground); 12 | } 13 | 14 | .content-inner section.admonition.info { 15 | background-color: var(--infoBackground); 16 | } 17 | 18 | .content-inner section.admonition.neutral { 19 | background-color: var(--neutralBackground); 20 | } 21 | 22 | .content-inner section.admonition.tip { 23 | background-color: var(--tipBackground); 24 | } 25 | 26 | .content-inner section.admonition > .admonition-title { 27 | color: var(--contrast); 28 | margin: 0 -1.2rem; 29 | padding: .7rem 1.2rem .7rem 3.3rem; 30 | font-weight: 700; 31 | font-style: normal; 32 | } 33 | .content-inner section.admonition > .admonition-title::before { 34 | color: var(--contrast); 35 | position: absolute; 36 | left: 1rem; 37 | font-size: 1.8rem; 38 | font-family: 'remixicon'; 39 | font-style: normal; 40 | -webkit-font-smoothing: antialiased; 41 | -moz-osx-font-smoothing: grayscale; 42 | } 43 | 44 | .content-inner section.admonition > .admonition-title.warning { 45 | background-color: var(--warningHeadingBackground); 46 | color: var(--warningHeading); 47 | } 48 | .content-inner section.admonition > .admonition-title.warning::before { 49 | content: var(--icon-error-warning); 50 | color: var(--warningHeading); 51 | } 52 | 53 | .content-inner section.admonition > .admonition-title.error { 54 | background-color: var(--errorHeadingBackground); 55 | color: var(--errorHeading); 56 | } 57 | .content-inner section.admonition > .admonition-title.error::before { 58 | content: var(--icon-error-warning); 59 | color: var(--errorHeading); 60 | } 61 | 62 | .content-inner section.admonition > .admonition-title.info { 63 | background-color: var(--infoHeadingBackground); 64 | color: var(--infoHeading); 65 | } 66 | .content-inner section.admonition > .admonition-title.info::before { 67 | content: var(--icon-information); 68 | color: var(--infoHeading); 69 | } 70 | 71 | .content-inner section.admonition > .admonition-title.neutral { 72 | background-color: var(--neutralHeadingBackground); 73 | color: var(--neutralHeading); 74 | } 75 | .content-inner section.admonition > .admonition-title.neutral::before { 76 | content: var(--icon-double-quotes-l); 77 | color: var(--neutralHeading); 78 | } 79 | 80 | .content-inner section.admonition > .admonition-title.tip { 81 | background-color: var(--tipHeadingBackground); 82 | color: var(--tipHeading); 83 | } 84 | .content-inner section.admonition > .admonition-title.tip::before { 85 | content: var(--icon-information); 86 | color: var(--tipHeading); 87 | } 88 | 89 | .content-inner section.admonition > .admonition-title code { 90 | margin: 0 0.5ch; 91 | } 92 | 93 | .content-inner section.admonition code { 94 | background-color: var(--admInlineCodeBackground); 95 | border: 1px solid var(--admInlineCodeBorder); 96 | color: var(--admInlineCodeColor); 97 | } 98 | 99 | .content-inner section.admonition pre code { 100 | background-color: var(--admCodeBackground); 101 | border: 1px solid var(--admCodeBorder); 102 | color: var(--admCodeColor); 103 | } 104 | 105 | .content-inner section.admonition > .admonition-title :is(a, a:visited) { 106 | color: inherit; 107 | text-decoration-color: currentColor; 108 | } 109 | 110 | @media screen and (max-width: 768px) { 111 | .content-inner section.admonition { 112 | margin-left: calc(-1 * var(--content-gutter)); 113 | margin-right: calc(-1 * var(--content-gutter)); 114 | padding-left: var(--content-gutter); 115 | padding-right: var(--content-gutter); 116 | border-radius: 0; 117 | } 118 | 119 | .content-inner section.admonition > .admonition-title { 120 | margin: 0 calc(-1 * var(--content-gutter)); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /assets/css/content/bottom-actions.css: -------------------------------------------------------------------------------- 1 | .content-inner .bottom-actions { 2 | display: flex; 3 | justify-content: space-between; 4 | margin-top: 4em; 5 | gap: 12px; 6 | } 7 | 8 | .bottom-actions-item { 9 | flex: 1 1 0%; 10 | } 11 | 12 | .content-inner .bottom-actions .bottom-actions-button { 13 | display: flex; 14 | text-decoration: none; 15 | flex-direction: column; 16 | border-radius: var(--borderRadius-sm); 17 | border: 1px solid var(--bottomActionsBtnBorder); 18 | padding: 12px 16px; 19 | min-width: 150px; 20 | transition: var(--transition-all); 21 | } 22 | 23 | .content-inner .bottom-actions .bottom-actions-button:hover { 24 | border-color: var(--mainLight); 25 | } 26 | .content-inner .bottom-actions .bottom-actions-button .subheader { 27 | font-size: .8em; 28 | color: var(--textHeaders); 29 | white-space: nowrap; 30 | } 31 | 32 | .content-inner .bottom-actions .bottom-actions-button .title { 33 | color: var(--bottomActionsBtnTitle); 34 | } 35 | 36 | .content-inner .bottom-actions .bottom-actions-button[rel="prev"] { 37 | text-align: start; 38 | } 39 | 40 | .content-inner .bottom-actions .bottom-actions-button[rel="next"] { 41 | text-align: end; 42 | } 43 | 44 | @media screen and (max-width: 768px) { 45 | .content-inner .bottom-actions { 46 | flex-direction: column-reverse; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /assets/css/content/code.css: -------------------------------------------------------------------------------- 1 | /* The Consolas font on Windows is too small compared to other ones */ 2 | @font-face { 3 | font-family: "Consolas"; 4 | src: local("Consolas"); 5 | size-adjust: 110%; 6 | } 7 | 8 | .content-inner.content-inner :is(a:has(code, img), pre a) { 9 | color: var(--link-color); 10 | text-shadow: none; 11 | text-decoration: none; 12 | background-image: none; 13 | } 14 | 15 | .content-inner.content-inner :is(a:has(code, img), pre a):is(:visited, :active, :focus, :hover) { 16 | color: var(--link-visited-color); 17 | } 18 | 19 | .content-inner code { 20 | background-color: var(--codeBackground); 21 | vertical-align: baseline; 22 | border-radius: var(--borderRadius-sm); 23 | padding: .1em .2em; 24 | border: 1px solid var(--codeBorder); 25 | text-transform: none; 26 | } 27 | 28 | .content-inner code.inline { 29 | border-radius: var(--borderRadius-sm); 30 | word-wrap: break-word; 31 | } 32 | 33 | .content-inner pre { 34 | margin: var(--baseLineHeight) 0; 35 | } 36 | 37 | .content-inner pre code { 38 | display: block; 39 | overflow-x: auto; 40 | white-space: inherit; 41 | padding: 1em; 42 | scrollbar-width: thin; 43 | } 44 | 45 | .content-inner pre code.output { 46 | margin: 0 12px; 47 | max-height: 400px; 48 | overflow: auto; 49 | } 50 | 51 | .content-inner pre code.output + .copy-button { 52 | margin-right: 12px; 53 | } 54 | 55 | .content-inner pre code.output:before { 56 | content: "Output"; 57 | display: block; 58 | position: absolute; 59 | top: -16px; 60 | left: 12px; 61 | padding: 2px 4px; 62 | font-size: var(--text-xs); 63 | font-family: var(--monoFontFamily); 64 | line-height: 1; 65 | color: var(--textHeaders); 66 | background-color: var(--codeBackground); 67 | border: 1px solid var(--codeBorder); 68 | border-bottom: 0; 69 | border-radius: 2px; 70 | } 71 | 72 | @media screen and (max-width: 768px) { 73 | .content-inner > pre:has(code), 74 | .content-inner section > pre:has(code) { 75 | margin-left: calc(-1 * var(--content-gutter)); 76 | margin-right: calc(-1 * var(--content-gutter)); 77 | } 78 | 79 | .content-inner > pre code, 80 | .content-inner section > pre code { 81 | padding-left: var(--content-gutter); 82 | padding-right: var(--content-gutter); 83 | border-radius: 0; 84 | border-left-width: 0; 85 | border-right-width: 0; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /assets/css/content/epub-admonition.css: -------------------------------------------------------------------------------- 1 | .content-inner section.admonition { 2 | border-left: solid 4px; 3 | color: var(--black); 4 | font-size: 0.9em; 5 | line-height: 1.4em; 6 | margin-bottom: 1.5em; 7 | margin-left: 5px; 8 | padding: 7px 15px; 9 | page-break-inside: avoid; 10 | } 11 | 12 | .content-inner section.admonition.warning { 13 | background-color: var(--warningBackground); 14 | border-left-color: var(--warningHeadingBackground); 15 | } 16 | 17 | .content-inner section.admonition.error { 18 | background-color: var(--errorBackground); 19 | border-left-color: var(--errorHeadingBackground); 20 | } 21 | 22 | .content-inner section.admonition.info { 23 | background-color: var(--infoBackground); 24 | border-left-color: var(--infoHeadingBackground); 25 | } 26 | 27 | .content-inner section.admonition.neutral { 28 | background-color: var(--neutralBackground); 29 | border-left-color: var(--neutralHeadingBackground); 30 | } 31 | 32 | .content-inner section.admonition.tip { 33 | background-color: var(--tipBackground); 34 | border-left-color: var(--tipHeadingBackground); 35 | } 36 | 37 | .content-inner section.admonition > .admonition-title { 38 | font-weight: bold; 39 | margin: 0px 10px 5px 0px; 40 | font-style: normal; 41 | font-weight: 700; 42 | } 43 | 44 | .content-inner section.admonition > .admonition-title.warning { 45 | color: var(--warningHeadingBackground); 46 | } 47 | .content-inner section.admonition > .admonition-title.error { 48 | color: var(--errorHeadingBackground); 49 | } 50 | .content-inner section.admonition > .admonition-title.info { 51 | color: var(--infoHeadingBackground); 52 | } 53 | .content-inner section.admonition > .admonition-title.neutral { 54 | color: var(--neutralHeadingBackground); 55 | } 56 | .content-inner section.admonition > .admonition-title.tip { 57 | color: var(--tipHeadingBackground); 58 | } 59 | 60 | .content-inner section.admonition > .admonition-title code { 61 | margin: 0 0.5ch; 62 | } 63 | 64 | .content-inner section.admonition code { 65 | background-color: var(--admInlineCodeBackground); 66 | border: 1px solid var(--admInlineCodeBorder); 67 | color: var(--admInlineCodeColor); 68 | } 69 | 70 | .content-inner section.admonition pre code { 71 | background-color: var(--admCodeBackground); 72 | border: 1px solid var(--admCodeBorder); 73 | color: var(--admCodeColor); 74 | } 75 | -------------------------------------------------------------------------------- /assets/css/content/footer.css: -------------------------------------------------------------------------------- 1 | .content-inner .footer { 2 | margin: 4em auto 1em; 3 | text-align: center; 4 | font-size: var(--text-sm); 5 | } 6 | 7 | .content-inner .footer .line { 8 | display: inline-block; 9 | } 10 | 11 | .content-inner .footer .footer-button { 12 | background-color: transparent; 13 | border: 0; 14 | cursor: pointer; 15 | padding: 0 4px; 16 | } 17 | 18 | .content-inner .footer .footer-hex-package { 19 | margin-right: 4px; 20 | } 21 | -------------------------------------------------------------------------------- /assets/css/content/functions.css: -------------------------------------------------------------------------------- 1 | @keyframes blink-background { 2 | 0%, 100% { 3 | background-color: var(--textDetailBackground); 4 | } 5 | 6 | 50% { 7 | background-color: var(--blink); 8 | } 9 | } 10 | 11 | .content-inner .detail:target .detail-header { 12 | animation-duration: 0.55s; 13 | animation-name: blink-background; 14 | animation-iteration-count: 1; 15 | animation-timing-function: ease-in-out; 16 | } 17 | 18 | .content-inner .detail-header { 19 | margin: 1em 0; 20 | padding: 0.5em 0.85em 0.5em 1em; 21 | background-color: var(--textDetailBackground); 22 | border-left: 3px solid var(--textDetailAccent); 23 | font-size: 1em; 24 | font-family: var(--monoFontFamily); 25 | position: relative; 26 | } 27 | 28 | .content-inner .detail-header .signature { 29 | font-family: var(--monoFontFamily); 30 | font-size: 13px; 31 | font-weight: 700; 32 | line-height: 2em; 33 | } 34 | 35 | .content-inner .detail-header:hover a.detail-link, 36 | .content-inner .detail-header a.detail-link:focus { 37 | opacity: 1; 38 | text-decoration: none; 39 | } 40 | 41 | .content-inner .detail-header a.detail-link { 42 | transition: var(--transition-opacity); 43 | position: absolute; 44 | top: 0; 45 | left: 0; 46 | display: block; 47 | opacity: 0; 48 | padding: 0.6em; 49 | line-height: 1.5em; 50 | margin-left: -2.5em; 51 | text-decoration: none; 52 | border: none; 53 | } 54 | 55 | @media screen and (max-width: 768px) { 56 | .content-inner .detail-header a.detail-link { 57 | margin-left: -30px; 58 | } 59 | } 60 | 61 | .content-inner .specs pre { 62 | font-family: var(--monoFontFamily); 63 | font-size: var(--text-xs); 64 | font-style: normal; 65 | line-height: 24px; 66 | white-space: pre-wrap; 67 | margin: 0; 68 | padding: 0; 69 | } 70 | 71 | .content-inner .specs .attribute { 72 | color: var(--fnSpecAttr); 73 | } 74 | 75 | .content-inner .docstring { 76 | margin: 1.2em 0 3em 1.2em; 77 | } 78 | 79 | @media screen and (max-width: 768px) { 80 | .content-inner .docstring { 81 | margin-left: 0; 82 | } 83 | } 84 | 85 | .content-inner .docstring:is(h2, h3, h4, h5) { 86 | font-weight: 700; 87 | } 88 | 89 | .content-inner .docstring h2 { 90 | font-size: 1.1em; 91 | } 92 | 93 | .content-inner .docstring h3 { 94 | font-size: 1em; 95 | } 96 | 97 | .content-inner .docstring h4 { 98 | font-size: 0.95em; 99 | } 100 | 101 | .content-inner .docstring h5 { 102 | font-size: 0.9em; 103 | } 104 | 105 | .content-inner div.deprecated { 106 | display: block; 107 | padding: 1em; 108 | background-color: var(--fnDeprecated); 109 | border-radius: var(--borderRadius-sm); 110 | margin: var(--baseLineHeight) 0; 111 | } 112 | -------------------------------------------------------------------------------- /assets/css/content/summary.css: -------------------------------------------------------------------------------- 1 | .content-inner .summary h2 a { 2 | text-decoration: none; 3 | border: none; 4 | color: var(--textHeaders) !important; 5 | } 6 | 7 | .content-inner .summary span.deprecated { 8 | color: var(--darkDeprecated); 9 | font-weight: normal; 10 | } 11 | 12 | .content-inner .summary .summary-row .summary-signature { 13 | font-family: var(--monoFontFamily); 14 | font-size: 13px; 15 | font-weight: 700; 16 | } 17 | 18 | .content-inner .summary .summary-row .summary-signature a { 19 | text-decoration: none; 20 | border: none; 21 | } 22 | 23 | .content-inner .summary .summary-row .summary-synopsis { 24 | padding: 0 1.2em; 25 | margin: 0 0 0.5em; 26 | } 27 | 28 | .content-inner .summary .summary-row .summary-synopsis p { 29 | margin: 0; 30 | padding: 0; 31 | } 32 | -------------------------------------------------------------------------------- /assets/css/copy-button.css: -------------------------------------------------------------------------------- 1 | pre { 2 | position: relative; 3 | } 4 | 5 | pre:hover .copy-button, 6 | pre .copy-button:focus { 7 | opacity: 1; 8 | } 9 | 10 | .copy-button { 11 | display: flex; 12 | opacity: 0; 13 | position: absolute; 14 | top: 7px; 15 | right: 8px; 16 | padding: 8px; 17 | background-color: transparent; 18 | backdrop-filter: blur(8px); 19 | border-radius: var(--borderRadius-sm); 20 | border: 1px solid var(--codeBorder); 21 | cursor: pointer; 22 | transition: var(--transition-all); 23 | font-size: var(--text-sm); 24 | line-height: 24px; 25 | color: currentColor; 26 | 27 | & svg[aria-live="polite"] { 28 | display: none; 29 | } 30 | } 31 | 32 | .copy-button svg { 33 | opacity: 0.5; 34 | transition: var(--transition-all); 35 | } 36 | 37 | pre .copy-button:hover svg, 38 | pre .copy-button:focus-visible svg { 39 | opacity: 1; 40 | } 41 | 42 | .copy-button svg { 43 | width: 20px; 44 | } 45 | 46 | .copy-button.clicked { 47 | opacity: 1; 48 | color: var(--success); 49 | 50 | & svg[aria-live="polite"] { 51 | display: block; 52 | } 53 | } 54 | 55 | .copy-button.clicked svg { 56 | display: none; 57 | color: currentColor; 58 | } 59 | -------------------------------------------------------------------------------- /assets/css/custom-props/_elixir.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --main: hsl(250, 68%, 69%); /* purple 600 */ 3 | --mainDark: hsl(250, 68%, 59%); 4 | --mainDarkest: hsl(250, 68%, 49%); 5 | --mainLight: hsl(250, 68%, 74%); 6 | --mainLightest: hsl(250, 68%, 79%); 7 | 8 | --searchBarFocusColor: #8E7CE6; 9 | --searchBarBorderColor: rgba(142, 124, 230, 0.25); 10 | 11 | --link-color: var(--mainDark); 12 | --link-visited-color: var(--mainDarkest); 13 | } 14 | 15 | body.dark { 16 | --link-color: var(--mainLightest); 17 | --link-visited-color: var(--mainLight); 18 | } 19 | -------------------------------------------------------------------------------- /assets/css/custom-props/_erlang.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --main: hsl(0, 100%, 44%); 3 | --mainDark: hsl(0, 100%, 34%); 4 | --mainDarkest: hsl(0, 100%, 24%); 5 | --mainLight: hsl(0, 100%, 64%); 6 | --mainLightest: hsl(0, 100%, 74%); 7 | 8 | --searchBarFocusColor: hsl(0, 100%, 50%); 9 | --searchBarBorderColor: rgb(255, 71, 71, 0.1); 10 | 11 | --link-color: hsl(212, 96%, 45%); 12 | --link-visited-color: hsl(212, 96%, 40%); 13 | } 14 | 15 | body.dark { 16 | --link-color: hsl(212, 56%, 72%); 17 | --link-visited-color: hsl(212, 56%, 67%); 18 | } 19 | -------------------------------------------------------------------------------- /assets/css/custom-props/common.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* Layout & Whitespace */ 3 | --content-width: 949px; 4 | --content-gutter: 60px; 5 | --borderRadius-lg: 14px; 6 | --borderRadius-base: 8px; 7 | --borderRadius-sm: 3px; 8 | --navTabBorderWidth: 2px; 9 | 10 | /* Font Families */ 11 | /* These mirror modern-normalize.css with "Lato" on top */ 12 | --sansFontFamily: "Lato", system-ui, Segoe UI, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; 13 | --monoFontFamily: ui-monospace, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace; 14 | 15 | /* Typography */ 16 | --baseLineHeight: 1.5em; 17 | 18 | /* Colours */ 19 | --gray25: hsl(207, 43%, 98%); 20 | --gray50: hsl(207, 43%, 96%); 21 | --gray100: hsl(212, 33%, 91%); 22 | --gray200: hsl(210, 29%, 88%); 23 | --gray300: hsl(210, 26%, 84%); 24 | --gray400: hsl(210, 21%, 64%); 25 | --gray450: hsl(210, 21%, 49%); 26 | --gray500: hsl(210, 21%, 34%); 27 | --gray600: hsl(210, 27%, 26%); 28 | --gray700: hsl(212, 35%, 17%); 29 | --gray750: hsl(214, 46%, 14%); 30 | --gray800: hsl(216, 52%, 11%); 31 | --gray800-opacity-0: hsla(216, 52%, 11%, 0%); 32 | --gray850: hsl(216, 63%, 8%); 33 | --gray900: hsl(218, 73%, 4%); 34 | --gray900-opacity-50: hsla(218, 73%, 4%, 50%); 35 | --gray900-opacity-0: hsla(218, 73%, 4%, 0%); 36 | --coldGrayFaint: hsl(240, 5%, 97%); 37 | --coldGrayLight: hsl(240, 5%, 88%); 38 | --coldGray-lightened-10: hsl(240, 5%, 56%); 39 | --coldGray: hsl(240, 5%, 46%); 40 | --coldGray-opacity-10: hsla(240, 5%, 46%, 10%); 41 | --coldGrayDark: hsl(240, 5%, 28%); 42 | --coldGrayDim: hsl(240, 5%, 18%); 43 | --yellowLight: hsl(43, 100%, 95%); 44 | --yellowDark: hsl(44, 100%, 15%); 45 | --yellow: hsl(60, 100%, 43%); 46 | --green-lightened-10: hsl(90, 100%, 45%); 47 | --green: hsl(90, 100%, 35%); 48 | --white: hsl(0, 0%, 100%); 49 | --white-opacity-50: hsla(0, 0%, 100%, 50%); 50 | --white-opacity-10: hsla(0, 0%, 100%, 10%); 51 | --white-opacity-0: hsla(0, 0%, 100%, 0%); 52 | --black: hsl(0, 0%, 0%); 53 | --black-opacity-10: hsla(0, 0%, 0%, 10%); 54 | --black-opacity-50: hsla(0, 0%, 0%, 50%); 55 | --orangeDark: hsl(30, 90%, 40%); 56 | --orangeLight: hsl(30, 80%, 50%); 57 | 58 | --text-xs: 0.75rem; /* 12px */ 59 | --text-sm: 0.875rem; /* 14px */ 60 | --text-md: 1rem; /* 16px */ 61 | --text-lg: 1.125rem; /* 18px */ 62 | --text-xl: 1.25rem; /* 20px */ 63 | 64 | --transition-duration: 150ms; 65 | --transition-timing: cubic-bezier(0.4, 0, 0.2, 1); 66 | 67 | --transition-all: all var(--transition-duration) var(--transition-timing); 68 | 69 | --transition-colors: color var(--transition-duration) var(--transition-timing), 70 | background-color var(--transition-duration) var(--transition-timing), 71 | border-color var(--transition-duration) var(--transition-timing), 72 | text-decoration-color var(--transition-duration) var(--transition-timing), 73 | fill var(--transition-duration) var(--transition-timing), 74 | stroke var(--transition-duration) var(--transition-timing); 75 | 76 | --transition-opacity: opacity var(--transition-duration) var(--transition-timing); 77 | } 78 | 79 | @media screen and (max-width: 768px) { 80 | :root { 81 | --content-width: 100%; 82 | --content-gutter: 20px; 83 | } 84 | } 85 | 86 | option { 87 | background-color: var(--sidebarBackground); 88 | } 89 | -------------------------------------------------------------------------------- /assets/css/custom-props/theme-dark.css: -------------------------------------------------------------------------------- 1 | body.dark { 2 | --background: var(--gray900); 3 | --contrast: var(--white); 4 | --textBody: var(--gray200); 5 | --textHeaders: var(--gray100); 6 | --textDetailAccent: var(--mainLight); 7 | --textDetailBackground: var(--gray700); 8 | 9 | --iconAction: var(--coldGray-lightened-10); 10 | --iconActionHover: var(--white); 11 | 12 | --blockquoteBackground: var(--coldGray-opacity-10); 13 | --blockquoteBorder: var(--coldGrayDim); 14 | 15 | --tableHeadBorder: var(--gray600); 16 | --tableBodyBorder: var(--gray700); 17 | 18 | --warningBackground: hsla( 33, 30%, 60%, 10%); 19 | --warningHeadingBackground: hsla( 33, 66%, 35%, 80%); 20 | --warningHeading: var(--white); 21 | --errorBackground: hsla( 7, 30%, 60%, 10%); 22 | --errorHeadingBackground: hsla( 6, 70%, 40%, 80%); 23 | --errorHeading: var(--white); 24 | --infoBackground: hsla(206, 30%, 60%, 10%); 25 | --infoHeadingBackground: hsla(213, 55%, 35%, 80%); 26 | --infoHeading: var(--white); 27 | --neutralBackground: hsl(210, 30%, 60%, 10%); 28 | --neutralHeadingBackground: var(--gray600); 29 | --neutralHeading: var(--white); 30 | --tipBackground: hsla(142, 30%, 60%, 10%); 31 | --tipHeadingBackground: hsla(134, 45%, 30%, 80%); 32 | --tipHeading: var(--white); 33 | 34 | --fnSpecAttr: var(--gray400); 35 | --fnDeprecated: var(--yellowDark); 36 | --blink: var(--gray600); 37 | 38 | --codeBackground: var(--gray750); 39 | --codeBorder: var(--gray600); 40 | --codeScrollThumb: var(--gray500); 41 | --codeScrollBackground: var(--codeBorder); 42 | --admCodeBackground: var(--gray750); 43 | --admCodeBorder: var(--gray600); 44 | --admCodeColor: var(--gray100); 45 | --admInlineCodeColor: var(--gray100); 46 | --admInlineCodeBackground: var(--gray750); 47 | --admInlineCodeBorder: var(--gray600); 48 | 49 | --tabBorder: var(--gray700); 50 | --tabBorderTop: var(--gray700); 51 | --tabShadow: var(--black); 52 | 53 | --bottomActionsBtnBorder: var(--white-opacity-10); 54 | --bottomActionsBtnTitle: var(--mainLightest); 55 | 56 | --modalBackground: var(--gray800); 57 | 58 | --settingsInput: var(--white); 59 | --settingsInputBackground: var(--gray700); 60 | --settingsInputBorder: var(--gray700); 61 | --settingsSectionBorder: var(--gray700); 62 | 63 | --quickSwitchInput: var(--gray300); 64 | --quickSwitchContour: var(--gray500); 65 | 66 | --success: var(--green-lightened-10); 67 | --progressBarColor: var(--gray300); 68 | 69 | --sidebarAccentMain: var(--gray50); 70 | --sidebarBackground: var(--gray800); 71 | --sidebarHeader: var(--gray700); 72 | --sidebarMuted: var(--gray300); 73 | --sidebarHover: var(--white); 74 | --sidebarStaleVersion: var(--orangeLight); 75 | --sidebarSubheadings: var(--gray400); 76 | --sidebarItem: var(--gray200); 77 | --sidebarInactiveItemBorder: var(--gray400); 78 | --sidebarInactiveItemMarker: var(--gray600); 79 | --sidebarLanguageAccentBar: var(--mainLight); 80 | --sidebarActiveItem: var(--mainLightest); 81 | --searchBarBorder: var(--gray500); 82 | --searchAccentMain: var(--gray300); 83 | --searchSearch: var(--gray900); 84 | --autocompleteBorder: rgba(28,42,60,.75); 85 | --autocompletePreview: var(--gray750); 86 | --autocompleteSelected: var(--gray750); 87 | --autocompleteHover: var(--gray700); 88 | --autocompleteBackground: var(--gray800); 89 | --suggestionBorder: var(--gray600); 90 | --autocompleteResults: var(--gray200); 91 | --autocompleteResultsBold: var(--gray100); 92 | --autocompleteLabelBack: var(--gray600); 93 | --autocompleteLabelFont: rgba(255, 255, 255, 0.8); 94 | } 95 | 96 | :root:has(body.dark) { 97 | color-scheme: dark; 98 | } 99 | -------------------------------------------------------------------------------- /assets/css/custom-props/theme-light.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --background: var(--white); 3 | --contrast: var(--black); 4 | --textBody: var(--gray800); 5 | --textHeaders: var(--gray900); 6 | --textDetailAccent: var(--mainLight); 7 | --textDetailBackground: var(--coldGrayFaint); 8 | 9 | --iconAction: var(--coldGray); 10 | --iconActionHover: var(--gray800); 11 | 12 | --blockquoteBackground: var(--coldGrayFaint); 13 | --blockquoteBorder: var(--coldGrayLight); 14 | 15 | --tableHeadBorder: var(--gray100); 16 | --tableBodyBorder: var(--gray50); 17 | 18 | --warningBackground: hsl( 33, 100%, 97%); 19 | --warningHeadingBackground: hsl( 33, 87%, 64%); 20 | --warningHeading: var(--black); 21 | --errorBackground: hsl( 7, 81%, 96%); 22 | --errorHeadingBackground: hsl( 6, 80%, 60%); 23 | --errorHeading: var(--white); 24 | --infoBackground: hsl(206, 91%, 96%); 25 | --infoHeadingBackground: hsl(213, 92%, 62%); 26 | --infoHeading: var(--white); 27 | --neutralBackground: hsl(212, 29%, 92%); 28 | --neutralHeadingBackground: hsl(220, 43%, 11%); 29 | --neutralHeading: var(--white); 30 | --tipBackground: hsl(142, 31%, 93%); 31 | --tipHeadingBackground: hsl(134, 39%, 36%); 32 | --tipHeading: var(--white); 33 | 34 | --fnSpecAttr: var(--coldGray); 35 | --fnDeprecated: var(--yellowLight); 36 | --blink: var(--yellowLight); 37 | 38 | --codeBackground: var(--gray25); 39 | --codeBorder: var(--gray100); 40 | --codeScrollThumb: var(--gray400); 41 | --codeScrollBackground: var(--codeBorder); 42 | --admCodeBackground: var(--gray25); 43 | --admCodeBorder: var(--gray100); 44 | --admCodeColor: var(--black); 45 | --admInlineCodeColor: var(--black); 46 | --admInlineCodeBackground: var(--gray25); 47 | --admInlineCodeBorder: var(--gray100); 48 | 49 | --tabBorder: var(--gray300); 50 | --tabBorderTop: var(--gray100); 51 | --tabShadow: var(--gray25); 52 | 53 | --bottomActionsBtnBorder: var(--black-opacity-10); 54 | --bottomActionsBtnTitle: var(--mainDark); 55 | 56 | --modalBackground: var(--white); 57 | 58 | --settingsInput: var(--gray500); 59 | --settingsInputBackground: var(--white); 60 | --settingsInputBorder: var(--gray300); 61 | --settingsSectionBorder: var(--gray300); 62 | 63 | --quickSwitchInput: var(--gray500); 64 | --quickSwitchContour: var(--coldGray); 65 | 66 | --success: var(--green); 67 | --progressBarColor: var(--gray400); 68 | 69 | --sidebarAccentMain: var(--black); 70 | --sidebarBackground: var(--gray50); 71 | --sidebarHeader: var(--gray100); 72 | --sidebarMuted: var(--gray800); 73 | --sidebarHover: var(--black); 74 | --sidebarStaleVersion: var(--orangeDark); 75 | --sidebarSubheadings: var(--gray500); 76 | --sidebarItem: var(--black); 77 | --sidebarInactiveItemBorder: var(--gray500); 78 | --sidebarInactiveItemMarker: var(--gray200); 79 | --sidebarLanguageAccentBar: var(--mainDark); 80 | --sidebarActiveItem: var(--mainDarkest); 81 | --searchBarBorder: var(--gray200); 82 | --searchAccentMain: var(--gray600); 83 | --searchLanguageAccentBar: var(--main); 84 | --searchSearch: var(--white); 85 | --autocompleteBorder: rgba(3, 9, 19, 0.10); 86 | --autocompletePreview: var(--gray25); 87 | --autocompleteSelected: var(--gray25); 88 | --autocompleteHover: var(--gray50); 89 | --autocompleteBackground: var(--white); 90 | --suggestionBorder: var(--gray200); 91 | --autocompleteResults: var(--gray600); 92 | --autocompleteResultsBold: var(--gray800); 93 | --autocompleteLabelBack: var(--gray100); 94 | --autocompleteLabelFont: var(--gray600); 95 | } 96 | -------------------------------------------------------------------------------- /assets/css/entry/epub-elixir.css: -------------------------------------------------------------------------------- 1 | @import '../custom-props/_elixir.css'; 2 | @import '../_epub.css'; 3 | -------------------------------------------------------------------------------- /assets/css/entry/epub-erlang.css: -------------------------------------------------------------------------------- 1 | @import '../custom-props/_erlang.css'; 2 | @import '../_epub.css'; 3 | -------------------------------------------------------------------------------- /assets/css/entry/html-elixir.css: -------------------------------------------------------------------------------- 1 | @import '../custom-props/_elixir.css'; 2 | @import '../_html.css'; 3 | -------------------------------------------------------------------------------- /assets/css/entry/html-erlang.css: -------------------------------------------------------------------------------- 1 | @import '../custom-props/_erlang.css'; 2 | @import '../_html.css'; 3 | -------------------------------------------------------------------------------- /assets/css/focus.css: -------------------------------------------------------------------------------- 1 | /* For backwards compatibility, style :focus state so that it works in all browsers, 2 | but unstyle not-focus-visible focus state in browsers that support focus-visible */ 3 | 4 | /* extra button selectors necessary to overwrite normalize.css */ 5 | *:focus, 6 | button:focus, 7 | [type="button"]:focus, 8 | [type="reset"]:focus, 9 | [type="submit"]:focus { 10 | outline: 2px solid var(--main); 11 | /* negative offset to make outline visible when overflow hidden */ 12 | outline-offset: -2px; 13 | } 14 | 15 | *:focus:not(:focus-visible), 16 | button:focus:not(:focus-visible), 17 | [type="button"]:focus:not(:focus-visible), 18 | [type="reset"]:focus:not(:focus-visible), 19 | [type="submit"]:focus:not(:focus-visible) { 20 | outline: 0; 21 | } 22 | 23 | /* inputs you can type into don't need an extra focus style 24 | because they have a visible cursor */ 25 | input[type="text"], 26 | input[type="number"], 27 | input[type="date"], 28 | input[type="datetime"], 29 | input[type="datetime-local"], 30 | input[type="email"], 31 | input[type="month"], 32 | input[type="number"], 33 | input[type="password"], 34 | input[type="search"], 35 | input[type="tel"], 36 | input[type="time"], 37 | input[type="url"], 38 | input[type="week"], 39 | textarea { 40 | outline: 0; 41 | } 42 | -------------------------------------------------------------------------------- /assets/css/icons.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Only used icons are included in the font 3 | * Icons can be generated at https://remixicon.com/ 4 | * In assets/fonts/RemixIconCollection.remixicon there's easy to import on website list of icons 5 | */ 6 | 7 | @font-face { 8 | font-family: 'remixicon'; 9 | src: url('../fonts/remixicon.woff2') format('woff2'); 10 | font-display: swap; 11 | } 12 | 13 | [class^="ri-"], [class*=" ri-"], .remix-icon { 14 | font-family: 'remixicon'; 15 | font-style: normal; 16 | -webkit-font-smoothing: antialiased; 17 | -moz-osx-font-smoothing: grayscale; 18 | } 19 | 20 | :root { 21 | --icon-arrow-up-s: "\ea78"; 22 | --icon-arrow-down-s: "\ea4e"; 23 | --icon-arrow-right-s: "\ea6e"; 24 | --icon-add: "\ea13"; 25 | --icon-subtract: "\f1af"; 26 | --icon-error-warning: "\eca1"; 27 | --icon-external-link-line: "\ecaf"; 28 | --icon-information: "\ee59"; 29 | --icon-alert: "\ea21"; 30 | --icon-double-quotes-l: "\ec51"; 31 | --icon-link-m: "\eeaf"; 32 | --icon-close-line: "\eb99"; 33 | --icon-code-s-slash-line: "\ebad"; 34 | --icon-menu-line: "\ef3e"; 35 | --icon-search-2-line: "\f0cd"; 36 | --icon-settings-3-line: "\f0e6"; 37 | --icon-printer-line: "\f029"; 38 | } 39 | 40 | .ri-lg { font-size: 1.3333em; line-height: .75em; vertical-align: -.0667em; } 41 | 42 | .ri-settings-3-line:before { content: var(--icon-settings-3-line); } 43 | .ri-add-line:before { content: var(--icon-add); } 44 | .ri-subtract-line:before { content: var(--icon-subtract); } 45 | .ri-arrow-up-s-line:before { content: var(--icon-arrow-up-s); } 46 | .ri-arrow-down-s-line:before { content: var(--icon-arrow-down-s); } 47 | .ri-arrow-right-s-line:before { content: var(--icon-arrow-right-s); } 48 | .ri-external-link-line:before { content: var(--icon-external-link-line); } 49 | .ri-search-2-line:before { content: var(--icon-search-2-line); } 50 | .ri-menu-line:before { content: var(--icon-menu-line); } 51 | .ri-close-line:before { content: var(--icon-close-line); } 52 | .ri-link-m:before { content: var(--icon-link-m); } 53 | .ri-code-s-slash-line:before { content: var(--icon-code-s-slash-line); } 54 | .ri-error-warning-line:before { content: var(--icon-error-warning); } 55 | .ri-information-line:before { content: var(--icon-information); } 56 | .ri-alert-line:before { content: var(--icon-alert); } 57 | .ri-double-quotes-l:before { content: var(--icon-double-quotes-l); } 58 | .ri-printer-line:before { content: var(--icon-printer-line); } 59 | -------------------------------------------------------------------------------- /assets/css/keyboard-shortcuts.css: -------------------------------------------------------------------------------- 1 | #keyboard-shortcuts-content dl.shortcut-row { 2 | display: flex; 3 | align-items: center; 4 | justify-content: space-between; 5 | margin: 0; 6 | padding: 6px 0 8px; 7 | border-bottom: 1px solid var(--settingsSectionBorder); 8 | } 9 | 10 | #keyboard-shortcuts-content dl.shortcut-row:last-of-type { 11 | border-bottom-style: none; 12 | } 13 | 14 | #keyboard-shortcuts-content dl.shortcut-row:first-child { 15 | padding-top: 0; 16 | } 17 | 18 | #keyboard-shortcuts-content :is(.shortcut-keys, .shortcut-description) { 19 | display: inline-block; 20 | } 21 | 22 | #keyboard-shortcuts-content kbd > kbd { 23 | background-color: var(--settingsInputBorder); 24 | color: var(--contrast); 25 | border-radius: var(--borderRadius-sm); 26 | font-family: inherit; 27 | font-weight: bold; 28 | display: inline-block; 29 | line-height: 1; 30 | padding: 4px 7px 6px 7px; 31 | min-width: 26px; 32 | text-align: center; 33 | font-size: var(--text-sm); 34 | } 35 | 36 | #keyboard-shortcuts-content :is(.shortcut-keys, .shortcut-description) { 37 | margin: 0; 38 | } 39 | -------------------------------------------------------------------------------- /assets/css/layout.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | box-sizing: border-box; 4 | height: 100%; 5 | width: 100%; 6 | } 7 | 8 | body { 9 | --sidebarWidth: 300px; 10 | --sidebarMinWidth: 300px; 11 | --sidebarTransitionDuration: 0.3s; 12 | background-color: var(--background); 13 | color: var(--textBody); 14 | font-size: var(--text-md); 15 | line-height: 1.6875em; 16 | /* swup.js adds tabindex=-1 on Chrome, 17 | which then adds an outline when clicked */ 18 | outline: none !important; 19 | } 20 | 21 | *, 22 | *:before, 23 | *:after { 24 | box-sizing: inherit; 25 | } 26 | 27 | .body-wrapper { 28 | display: flex; 29 | height: 100%; 30 | } 31 | 32 | /* Sidebar is closed by default and opened with body.sidebar-opened. */ 33 | .sidebar { 34 | display: none; 35 | flex-direction: column; 36 | width: var(--sidebarWidth); 37 | min-width: var(--sidebarMinWidth); 38 | max-width: 50vw; 39 | height: 100%; 40 | position: fixed; 41 | top: 0; 42 | left: calc(-1 * var(--sidebarWidth)); 43 | z-index: 100; 44 | resize: horizontal; 45 | } 46 | 47 | .sidebar-button { 48 | padding: 26px 12px 18px 19px; 49 | position: fixed; 50 | z-index: 200; 51 | top: 0; 52 | left: 0; 53 | will-change: transform; 54 | transform: translateX(0); 55 | } 56 | 57 | .content { 58 | left: 0; 59 | width: 100%; 60 | height: 100%; 61 | position: absolute; 62 | } 63 | 64 | .content .content-inner { 65 | container: content / inline-size; 66 | max-width: var(--content-width); 67 | min-height: 100%; 68 | margin: 0 auto; 69 | padding: 0 var(--content-gutter) 10px; 70 | } 71 | 72 | .content-inner:focus { 73 | outline: none; 74 | } 75 | 76 | .sidebar-transition .sidebar, 77 | .sidebar-transition .sidebar-button, 78 | .sidebar-transition .content { 79 | transition: all var(--sidebarTransitionDuration) ease-in-out allow-discrete; 80 | } 81 | 82 | .sidebar-open .sidebar, 83 | .sidebar-transition .sidebar { 84 | display: flex; 85 | } 86 | 87 | .sidebar-open .sidebar { 88 | left: 0; 89 | } 90 | 91 | .sidebar-open .sidebar-button { 92 | transform: translateX(calc(var(--sidebarWidth) - 100%)); 93 | } 94 | 95 | .sidebar-open .content { 96 | width: calc(100% - var(--sidebarWidth)); 97 | left: var(--sidebarWidth); 98 | } 99 | 100 | @media screen and (max-width: 768px) { 101 | .sidebar-open .content { 102 | left: 0; 103 | width: 100%; 104 | } 105 | 106 | .sidebar { 107 | max-width: 90vw; 108 | } 109 | 110 | body:not(.sidebar-open) .sidebar-button { 111 | position: absolute; 112 | } 113 | } 114 | 115 | .swup-progress-bar { 116 | height: 2px; 117 | background-color: var(--progressBarColor); 118 | } 119 | -------------------------------------------------------------------------------- /assets/css/modal.css: -------------------------------------------------------------------------------- 1 | @keyframes keyboard-shortcuts-show { 2 | from { 3 | opacity: 0; 4 | } 5 | to { 6 | opacity: 1; 7 | } 8 | } 9 | 10 | .modal { 11 | animation-duration: .15s; 12 | animation-name: keyboard-shortcuts-show; 13 | animation-iteration-count: 1; 14 | animation-timing-function: ease-in-out; 15 | display: none; 16 | background-color: rgba(0, 0, 0, .75); 17 | position: fixed; 18 | inset: 0; 19 | z-index: 300; 20 | } 21 | 22 | .modal.shown { 23 | display: block; 24 | } 25 | 26 | .modal .modal-contents { 27 | margin: 75px auto 0 auto; 28 | max-width: 500px; 29 | background-color: var(--modalBackground); 30 | border-radius: var(--borderRadius-sm); 31 | box-shadow: 2px 2px 8px rgba(0, 0, 0, .2); 32 | padding: 25px 35px 35px; 33 | } 34 | 35 | @media screen and (max-width: 768px) { 36 | .modal .modal-contents { 37 | padding: 20px; 38 | } 39 | } 40 | 41 | .modal .modal-header { 42 | display: flex; 43 | align-items: start; 44 | } 45 | 46 | .modal .modal-title { 47 | display: inline-block; 48 | flex-grow: 1; 49 | font-size: 1.2rem; 50 | font-weight: bold; 51 | margin-bottom: 20px; 52 | } 53 | 54 | .modal .modal-title button { 55 | border: none; 56 | background-color: transparent; 57 | color: var(--textHeaders); 58 | font-weight: bold; 59 | margin-right: 30px; 60 | padding-left: 0; 61 | text-align: left; 62 | transition: var(--transition-colors); 63 | } 64 | .modal .modal-title button:hover { 65 | color: var(--main); 66 | cursor: pointer; 67 | } 68 | .modal .modal-title button.active { 69 | color: var(--main); 70 | } 71 | 72 | .modal .modal-close { 73 | cursor: pointer; 74 | display: block; 75 | font-size: 1.5rem; 76 | margin: -8px -8px 0 0; 77 | padding: 8px; 78 | opacity: .7; 79 | background-color: transparent; 80 | color: var(--textHeaders); 81 | border: none; 82 | transition: var(--transition-opacity); 83 | } 84 | .modal .modal-close:hover { 85 | opacity: 1; 86 | } 87 | -------------------------------------------------------------------------------- /assets/css/preview.css: -------------------------------------------------------------------------------- 1 | body.preview { 2 | --sidebarWidth: 0px; 3 | overflow: hidden; 4 | } 5 | 6 | body.preview .content { 7 | height: auto; 8 | } 9 | 10 | body.preview .content-inner { 11 | padding: 0; 12 | } 13 | 14 | body.preview .sidebar, body.preview #sidebar-menu { 15 | display: none; 16 | } 17 | 18 | body.preview .hover-link, body.preview .detail-link { 19 | display: none; 20 | } 21 | 22 | body.preview :is(h1, h2, h3):first-of-type { 23 | margin-top: 0; 24 | } 25 | -------------------------------------------------------------------------------- /assets/css/print-cheatsheet.css: -------------------------------------------------------------------------------- 1 | @media print { 2 | 3 | /* Remove background colors and set border colors */ 4 | .page-cheatmd .content-inner * { 5 | background-color: transparent !important; 6 | border-color: var(--gray400) !important; 7 | } 8 | 9 | /* Basic layout and columns */ 10 | 11 | .page-cheatmd .content-inner { 12 | max-width: 100%; 13 | width: 100%; 14 | padding: 0; 15 | font-size: .7em; 16 | } 17 | 18 | .page-cheatmd .content-inner section:is(.col-2, .col-2-left, .col-3) { 19 | column-gap: 30px; 20 | } 21 | 22 | /* column-count and grid display required to be set again within @print media query to take effect when actually printing */ 23 | .page-cheatmd .content-inner section.col-2 { 24 | column-count: 2; 25 | } 26 | .page-cheatmd .content-inner section.col-2-left { 27 | display: grid; 28 | } 29 | .page-cheatmd .content-inner section.col-3 { 30 | column-count: 3; 31 | } 32 | 33 | /* h1 */ 34 | 35 | .page-cheatmd .content-inner h1 { 36 | margin-top: 0; 37 | margin-bottom: .5em; 38 | } 39 | 40 | /* h2 */ 41 | 42 | .page-cheatmd .content-inner h2.section-heading { 43 | font-weight: bold; 44 | margin-top: 1em; 45 | column-span: all; 46 | } 47 | 48 | .page-cheatmd .content-inner section.h2 { 49 | break-inside: avoid; 50 | } 51 | 52 | /* h3 */ 53 | 54 | .page-cheatmd .content-inner h3 { 55 | font-weight: bold; 56 | color: var(--mainDark); 57 | } 58 | 59 | .page-cheatmd .content-inner h3::after { 60 | height: 2px; 61 | background-color: var(--gray400); 62 | } 63 | 64 | .page-cheatmd .content-inner section.h3 { 65 | min-width: 300px; 66 | break-inside: avoid; 67 | } 68 | 69 | /* h4 */ 70 | 71 | .page-cheatmd .content-inner h4 { 72 | padding: .5em 0; 73 | border: none; 74 | font-weight: bold; 75 | color: black; 76 | } 77 | 78 | /* Paragraphs */ 79 | 80 | .page-cheatmd .content-inner .h2 p { 81 | padding-left: 0; 82 | padding-right: 0; 83 | border: none !important; 84 | } 85 | 86 | /* Code blocks */ 87 | 88 | .page-cheatmd .content-inner code { 89 | line-height: 1.5em; 90 | } 91 | 92 | /* Tables */ 93 | 94 | .page-cheatmd .content-inner .h2 table { 95 | font-variant-numeric: tabular-nums; 96 | break-inside: avoid; 97 | } 98 | 99 | .page-cheatmd .content-inner .h2 :is(th, td) { 100 | vertical-align: top; 101 | padding-left: 0; 102 | padding-right: 0; 103 | } 104 | 105 | .page-cheatmd .content-inner .h2 thead { 106 | border-style: solid none; 107 | border-width: 1px; 108 | } 109 | 110 | .page-cheatmd .content-inner .h2 tr { 111 | border-bottom: none; 112 | } 113 | 114 | .page-cheatmd .content-inner .h2 th { 115 | font-weight: bold; 116 | } 117 | 118 | /* Lists */ 119 | 120 | .page-cheatmd .content-inner .h2 li { 121 | padding-left: 0; 122 | padding-right: 0; 123 | vertical-align: middle; 124 | border-bottom: none; 125 | } 126 | 127 | /* Remove copy button from code blocks */ 128 | .page-cheatmd .content-inner pre:hover button.copy-button { 129 | display: none; 130 | } 131 | 132 | /* Remove hover tooltip from inline code references */ 133 | .page-cheatmd .content-inner div.tooltip { 134 | display: none; 135 | } 136 | 137 | .page-cheatmd .content-inner footer p:not(.built-using) { 138 | display: none; 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /assets/css/print.css: -------------------------------------------------------------------------------- 1 | @media print { 2 | .body-wrapper { 3 | display: block; 4 | } 5 | 6 | .sidebar, 7 | .sidebar-button { 8 | display: none; 9 | } 10 | 11 | .top-search { 12 | display: none; 13 | } 14 | 15 | .content { 16 | padding-left: 0; 17 | overflow: visible; 18 | left: 0px; 19 | width: 100%; 20 | } 21 | 22 | .summary-row { 23 | break-inside: avoid; 24 | } 25 | 26 | #toast { 27 | display: none; 28 | } 29 | 30 | .content-inner { 31 | padding: 0; 32 | } 33 | 34 | .content-inner .section-heading a.hover-link { 35 | display: none; 36 | } 37 | 38 | .content-inner button.icon-action { 39 | display: none; 40 | } 41 | 42 | .content-inner a.icon-action { 43 | display: none; 44 | } 45 | 46 | .content-inner .bottom-actions { 47 | display: none; 48 | } 49 | 50 | /* Hide On Hex.pm text but keep Built Using ExDoc */ 51 | .footer p:first-of-type { 52 | display: none; 53 | } 54 | 55 | .content-inner section.admonition { 56 | border: 2px solid var(--gray400); 57 | } 58 | 59 | .content-inner section.admonition > .admonition-title { 60 | color: var(--textHeaders); 61 | border-bottom: 2px solid var(--gray400); 62 | } 63 | 64 | .content-inner pre code.makeup { 65 | border-color: var(--gray400); 66 | white-space: break-spaces; 67 | break-inside: avoid; 68 | } 69 | 70 | .content-inner blockquote code.inline { 71 | border-color: var(--gray400); 72 | } 73 | 74 | .content-inner code.inline { 75 | border-color: var(--gray400); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /assets/css/quick-switch.css: -------------------------------------------------------------------------------- 1 | #quick-switch-modal-body { 2 | width: 100%; 3 | position: relative; 4 | } 5 | 6 | #quick-switch-modal-body .ri-search-2-line { 7 | position: absolute; 8 | left: 0; 9 | top: 0; 10 | padding: 4px 10px; 11 | color: var(--quickSwitchContour); 12 | font-weight: bold; 13 | } 14 | 15 | #quick-switch-modal-body #quick-switch-input { 16 | width: 100%; 17 | padding: 8px 6px 8px 38px; 18 | border: none; 19 | color: var(--quickSwitchInput); 20 | background-color: transparent; 21 | border-bottom: 1px solid var(--quickSwitchContour); 22 | box-sizing: border-box; 23 | transition: all .12s ease-out; 24 | } 25 | 26 | #quick-switch-modal-body #quick-switch-results { 27 | margin: 0; 28 | } 29 | 30 | #quick-switch-modal-body .quick-switch-result { 31 | padding: 2px 5px; 32 | border-bottom: 1px dotted var(--quickSwitchContour); 33 | transition: all .12s ease-out; 34 | } 35 | 36 | #quick-switch-modal-body .quick-switch-result:last-child { 37 | border-bottom: none; 38 | } 39 | 40 | #quick-switch-modal-body .quick-switch-result:hover { 41 | cursor: pointer; 42 | } 43 | 44 | #quick-switch-modal-body .quick-switch-result:is(:hover, .selected) { 45 | border-left: 4px solid var(--main); 46 | background-color: var(--codeBackground); 47 | } 48 | -------------------------------------------------------------------------------- /assets/css/screen-reader.css: -------------------------------------------------------------------------------- 1 | .sr-only { 2 | position: absolute; 3 | width: 1px; 4 | height: 1px; 5 | padding: 0; 6 | margin: -1px; 7 | overflow: hidden; 8 | clip: rect(0, 0, 0, 0); 9 | border: 0; 10 | user-select: none; 11 | } 12 | -------------------------------------------------------------------------------- /assets/css/search.css: -------------------------------------------------------------------------------- 1 | #search { 2 | min-height: 200px; 3 | position: relative; 4 | } 5 | 6 | #search .loading { 7 | height: 64px; 8 | width: 64px; 9 | position: absolute; 10 | top: 50%; 11 | left: calc(50% - 32px); 12 | } 13 | 14 | #search .loading div { 15 | box-sizing: border-box; 16 | display: block; 17 | position: absolute; 18 | width: 51px; 19 | height: 51px; 20 | margin: 6px; 21 | border: 6px solid var(--coldGray); 22 | border-radius: 50%; 23 | animation: loading 1.2s cubic-bezier(.5, 0, .5, 1) infinite; 24 | border-color: var(--coldGray) transparent transparent transparent; 25 | } 26 | 27 | #search .loading div:nth-child(1) { 28 | animation-delay: -.45s; 29 | } 30 | #search .loading div:nth-child(2) { 31 | animation-delay: -.3s; 32 | } 33 | #search .loading div:nth-child(3) { 34 | animation-delay: -.15s; 35 | } 36 | 37 | @keyframes loading { 38 | 0% { 39 | transform: rotate(0deg); 40 | } 41 | 100% { 42 | transform: rotate(360deg); 43 | } 44 | } 45 | 46 | #search .result { 47 | margin: 2em 0 2.5em; 48 | } 49 | 50 | #search .result p { 51 | margin: 0; 52 | } 53 | 54 | #search .result-id { 55 | font-size: 1.4em; 56 | margin: 0; 57 | } 58 | 59 | #search .result-id a { 60 | text-decoration: none; 61 | color: var(--textHeaders); 62 | transition: var(--transition-colors); 63 | } 64 | #search .result-id a:is(:visited, :active) { 65 | color: var(--textHeaders); 66 | } 67 | #search .result-id a:is(:hover, :focus) { 68 | color: var(--main); 69 | } 70 | 71 | #search :is(.result-id, .result-elem) em { 72 | font-style: normal; 73 | color: var(--main); 74 | } 75 | 76 | #search .result-id small { 77 | font-weight: normal; 78 | } 79 | -------------------------------------------------------------------------------- /assets/css/settings.css: -------------------------------------------------------------------------------- 1 | #settings-modal-content { 2 | margin-top: 10px; 3 | } 4 | 5 | #settings-modal-content .hidden { 6 | display: none; 7 | } 8 | 9 | #settings-modal-content .input { 10 | box-sizing: border-box; 11 | width: 80%; 12 | padding: 8px; 13 | font-size: var(--text-sm); 14 | background-color: var(--settingsInputBackground); 15 | color: var(--settingsInput); 16 | border: 1px solid var(--settingsInputBorder); 17 | border-radius: var(--borderRadius-base); 18 | transition: var(--transition-all); 19 | } 20 | #settings-modal-content .input:focus { 21 | border-color: var(--main); 22 | } 23 | #settings-modal-content .input::placeholder { 24 | color: var(--gray400); 25 | } 26 | 27 | #settings-modal-content .switch-button-container { 28 | display: flex; 29 | align-items: center; 30 | justify-content: space-between; 31 | border-top: 1px solid var(--settingsSectionBorder); 32 | padding: 10px 0; 33 | } 34 | #settings-modal-content .switch-button-container:first-of-type { 35 | border-top-style: none; 36 | padding-top: 0; 37 | } 38 | 39 | #settings-modal-content .switch-button-container > div > span { 40 | font-size: var(--text-md); 41 | } 42 | 43 | #settings-modal-content .switch-button-container > div > p { 44 | -webkit-font-smoothing: antialiased; 45 | -moz-osx-font-smoothing: grayscale; 46 | font-size: var(--text-sm); 47 | line-height: 1.4; 48 | margin: 0; 49 | padding-bottom: 6px; 50 | padding-right: 10px; 51 | } 52 | 53 | #settings-modal-content .switch-button { 54 | position: relative; 55 | display: inline-block; 56 | flex-shrink: 0; 57 | width: 40px; 58 | height: 20px; 59 | user-select: none; 60 | transition: var(--transition-all); 61 | } 62 | 63 | #settings-modal-content .switch-button__checkbox { 64 | appearance: none; 65 | position: absolute; 66 | display: block; 67 | width: 20px; 68 | height: 20px; 69 | border-radius: 1000px; 70 | background-color: #91a4b7; 71 | border: 3px solid #e5edf5; 72 | cursor: pointer; 73 | transition: var(--transition-all); 74 | } 75 | 76 | #settings-modal-content .switch-button__bg { 77 | display: block; 78 | width: 100%; 79 | height: 100%; 80 | border-radius: 1000px; 81 | background-color: #e5edf5; 82 | cursor: pointer; 83 | transition: var(--transition-all); 84 | } 85 | 86 | #settings-modal-content .switch-button__checkbox:checked { 87 | background-color: white; 88 | border-color: var(--main); 89 | transform: translateX(100%); 90 | } 91 | 92 | #settings-modal-content .switch-button__checkbox:checked + .switch-button__bg { 93 | background-color: var(--main); 94 | } 95 | 96 | #settings-modal-content .switch-button__checkbox:focus { 97 | outline: 0; 98 | } 99 | 100 | #settings-modal-content .switch-button__checkbox:focus + .switch-button__bg { 101 | outline: 2px solid var(--main); 102 | outline-offset: 2px; 103 | } 104 | 105 | #settings-modal-content .switch-button__checkbox:focus:not(:focus-visible) + .switch-button__bg { 106 | outline: 0; 107 | } 108 | 109 | #settings-modal-content .settings-select { 110 | cursor: pointer; 111 | position: relative; 112 | border: none; 113 | background-color: transparent; 114 | color: var(--textBody); 115 | } 116 | 117 | #settings-modal-content .settings-select option { 118 | color: initial; 119 | } 120 | -------------------------------------------------------------------------------- /assets/css/tabset.css: -------------------------------------------------------------------------------- 1 | .tabset { 2 | --borderWidth: 1px; 3 | --tabsetPadding: var(--baseLineHeight); 4 | margin: var(--baseLineHeight) 0; 5 | border: var(--borderWidth) solid var(--tabBorder); 6 | padding: 0 var(--tabsetPadding); 7 | border-radius: var(--borderRadius-lg); 8 | } 9 | 10 | .tabset-tablist { 11 | display: flex; 12 | overflow: auto; 13 | scrollbar-width: thin; 14 | border-bottom-width: 1px; 15 | border-bottom-style: solid; 16 | border-bottom-color: var(--tabBorderTop); 17 | } 18 | 19 | .tabset-tab { 20 | padding: 1.1rem var(--tabsetPadding); 21 | font-family: var(--sansFontFamily); 22 | color: var(--textColor); 23 | margin-right: calc(-1 * var(--borderWidth)); 24 | background-color: transparent; 25 | border:0; 26 | box-shadow: none; 27 | cursor: pointer; 28 | border-bottom-width: 2px; 29 | border-bottom-style: solid; 30 | border-bottom-color: transparent; 31 | transition: var(--transition-all); 32 | } 33 | 34 | :hover.tabset-tab { 35 | border-bottom-color: var(--tabBorderTop); 36 | color: var(--textHeaders); 37 | } 38 | 39 | .tabset-tab[aria-selected=true] { 40 | border-bottom-color: var(--mainLight); 41 | color: var(--textHeaders); 42 | } 43 | 44 | /* Keyboard navigation focus state (increased contrast) */ 45 | .tabset-tab[aria-selected=true]:focus-visible { 46 | background-color: var(--mainLight); 47 | border-color: var(--mainLight); 48 | color: var(--white); /* light works best for both light and dark themes given background colors */ 49 | } 50 | 51 | @media screen and (max-width: 768px) { 52 | .tabset { 53 | --tabsetPadding: calc(var(--baseLineHeight) / 2); 54 | } 55 | 56 | .tabset-panel { 57 | padding-top: calc(var(--tabsetPadding) / 2); 58 | padding-bottom: calc(var(--tabsetPadding) / 2); 59 | } 60 | 61 | .tabset-panel pre, 62 | .tabset-panel blockquote, 63 | .tabset-panel section.admonition { 64 | margin-left: calc(-1 * var(--tabsetPadding)) !important; 65 | margin-right: calc(-1 * var(--tabsetPadding)) !important; 66 | } 67 | 68 | .tabset-panel > pre code { 69 | border-left-width: 0; 70 | border-right-width: 0; 71 | } 72 | } 73 | 74 | /* tabpanel content: top margin of first and bottom margin of last */ 75 | @media screen and (max-width: 768px) { 76 | .tabset-panel > :is(:first-child) { 77 | &:is(table) { 78 | margin: .5em 0; 79 | } 80 | } 81 | } 82 | @media screen and (min-width: 769px) { 83 | .tabset-panel > :is(:first-child) { 84 | &:is(blockquote, .admonition) { 85 | margin-top: 1.5em; 86 | } 87 | &:is(p:has(img)) { 88 | margin-top: 1.25em; 89 | } 90 | &:is(table) { 91 | margin-top: .75em; 92 | } 93 | } 94 | .tabset-panel > :is(:last-child) { 95 | &:is(blockquote, .admonition) { 96 | margin-bottom: 1.5em; 97 | } 98 | &:is(p:not(:has(img)), ul, ol) { 99 | margin-bottom: 1.25em; 100 | } 101 | &:is(table) { 102 | margin-bottom: .75em; 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /assets/css/toast.css: -------------------------------------------------------------------------------- 1 | #toast { 2 | visibility: hidden; 3 | opacity: 0; 4 | position: fixed; 5 | z-index: 1; 6 | left: 50%; 7 | bottom: 1rem; 8 | min-width: 3rem; 9 | margin: 0 -1.2rem; 10 | padding: .7rem 1.2rem; 11 | text-align: center; 12 | font-weight: 700; 13 | border-radius: var(--borderRadius-base); 14 | border: 1px solid var(--codeBorder); 15 | background-color: var(--codeBackground); 16 | color: var(--textBody); 17 | transition: opacity .4s ease-in-out, transform .3s ease-out; 18 | cursor: default; 19 | } 20 | 21 | #toast.show { 22 | visibility: visible; 23 | opacity: 1; 24 | transform: translateY(-.75rem); 25 | } 26 | 27 | @media (prefers-reduced-motion: reduce) { 28 | #toast { 29 | transition: none; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /assets/css/tooltips.css: -------------------------------------------------------------------------------- 1 | .tooltip { 2 | box-shadow: 0 0 10px var(--black-opacity-10); 3 | max-height: 300px; 4 | max-width: 500px; 5 | padding: 0; 6 | position: absolute; 7 | pointer-events: none; 8 | margin: 0; 9 | z-index: 99; 10 | top: 0; 11 | left: 0; 12 | visibility: hidden; 13 | transform: translateY(20px); 14 | opacity: 0; 15 | transition: .2s visibility ease-out, .2s transform ease-out, .2s opacity ease-out; 16 | } 17 | 18 | /* 19 | Show-up animation 20 | ====== 21 | Note: it's fine to hide the tooltip with `visibility: hidden` (rather than `display: none`) 22 | as it has absolute positioning, so doesn't impact the layout and click events pass through. 23 | */ 24 | .tooltip.tooltip-shown { 25 | visibility: visible; 26 | transform: translateY(0); 27 | opacity: 1; 28 | } 29 | 30 | .tooltip .tooltip-body { 31 | border: 1px solid var(--codeBorder); 32 | border-radius: var(--borderRadius-sm); 33 | overflow: auto; 34 | } 35 | 36 | .tooltip .tooltip-body .signature { 37 | min-width: 320px; 38 | width: 100%; 39 | line-height: 1em; 40 | } 41 | 42 | .tooltip .tooltip-body .detail-header { 43 | border-left: 0; 44 | margin-bottom: 0; 45 | margin-top: 0; 46 | } 47 | 48 | .tooltip .tooltip-body .docstring { 49 | background-color: var(--background); 50 | padding: 1.2em; 51 | margin: 0; 52 | width: 498px; /* Taking 2 * 1px of border into account */ 53 | } 54 | 55 | /* Used for simple tooltips having only description. */ 56 | .tooltip .tooltip-body .docstring-plain { 57 | max-width: 498px; 58 | width: auto; 59 | } 60 | 61 | .tooltip .tooltip-body .version-info { 62 | float: right; 63 | font-family: var(--monoFontFamily); 64 | font-weight: normal; 65 | opacity: .3; 66 | padding-left: .3em; 67 | } 68 | -------------------------------------------------------------------------------- /assets/fonts/RemixIconCollection.remixicon: -------------------------------------------------------------------------------- 1 | add-line,alert-line,arrow-down-s-line,arrow-right-s-line,arrow-up-s-line,close-line,code-s-slash-line,double-quotes-l,error-warning-line,external-link-line,information-line,link-m,menu-line,printer-line,search-2-line,settings-3-line,subtract-line 2 | -------------------------------------------------------------------------------- /assets/fonts/remixicon.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elixir-lang/ex_doc/16647077462448f4276707016634718b39b03397/assets/fonts/remixicon.woff2 -------------------------------------------------------------------------------- /assets/js/constants.js: -------------------------------------------------------------------------------- 1 | // Constants separated to allow importing into inline_html.js without 2 | // bringing in other code. 3 | export const SETTINGS_KEY = 'ex_doc:settings' 4 | export const DARK_MODE_CLASS = 'dark' 5 | export const THEME_SYSTEM = 'system' 6 | export const THEME_DARK = 'dark' 7 | export const THEME_LIGHT = 'light' 8 | -------------------------------------------------------------------------------- /assets/js/content.js: -------------------------------------------------------------------------------- 1 | import { qsAll } from './helpers' 2 | import { settingsStore } from './settings-store' 3 | 4 | /** 5 | * Updates "Run in Livebook" badges to link to a notebook 6 | * corresponding to the current documentation page. 7 | */ 8 | 9 | window.addEventListener('exdoc:loaded', initialize) 10 | 11 | function initialize () { 12 | const notebookPath = window.location.pathname.replace(/(\.html)?$/, '.livemd') 13 | const notebookUrl = encodeURIComponent(new URL(notebookPath, window.location.href).toString()) 14 | 15 | settingsStore.getAndSubscribe(({livebookUrl}) => { 16 | const targetUrl = livebookUrl 17 | ? `${livebookUrl}/import?url=${notebookUrl}` 18 | : `https://livebook.dev/run?url=${notebookUrl}` 19 | 20 | for (const anchor of qsAll('.livebook-badge')) { 21 | anchor.href = targetUrl 22 | } 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /assets/js/copy-button.js: -------------------------------------------------------------------------------- 1 | import { qsAll } from './helpers' 2 | import buttonHtml from './handlebars/templates/copy-button.html' 3 | 4 | /** @type {HTMLButtonElement} */ 5 | let buttonTemplate 6 | 7 | /** 8 | * Initializes copy buttons. 9 | */ 10 | 11 | window.addEventListener('exdoc:loaded', initialize) 12 | 13 | function initialize () { 14 | if (!('clipboard' in navigator)) return 15 | 16 | qsAll('pre:has(> code:first-child):not(:has(.copy-button))').forEach(pre => { 17 | if (!buttonTemplate) { 18 | const div = document.createElement('div') 19 | div.innerHTML = buttonHtml 20 | buttonTemplate = div.firstChild 21 | } 22 | 23 | const button = buttonTemplate.cloneNode(true) 24 | pre.appendChild(button) 25 | 26 | let timeout 27 | button.addEventListener('click', () => { 28 | clearTimeout(timeout) 29 | 30 | const text = 31 | Array.from(pre.querySelectorAll('code > *:not(.unselectable)')) 32 | .map(elem => elem.textContent) 33 | .join('') 34 | 35 | navigator.clipboard.writeText(text) 36 | button.classList.add('clicked') 37 | button.disabled = true 38 | timeout = setTimeout(() => { 39 | button.classList.remove('clicked') 40 | button.disabled = false 41 | }, 3000) 42 | }) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /assets/js/entry/epub.js: -------------------------------------------------------------------------------- 1 | import { onDocumentReady } from '../helpers' 2 | import { initialize as initMakeup } from '../makeup' 3 | 4 | onDocumentReady(() => { 5 | initMakeup() 6 | }) 7 | -------------------------------------------------------------------------------- /assets/js/entry/html.js: -------------------------------------------------------------------------------- 1 | // Load preview & hint-page first because they could remove DOM. 2 | // This prevents later modules doing unnecessary work. 3 | import '../preview' 4 | import '../tooltips/hint-page' 5 | // The remaining modules are loaded in order of visible impact. 6 | import '../theme' 7 | import '../sidebar/sidebar-drawer' 8 | import '../sidebar/sidebar-version-select' 9 | import '../tabsets' 10 | import '../content' 11 | import '../makeup' 12 | import '../search-bar' 13 | import '../tooltips/tooltips' 14 | import '../copy-button' 15 | import '../search-page' 16 | import '../settings' 17 | import '../keyboard-shortcuts' 18 | import '../quick-switch' 19 | import '../swup' 20 | -------------------------------------------------------------------------------- /assets/js/entry/inline_html.js: -------------------------------------------------------------------------------- 1 | // CAREFUL 2 | // This file is inlined into each HTML document. 3 | // Only code that must be executed ASAP belongs here. 4 | // Imports should only bring in inlinable constants. 5 | // Check compiled output to make sure no unnecessary code is imported. 6 | import { DARK_MODE_CLASS, SETTINGS_KEY, THEME_DARK, THEME_LIGHT } from '../constants' 7 | import { SIDEBAR_CLASS_OPEN, SIDEBAR_PREF_CLOSED, SIDEBAR_STATE_KEY, SIDEBAR_WIDTH_KEY, SMALL_SCREEN_BREAKPOINT } from '../sidebar/constants' 8 | 9 | const params = new URLSearchParams(window.location.search) 10 | 11 | // Immediately apply night mode preference to avoid a flash effect. 12 | // Should match logic in theme.js. 13 | const theme = params.get('theme') || JSON.parse(localStorage.getItem(SETTINGS_KEY) || '{}').theme 14 | if (theme === THEME_DARK || 15 | (theme !== THEME_LIGHT && 16 | window.matchMedia('(prefers-color-scheme: dark)').matches) 17 | ) { 18 | document.body.classList.add(DARK_MODE_CLASS) 19 | } 20 | 21 | // Set sidebar state and width. 22 | // Should match logic in sidebar-drawer.js. 23 | const sidebarPref = sessionStorage.getItem(SIDEBAR_STATE_KEY) 24 | const open = sidebarPref !== SIDEBAR_PREF_CLOSED && !window.matchMedia(`screen and (max-width: ${SMALL_SCREEN_BREAKPOINT}px)`).matches 25 | document.body.classList.toggle(SIDEBAR_CLASS_OPEN, open) 26 | 27 | const sidebarWidth = sessionStorage.getItem(SIDEBAR_WIDTH_KEY) 28 | if (sidebarWidth) { 29 | document.body.style.setProperty('--sidebarWidth', `${sidebarWidth}px`) 30 | } 31 | 32 | // Set OS class. 33 | const isAppleOS = /(Macintosh|iPhone|iPad|iPod)/.test(window.navigator.userAgent) 34 | document.documentElement.classList.toggle('apple-os', isAppleOS) 35 | -------------------------------------------------------------------------------- /assets/js/globals.js: -------------------------------------------------------------------------------- 1 | const params = new URLSearchParams(window.location.search) 2 | const isFrame = window.self !== window.parent 3 | 4 | export const isPreview = isFrame && params.has('preview') 5 | export const isHint = isFrame && params.has('hint') 6 | export const isEmbedded = isPreview || isHint 7 | 8 | // These variables are set by other scripts (e.g. generated by the docs task). 9 | 10 | export function getSidebarNodes () { 11 | return window.sidebarNodes || {} 12 | } 13 | 14 | export function getVersionNodes () { 15 | return window.versionNodes || [] 16 | } 17 | -------------------------------------------------------------------------------- /assets/js/handlebars/helpers.js: -------------------------------------------------------------------------------- 1 | import * as Handlebars from 'handlebars/runtime' 2 | 3 | Handlebars.registerHelper('isArray', function (entry, options) { 4 | if (Array.isArray(entry)) { 5 | return options.fn(this) 6 | } else { 7 | return options.inverse(this) 8 | } 9 | }) 10 | 11 | Handlebars.registerHelper('isNonEmptyArray', function (entry, options) { 12 | if (Array.isArray(entry) && entry.length > 0) { 13 | return options.fn(this) 14 | } else { 15 | return options.inverse(this) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /assets/js/handlebars/templates/autocomplete-suggestions.handlebars: -------------------------------------------------------------------------------- 1 |
2 |{{{this}}}
19 | {{/each}} 20 |Sorry, we couldn't find anything for {{value}}.
25 | {{else if value}} 26 |Invalid search: {{errorMessage}}.
27 | {{else}} 28 |Please type something into the search bar to perform a search.
29 | {{/isArray}} 30 | 31 |The search functionality is full-text based. Here are some tips:
32 | 33 |foo bar
) are searched as OR
*
anywhere (such as fo*
) as wildcard+
before a word (such as +foo
) to make its presence required-
before a word (such as -foo
) to make its absence required:
to search on a particular field (such as field:word
). The available fields are title
, doc
and type
WORD^NUMBER
(such as foo^2
) to boost the given wordWORD~NUMBER
(such as foo~2
) to do a search with edit distance on wordTo quickly go to a module, type, or function, use the autocompletion feature in the sidebar search.
44 | {{/isNonEmptyArray}} 45 | -------------------------------------------------------------------------------- /assets/js/handlebars/templates/settings-modal-body.handlebars: -------------------------------------------------------------------------------- 1 |17 | Module description here 18 |
19 |First line of description.
49 |Second line of description.
50 |