Repository: docsifyjs/docsify Branch: develop Commit: 851db13891fe Files: 174 Total size: 722.0 KB Directory structure: gitextract_4_14r9ru/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ └── feature_request.yml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── crowdin/ │ │ └── crowdin-push.yml │ ├── dependabot.yml │ ├── semantic.yml │ └── workflows/ │ ├── crowdin.yml │ ├── emoji.yml │ └── test.yml ├── .gitignore ├── .gitpod.yml ├── .husky/ │ └── pre-commit ├── .npmignore ├── .prettierignore ├── .prettierrc.json ├── .vscode/ │ ├── launch.json │ └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── HISTORY.md ├── LICENSE ├── README.md ├── SECURITY.md ├── babel.config.json ├── build/ │ ├── cover.js │ ├── emoji.js │ ├── release.sh │ └── util.js ├── docs/ │ ├── .nojekyll │ ├── CNAME │ ├── README.md │ ├── _coverpage.md │ ├── _media/ │ │ ├── example-with-yaml.md │ │ ├── example.html │ │ ├── example.js │ │ └── example.md │ ├── _navbar.md │ ├── _sidebar.md │ ├── adding-pages.md │ ├── cdn.md │ ├── configuration.md │ ├── cover.md │ ├── custom-navbar.md │ ├── deploy.md │ ├── embed-files.md │ ├── emoji.md │ ├── helpers.md │ ├── index.html │ ├── language-highlight.md │ ├── markdown.md │ ├── plugins.md │ ├── pwa.md │ ├── quickstart.md │ ├── themes.md │ ├── ui-kit.md │ ├── v5-upgrade.md │ ├── vue.md │ └── write-a-plugin.md ├── eslint.config.js ├── jest.config.js ├── middleware.js ├── package.json ├── playwright.config.js ├── postcss.config.cjs ├── rollup.config.js ├── server.configs.js ├── server.js ├── src/ │ ├── core/ │ │ ├── Docsify.js │ │ ├── config.js │ │ ├── event/ │ │ │ └── index.js │ │ ├── fetch/ │ │ │ └── index.js │ │ ├── global-api.js │ │ ├── globals.ts │ │ ├── index.js │ │ ├── init/ │ │ │ └── lifecycle.js │ │ ├── module.js │ │ ├── modules.ts │ │ ├── render/ │ │ │ ├── compiler/ │ │ │ │ ├── blockquote.js │ │ │ │ ├── code.js │ │ │ │ ├── heading.js │ │ │ │ ├── image.js │ │ │ │ ├── link.js │ │ │ │ ├── media.js │ │ │ │ ├── paragraph.js │ │ │ │ ├── tableCell.js │ │ │ │ ├── taskList.js │ │ │ │ └── taskListItem.js │ │ │ ├── compiler.js │ │ │ ├── embed.js │ │ │ ├── emoji-data.js │ │ │ ├── emojify.js │ │ │ ├── gen-tree.js │ │ │ ├── index.js │ │ │ ├── progressbar.js │ │ │ ├── slugify.js │ │ │ ├── tpl.js │ │ │ └── utils.js │ │ ├── router/ │ │ │ ├── history/ │ │ │ │ ├── base.js │ │ │ │ ├── hash.js │ │ │ │ └── html5.js │ │ │ ├── index.js │ │ │ └── util.js │ │ ├── util/ │ │ │ ├── ajax.js │ │ │ ├── core.js │ │ │ ├── dom.js │ │ │ ├── env.js │ │ │ ├── index.js │ │ │ └── prism.js │ │ └── virtual-routes/ │ │ ├── exact-match.js │ │ ├── index.js │ │ └── next.js │ └── plugins/ │ ├── disqus.js │ ├── emoji.js │ ├── external-script.js │ ├── front-matter/ │ │ ├── index.js │ │ ├── parser.js │ │ └── yaml.js │ ├── ga.js │ ├── gitalk.js │ ├── gtag.js │ ├── matomo.js │ ├── search/ │ │ ├── component.js │ │ ├── index.js │ │ ├── markdown-to-txt.js │ │ ├── search.js │ │ └── style.css │ └── zoom-image.js ├── test/ │ ├── README.md │ ├── config/ │ │ ├── jest.setup-tests.js │ │ ├── jest.setup.js │ │ ├── jest.teardown.js │ │ ├── playwright.setup.js │ │ ├── playwright.teardown.js │ │ └── server.js │ ├── consume-types/ │ │ ├── README.md │ │ ├── example.js │ │ ├── index.html │ │ ├── package.json │ │ ├── prism.js │ │ ├── register-sw.js │ │ ├── sw.js │ │ └── tsconfig.json │ ├── e2e/ │ │ ├── configuration.test.js │ │ ├── example.test.js │ │ ├── fixtures/ │ │ │ └── docsify-init-fixture.js │ │ ├── gtag.test.js │ │ ├── index-file.test.js │ │ ├── plugins.test.js │ │ ├── search.test.js │ │ ├── security.test.js │ │ ├── sidebar.test.js │ │ ├── virtual-routes.test.js │ │ └── vue.test.js │ ├── helpers/ │ │ ├── docsify-init.js │ │ └── wait-for.js │ ├── integration/ │ │ ├── __snapshots__/ │ │ │ ├── docs.test.js.snap │ │ │ └── emoji.test.js.snap │ │ ├── docs.test.js │ │ ├── docsify.test.js │ │ ├── emoji.test.js │ │ ├── example.test.js │ │ ├── global-apis.test.js │ │ ├── render.test.js │ │ └── sidebar.test.js │ └── unit/ │ ├── core-util.test.js │ ├── render-util.test.js │ ├── router-history-base.test.js │ └── router-util.test.js ├── tsconfig.json └── vercel.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # http://EditorConfig.org root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true ================================================ FILE: .gitattributes ================================================ * text=auto eol=lf ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: Bug Report description: Submit a bug report. labels: ['bug', 'Triage'] body: - type: markdown attributes: value: | Thanks for taking the time to fill out this bug report! **Please write in English**. Before creating an issue please make sure you are using the latest version of Docsify. - type: textarea attributes: label: Description description: A clear and concise description of what the bug is, and why you consider it to be a bug. validations: required: true - type: textarea attributes: label: Expected behavior description: A description of what you expected to happen. validations: required: true - type: textarea attributes: label: Actual behavior description: A description of what is actually happening. validations: required: true - type: textarea attributes: label: Steps to reproduce description: | A description with steps to reproduce the issue. Provide a link to a public repository or create a reproducible [sandbox](https://codesandbox.io/s/307qqv236): placeholder: | 1. Step 1 2. Step 2 validations: required: true - type: textarea attributes: label: Environment description: | Please provide the following information if relevant to the issue: ```markdown - Your OS: - Node.js version: - npm/yarn version: - Browser version: - Docsify version: - Docsify plugins (if the bug happens when plugins enabled, please try to isolate the issue): ``` - type: checkboxes attributes: label: Additional Information options: - label: Bug still occurs when all/other plugins are disabled? ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Discord - the community chat url: https://discord.gg/docsify about: Join the Discord community and chat about Docsify ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yml ================================================ name: Feature Request description: Propose a new feature or improvement for this project. labels: ['feature request', 'Triage'] body: - type: markdown attributes: value: | Thank you for suggesting a feature! Please provide as much detail as possible to help us understand your idea. **Write in English.** - type: textarea attributes: label: Problem or Desire description: What problem or need will this feature address? Why is it important? validations: required: true - type: textarea attributes: label: Proposal description: What is your proposed solution? How should this feature work? validations: required: true - type: textarea attributes: label: Implementation Details description: If you have any ideas about how this feature could be implemented, please share them here. - type: textarea attributes: label: Additional Context description: Add any other context, screenshots, or references that might help us understand your request. ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ## Summary ## Related issue, if any: ## What kind of change does this PR introduce? - [ ] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no api changes) - [ ] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## For any code change, - [ ] Related documentation has been updated, if needed - [ ] Related tests have been added or updated, if needed ## Does this PR introduce a breaking change? - [ ] Yes - [ ] No ## Tested in the following browsers: - [ ] Chrome - [ ] Firefox - [ ] Safari - [ ] Edge ================================================ FILE: .github/crowdin/crowdin-push.yml ================================================ files: - source: /*.md translation: /%two_letters_code%/%original_file_name% ignore: - '/_coverpage.md' - '/_navbar.md' project_id_env: CROWDIN_PROJECT_ID api_token_env: CROWDIN_PERSONAL_TOKEN ================================================ FILE: .github/dependabot.yml ================================================ # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: npm # See documentation for possible values directory: '/' # Location of package manifests open-pull-requests-limit: 10 schedule: interval: monthly ignore: # For all dependencies, trigger pull requests only for semver-major updates (ignore minor and patch). - dependency-name: '*' update-types: ['version-update:semver-patch', 'version-update:semver-minor'] - package-ecosystem: 'github-actions' directory: '/' open-pull-requests-limit: 10 schedule: interval: monthly ignore: # For all dependencies, trigger pull requests only for semver-major and semver-minor updates (ignore patch). - dependency-name: '*' update-types: ['version-update:semver-patch', 'version-update:semver-minor'] ================================================ FILE: .github/semantic.yml ================================================ titleAndCommits: true allowMergeCommits: true allowRevertCommits: true anyCommit: true ================================================ FILE: .github/workflows/crowdin.yml ================================================ name: Crowdin Action on: workflow_dispatch: push: paths: - 'docs/**.md' branches: [develop, main] jobs: crowdin: if: github.repository == 'docsifyjs/docsify' runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - name: Crowdin push uses: crowdin/github-action@v2 with: upload_sources: true upload_translations: false download_translations: false push_translations: false config: '.github/crowdin/crowdin-push.yml' crowdin_branch_name: ${{ github.ref_name }} base_path: ${{ github.workspace }}/docs env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} ================================================ FILE: .github/workflows/emoji.yml ================================================ name: Sync Emoji on: schedule: - cron: '0 2 * * *' workflow_dispatch: jobs: sync-emoji: if: github.repository == 'docsifyjs/docsify' runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: latest cache: 'npm' - name: Install dependencies run: npm ci --ignore-scripts - name: Run script to sync emoji data run: npm run build:emoji - name: Commit id: auto-commit-action uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: 'chore: Sync emoji data with GitHub emoji API' branch: sync-emoji create_branch: true file_pattern: 'src/core/render/emoji-data.js docs/emoji.md' push_options: '--force' - name: Create Pull Request if: ${{ steps.auto-commit-action.outputs.changes_detected == 'true' }} run: | gh pr create --title 'chore: Sync emoji data with GitHub emoji API' --body 'Found updated github emojis need to sync.' --base develop --reviewer docsifyjs/reviewers continue-on-error: true env: GH_TOKEN: ${{ secrets.READ_TEAM_TOKEN }} ================================================ FILE: .github/workflows/test.yml ================================================ name: Build & Test on: push: branches: [main, develop] pull_request: branches: [main, develop] workflow_dispatch: jobs: lint: runs-on: ubuntu-latest strategy: matrix: node-version: ['lts/*'] steps: - uses: actions/checkout@v6 - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} cache: 'npm' - name: Install dependencies run: npm ci --ignore-scripts - name: Build run: npm run build - name: Lint run: npm run lint test-jest: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: node-version: ['lts/*'] os: ['macos-latest', 'ubuntu-latest', 'windows-latest'] steps: - uses: actions/checkout@v6 - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} cache: 'npm' - name: Install dependencies run: npm ci --ignore-scripts - name: Build run: npm run build - name: Unit Tests run: npm run test:unit -- --ci --runInBand - name: Integration Tests run: npm run test:integration -- --ci --runInBand - name: Consumption Tests run: npm run test:consume-types test-playwright: runs-on: ubuntu-latest strategy: matrix: node-version: ['lts/*'] steps: - uses: actions/checkout@v6 - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} cache: 'npm' - name: Install dependencies run: npm ci --ignore-scripts - name: Build run: npm run build - name: Install Playwright run: npx playwright install --with-deps - name: E2E Tests (Playwright) run: npm run test:e2e # In the interest of not having to modify GitHub settings to update required # builds for passing, I stuck this test here. - name: Test v4 build run: | git fetch --tags npm run build:v4 # ensure v4 build files exist: test -f lib/docsify.js test -f themes/pure.css # ensure no git changes after building v4: git diff --exit-code npm run clean:v4 # ensure v4 build files are removed: test ! -f lib/docsify.js test ! -f themes/pure.css # ensure no git changes after cleaning v4: git diff --exit-code - name: Store artifacts uses: actions/upload-artifact@v7 if: failure() with: name: ${{ matrix.os }}-${{ matrix.node-version }}-artifacts path: | _playwright-results/ _playwright-report/ ================================================ FILE: .gitignore ================================================ # Directories .husky/_ .idea .vercel _playwright-report _playwright-results node_modules # Files .DS_Store *.log # Exceptions !.gitkeep # Output folder for the global build only dist/ # Output folders for the legacy v4 build lib/ themes/ # TypeScript declaration files for standard ESM consumption src/**/*.d.ts src/**/*.d.ts.map ================================================ FILE: .gitpod.yml ================================================ tasks: - command: gp await-port 3000 && sleep 3 && gp preview $(gp url 3000) - init: npm install command: npm run dev ports: - port: 3000 onOpen: ignore vscode: extensions: - sysoev.language-stylus@1.11.0:xX39oruAJ5UQzTNVRdbBaQ== ================================================ FILE: .husky/pre-commit ================================================ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" npm run prettier npx lint-staged ================================================ FILE: .npmignore ================================================ .eslintignore .github .gitignore ================================================ FILE: .prettierignore ================================================ # Directories _playwright-* dist lib # Files _vars.css _vars-advanced.css CHANGELOG.md emoji-data.* HISTORY.md ================================================ FILE: .prettierrc.json ================================================ { "arrowParens": "avoid", "singleQuote": true } ================================================ FILE: .vscode/launch.json ================================================ { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Run unit tests", "runtimeExecutable": "npm", "runtimeArgs": ["run", "test:jest"], "console": "integratedTerminal" }, { "type": "node", "request": "launch", "name": "Run current test file", "runtimeExecutable": "npm", "runtimeArgs": ["run", "test:jest", "--"], "args": ["-i", "${relativeFile}", "--testPathIgnorePatterns"], "console": "integratedTerminal" }, { "type": "node", "request": "launch", "name": "Run selected test name", "runtimeExecutable": "npm", "runtimeArgs": ["run", "test:jest", "--"], "args": [ "-i", "${relativeFile}", "-t", "${selectedText}", "--testPathIgnorePatterns" ], "console": "integratedTerminal" }, { "type": "node", "request": "launch", "name": "Update current test file snapshot(s)", "runtimeExecutable": "npm", "runtimeArgs": ["run", "test:jest", "--"], "args": [ "-i", "${relativeFile}", "--updateSnapshot", "--testPathIgnorePatterns" ], "console": "integratedTerminal" }, { "type": "node", "request": "launch", "name": "Update selected test name snapshot(s)", "runtimeExecutable": "npm", "runtimeArgs": ["run", "test:jest", "--"], "args": [ "-i", "${relativeFile}", "-t", "${selectedText}", "--updateSnapshot", "--testPathIgnorePatterns" ], "console": "integratedTerminal" } ] } ================================================ FILE: .vscode/settings.json ================================================ { "editor.defaultFormatter": "esbenp.prettier-vscode", "cSpell.words": ["coverpage"], "typescript.tsdk": "node_modules/typescript/lib" } ================================================ FILE: CHANGELOG.md ================================================ # [5.0.0-rc.4](https://github.com/docsifyjs/docsify/compare/v5.0.0-rc.3...v5.0.0-rc.4) (2026-03-11) ### Bug Fixes * adjust intersection observer threshold for sticky class toggle ([#2637](https://github.com/docsifyjs/docsify/issues/2637)) ([4e8be38](https://github.com/docsifyjs/docsify/commit/4e8be38304ad5fcf64c585f42afef46c72adabb7)) * enhance accessibility for sidebar toggle button ([#2604](https://github.com/docsifyjs/docsify/issues/2604)) ([3014945](https://github.com/docsifyjs/docsify/commit/3014945e4d677d24124a3a01fe88ec82951ebfa7)) * improve word breaking for code blocks in markdown ([#2636](https://github.com/docsifyjs/docsify/issues/2636)) ([73d41e9](https://github.com/docsifyjs/docsify/commit/73d41e9e48ab7effae4c5dffd4aec4bd4e1b9168)) * include h6 headings in heading element selection ([#2649](https://github.com/docsifyjs/docsify/issues/2649)) ([44326ca](https://github.com/docsifyjs/docsify/commit/44326ca423b25ddbb12b172ab7180edd553c61f4)) * strip HTML tags from config.name when setting page title ([#2690](https://github.com/docsifyjs/docsify/issues/2690)) ([0cfee34](https://github.com/docsifyjs/docsify/commit/0cfee34dbc0d511a272070e4124eb2f3102d988e)), closes [#2610](https://github.com/docsifyjs/docsify/issues/2610) * typo ([#2632](https://github.com/docsifyjs/docsify/issues/2632)) ([2d45376](https://github.com/docsifyjs/docsify/commit/2d45376bb8dc3f7a07c633a7be4d38af3a56501b)) ### Features * add fallback default language support ([#2607](https://github.com/docsifyjs/docsify/issues/2607)) ([1abe3a9](https://github.com/docsifyjs/docsify/commit/1abe3a9ea48cd3a96ee65cd958090bf2936de3c9)) * enhance embed handling for table cells ([#2606](https://github.com/docsifyjs/docsify/issues/2606)) ([422a745](https://github.com/docsifyjs/docsify/commit/422a745acc4783c325d55413c5349ca70c42c67c)) * Fragment identifier full line ignore ([#2626](https://github.com/docsifyjs/docsify/issues/2626)) ([e811756](https://github.com/docsifyjs/docsify/commit/e8117563eae4aa9468c36b76fe83c5ce3f7423ea)) * output type definitions, and allow `new Docsify(opts)` to accept options for ESM usage ([#2392](https://github.com/docsifyjs/docsify/issues/2392)) ([b960519](https://github.com/docsifyjs/docsify/commit/b960519608747015d1ab974c9ed809c21f3b1bda)) # [5.0.0-rc.3](https://github.com/docsifyjs/docsify/compare/v5.0.0-rc.1...v5.0.0-rc.3) (2025-09-05) ### Bug Fixes * enhance focus handling ([#2595](https://github.com/docsifyjs/docsify/issues/2595)) ([22ac7e8](https://github.com/docsifyjs/docsify/commit/22ac7e855de24b13280e9904930e58af58dbd6a7)) * enhance focus handling by adding smooth scroll to content area ([#2569](https://github.com/docsifyjs/docsify/issues/2569)) ([20d095b](https://github.com/docsifyjs/docsify/commit/20d095b53c827c4d0590dee5fd377ba21560d7fb)) * escape HTML in search keywords ([#2586](https://github.com/docsifyjs/docsify/issues/2586)) ([743e9cb](https://github.com/docsifyjs/docsify/commit/743e9cb484cc70859a582fb74d78ea1f52ef2c2a)) * exclude app-name-link from sidebar text overflow styling ([#2564](https://github.com/docsifyjs/docsify/issues/2564)) ([375c058](https://github.com/docsifyjs/docsify/commit/375c058e3e1b12f3507d97a41b3fd8d6c4e904b0)) * handle hash navigation to prevent duplicate callbacks ([#2575](https://github.com/docsifyjs/docsify/issues/2575)) ([72569de](https://github.com/docsifyjs/docsify/commit/72569dee8db92a01e8de5448e05c32afbd090fb2)) * heading link overflow style ([#2568](https://github.com/docsifyjs/docsify/issues/2568)) ([be21637](https://github.com/docsifyjs/docsify/commit/be2163725ae314de3aa9a56c1216ce8c774cfcc6)) * improve slug generation ([#2581](https://github.com/docsifyjs/docsify/issues/2581)) ([9bc58c9](https://github.com/docsifyjs/docsify/commit/9bc58c9ca24db513a90a06752181954aab0628ca)) * normalize slugs to NFC and remove emoji variation selector ([#2597](https://github.com/docsifyjs/docsify/issues/2597)) ([5999f09](https://github.com/docsifyjs/docsify/commit/5999f09a4bac23169766af68fbdc71215161c1c6)) * remove block display from anchor links to prevent layout issues ([#2576](https://github.com/docsifyjs/docsify/issues/2576)) ([6e45024](https://github.com/docsifyjs/docsify/commit/6e4502416886b9494cd7b510968c351c7e6fc3cd)) * **sidebar:** remove ignored headings and children ([#2580](https://github.com/docsifyjs/docsify/issues/2580)) ([2a49bd0](https://github.com/docsifyjs/docsify/commit/2a49bd0aa4bb17eeb3d9f363c6f2b01e61e778ac)) * update relative link handling ([#2579](https://github.com/docsifyjs/docsify/issues/2579)) ([eeacfcc](https://github.com/docsifyjs/docsify/commit/eeacfcce2a3df4e4146b5b54275553452b317352)) ### Features * GitHub style callouts ([#2487](https://github.com/docsifyjs/docsify/issues/2487)) ([2e59b0f](https://github.com/docsifyjs/docsify/commit/2e59b0f50cf8123b98e4c97c3053aa6985ae13a2)) * support config helper multi keys if supported ([#2571](https://github.com/docsifyjs/docsify/issues/2571)) ([a2f734f](https://github.com/docsifyjs/docsify/commit/a2f734f223cae475aefe7d41995a2030a1f9c0a2)) * support extract content between fragment markers from Markdown ([#2582](https://github.com/docsifyjs/docsify/issues/2582)) ([32aa74e](https://github.com/docsifyjs/docsify/commit/32aa74e0d2fcd89705e5c30fc8b8e2562e7b774e)) # [5.0.0-rc.2](https://github.com/docsifyjs/docsify/compare/v5.0.0-rc.1...v5.0.0-rc.2) (2025-09-05) ### Bug Fixes * enhance focus handling ([#2595](https://github.com/docsifyjs/docsify/issues/2595)) ([22ac7e8](https://github.com/docsifyjs/docsify/commit/22ac7e855de24b13280e9904930e58af58dbd6a7)) * enhance focus handling by adding smooth scroll to content area ([#2569](https://github.com/docsifyjs/docsify/issues/2569)) ([20d095b](https://github.com/docsifyjs/docsify/commit/20d095b53c827c4d0590dee5fd377ba21560d7fb)) * escape HTML in search keywords ([#2586](https://github.com/docsifyjs/docsify/issues/2586)) ([743e9cb](https://github.com/docsifyjs/docsify/commit/743e9cb484cc70859a582fb74d78ea1f52ef2c2a)) * exclude app-name-link from sidebar text overflow styling ([#2564](https://github.com/docsifyjs/docsify/issues/2564)) ([375c058](https://github.com/docsifyjs/docsify/commit/375c058e3e1b12f3507d97a41b3fd8d6c4e904b0)) * handle hash navigation to prevent duplicate callbacks ([#2575](https://github.com/docsifyjs/docsify/issues/2575)) ([72569de](https://github.com/docsifyjs/docsify/commit/72569dee8db92a01e8de5448e05c32afbd090fb2)) * heading link overflow style ([#2568](https://github.com/docsifyjs/docsify/issues/2568)) ([be21637](https://github.com/docsifyjs/docsify/commit/be2163725ae314de3aa9a56c1216ce8c774cfcc6)) * improve slug generation ([#2581](https://github.com/docsifyjs/docsify/issues/2581)) ([9bc58c9](https://github.com/docsifyjs/docsify/commit/9bc58c9ca24db513a90a06752181954aab0628ca)) * remove block display from anchor links to prevent layout issues ([#2576](https://github.com/docsifyjs/docsify/issues/2576)) ([6e45024](https://github.com/docsifyjs/docsify/commit/6e4502416886b9494cd7b510968c351c7e6fc3cd)) * **sidebar:** remove ignored headings and children ([#2580](https://github.com/docsifyjs/docsify/issues/2580)) ([2a49bd0](https://github.com/docsifyjs/docsify/commit/2a49bd0aa4bb17eeb3d9f363c6f2b01e61e778ac)) * update relative link handling ([#2579](https://github.com/docsifyjs/docsify/issues/2579)) ([eeacfcc](https://github.com/docsifyjs/docsify/commit/eeacfcce2a3df4e4146b5b54275553452b317352)) ### Features * GitHub style callouts ([#2487](https://github.com/docsifyjs/docsify/issues/2487)) ([2e59b0f](https://github.com/docsifyjs/docsify/commit/2e59b0f50cf8123b98e4c97c3053aa6985ae13a2)) * support config helper multi keys if supported ([#2571](https://github.com/docsifyjs/docsify/issues/2571)) ([a2f734f](https://github.com/docsifyjs/docsify/commit/a2f734f223cae475aefe7d41995a2030a1f9c0a2)) * support extract content between fragment markers from Markdown ([#2582](https://github.com/docsifyjs/docsify/issues/2582)) ([32aa74e](https://github.com/docsifyjs/docsify/commit/32aa74e0d2fcd89705e5c30fc8b8e2562e7b774e)) # Changelog ## [5.0.0-rc.1](https://github.com/docsifyjs/docsify/compare/v4.13.1...v5.0.0-rc.1) (2025-05-27) ### Bug Fixes * auto header config heading generate func ([#2474](https://github.com/docsifyjs/docsify/issues/2474)) ([4bc5062](https://github.com/docsifyjs/docsify/commit/4bc5062fc13a3a43c7ed432f1b585fdab41f1447)) * carbon broken ([#2387](https://github.com/docsifyjs/docsify/issues/2387)) ([87fd55d](https://github.com/docsifyjs/docsify/commit/87fd55d7125539b929f3260fca92e666e988b6da)) * **ci:** run test action for pull requests ([#2445](https://github.com/docsifyjs/docsify/issues/2445)) ([15ed3b7](https://github.com/docsifyjs/docsify/commit/15ed3b76b00eac06cc4230b1f592993adf2a893b)) * correct loadSidebar=false render structure issue ([#2470](https://github.com/docsifyjs/docsify/issues/2470)) ([7cbd532](https://github.com/docsifyjs/docsify/commit/7cbd5322d056bb87b4340bdb19909a9c32d19abb)) * dev mode hot reload and add sourcemaps ([#2402](https://github.com/docsifyjs/docsify/issues/2402)) ([947d8de](https://github.com/docsifyjs/docsify/commit/947d8decb8c5c62f3ce50d0c6ac0e27bb6c7a6b5)) * enhancement of isExternal ([#2093](https://github.com/docsifyjs/docsify/issues/2093)) ([7f13ba0](https://github.com/docsifyjs/docsify/commit/7f13ba0f9841776008d0707f027bd80c4e3cbf0c)) * fix cross-origin links in history router mode ([#1967](https://github.com/docsifyjs/docsify/issues/1967)) ([ef6905b](https://github.com/docsifyjs/docsify/commit/ef6905b53a5c1587c3ebf870f0d11ff111a2350d)) * fix dependabot.yml ([a321e83](https://github.com/docsifyjs/docsify/commit/a321e8373b3c6afc9d0b08714e34bd8bd68716d9)) * fix docsify-server-renderer dependency. ([#1915](https://github.com/docsifyjs/docsify/issues/1915)) ([c73f858](https://github.com/docsifyjs/docsify/commit/c73f8587b2f67e28c228e521518110ff504cefeb)) * fix id with pure number. ([#2021](https://github.com/docsifyjs/docsify/issues/2021)) ([f4f21a3](https://github.com/docsifyjs/docsify/commit/f4f21a3f74d265f16b1e658feda417bf851458da)) * genIndex error for search ([#1933](https://github.com/docsifyjs/docsify/issues/1933)) ([a8f9fc1](https://github.com/docsifyjs/docsify/commit/a8f9fc1d5f5c7808efe65e5ca9359f38014b1bcd)) * husky can not auto install issue after upgrade. ([#2325](https://github.com/docsifyjs/docsify/issues/2325)) ([cec43d7](https://github.com/docsifyjs/docsify/commit/cec43d71774556d38fa9746f4df4319a6ad768c2)) * parse heading error ([#2526](https://github.com/docsifyjs/docsify/issues/2526)) ([561c777](https://github.com/docsifyjs/docsify/commit/561c777df898a71f8b44b25e5946b56eb1d2c106)) * Prevent initial unnecessary IntersectionObserver callback execution ([#2523](https://github.com/docsifyjs/docsify/issues/2523)) ([a73e07e](https://github.com/docsifyjs/docsify/commit/a73e07eac06a70f63896e2bb7f0c205534dc21c8)) * prevent unnecessary themeColor deprecation notice ([#2403](https://github.com/docsifyjs/docsify/issues/2403)) ([a3ab2be](https://github.com/docsifyjs/docsify/commit/a3ab2be0b070a1bacc4fec3c2d2f0ffe279bada0)) * **search:** clean markdown elements in search contents ([#2457](https://github.com/docsifyjs/docsify/issues/2457)) ([95901eb](https://github.com/docsifyjs/docsify/commit/95901eb8a829865c883a4374c53a0b8909b53c41)) * skip-to-content scroll behavior ([#2401](https://github.com/docsifyjs/docsify/issues/2401)) ([2d986fe](https://github.com/docsifyjs/docsify/commit/2d986feb34fcdb2a2731cff7b29b0a4d0563787e)) * sync the page title regarding the title config ([#2478](https://github.com/docsifyjs/docsify/issues/2478)) ([2eec7c4](https://github.com/docsifyjs/docsify/commit/2eec7c4884146add6e0c8142e463cfad34fb7637)) * upgrade debug from 4.3.3 to 4.3.4 ([#1919](https://github.com/docsifyjs/docsify/issues/1919)) ([0c9221e](https://github.com/docsifyjs/docsify/commit/0c9221e5a425984470ebc89ffb682e988b08b06e)) * upgrade medium-zoom from 1.0.6 to 1.0.7 ([#1934](https://github.com/docsifyjs/docsify/issues/1934)) ([2601392](https://github.com/docsifyjs/docsify/commit/26013929ccf1927cc6a46c5455fb70f71d90492f)) * upgrade medium-zoom from 1.0.7 to 1.0.8 ([#1939](https://github.com/docsifyjs/docsify/issues/1939)) ([81fc1b7](https://github.com/docsifyjs/docsify/commit/81fc1b7dce15d74bb025d3f95bfa5dae55540a10)) * upgrade node-fetch from 2.6.8 to 2.6.9 ([#1996](https://github.com/docsifyjs/docsify/issues/1996)) ([bb88d63](https://github.com/docsifyjs/docsify/commit/bb88d6302b22b2484cca6436c7b4279d5e7fc5ea)) * When alias contains parameters, append ext error ([#1855](https://github.com/docsifyjs/docsify/issues/1855)) ([1a32fb7](https://github.com/docsifyjs/docsify/commit/1a32fb73347308696f6e82c0757ba081a4e7b8de)) ### Features * Add "Skip to main content" link and update nav behavior ([#2253](https://github.com/docsifyjs/docsify/issues/2253)) ([50b84f7](https://github.com/docsifyjs/docsify/commit/50b84f74b25c27f1a3a102055e7f099db31155f8)) * add google analytics gtag.js plugin ([#1702](https://github.com/docsifyjs/docsify/issues/1702)) ([29f3c82](https://github.com/docsifyjs/docsify/commit/29f3c82faaffb817d2149d23a0e3a884cd1cc843)) * Allow top nav to receive keyboard focus ([#2269](https://github.com/docsifyjs/docsify/issues/2269)) ([4d5bf5a](https://github.com/docsifyjs/docsify/commit/4d5bf5ac48499c57cfdffd98bfaa29bf20d85260)) * Keyboard bindings ([#2279](https://github.com/docsifyjs/docsify/issues/2279)) ([cf61192](https://github.com/docsifyjs/docsify/commit/cf61192f9a467c96372bce9e4e371a3f0c6a1780)) * **search:** use dexie.js instead of localStorage ([#2464](https://github.com/docsifyjs/docsify/issues/2464)) ([42f2548](https://github.com/docsifyjs/docsify/commit/42f25482fa1ea6ec666800145f6da33e9119aa8c)) * Support dark mode for zoom-image plugin ([#2524](https://github.com/docsifyjs/docsify/issues/2524)) ([5826863](https://github.com/docsifyjs/docsify/commit/58268632c8ebc2855c38305a05b8326727e388c1)) * support prism langs dependencies import validation ([#2489](https://github.com/docsifyjs/docsify/issues/2489)) ([87e43f1](https://github.com/docsifyjs/docsify/commit/87e43f157f749dda01b54e33117ef851b12bda7c)) * support relative path with target config. ([#1751](https://github.com/docsifyjs/docsify/issues/1751)) ([e15ad0c](https://github.com/docsifyjs/docsify/commit/e15ad0c7d530287692e98ca587f8f07d90624919)) * v5 style overhaul ([#2469](https://github.com/docsifyjs/docsify/issues/2469)) ([77d93fa](https://github.com/docsifyjs/docsify/commit/77d93fae7886346e739285630ea865ac8197149e)) ## [4.13.1](https://github.com/docsifyjs/docsify/compare/v4.13.0...v4.13.1) (2023-06-24) ### Bug Fixes * enhancement of isExternal ([#2093](https://github.com/docsifyjs/docsify/issues/2093)) ([6a7d15b](https://github.com/docsifyjs/docsify/commit/6a7d15b1d5b93e19d3cf9a328cdbf5f1a166b5bd)) * fix cross-origin links in history router mode ([#1967](https://github.com/docsifyjs/docsify/issues/1967)) ([2312fee](https://github.com/docsifyjs/docsify/commit/2312feef459211a8bcdcbf9164a9ffe051609b70)) * genIndex error for search ([#1933](https://github.com/docsifyjs/docsify/issues/1933)) ([68d8735](https://github.com/docsifyjs/docsify/commit/68d873587c29d694ece466177984aa5fd739dd4b)) # [4.13.0](https://github.com/docsifyjs/docsify/compare/v4.12.4...v4.13.0) (2022-10-26) ### Bug Fixes * cornerExternalLinkTarget config. ([#1814](https://github.com/docsifyjs/docsify/issues/1814)) ([54cc5f9](https://github.com/docsifyjs/docsify/commit/54cc5f939b9499ae56604f589eef4e3f1c13cdc5)) * correctly fix missing +1, -1 ([#1722](https://github.com/docsifyjs/docsify/issues/1722)) ([719dcbe](https://github.com/docsifyjs/docsify/commit/719dcbea5cb0c8b0835f8e9fc473b094feecb7ec)) * Coverpage when content > viewport height ([#1744](https://github.com/docsifyjs/docsify/issues/1744)) ([301b516](https://github.com/docsifyjs/docsify/commit/301b5169613e95765eda335a4b21d0f9f9cbbbfd)), closes [#381](https://github.com/docsifyjs/docsify/issues/381) * filter null node first. ([#1909](https://github.com/docsifyjs/docsify/issues/1909)) ([d27af3d](https://github.com/docsifyjs/docsify/commit/d27af3dd09a882fce4f1e2774065de57a3501858)) * fix docsify-ignore in search title. ([#1872](https://github.com/docsifyjs/docsify/issues/1872)) ([9832805](https://github.com/docsifyjs/docsify/commit/9832805681cc6099e9c78deecf6dc0c6fb61fd9b)) * fix search with no content. ([#1878](https://github.com/docsifyjs/docsify/issues/1878)) ([9b74744](https://github.com/docsifyjs/docsify/commit/9b74744299ece0108573a142e5a2e949dc569254)) * Ignore emoji shorthand codes in URIs ([#1847](https://github.com/docsifyjs/docsify/issues/1847)) ([3c9b3d9](https://github.com/docsifyjs/docsify/commit/3c9b3d9702bb05a5ff45a4ce4233e144cf1ebecb)), closes [#1823](https://github.com/docsifyjs/docsify/issues/1823) * Legacy bugs (styles, site plugin error, and dev server error) ([#1743](https://github.com/docsifyjs/docsify/issues/1743)) ([fa6df6d](https://github.com/docsifyjs/docsify/commit/fa6df6d58487ec294e22be04ac159dfb5745bd66)) * package.json & package-lock.json to reduce vulnerabilities ([#1756](https://github.com/docsifyjs/docsify/issues/1756)) ([2dc5b12](https://github.com/docsifyjs/docsify/commit/2dc5b12b715e3ad1922a6401e3fd9837a99ef9c0)) * packages/docsify-server-renderer/package.json & packages/docsify-server-renderer/package-lock.json to reduce vulnerabilities ([#1715](https://github.com/docsifyjs/docsify/issues/1715)) ([c1227b2](https://github.com/docsifyjs/docsify/commit/c1227b22cb8a3fb6c362ca8ac109082ab2251cc3)) ### Features * Emoji build ([#1766](https://github.com/docsifyjs/docsify/issues/1766)) ([ba5ee26](https://github.com/docsifyjs/docsify/commit/ba5ee26f00e957b58173f96b1901f6ffd3d8e5f5)) * Native emoji w/ image-based fallbacks and improved parsing ([#1746](https://github.com/docsifyjs/docsify/issues/1746)) ([35002c9](https://github.com/docsifyjs/docsify/commit/35002c92b762f0d12e51582d7de7914fa380596a)), closes [#779](https://github.com/docsifyjs/docsify/issues/779) * Plugin error handling ([#1742](https://github.com/docsifyjs/docsify/issues/1742)) ([63b2535](https://github.com/docsifyjs/docsify/commit/63b2535a45a98945ec897277f4fbddec2ddba887)) ## [4.12.2](https://github.com/docsifyjs/docsify/compare/v4.12.1...v4.12.2) (2022-01-06) ### Bug Fixes * Add escapeHtml for search ([#1551](https://github.com/docsifyjs/docsify/issues/1551)) ([c24f7f6](https://github.com/docsifyjs/docsify/commit/c24f7f6f0b87a87f6dd3755f69eb0969ebb029c9)) * allow also " inside of an embed ([ec16e4a](https://github.com/docsifyjs/docsify/commit/ec16e4a9d5718ac4f4c25bb3dcaea3b7551372e0)) * buble theme missing generic fallback font ([#1568](https://github.com/docsifyjs/docsify/issues/1568)) ([37d9f0e](https://github.com/docsifyjs/docsify/commit/37d9f0e1214276e93b2a11ed87390aafa1bdbcec)) * Cannot read property 'classList' of null ([#1527](https://github.com/docsifyjs/docsify/issues/1527)) ([d6df2b8](https://github.com/docsifyjs/docsify/commit/d6df2b85a99371bb9a87402a10dd515bb734182e)) * Cannot read property 'tagName' of null ([#1655](https://github.com/docsifyjs/docsify/issues/1655)) ([c3cdadc](https://github.com/docsifyjs/docsify/commit/c3cdadc37137edcd9e219359973902d2fc8b66ff)) * upgrade debug from 4.3.2 to 4.3.3 ([#1692](https://github.com/docsifyjs/docsify/issues/1692)) ([40e7749](https://github.com/docsifyjs/docsify/commit/40e77490c68b4143c75dfaebcd0b7f640581306b)) * Upgrade docsify from 4.12.0 to 4.12.1 ([#1544](https://github.com/docsifyjs/docsify/issues/1544)) ([d607f6d](https://github.com/docsifyjs/docsify/commit/d607f6d71c35b50f586806a832f65061f5e3427e)) * upgrade dompurify from 2.2.6 to 2.2.7 ([#1552](https://github.com/docsifyjs/docsify/issues/1552)) ([407e4d4](https://github.com/docsifyjs/docsify/commit/407e4d4f3de78bebd639a3fdae751f8045728e57)) * Upgrade dompurify from 2.2.6 to 2.2.7 ([#1553](https://github.com/docsifyjs/docsify/issues/1553)) ([93c48f3](https://github.com/docsifyjs/docsify/commit/93c48f3d615d95dba550a0e95df6b545d68c3593)) * upgrade dompurify from 2.2.7 to 2.2.8 ([#1577](https://github.com/docsifyjs/docsify/issues/1577)) ([0dd44cc](https://github.com/docsifyjs/docsify/commit/0dd44cc828cc54f7c3b776d45b32925b66cae499)) * upgrade dompurify from 2.2.7 to 2.3.0 ([#1619](https://github.com/docsifyjs/docsify/issues/1619)) ([66303fe](https://github.com/docsifyjs/docsify/commit/66303fec4c7115621e556ad742cfac9d19f26bd9)) * upgrade dompurify from 2.2.8 to 2.2.9 ([#1600](https://github.com/docsifyjs/docsify/issues/1600)) ([baf5a8a](https://github.com/docsifyjs/docsify/commit/baf5a8a4962656d8be8f714283064d2ea10c7e14)) * upgrade dompurify from 2.2.9 to 2.3.0 ([#1616](https://github.com/docsifyjs/docsify/issues/1616)) ([b07fa3c](https://github.com/docsifyjs/docsify/commit/b07fa3cc8323e63dd7b105c7e29b2e1914f5c117)) * upgrade dompurify from 2.3.0 to 2.3.1 ([#1635](https://github.com/docsifyjs/docsify/issues/1635)) ([5ac8237](https://github.com/docsifyjs/docsify/commit/5ac8237cc76e19ca2b373a1a1da6eb4a4da6d8b2)) * upgrade dompurify from 2.3.1 to 2.3.2 ([#1647](https://github.com/docsifyjs/docsify/issues/1647)) ([ff6acfa](https://github.com/docsifyjs/docsify/commit/ff6acfa7623a7db8b00d62c51a9c3037215c4888)) * upgrade node-fetch from 2.6.1 to 2.6.2 ([#1641](https://github.com/docsifyjs/docsify/issues/1641)) ([6ee1c14](https://github.com/docsifyjs/docsify/commit/6ee1c142769a6442aa8c1523ab215106707fa7fc)) * upgrade node-fetch from 2.6.2 to 2.6.4 ([#1649](https://github.com/docsifyjs/docsify/issues/1649)) ([6f81034](https://github.com/docsifyjs/docsify/commit/6f81034ba6a7a6b64ccf1acd2d1fc73761f70a63)) * upgrade node-fetch from 2.6.4 to 2.6.5 ([#1654](https://github.com/docsifyjs/docsify/issues/1654)) ([d16e657](https://github.com/docsifyjs/docsify/commit/d16e657f708777e8377d8e158b50b4010623282d)) * upgrade node-fetch from 2.6.5 to 2.6.6 ([#1668](https://github.com/docsifyjs/docsify/issues/1668)) ([cefe3f8](https://github.com/docsifyjs/docsify/commit/cefe3f87e697a6c54a74d601df2eeb331fcd8933)) ## [4.12.1](https://github.com/docsifyjs/docsify/compare/v4.12.0...v4.12.1) (2021-03-07) ### Bug Fixes * isExternal check with malformed URL + tests ([#1510](https://github.com/docsifyjs/docsify/issues/1510)) ([ff2a66f](https://github.com/docsifyjs/docsify/commit/ff2a66f12752471277fe81a64ad6c4b2c08111fe)), closes [#1477](https://github.com/docsifyjs/docsify/issues/1477) [#1126](https://github.com/docsifyjs/docsify/issues/1126) [#1489](https://github.com/docsifyjs/docsify/issues/1489) * Replace ES6 usage for IE11 compatibility ([#1500](https://github.com/docsifyjs/docsify/issues/1500)) ([a0f61b2](https://github.com/docsifyjs/docsify/commit/a0f61b2af72cb888ea5b635021a5c9da6beb7ac5)) * theme switcher in IE11 ([#1502](https://github.com/docsifyjs/docsify/issues/1502)) ([8cda078](https://github.com/docsifyjs/docsify/commit/8cda07891afeb1ea6e198d2a600f205357ab4b89)) * Upgrade docsify from 4.11.6 to 4.12.0 ([#1518](https://github.com/docsifyjs/docsify/issues/1518)) ([47cd86c](https://github.com/docsifyjs/docsify/commit/47cd86c8f196a241fc23720e3addfe95d4c973cd)) ### Features * Support search when there is no title ([#1519](https://github.com/docsifyjs/docsify/issues/1519)) ([bc37268](https://github.com/docsifyjs/docsify/commit/bc3726853fb2d1f9241927ea0317970ab0c8a2f2)) ### Chore - Fix missing carbon ([#1501](https://github.com/docsifyjs/docsify/issues/1501)) - Change Gitter to Discord throughout project ([#1507](https://github.com/docsifyjs/docsify/issues/1507)) - Add test cases on isExternal ([#1515](https://github.com/docsifyjs/docsify/issues/1515)) # [4.12.0](https://github.com/docsifyjs/docsify/compare/v4.11.6...v4.12.0) (2021-02-08) ### Bug Fixes * add missing argument for highlighting code ([#1365](https://github.com/docsifyjs/docsify/issues/1365)) ([f35bf99](https://github.com/docsifyjs/docsify/commit/f35bf99d9c762774e328b30347707e62eb8e6f63)) * Can't search homepage content ([#1391](https://github.com/docsifyjs/docsify/issues/1391)) ([25bc9b7](https://github.com/docsifyjs/docsify/commit/25bc9b7eb7e878a6a50ed5f91d33d6a75f9811b0)) * Cannot read property 'startsWith' of undefined ([#1358](https://github.com/docsifyjs/docsify/issues/1358)) ([9351729](https://github.com/docsifyjs/docsify/commit/9351729634b52db0e7e241bed7784fbcd5c39fe0)) * Cannot read property level of undefined ([#1357](https://github.com/docsifyjs/docsify/issues/1357)) ([4807e58](https://github.com/docsifyjs/docsify/commit/4807e58cb994de83f063cb82d2b4a695f29378c8)) * cannot search list content ([#1361](https://github.com/docsifyjs/docsify/issues/1361)) ([8d17dcb](https://github.com/docsifyjs/docsify/commit/8d17dcbe68048d654e62adb01a3925e39a8e0c44)) * duplicate search content when `/README` or `/` exists in the sidebar ([#1403](https://github.com/docsifyjs/docsify/issues/1403)) ([7c3bf98](https://github.com/docsifyjs/docsify/commit/7c3bf98df869188d5956ed1a331f7048b6eba441)) * package.json & package-lock.json to reduce vulnerabilities ([#1419](https://github.com/docsifyjs/docsify/issues/1419)) ([69b6907](https://github.com/docsifyjs/docsify/commit/69b6907c864dbcdf1fe5c75f51a80e6ae6ec279d)) * packages/docsify-server-renderer/package.json & packages/docsify-server-renderer/package-lock.json to reduce vulnerabilities ([#1389](https://github.com/docsifyjs/docsify/issues/1389)) ([62cd35e](https://github.com/docsifyjs/docsify/commit/62cd35ee8345270aab7a48bc761db007d54a0f48)) * packages/docsify-server-renderer/package.json & packages/docsify-server-renderer/package-lock.json to reduce vulnerabilities ([#1418](https://github.com/docsifyjs/docsify/issues/1418)) ([58fbca0](https://github.com/docsifyjs/docsify/commit/58fbca00ebd12f636c213d386761df9ebfb2bd4c)) * Prevent loading remote content via URL hash ([#1489](https://github.com/docsifyjs/docsify/issues/1489)) ([14ce7f3](https://github.com/docsifyjs/docsify/commit/14ce7f3d862ac79fc7d9d316cb2e057d50e1b506)), closes [#1477](https://github.com/docsifyjs/docsify/issues/1477) [#1126](https://github.com/docsifyjs/docsify/issues/1126) * search on homepage test ([#1398](https://github.com/docsifyjs/docsify/issues/1398)) ([ee550d0](https://github.com/docsifyjs/docsify/commit/ee550d0c51f222851854c7cd7e9e6f76e26eb6f4)) * search titles containing ignored characters ([#1395](https://github.com/docsifyjs/docsify/issues/1395)) ([a2ebb21](https://github.com/docsifyjs/docsify/commit/a2ebb2192ac73211a8924111d736df9574abf61b)) * sidebar active class and expand don't work as expect when use "space" in markdown filename ([#1454](https://github.com/docsifyjs/docsify/issues/1454)) ([dcf5a64](https://github.com/docsifyjs/docsify/commit/dcf5a644eb6a2eef65fb940f3407c12828a679bc)), closes [#1032](https://github.com/docsifyjs/docsify/issues/1032) * sidebar horizontal scroll bar ([#1362](https://github.com/docsifyjs/docsify/issues/1362)) ([b480822](https://github.com/docsifyjs/docsify/commit/b480822286c66b478e5a7a9b2c82a10b99c69121)) * sidebar title error ([#1360](https://github.com/docsifyjs/docsify/issues/1360)) ([2100fc3](https://github.com/docsifyjs/docsify/commit/2100fc318b009c6e448e48ffff6a693f1988916c)) * slugs are still broken when headings contain html ([#1443](https://github.com/docsifyjs/docsify/issues/1443)) ([76c5e68](https://github.com/docsifyjs/docsify/commit/76c5e685d75ee6df9acc0a7cf92d5daa138c3240)) * the sidebar links to another site. ([#1336](https://github.com/docsifyjs/docsify/issues/1336)) ([c9d4f7a](https://github.com/docsifyjs/docsify/commit/c9d4f7abc94a2cbc4bb558013775df380c1c8376)) * title error when sidebar link exists with html tag ([#1404](https://github.com/docsifyjs/docsify/issues/1404)) ([8ccc202](https://github.com/docsifyjs/docsify/commit/8ccc20225104376af2e5df6757c4dbd58c0e758e)), closes [#1408](https://github.com/docsifyjs/docsify/issues/1408) * Unable to navigate on server without default index support ([#1372](https://github.com/docsifyjs/docsify/issues/1372)) ([759ffac](https://github.com/docsifyjs/docsify/commit/759ffac992b19dbb05b92114ec5620d3bb180d0d)) * upgrade debug from 4.1.1 to 4.3.0 ([#1390](https://github.com/docsifyjs/docsify/issues/1390)) ([ae45b32](https://github.com/docsifyjs/docsify/commit/ae45b3201ba03303a2feb5a347b18fda818a569a)) * upgrade debug from 4.3.0 to 4.3.1 ([#1446](https://github.com/docsifyjs/docsify/issues/1446)) ([bc3350f](https://github.com/docsifyjs/docsify/commit/bc3350f6e6bfe670c95569b4e9c51321f5c7dbb9)) * upgrade debug from 4.3.1 to 4.3.2 ([#1463](https://github.com/docsifyjs/docsify/issues/1463)) ([df21153](https://github.com/docsifyjs/docsify/commit/df21153ab5e841ad89707e07c68a04873a2f3fe8)) * upgrade docsify from 4.11.4 to 4.11.6 ([#1373](https://github.com/docsifyjs/docsify/issues/1373)) ([c2d12ed](https://github.com/docsifyjs/docsify/commit/c2d12ed27fe86b0c5cd690b4d8a22bf06d2a90b9)) * upgrade dompurify from 2.0.17 to 2.1.0 ([#1397](https://github.com/docsifyjs/docsify/issues/1397)) ([1863d8e](https://github.com/docsifyjs/docsify/commit/1863d8edb70da234bf7f211986ba71706351682f)) * upgrade dompurify from 2.1.0 to 2.1.1 ([#1402](https://github.com/docsifyjs/docsify/issues/1402)) ([8cf9fd8](https://github.com/docsifyjs/docsify/commit/8cf9fd8150bd67709c68d8dfe4dc881624583ac8)) * upgrade dompurify from 2.2.2 to 2.2.3 ([#1457](https://github.com/docsifyjs/docsify/issues/1457)) ([720d909](https://github.com/docsifyjs/docsify/commit/720d9091c8e571d6c891426983f54d9c94739182)) * upgrade dompurify from 2.2.2 to 2.2.6 ([#1483](https://github.com/docsifyjs/docsify/issues/1483)) ([eee9507](https://github.com/docsifyjs/docsify/commit/eee9507d435459ae8e68e1112bb58a63b2f58530)) * upgrade dompurify from 2.2.3 to 2.2.6 ([#1482](https://github.com/docsifyjs/docsify/issues/1482)) ([7adad57](https://github.com/docsifyjs/docsify/commit/7adad57df1b7efa469b0cde37f788c36dc27cf8b)) * upgrade marked from 1.2.4 to 1.2.9 ([#1486](https://github.com/docsifyjs/docsify/issues/1486)) ([716a7fa](https://github.com/docsifyjs/docsify/commit/716a7fa777a1eee66964977e1d4405cff3275c53)) * upgrade prismjs from 1.21.0 to 1.22.0 ([#1415](https://github.com/docsifyjs/docsify/issues/1415)) ([0806f48](https://github.com/docsifyjs/docsify/commit/0806f48531b6cb5e6a395c12ab88f0b59281bbf8)) * upgrade prismjs from 1.22.0 to 1.23.0 ([#1481](https://github.com/docsifyjs/docsify/issues/1481)) ([5f29cde](https://github.com/docsifyjs/docsify/commit/5f29cde84c7b74d70c34e3f1f43c479f1bba670d)) * Use legacy-compatible methods for IE11 ([#1495](https://github.com/docsifyjs/docsify/issues/1495)) ([06cbebf](https://github.com/docsifyjs/docsify/commit/06cbebfc0d34726f4a7102a7dc9020520f3a7f86)) ### Features * search ignore diacritical marks ([#1434](https://github.com/docsifyjs/docsify/issues/1434)) ([8968a74](https://github.com/docsifyjs/docsify/commit/8968a744cea5910057ba59ef690316722a35b341)) * Add Jest + Playwright Testing ([#1276](https://github.com/docsifyjs/docsify/issues/1276)) * Add Vue components, mount options, global options, and v3 support ([#1409](https://github.com/docsifyjs/docsify/issues/1409)) ## [4.11.6](https://github.com/docsifyjs/docsify/compare/v4.11.5...v4.11.6) (2020-08-22) ### Bug Fixes * Add patch for {docsify-ignore} and {docsify-ignore-all} ([ce31607](https://github.com/docsifyjs/docsify/commit/ce316075e033afdbeb43ce01e284a29fe1870e38)) ## [4.11.5](https://github.com/docsifyjs/docsify/compare/v4.11.4...v4.11.5) (2020-08-21) ### Bug Fixes * Russian language link error ([#1270](https://github.com/docsifyjs/docsify/issues/1270)) ([2a52460](https://github.com/docsifyjs/docsify/commit/2a52460a59448abaf681046fbc5dca642285ae1f)) * {docsify-updated} in the sample code is parsed into time ([#1321](https://github.com/docsifyjs/docsify/issues/1321)) ([2048610](https://github.com/docsifyjs/docsify/commit/2048610aacd4e3c6a592f4247834a726c7ca33fb)) * Add error handling for missing dependencies (fixes [#1210](https://github.com/docsifyjs/docsify/issues/1210)) ([#1232](https://github.com/docsifyjs/docsify/issues/1232)) ([3673001](https://github.com/docsifyjs/docsify/commit/3673001a24cb24c57454f9bc7619de49d2c3a044)) * after setting the background image, the button is obscured ([#1234](https://github.com/docsifyjs/docsify/issues/1234)) ([34d918f](https://github.com/docsifyjs/docsify/commit/34d918f9973bdb8e893248853e3ef7e803d4c253)) * convert {docsify-ignore} and {docsify-ignore-all} to HTML comments ([#1318](https://github.com/docsifyjs/docsify/issues/1318)) ([90d283d](https://github.com/docsifyjs/docsify/commit/90d283d340502456a5d8495df596bb4a02ceb39b)) * fallback page should use path not file location ([#1301](https://github.com/docsifyjs/docsify/issues/1301)) ([2bceabc](https://github.com/docsifyjs/docsify/commit/2bceabcb8e623570540493e2f1d956adf45c99e7)) * Fix search error when exist translations documents ([#1300](https://github.com/docsifyjs/docsify/issues/1300)) ([b869019](https://github.com/docsifyjs/docsify/commit/b8690199006366e86084e9e018def7b9b8f46512)) * gitignore was ignoring folders in src, so VS Code search results or file fuzzy finder were not working, etc ([d4c9247](https://github.com/docsifyjs/docsify/commit/d4c9247b87c0a2701683ed1a17383cfb451cf609)) * packages/docsify-server-renderer/package.json & packages/docsify-server-renderer/package-lock.json to reduce vulnerabilities ([#1250](https://github.com/docsifyjs/docsify/issues/1250)) ([d439bac](https://github.com/docsifyjs/docsify/commit/d439bac93f479d0480799880538fc3104e54c907)) * search can not search the table header ([#1256](https://github.com/docsifyjs/docsify/issues/1256)) ([3f03e78](https://github.com/docsifyjs/docsify/commit/3f03e78418993d8e9a4f5062e10dc79c3753389e)) * Search plugin: matched text is replaced with search text ([#1298](https://github.com/docsifyjs/docsify/issues/1298)) ([78775b6](https://github.com/docsifyjs/docsify/commit/78775b6ee73102cc5ac71c0ee2b392c5f4f6f4f8)) * the uncaught typeerror when el is null ([#1308](https://github.com/docsifyjs/docsify/issues/1308)) ([952f4c9](https://github.com/docsifyjs/docsify/commit/952f4c921b7a6a558c500ca6b105582d39ad36a2)) * Updated docs with instructions for installing specific version (fixes [#780](https://github.com/docsifyjs/docsify/issues/780)) ([#1225](https://github.com/docsifyjs/docsify/issues/1225)) ([b90c948](https://github.com/docsifyjs/docsify/commit/b90c948090e89fa778279c95060dbd7668285658)) * upgrade medium-zoom from 1.0.5 to 1.0.6 ([3beaa66](https://github.com/docsifyjs/docsify/commit/3beaa6666b78518f1ffaa37f6942f3cb08fef896)) * upgrade tinydate from 1.2.0 to 1.3.0 ([#1341](https://github.com/docsifyjs/docsify/issues/1341)) ([59d090f](https://github.com/docsifyjs/docsify/commit/59d090fe9096bc03e259c166634bb75bb2623f85)) ### Features * **search:** add pathNamespaces option ([d179dde](https://github.com/docsifyjs/docsify/commit/d179dde1c71acdcbe66cb762377b123926c55bf2)) * Add title to sidebar links ([#1286](https://github.com/docsifyjs/docsify/issues/1286)) ([667496b](https://github.com/docsifyjs/docsify/commit/667496b85d99b168255f58e60a6bfe902cc6ee03)) ## [4.11.4](https://github.com/docsifyjs/docsify/compare/v4.11.3...v4.11.4) (2020-06-18) ### Bug Fixes * consistent location of search result ([e9dd2de](https://github.com/docsifyjs/docsify/commit/e9dd2de384b81619aae2bcbf92f52721cb76a177)) * cover overlapping sidebar by removing z-index ([0bf03f5](https://github.com/docsifyjs/docsify/commit/0bf03f58103037d100b1635cf3989c8d3672b4ba)) * cross-origin url cannot be redirected when "externalLinkTarget" is set to "_self" and "routerMode" is set to "history". ([#1062](https://github.com/docsifyjs/docsify/issues/1062)) ([fd2cec6](https://github.com/docsifyjs/docsify/commit/fd2cec6bd66c46d6957811fefae4c615c3052a4f)), closes [#1046](https://github.com/docsifyjs/docsify/issues/1046) [#1046](https://github.com/docsifyjs/docsify/issues/1046) [#1046](https://github.com/docsifyjs/docsify/issues/1046) * default html img resize if no height included ([#1065](https://github.com/docsifyjs/docsify/issues/1065)) ([9ff4d06](https://github.com/docsifyjs/docsify/commit/9ff4d0677304bc190e7bd9e89bbbdc64895197fa)) * fixed target and rel issue (fixes [#1183](https://github.com/docsifyjs/docsify/issues/1183)) ([3d662a5](https://github.com/docsifyjs/docsify/commit/3d662a5bf71bbfef077cfbc478df241d794f55a0)) * Inconsistent search and body rendering ([dcb0aae](https://github.com/docsifyjs/docsify/commit/dcb0aaea99efbd68175f1d1aeb5076b6dde9801e)) * rendering cover width bug ([717991c](https://github.com/docsifyjs/docsify/commit/717991c90cf709f4da91fe32610129de6529266b)) * search does not find the contents of the table ([#1198](https://github.com/docsifyjs/docsify/issues/1198)) ([31010e4](https://github.com/docsifyjs/docsify/commit/31010e4979b3d3ab4bd247a09c4ac5fd1405eaa8)) * The search error after setting the ID in the title ([#1159](https://github.com/docsifyjs/docsify/issues/1159)) ([6e554f8](https://github.com/docsifyjs/docsify/commit/6e554f8ebd3d4a2c5c7e4f66cff3dfe2b6aa1e31)) * upgrade docsify from 4.10.2 to 4.11.2 ([60b7f89](https://github.com/docsifyjs/docsify/commit/60b7f89b373b0d48ec8406a51eddeaed8126696d)) ### Features * added html sanitizer for remote rendering ([#1128](https://github.com/docsifyjs/docsify/issues/1128)) ([714ef29](https://github.com/docsifyjs/docsify/commit/714ef29afe779a6db5c4761ebaacdfc70ee2d8dd)) * update src/core/index.js to export all global APIs, deprecate old globals in favor of a single global DOCSIFY, and add tests for this ([7e002bf](https://github.com/docsifyjs/docsify/commit/7e002bf939d7837843908417b5445b4f8d36c1cd)) ### Reverts * Revert "Updated docs site dark and light mode with switch and redesigned search bar using docsify-darklight-theme" (#1207) ([26cb940](https://github.com/docsifyjs/docsify/commit/26cb940b51d34ee584b8425012a336f38a4abd76)), closes [#1207](https://github.com/docsifyjs/docsify/issues/1207) [#1182](https://github.com/docsifyjs/docsify/issues/1182) ## [4.11.3](https://github.com/docsifyjs/docsify/compare/v4.11.2...v4.11.3) (2020-03-24) ### Bug Fixes * fix: digit issue with sidebar (complete REVERT to old method) ([154abf5](https://github.com/docsifyjs/docsify/commit/154abf59a6153e84b018fcdffa86892776d6da7d)) ## [4.11.2](https://github.com/docsifyjs/docsify/compare/v4.11.1...v4.11.2) (2020-03-09) ### Bug Fixes * fixed rendering of color in coverpage issue ([406670c](https://github.com/docsifyjs/docsify/commit/406670c3d619a627142900fd45019fb8ce00f60a)) ## [4.11.1](https://github.com/docsifyjs/docsify/compare/v4.11.0...v4.11.1) (2020-03-09) # [4.11.0](https://github.com/docsifyjs/docsify/compare/v4.10.2...v4.11.0) (2020-03-09) ### Bug Fixes * emojis in titles not working correctly and update ([#1016](https://github.com/docsifyjs/docsify/issues/1016)) ([b3d9b96](https://github.com/docsifyjs/docsify/commit/b3d9b966dfbb6f456c2c457da1d2a366e85d9190)) * searching table content ([6184e50](https://github.com/docsifyjs/docsify/commit/6184e502629932ca71fdd0a1b10150d118f5a7c8)) * stage modified files as part of pre-commit hook ([#985](https://github.com/docsifyjs/docsify/issues/985)) ([5b77b0f](https://github.com/docsifyjs/docsify/commit/5b77b0f628f056b7ebb6d0b617561d19964516a2)) * config initialization and coercion ([#861](https://github.com/docsifyjs/docsify/pull/861)) * strip indent when embedding code fragment ([#996](https://github.com/docsifyjs/docsify/pull/996)) * Ensure autoHeader dom result is similar to parsed H1 ([#811](https://github.com/docsifyjs/docsify/pull/811)) * upgrade docsify from 4.9.4 to 4.10.2 ([#1054](https://github.com/docsifyjs/docsify/issues/1054)) ([78290b2](https://github.com/docsifyjs/docsify/commit/78290b21038a3ae09c4c7438bd89b14ca4c02805)) * upgrade medium-zoom from 1.0.4 to 1.0.5 ([39ebd73](https://github.com/docsifyjs/docsify/commit/39ebd73021290439180878cae32e663b9e60e214)) * upgrade prismjs from 1.17.1 to 1.19.0 ([9981c43](https://github.com/docsifyjs/docsify/commit/9981c4361ad690d0ed32cf1fb5b48cc5b9f770bb)) ### Features * configure pre-commit hook ([#983](https://github.com/docsifyjs/docsify/issues/983)) ([eea41a1](https://github.com/docsifyjs/docsify/commit/eea41a1207c46533ea9c6c59d82e2c94aa4dd70e)) * Add a prepare script. ([efbea24](https://github.com/docsifyjs/docsify/commit/efbea24de71f2287993b52ed1cef1a2dd6a53f81)) * added capability to add css class and id to images + links + refactoring ([#820](https://github.com/docsifyjs/docsify/issues/820)) ([724ac02](https://github.com/docsifyjs/docsify/commit/724ac024ddfc28e93d8b5dd909e722747286fa00)) * added dark mode to docs closes [#1031](https://github.com/docsifyjs/docsify/issues/1031) ([dc43d3c](https://github.com/docsifyjs/docsify/commit/dc43d3c512c2f04750e76176c25ece626ae7fe2a)) * new option `hideSidebar` ([#1026](https://github.com/docsifyjs/docsify/issues/1026)) ([b7547f1](https://github.com/docsifyjs/docsify/commit/b7547f151e928b3a0eb6a94b2af36023da4fa877)) * new option `topMargin` ([#1045](https://github.com/docsifyjs/docsify/pull/1045)) ([8faee03](https://github.com/docsifyjs/docsify/pull/1024/commits/b53ea1e304d3a2782b125c1d8711295d88faee03)) ### Docs * update docs for the `name` config option ([#992](https://github.com/docsifyjs/docsify/pull/992)) * about cache ([#854](https://github.com/docsifyjs/docsify/pull/854)) * removed FOSSA badge * documented `__colon__` tip ([#1025](https://github.com/docsifyjs/docsify/pull/1025)) ### Chore * Migrate relative links to absolute in embedded markdown ([#867](https://github.com/docsifyjs/docsify/pull/867)) * smarter scroll behavior ([#744](https://github.com/docsifyjs/docsify/pull/744)) * improve basic layout style ([#884](https://github.com/docsifyjs/docsify/pull/884)) * There are currently {three=>four} themes available. ([#892](https://github.com/docsifyjs/docsify/pull/892)) * Added a redirect for images to show up in Amplify ([#918](https://github.com/docsifyjs/docsify/pull/918)) * removed the escaping of the name of sidebar ([#991](https://github.com/docsifyjs/docsify/pull/991)) * Eslint fixes for v4x ([#989](https://github.com/docsifyjs/docsify/pull/989)) * added github Actions for CI ([#1000](https://github.com/docsifyjs/docsify/pull/1000)) * Add a prepare script. ([#1010](https://github.com/docsifyjs/docsify/pull/1010)) * chore(GH-action): using ubuntu 16 and removed node 8 from CI * chore: add config ([#1014](https://github.com/docsifyjs/docsify/pull/1014)) * chore(stale): added enhancement label to exemptlabels * chore(stale): added bug label to exemptlabels * .markdown-section max-width 800px to 80% ([#1017](https://github.com/docsifyjs/docsify/pull/1017)) * changed the CDN from unpkg to jsDelivr #1020 ([#1022](https://github.com/docsifyjs/docsify/pull/1022)) * migrate CI to github, refactore CI and npm scripts, linting fixes ([#1023](https://github.com/docsifyjs/docsify/pull/1023)) * chore(readme): added CI badges and fixed the logo issue * added new linter config ([#1028](https://github.com/docsifyjs/docsify/pull/1028)) ## [4.10.2](https://github.com/docsifyjs/docsify/compare/v4.10.0...v4.10.2) (2019-12-16) # [4.10.0](https://github.com/docsifyjs/docsify/compare/v4.9.4...v4.10.0) (2019-12-16) ### Bug Fixes * fixed security alert for chokidar(update dep) ([a62b037](https://github.com/docsifyjs/docsify/commit/a62b037becb36941c11c8eab6e4d83df8db85af3)) * npm audit issues ([#934](https://github.com/docsifyjs/docsify/issues/934)) ([615205c](https://github.com/docsifyjs/docsify/commit/615205cfdb7aea8f37a1ec5dd928105eeef56357)) * package security alerts ([f5f1561](https://github.com/docsifyjs/docsify/commit/f5f15619f1a239d6ce12a2f83ad8817352a3352b)) * security alerts of cssnano ([d7d5c8f](https://github.com/docsifyjs/docsify/commit/d7d5c8f302d7c18dbb32e982202a07b73badf6f6)) ## [4.9.4](https://github.com/docsifyjs/docsify/compare/v4.9.2...v4.9.4) (2019-05-05) ## [4.9.2](https://github.com/docsifyjs/docsify/compare/v4.9.1...v4.9.2) (2019-04-21) ### Bug Fixes * re-render gitalk when router changed ([11ea1f8](https://github.com/docsifyjs/docsify/commit/11ea1f8)) ### Features * allows relative path, fixed [#590](https://github.com/docsifyjs/docsify/issues/590) ([31654f1](https://github.com/docsifyjs/docsify/commit/31654f1)) ## [4.9.1](https://github.com/docsifyjs/docsify/compare/v4.9.0...v4.9.1) (2019-02-21) ### Bug Fixes * github assets url ([#774](https://github.com/docsifyjs/docsify/issues/774)) ([140bf10](https://github.com/docsifyjs/docsify/commit/140bf10)) # [4.9.0](https://github.com/docsifyjs/docsify/compare/v4.8.6...v4.9.0) (2019-02-19) ### Bug Fixes * task list rendering (Fix [#749](https://github.com/docsifyjs/docsify/issues/749)) ([#757](https://github.com/docsifyjs/docsify/issues/757)) ([69ef489](https://github.com/docsifyjs/docsify/commit/69ef489)) * upgrade npm-run-all ([049726e](https://github.com/docsifyjs/docsify/commit/049726e)) ### Features * **search-plugin:** add namespace option ([#706](https://github.com/docsifyjs/docsify/issues/706)) ([28beff8](https://github.com/docsifyjs/docsify/commit/28beff8)) * Add new theme "dolphin" ([#735](https://github.com/docsifyjs/docsify/issues/735)) ([c3345ba](https://github.com/docsifyjs/docsify/commit/c3345ba)) * Provide code fragments feature ([#748](https://github.com/docsifyjs/docsify/issues/748)) ([1447c8a](https://github.com/docsifyjs/docsify/commit/1447c8a)) ## [4.8.6](https://github.com/docsifyjs/docsify/compare/v4.8.5...v4.8.6) (2018-11-12) ### Bug Fixes * IE10 compatibility ([#691](https://github.com/docsifyjs/docsify/issues/691)) ([4db8cd6](https://github.com/docsifyjs/docsify/commit/4db8cd6)) ## [4.8.5](https://github.com/docsifyjs/docsify/compare/v4.8.4...v4.8.5) (2018-11-02) ### Bug Fixes * expose version info for Docsify, fixed [#641](https://github.com/docsifyjs/docsify/issues/641) ([aa719e3](https://github.com/docsifyjs/docsify/commit/aa719e3)) ## [4.8.4](https://github.com/docsifyjs/docsify/compare/v4.8.3...v4.8.4) (2018-11-01) ### Bug Fixes * **cover:** Compatible with legacy styles, fixed [#677](https://github.com/docsifyjs/docsify/issues/677) ([#678](https://github.com/docsifyjs/docsify/issues/678)) ([1a945d4](https://github.com/docsifyjs/docsify/commit/1a945d4)) ## [4.8.3](https://github.com/docsifyjs/docsify/compare/v4.8.2...v4.8.3) (2018-11-01) Fix the last release files has the old version marked... ## [4.8.2](https://github.com/docsifyjs/docsify/compare/v4.8.1...v4.8.2) (2018-11-01) ### Bug Fixes - cover button style, fixed [#670](https://github.com/docsifyjs/docsify/issues/670), fixed [#665](https://github.com/docsifyjs/docsify/issues/665) ([#675](https://github.com/docsifyjs/docsify/issues/675)) ([fcd1087](https://github.com/docsifyjs/docsify/commit/fcd1087)) - update match regex ([#669](https://github.com/docsifyjs/docsify/issues/669)) ([2edf47e](https://github.com/docsifyjs/docsify/commit/2edf47e)) - use copy of cached value ([#668](https://github.com/docsifyjs/docsify/issues/668)) ([5fcf210](https://github.com/docsifyjs/docsify/commit/5fcf210)) - **compiler:** import prism-markup-templating, fixed [#672](https://github.com/docsifyjs/docsify/issues/672) ([#676](https://github.com/docsifyjs/docsify/issues/676)) ([fdd8826](https://github.com/docsifyjs/docsify/commit/fdd8826)) ### Features - add heading config id ([#671](https://github.com/docsifyjs/docsify/issues/671)) ([ab19b13](https://github.com/docsifyjs/docsify/commit/ab19b13)) ## [4.8.1](https://github.com/docsifyjs/docsify/compare/v4.8.0...v4.8.1) (2018-10-31) ### Bug Fixes - ssr package dep, fixed [#605](https://github.com/docsifyjs/docsify/issues/605) ([2bc880d](https://github.com/docsifyjs/docsify/commit/2bc880d)) - **compiler:** extra quotes for codeblock ([4f588e0](https://github.com/docsifyjs/docsify/commit/4f588e0)) - **compiler:** prevent render of html code in paragraph, fixed [#663](https://github.com/docsifyjs/docsify/issues/663) ([d35059d](https://github.com/docsifyjs/docsify/commit/d35059d)) ### Features - upgrade PrismJS, fixed [#534](https://github.com/docsifyjs/docsify/issues/534) ([4805cb5](https://github.com/docsifyjs/docsify/commit/4805cb5)) # [4.8.0](https://github.com/docsifyjs/docsify/compare/v4.7.1...v4.8.0) (2018-10-31) ### Bug Fixes - Cache TOC for later usage in the case of cached file html ([#649](https://github.com/docsifyjs/docsify/issues/649)) ([9e86017](https://github.com/docsifyjs/docsify/commit/9e86017)) - improve external script plugin ([#632](https://github.com/docsifyjs/docsify/issues/632)) ([50c2434](https://github.com/docsifyjs/docsify/commit/50c2434)) - missing variable declaration ([#660](https://github.com/docsifyjs/docsify/issues/660)) ([1ce37bd](https://github.com/docsifyjs/docsify/commit/1ce37bd)) - Remove target for mailto links ([#652](https://github.com/docsifyjs/docsify/issues/652)) ([18f0f03](https://github.com/docsifyjs/docsify/commit/18f0f03)) - Update getAllPath query selector ([#653](https://github.com/docsifyjs/docsify/issues/653)) ([f6f4e32](https://github.com/docsifyjs/docsify/commit/f6f4e32)) - Update vue.styl ([#634](https://github.com/docsifyjs/docsify/issues/634)) ([bf060be](https://github.com/docsifyjs/docsify/commit/bf060be)) ### Features - Add docsify version to $window.docsify object ([#641](https://github.com/docsifyjs/docsify/issues/641)) ([94bc415](https://github.com/docsifyjs/docsify/commit/94bc415)), closes [#521](https://github.com/docsifyjs/docsify/issues/521) - **compiler:** support embedded mermaid ([#629](https://github.com/docsifyjs/docsify/issues/629)) ([42ea8af](https://github.com/docsifyjs/docsify/commit/42ea8af)) - Add hideOtherSidebarContent option ([#661](https://github.com/docsifyjs/docsify/issues/661)) ([4a23c4a](https://github.com/docsifyjs/docsify/commit/4a23c4a)) - Allow base64, external, and relative logo values ([#642](https://github.com/docsifyjs/docsify/issues/642)) ([0a0802a](https://github.com/docsifyjs/docsify/commit/0a0802a)), closes [#577](https://github.com/docsifyjs/docsify/issues/577) - upgrade marked to 0.5.x, fixed [#645](https://github.com/docsifyjs/docsify/issues/645), close [#644](https://github.com/docsifyjs/docsify/issues/644) ([#662](https://github.com/docsifyjs/docsify/issues/662)) ([a39b214](https://github.com/docsifyjs/docsify/commit/a39b214)) ## [4.7.1](https://github.com/docsifyjs/docsify/compare/v4.7.0...v4.7.1) (2018-08-30) # [4.7.0](https://github.com/QingWei-Li/docsify/compare/v4.6.9...v4.7.0) (2018-06-29) ### Bug Fixes - alldow addition content in sidebar, fix [#518](https://github.com/QingWei-Li/docsify/issues/518), fix 539 ([#543](https://github.com/QingWei-Li/docsify/issues/543)) ([04b36b0](https://github.com/QingWei-Li/docsify/commit/04b36b0)) - async install config, fixed [#425](https://github.com/QingWei-Li/docsify/issues/425) ([e4e011c](https://github.com/QingWei-Li/docsify/commit/e4e011c)) - loading embed files synchronously, fixed [#525](https://github.com/QingWei-Li/docsify/issues/525), fixed [#527](https://github.com/QingWei-Li/docsify/issues/527) ([#544](https://github.com/QingWei-Li/docsify/issues/544)) ([feea7f9](https://github.com/QingWei-Li/docsify/commit/feea7f9)) - path include chinese character cause hilight bug ([#556](https://github.com/QingWei-Li/docsify/issues/556)) ([a5f333a](https://github.com/QingWei-Li/docsify/commit/a5f333a)) ### Features - add logo option, [#264](https://github.com/QingWei-Li/docsify/issues/264) ([#541](https://github.com/QingWei-Li/docsify/issues/541)) ([ee72dd0](https://github.com/QingWei-Li/docsify/commit/ee72dd0)) - add unpkg field, close [#531](https://github.com/QingWei-Li/docsify/issues/531) ([#558](https://github.com/QingWei-Li/docsify/issues/558)) ([5c0de0a](https://github.com/QingWei-Li/docsify/commit/5c0de0a)) - support image resizing, resolve [#508](https://github.com/QingWei-Li/docsify/issues/508) ([#545](https://github.com/QingWei-Li/docsify/issues/545)) ([3a7ad62](https://github.com/QingWei-Li/docsify/commit/3a7ad62)) ## [4.6.10](https://github.com/QingWei-Li/docsify/compare/v4.6.9...v4.6.10) (2018-03-25) ### Bug Fixes - async install config, fixed [#425](https://github.com/QingWei-Li/docsify/issues/425) ([e4e011c](https://github.com/QingWei-Li/docsify/commit/e4e011c)) ## [4.6.9](https://github.com/QingWei-Li/docsify/compare/v4.6.8...v4.6.9) (2018-03-10) ### Bug Fixes - upgrade medium-zoom, fixed [#417](https://github.com/QingWei-Li/docsify/issues/417) ([6a3d69a](https://github.com/QingWei-Li/docsify/commit/6a3d69a)) ## [4.6.8](https://github.com/QingWei-Li/docsify/compare/v4.6.7...v4.6.8) (2018-03-06) ### Bug Fixes - resolve path of image and embed files, fixed [#412](https://github.com/QingWei-Li/docsify/issues/412) ([bfd0d18](https://github.com/QingWei-Li/docsify/commit/bfd0d18)) ## [4.6.7](https://github.com/QingWei-Li/docsify/compare/v4.6.6...v4.6.7) (2018-03-03) ### Bug Fixes - layout css, fixed [#409](https://github.com/QingWei-Li/docsify/issues/409) ([aeb692e](https://github.com/QingWei-Li/docsify/commit/aeb692e)) ## [4.6.6](https://github.com/QingWei-Li/docsify/compare/v4.6.5...v4.6.6) (2018-03-03) ## [4.6.5](https://github.com/QingWei-Li/docsify/compare/v4.6.4...v4.6.5) (2018-03-03) ### Bug Fixes - **navbar:** Now Navbar isn't append to DOM when loadNavbar is falsy ([#407](https://github.com/QingWei-Li/docsify/issues/407)) ([0933445](https://github.com/QingWei-Li/docsify/commit/0933445)) ### Features - **config:** Add 404 page options. ([#406](https://github.com/QingWei-Li/docsify/issues/406)) ([9b3b445](https://github.com/QingWei-Li/docsify/commit/9b3b445)) ## [4.6.4](https://github.com/QingWei-Li/docsify/compare/v4.6.3...v4.6.4) (2018-03-01) ### Bug Fixes - **render:** Disable markdown parsing when the file is an HTML ([#403](https://github.com/QingWei-Li/docsify/issues/403)) ([278a75e](https://github.com/QingWei-Li/docsify/commit/278a75e)) ### Features - **fetch:** Add fallback languages configuration. ([#402](https://github.com/QingWei-Li/docsify/issues/402)) ([ecc0e04](https://github.com/QingWei-Li/docsify/commit/ecc0e04)) ## [4.6.3](https://github.com/QingWei-Li/docsify/compare/v4.6.2...v4.6.3) (2018-02-15) ### Bug Fixes - **hook:** beforeEach don\'t work, fixed [#393](https://github.com/QingWei-Li/docsify/issues/393) ([6a09059](https://github.com/QingWei-Li/docsify/commit/6a09059)) ## [4.6.2](https://github.com/QingWei-Li/docsify/compare/v4.6.1...v4.6.2) (2018-02-14) ### Bug Fixes - **embed:** broken in IE, fixed [#389](https://github.com/QingWei-Li/docsify/issues/389), fixed [#391](https://github.com/QingWei-Li/docsify/issues/391) ([45a7464](https://github.com/QingWei-Li/docsify/commit/45a7464)) - **embed:** init value ([890a7bf](https://github.com/QingWei-Li/docsify/commit/890a7bf)) ## [4.6.1](https://github.com/QingWei-Li/docsify/compare/v4.6.0...v4.6.1) (2018-02-12) ### Bug Fixes - **embed** compatible ssr ([dc0c3ce](https://github.com/QingWei-Li/docsify/commit/dc0c3ce)) - **embed** async fetch embed files, fixed [#387](https://github.com/QingWei-Li/docsify/issues/387) # [4.6.0](https://github.com/QingWei-Li/docsify/compare/v4.5.9...v4.6.0) (2018-02-11) ### Bug Fixes - **search:** custom clear button, fixed [#271](https://github.com/QingWei-Li/docsify/issues/271) ([864aa18](https://github.com/QingWei-Li/docsify/commit/864aa18)) - **search:** escape special characters for search, fixed [#369](https://github.com/QingWei-Li/docsify/issues/369) ([9755439](https://github.com/QingWei-Li/docsify/commit/9755439)) - build config ([342438f](https://github.com/QingWei-Li/docsify/commit/342438f)) - button style for coverpage, fixed [#362](https://github.com/QingWei-Li/docsify/issues/362) ([85428ef](https://github.com/QingWei-Li/docsify/commit/85428ef)) - dropdown scroll style, fixed [#346](https://github.com/QingWei-Li/docsify/issues/346) ([c4d83f2](https://github.com/QingWei-Li/docsify/commit/c4d83f2)) - highlight homepage link, fixed [#304](https://github.com/QingWei-Li/docsify/issues/304) ([f960c19](https://github.com/QingWei-Li/docsify/commit/f960c19)) - homepage link ([e097f88](https://github.com/QingWei-Li/docsify/commit/e097f88)) - onlyCover ([033be4f](https://github.com/QingWei-Li/docsify/commit/033be4f)) - ssr compatible embedd ([ebc10c4](https://github.com/QingWei-Li/docsify/commit/ebc10c4)) - ssr coverpage, fixed [#273](https://github.com/QingWei-Li/docsify/issues/273) ([9e824a4](https://github.com/QingWei-Li/docsify/commit/9e824a4)) ### Features - click sidebar menu add collapse and expand, close [#294](https://github.com/QingWei-Li/docsify/issues/294) ([5e161a1](https://github.com/QingWei-Li/docsify/commit/5e161a1)) - **compiler:** support embedded file as code block, close [#134](https://github.com/QingWei-Li/docsify/issues/134) ([761ccc2](https://github.com/QingWei-Li/docsify/commit/761ccc2)) - **compiler:** support embedded markdown, html, video, etc files, close [#383](https://github.com/QingWei-Li/docsify/issues/383), close [#333](https://github.com/QingWei-Li/docsify/issues/333) ([524f52f](https://github.com/QingWei-Li/docsify/commit/524f52f)) - **cover:** add onlyCover option, close [#382](https://github.com/QingWei-Li/docsify/issues/382) ([b265fdd](https://github.com/QingWei-Li/docsify/commit/b265fdd)) - **fetch:** add requestHeaders option, fixed [#336](https://github.com/QingWei-Li/docsify/issues/336) ([54ab4c9](https://github.com/QingWei-Li/docsify/commit/54ab4c9)) - **render:** add ext option for custom file extension, close [#340](https://github.com/QingWei-Li/docsify/issues/340) ([248aa72](https://github.com/QingWei-Li/docsify/commit/248aa72)) - **render:** mutilple coverpage, close [#315](https://github.com/QingWei-Li/docsify/issues/315) ([f68ddf5](https://github.com/QingWei-Li/docsify/commit/f68ddf5)) ## [4.5.9](https://github.com/QingWei-Li/docsify/compare/v4.5.8...v4.5.9) (2018-02-07) ### Bug Fixes - upgrade marked ([4157173](https://github.com/QingWei-Li/docsify/commit/4157173)) ## [4.5.8](https://github.com/QingWei-Li/docsify/compare/v4.5.6...v4.5.8) (2018-02-07) ### Bug Fixes - cover style, fixed [#381](https://github.com/QingWei-Li/docsify/issues/381) ([368754e](https://github.com/QingWei-Li/docsify/commit/368754e)) - updated deps ([#337](https://github.com/QingWei-Li/docsify/issues/337)) ([a12d393](https://github.com/QingWei-Li/docsify/commit/a12d393)) ## [4.5.7](https://github.com/QingWei-Li/docsify/compare/v4.5.6...v4.5.7) (2017-12-29) ### Features - add navigation plugin, closed [#180](https://github.com/QingWei-Li/docsify/issues/180) ([f78be4c](https://github.com/QingWei-Li/docsify/commit/f78be4c)) ## [4.5.6](https://github.com/QingWei-Li/docsify/compare/v4.5.3...v4.5.6) (2017-12-14) ### Bug Fixes - **style:** increase the tap targets of menu button, fixed [#325](https://github.com/QingWei-Li/docsify/issues/325) ([888f217](https://github.com/QingWei-Li/docsify/commit/888f217)) ## [4.5.5](https://github.com/QingWei-Li/docsify/compare/v4.5.4...v4.5.5) (2017-11-30) ### Bug Fixes - disqus plugin issue ([#318](https://github.com/QingWei-Li/docsify/issues/318)) ([041b33e](https://github.com/QingWei-Li/docsify/commit/041b33e)), closes [#317](https://github.com/QingWei-Li/docsify/issues/317) ## [4.5.4](https://github.com/QingWei-Li/docsify/compare/v4.5.2...v4.5.4) (2017-11-29) ### Bug Fixes - **compiler:** task lists style, fixed [#215](https://github.com/QingWei-Li/docsify/issues/215) ([e43ded4](https://github.com/QingWei-Li/docsify/commit/e43ded4)) ### Features - add gitalk plugin ([#306](https://github.com/QingWei-Li/docsify/issues/306)) ([9208e64](https://github.com/QingWei-Li/docsify/commit/9208e64)) ## [4.5.3](https://github.com/QingWei-Li/docsify/compare/v4.5.2...v4.5.3) (2017-11-11) ### Features - add gitalk plugin ([#306](https://github.com/QingWei-Li/docsify/issues/306)) ([9208e64](https://github.com/QingWei-Li/docsify/commit/9208e64)) ## [4.5.2](https://github.com/QingWei-Li/docsify/compare/v4.5.1...v4.5.2) (2017-11-09) ### Features - github task lists, close [#215](https://github.com/QingWei-Li/docsify/issues/215) ([#305](https://github.com/QingWei-Li/docsify/issues/305)) ([d486eef](https://github.com/QingWei-Li/docsify/commit/d486eef)) ## [4.5.1](https://github.com/QingWei-Li/docsify/compare/v4.5.0...v4.5.1) (2017-11-07) ### Features - fetch files with the query params, fixed [#303](https://github.com/QingWei-Li/docsify/issues/303) ([2a2ed96](https://github.com/QingWei-Li/docsify/commit/2a2ed96)) # [4.5.0](https://github.com/QingWei-Li/docsify/compare/v4.4.1...v4.5.0) (2017-11-04) ### Features - add disqus plugin, closed [#123](https://github.com/QingWei-Li/docsify/issues/123) ([fd7d4e0](https://github.com/QingWei-Li/docsify/commit/fd7d4e0)) ## [4.4.1](https://github.com/QingWei-Li/docsify/compare/v4.4.0...v4.4.1) (2017-10-31) ### Bug Fixes - {docsify-ignore-all} and {docsify-ignore} bug ([#299](https://github.com/QingWei-Li/docsify/issues/299)) ([cc98f56](https://github.com/QingWei-Li/docsify/commit/cc98f56)) - zoom image plugin issue, fixed [#187](https://github.com/QingWei-Li/docsify/issues/187) ([#300](https://github.com/QingWei-Li/docsify/issues/300)) ([fa772cf](https://github.com/QingWei-Li/docsify/commit/fa772cf)) # [4.4.0](https://github.com/QingWei-Li/docsify/compare/v4.3.15...v4.4.0) (2017-10-30) ### Bug Fixes - sidebar style issue on firefox, fixed [#184](https://github.com/QingWei-Li/docsify/issues/184) ([#297](https://github.com/QingWei-Li/docsify/issues/297)) ([36bfc9d](https://github.com/QingWei-Li/docsify/commit/36bfc9d)) ### Features - add helper for disabled link, fixed [#295](https://github.com/QingWei-Li/docsify/issues/295) ([#296](https://github.com/QingWei-Li/docsify/issues/296)) ([4ad96f3](https://github.com/QingWei-Li/docsify/commit/4ad96f3)) ## [4.3.15](https://github.com/QingWei-Li/docsify/compare/v4.3.14...v4.3.15) (2017-10-20) ### Bug Fixes - scroll active sidebar ([a2b8eae](https://github.com/QingWei-Li/docsify/commit/a2b8eae)) ## [4.3.14](https://github.com/QingWei-Li/docsify/compare/v4.3.13...v4.3.14) (2017-10-20) ### Bug Fixes - codesponsor style ([ab68268](https://github.com/QingWei-Li/docsify/commit/ab68268)) ## [4.3.13](https://github.com/QingWei-Li/docsify/compare/v4.3.12...v4.3.13) (2017-10-17) ### Bug Fixes - duplicate results in search fixed [#257](https://github.com/QingWei-Li/docsify/issues/257) ([#284](https://github.com/QingWei-Li/docsify/issues/284)) ([3476f6f](https://github.com/QingWei-Li/docsify/commit/3476f6f)) ### Features - make whole search result clickable ([#285](https://github.com/QingWei-Li/docsify/issues/285)) ([1b91227](https://github.com/QingWei-Li/docsify/commit/1b91227)) ## [4.3.12](https://github.com/QingWei-Li/docsify/compare/v4.3.11...v4.3.12) (2017-10-15) ### Bug Fixes - incorrect active link ([#281](https://github.com/QingWei-Li/docsify/issues/281)) ([a3ab379](https://github.com/QingWei-Li/docsify/commit/a3ab379)) ## [4.3.11](https://github.com/QingWei-Li/docsify/compare/v4.3.10...v4.3.11) (2017-10-15) ### Bug Fixes - broken links to same page heading, fix [#278](https://github.com/QingWei-Li/docsify/issues/278), fix [#279](https://github.com/QingWei-Li/docsify/issues/279) ([91d6337](https://github.com/QingWei-Li/docsify/commit/91d6337)) ## [4.3.10](https://github.com/QingWei-Li/docsify/compare/v4.3.9...v4.3.10) (2017-10-12) ### Bug Fixes - link render issue after page refreshing ([#276](https://github.com/QingWei-Li/docsify/issues/276)) ([abd885e](https://github.com/QingWei-Li/docsify/commit/abd885e)) ## [4.3.9](https://github.com/QingWei-Li/docsify/compare/v4.3.8...v4.3.9) (2017-10-11) ### Bug Fixes - scroll issue in IE ([#275](https://github.com/QingWei-Li/docsify/issues/275)) ([3e94cb6](https://github.com/QingWei-Li/docsify/commit/3e94cb6)) ## [4.3.8](https://github.com/QingWei-Li/docsify/compare/v4.3.7...v4.3.8) (2017-10-07) ### Bug Fixes - **slugify:** GitHub compatible heading links, fixed [#272](https://github.com/QingWei-Li/docsify/issues/272) ([9b4e666](https://github.com/QingWei-Li/docsify/commit/9b4e666)) ## [4.3.7](https://github.com/QingWei-Li/docsify/compare/v4.3.6...v4.3.7) (2017-10-02) ### Bug Fixes - **slugify:** GitHub compatible heading links, fixed [#267](https://github.com/QingWei-Li/docsify/issues/267) ([c195d2d](https://github.com/QingWei-Li/docsify/commit/c195d2d)) ## [4.3.6](https://github.com/QingWei-Li/docsify/compare/v4.3.5...v4.3.6) (2017-09-21) ### Bug Fixes - style for codesponsor plugin ([08afec7](https://github.com/QingWei-Li/docsify/commit/08afec7)) ## [4.3.5](https://github.com/QingWei-Li/docsify/compare/v4.3.4...v4.3.5) (2017-09-20) ### Bug Fixes - missed symbol ([#254](https://github.com/QingWei-Li/docsify/issues/254)) ([6c702d3](https://github.com/QingWei-Li/docsify/commit/6c702d3)) ### Features - **plugin:** add codesponsor plugin ([46ac4c3](https://github.com/QingWei-Li/docsify/commit/46ac4c3)) ## [4.3.4](https://github.com/QingWei-Li/docsify/compare/v4.3.3...v4.3.4) (2017-09-07) ### Bug Fixes - scroll position issue, fixed [#234](https://github.com/QingWei-Li/docsify/issues/234) ([388ed3d](https://github.com/QingWei-Li/docsify/commit/388ed3d)) ## [4.3.3](https://github.com/QingWei-Li/docsify/compare/v4.3.2...v4.3.3) (2017-09-06) ### Bug Fixes - **buble.css:** tweaks code block style, fixed [#249](https://github.com/QingWei-Li/docsify/issues/249) ([9d43051](https://github.com/QingWei-Li/docsify/commit/9d43051)) ### Features - add doc for react and vue demo box plugin ([#247](https://github.com/QingWei-Li/docsify/issues/247)) ([f0aca19](https://github.com/QingWei-Li/docsify/commit/f0aca19)) ## [4.3.2](https://github.com/QingWei-Li/docsify/compare/v4.3.1...v4.3.2) (2017-09-01) ### Bug Fixes - sidebar highlight ([f82f419](https://github.com/QingWei-Li/docsify/commit/f82f419)) ### Features - add Edit on github plugin (thanks [@njleonzhang](https://github.com/njleonzhang)) ([a0e1ea8](https://github.com/QingWei-Li/docsify/commit/a0e1ea8)) ## [4.3.1](https://github.com/QingWei-Li/docsify/compare/v4.2.9...v4.3.1) (2017-08-30) ### Features - **markdown:** supports mermaid [#137](https://github.com/QingWei-Li/docsify/issues/137) ([f4800e0](https://github.com/QingWei-Li/docsify/commit/f4800e0)) # [4.3.0](https://github.com/QingWei-Li/docsify/compare/v4.2.9...v4.3.0) (2017-08-17) ### Features - **markdown:** supports mermaid [#137](https://github.com/QingWei-Li/docsify/issues/137) ([f4800e0](https://github.com/QingWei-Li/docsify/commit/f4800e0)) ## [4.2.9](https://github.com/QingWei-Li/docsify/compare/v4.2.8...v4.2.9) (2017-08-15) ### Bug Fixes - ensure document ready before init Docsify [#233](https://github.com/QingWei-Li/docsify/issues/233) ## [4.2.8](https://github.com/QingWei-Li/docsify/compare/v4.2.7...v4.2.8) (2017-08-10) ### Features - **compiler:** support for setting target attribute for link, fixed [#230](https://github.com/QingWei-Li/docsify/issues/230) ([7f270f9](https://github.com/QingWei-Li/docsify/commit/7f270f9)) ## [4.2.7](https://github.com/QingWei-Li/docsify/compare/v4.2.4...v4.2.7) (2017-08-05) ### Bug Fixes - **release:** release shell ([628e211](https://github.com/QingWei-Li/docsify/commit/628e211)) - **style:** nowrap => pre-wrap, fixed [#228](https://github.com/QingWei-Li/docsify/issues/228) ([a88252c](https://github.com/QingWei-Li/docsify/commit/a88252c)) ## [4.2.6](https://github.com/QingWei-Li/docsify/compare/v4.2.4...v4.2.6) (2017-07-27) ### Bug Fixes - **css:** hide the nav when the content has not yet been loaded ([1fa1619](https://github.com/QingWei-Li/docsify/commit/1fa1619)) - **release:** release shell ([628e211](https://github.com/QingWei-Li/docsify/commit/628e211)) ## [4.2.4](https://github.com/QingWei-Li/docsify/compare/v4.2.2...v4.2.4) (2017-07-26) ### Bug Fixes - **render:** Remove getRootNode to be compatible with the lower version of Chrome, fixed [#225](https://github.com/QingWei-Li/docsify/issues/225) ([b8dd346](https://github.com/QingWei-Li/docsify/commit/b8dd346)) ## [4.2.3](https://github.com/QingWei-Li/docsify/compare/v4.2.2...v4.2.3) (2017-07-26) ### Features - **search:** Supports the max depth of the search headline, fixed [#223](https://github.com/QingWei-Li/docsify/issues/223), resolve [#129](https://github.com/QingWei-Li/docsify/issues/129) ([b7b589b](https://github.com/QingWei-Li/docsify/commit/b7b589b)) ## [4.2.2](https://github.com/QingWei-Li/docsify/compare/v4.2.1...v4.2.2) (2017-07-24) ### Bug Fixes - style rerender due to setting themeColor ([17ff3d1](https://github.com/QingWei-Li/docsify/commit/17ff3d1)) ## [4.2.1](https://github.com/QingWei-Li/docsify/compare/v4.2.0...v4.2.1) (2017-07-19) - give the navbar some line-height (#216) - Remove unnecessary moduleName option from rollup config for plugins (#209) # [4.2.0](https://github.com/QingWei-Li/docsify/compare/v4.1.14...v4.2.0) (2017-07-10) ### Bug Fixes - not found page ([9af8559](https://github.com/QingWei-Li/docsify/commit/9af8559)) ### Features - alias option supports regexp, resolve [#183](https://github.com/QingWei-Li/docsify/issues/183) ([c4aa22c](https://github.com/QingWei-Li/docsify/commit/c4aa22c)) - ignore to compiled link, fixed [#203](https://github.com/QingWei-Li/docsify/issues/203) ([#204](https://github.com/QingWei-Li/docsify/issues/204)) ([2e00f4c](https://github.com/QingWei-Li/docsify/commit/2e00f4c)) ## [4.1.14](https://github.com/QingWei-Li/docsify/compare/v4.1.13...v4.1.14) (2017-06-24) ### Bug Fixes - get file path ([e8117e5](https://github.com/QingWei-Li/docsify/commit/e8117e5)) ### Features - add context attribute, fixed [#191](https://github.com/QingWei-Li/docsify/issues/191) ([ce0e9ac](https://github.com/QingWei-Li/docsify/commit/ce0e9ac)) ## [4.1.13](https://github.com/QingWei-Li/docsify/compare/v4.1.12...v4.1.13) (2017-06-11) ## [4.1.12](https://github.com/QingWei-Li/docsify/compare/v4.1.11...v4.1.12) (2017-06-03) ### Bug Fixes - **render:** subtitle in side bar shows undefined, fixed [#182](https://github.com/QingWei-Li/docsify/issues/182) ([d087d57](https://github.com/QingWei-Li/docsify/commit/d087d57)) ## [4.1.11](https://github.com/QingWei-Li/docsify/compare/v4.1.10...v4.1.11) (2017-06-02) ### Bug Fixes - **compiler:** force reset toc when rendering sidebar fixed [#181](https://github.com/QingWei-Li/docsify/issues/181) ([ccf4c7c](https://github.com/QingWei-Li/docsify/commit/ccf4c7c)) - **render:** autoHeader does not work ([1304d2e](https://github.com/QingWei-Li/docsify/commit/1304d2e)) ## [4.1.10](https://github.com/QingWei-Li/docsify/compare/v4.1.9...v4.1.10) (2017-06-02) ### Bug Fixes - **hash:** hash routing crashes when url has querystring ([6d48ce1](https://github.com/QingWei-Li/docsify/commit/6d48ce1)) ## [4.1.9](https://github.com/QingWei-Li/docsify/compare/v4.1.8...v4.1.9) (2017-05-31) ### Bug Fixes - can't render toc on first load ([d9b487e](https://github.com/QingWei-Li/docsify/commit/d9b487e)) - **lifecycle:** continue to handle data ([955d3d5](https://github.com/QingWei-Li/docsify/commit/955d3d5)) - **render:** broken name link, fixed [#167](https://github.com/QingWei-Li/docsify/issues/167) ([91b66a5](https://github.com/QingWei-Li/docsify/commit/91b66a5)) ## [4.1.8](https://github.com/QingWei-Li/docsify/compare/v4.1.7...v4.1.8) (2017-05-31) ### Bug Fixes - auto replace version ([22b50f0](https://github.com/QingWei-Li/docsify/commit/22b50f0)) - update edit button demo ([ec887c1](https://github.com/QingWei-Li/docsify/commit/ec887c1)) ### Features - add edit button demo ([a64cee1](https://github.com/QingWei-Li/docsify/commit/a64cee1)) - add edit button demo, close [#162](https://github.com/QingWei-Li/docsify/issues/162) ([036fdac](https://github.com/QingWei-Li/docsify/commit/036fdac)) ## [4.1.7](https://github.com/QingWei-Li/docsify/compare/v4.1.6...v4.1.7) (2017-05-30) ### Bug Fixes - **ssr:** clean files ([0014895](https://github.com/QingWei-Li/docsify/commit/0014895)) ## [4.1.6](https://github.com/QingWei-Li/docsify/compare/v4.1.5...v4.1.6) (2017-05-30) ### Bug Fixes - **ssr:** add debug ([6b9e092](https://github.com/QingWei-Li/docsify/commit/6b9e092)) ## [4.1.5](https://github.com/QingWei-Li/docsify/compare/v4.1.4...v4.1.5) (2017-05-30) ### Bug Fixes - **ssr:** missing package ([6db8c9e](https://github.com/QingWei-Li/docsify/commit/6db8c9e)) ## [4.1.4](https://github.com/QingWei-Li/docsify/compare/v4.1.3...v4.1.4) (2017-05-30) ### Bug Fixes - **ssr:** file path ([79a83bc](https://github.com/QingWei-Li/docsify/commit/79a83bc)) ## [4.1.3](https://github.com/QingWei-Li/docsify/compare/v4.1.2...v4.1.3) (2017-05-30) ### Bug Fixes - update babel config ([9825db4](https://github.com/QingWei-Li/docsify/commit/9825db4)) ## [4.1.2](https://github.com/QingWei-Li/docsify/compare/v4.1.1...v4.1.2) (2017-05-30) ### Bug Fixes - update babel config ([80dba19](https://github.com/QingWei-Li/docsify/commit/80dba19)) ## [4.1.1](https://github.com/QingWei-Li/docsify/compare/v4.1.0...v4.1.1) (2017-05-30) ### Bug Fixes - build for ssr package ([4cb20a5](https://github.com/QingWei-Li/docsify/commit/4cb20a5)) - remove history mode ([0e74e6c](https://github.com/QingWei-Li/docsify/commit/0e74e6c)) # [4.1.0](https://github.com/QingWei-Li/docsify/compare/v4.0.2...v4.1.0) (2017-05-30) ## [4.0.2](https://github.com/QingWei-Li/docsify/compare/v4.0.1...v4.0.2) (2017-05-30) ### Bug Fixes - basePath for history mode ([fc1cd3f](https://github.com/QingWei-Li/docsify/commit/fc1cd3f)) ## [4.0.1](https://github.com/QingWei-Li/docsify/compare/v4.0.0...v4.0.1) (2017-05-29) ### Bug Fixes - **ssr:** remove context ([4626157](https://github.com/QingWei-Li/docsify/commit/4626157)) - lint ([b764b6e](https://github.com/QingWei-Li/docsify/commit/b764b6e)) # [4.0.0](https://github.com/QingWei-Li/docsify/compare/v3.7.3...v4.0.0) (2017-05-29) ### Bug Fixes - **render:** init event in ssr ([eba1c98](https://github.com/QingWei-Li/docsify/commit/eba1c98)) - lint ([1f4514d](https://github.com/QingWei-Li/docsify/commit/1f4514d)) ### Features - finish ssr ([3444884](https://github.com/QingWei-Li/docsify/commit/3444884)) - init docsify-server-renderer ([6dea685](https://github.com/QingWei-Li/docsify/commit/6dea685)) - support history mode ([f095eb8](https://github.com/QingWei-Li/docsify/commit/f095eb8)) ================================================ FILE: CONTRIBUTING.md ================================================ # Contribute ## Introduction First, thank you for considering contributing to docsify! It's people like you that make the open source community such a great community! 😊 We welcome any type of contribution, not only code. You can help with - **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open) - **Marketing**: writing blog posts, howto's, printing stickers, ... - **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ... - **Code**: take a look at the [open issues](https://github.com/docsifyjs/docsify/issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them. - **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/docsify). ## Your First Contribution Working on your first Pull Request ever? You can learn how from this _free_ series, [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github). ## Online one-click setup for Contributing You can use Gitpod (a free online VS Code-like IDE) for contributing. With a single click it'll launch a workspace and automatically: - clone the docsify repo. - install the dependencies. - start `npm run dev`. ```bash npm install && npm run dev ``` So that you can start straight away. [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/docsifyjs/docsify) - Fork it! - Create your feature branch: `git checkout -b my-new-feature` - Commit your changes: `git add . && git commit -m 'Add some feature'` - Push to the branch: `git push origin my-new-feature` - Submit a pull request ## Submitting code Any code change should be submitted as a pull request. The description should explain what the code does and give steps to execute it. The pull request should also contain tests. ## Testing Ensure that things work by running: ```sh npm test ``` ## Test Snapshots If a snapshot fails, or to add new snapshots, run: ```sh npx jest --updateSnapshot ``` ## Code review process The bigger the pull request, the longer it will take to review and merge. Try to break down large pull requests in smaller chunks that are easier to review and merge. It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you? ## Financial contributions We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/docsify). Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed. ## Questions If you have any questions, create an [issue](https://github.com/docsifyjs/docsify/issues) (protip: do a quick search first to see if someone else didn't ask the same question before!). You can also reach us at hello@docsify.opencollective.com. ## Credits ### Contributors Thank you to all the people who have already contributed to docsify! ### Backers Thank you to all our backers! [[Become a backer](https://opencollective.com/docsify#backer)] ### Sponsors Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/docsify#sponsor)) ================================================ FILE: Dockerfile ================================================ FROM mcr.microsoft.com/playwright:focal WORKDIR /app COPY . . RUN rm package-lock.json RUN npm install RUN npx playwright install RUN npm run build ENTRYPOINT ["npm", "run"] CMD ["test"] ================================================ FILE: HISTORY.md ================================================ ## [3.7.3](https://github.com/QingWei-Li/docsify/compare/v3.7.2...v3.7.3) (2017-05-22) ### Bug Fixes * **render:** find => filter ([eca3368](https://github.com/QingWei-Li/docsify/commit/eca3368)) ## [3.7.2](https://github.com/QingWei-Li/docsify/compare/v3.7.1...v3.7.2) (2017-05-19) ## [3.7.1](https://github.com/QingWei-Li/docsify/compare/v3.7.0...v3.7.1) (2017-05-19) ### Bug Fixes * docsify-updated is undefined ([b2b4742](https://github.com/QingWei-Li/docsify/commit/b2b4742)) # [3.7.0](https://github.com/QingWei-Li/docsify/compare/v3.6.6...v3.7.0) (2017-05-16) ### Features * add docsify-updated, close [#158](https://github.com/QingWei-Li/docsify/issues/158) ([d2be5ae](https://github.com/QingWei-Li/docsify/commit/d2be5ae)) * add externalLinkTarget, close [#149](https://github.com/QingWei-Li/docsify/issues/149) ([2d73285](https://github.com/QingWei-Li/docsify/commit/2d73285)) ## [3.6.6](https://github.com/QingWei-Li/docsify/compare/v3.6.5...v3.6.6) (2017-05-06) ### Features * support query string for the search, fixed [#156](https://github.com/QingWei-Li/docsify/issues/156) ([da75d70](https://github.com/QingWei-Li/docsify/commit/da75d70)) ## [3.6.5](https://github.com/QingWei-Li/docsify/compare/v3.6.4...v3.6.5) (2017-04-28) ### Bug Fixes * **util:** fix crash, fixed [#154](https://github.com/QingWei-Li/docsify/issues/154) ([51832d3](https://github.com/QingWei-Li/docsify/commit/51832d3)) ## [3.6.4](https://github.com/QingWei-Li/docsify/compare/v3.6.3...v3.6.4) (2017-04-28) ### Bug Fixes * **util:** correctly clean up duplicate slashes, fixed [#153](https://github.com/QingWei-Li/docsify/issues/153) ([76c041a](https://github.com/QingWei-Li/docsify/commit/76c041a)) ## [3.6.3](https://github.com/QingWei-Li/docsify/compare/v3.6.2...v3.6.3) (2017-04-25) ### Bug Fixes * **external-script:** script attrs ([2653849](https://github.com/QingWei-Li/docsify/commit/2653849)) ## [3.6.2](https://github.com/QingWei-Li/docsify/compare/v3.6.0...v3.6.2) (2017-04-12) ### Features * **event:** Collapse the sidebar when click outside element in the small screen ([9b7e5f5](https://github.com/QingWei-Li/docsify/commit/9b7e5f5)) * **external-script:** detect more than one script dom, fixed [#146](https://github.com/QingWei-Li/docsify/issues/146) ([94d6603](https://github.com/QingWei-Li/docsify/commit/94d6603)) # [3.6.0](https://github.com/QingWei-Li/docsify/compare/v3.5.2...v3.6.0) (2017-04-09) ### Features * **render:** add mergeNavbar option, close [#125](https://github.com/QingWei-Li/docsify/issues/125), [#124](https://github.com/QingWei-Li/docsify/issues/124) ([#145](https://github.com/QingWei-Li/docsify/issues/145)) ([9220523](https://github.com/QingWei-Li/docsify/commit/9220523)) ## [3.5.2](https://github.com/QingWei-Li/docsify/compare/v3.5.1...v3.5.2) (2017-04-05) ## [3.5.1](https://github.com/QingWei-Li/docsify/compare/v3.5.0...v3.5.1) (2017-03-25) ### Bug Fixes * .md file extension regex ([594299f](https://github.com/QingWei-Li/docsify/commit/594299f)) # [3.5.0](https://github.com/QingWei-Li/docsify/compare/v3.4.4...v3.5.0) (2017-03-25) ### Bug Fixes * adjust display on small screens ([bf35471](https://github.com/QingWei-Li/docsify/commit/bf35471)) * navbar labels for German ([b022aaf](https://github.com/QingWei-Li/docsify/commit/b022aaf)) ### Features * **route:** auto remove .md extension ([8f11653](https://github.com/QingWei-Li/docsify/commit/8f11653)) ## [3.4.4](https://github.com/QingWei-Li/docsify/compare/v3.4.3...v3.4.4) (2017-03-17) ### Bug Fixes * **search:** fix input style ([2d6a51b](https://github.com/QingWei-Li/docsify/commit/2d6a51b)) ## [3.4.3](https://github.com/QingWei-Li/docsify/compare/v3.4.2...v3.4.3) (2017-03-16) ## [3.4.2](https://github.com/QingWei-Li/docsify/compare/v3.4.1...v3.4.2) (2017-03-11) ### Features * **emojify:** add no-emoji option ([3aef37a](https://github.com/QingWei-Li/docsify/commit/3aef37a)) ## [3.4.1](https://github.com/QingWei-Li/docsify/compare/v3.4.0...v3.4.1) (2017-03-10) ### Bug Fixes * **dom:** Disable the dom cache when vue is present, fixed [#119](https://github.com/QingWei-Li/docsify/issues/119) ([b9a7275](https://github.com/QingWei-Li/docsify/commit/b9a7275)) # [3.4.0](https://github.com/QingWei-Li/docsify/compare/v3.3.0...v3.4.0) (2017-03-09) ### Features * **zoom-image:** add plugin ([50fa6fc](https://github.com/QingWei-Li/docsify/commit/50fa6fc)) # [3.3.0](https://github.com/QingWei-Li/docsify/compare/v3.2.0...v3.3.0) (2017-03-07) # [3.2.0](https://github.com/QingWei-Li/docsify/compare/v3.1.2...v3.2.0) (2017-02-28) ### Bug Fixes * **fetch:** load sidebar and navbar for parent path, fixed [#100](https://github.com/QingWei-Li/docsify/issues/100) ([f3fc596](https://github.com/QingWei-Li/docsify/commit/f3fc596)) * **render:** Toc rendering error, fixed [#106](https://github.com/QingWei-Li/docsify/issues/106) ([0d59ee9](https://github.com/QingWei-Li/docsify/commit/0d59ee9)) ### Features * **search:** Localization for no data tip, close [#103](https://github.com/QingWei-Li/docsify/issues/103) ([d3c9fbd](https://github.com/QingWei-Li/docsify/commit/d3c9fbd)) ## [3.1.2](https://github.com/QingWei-Li/docsify/compare/v3.1.1...v3.1.2) (2017-02-27) ## [3.1.1](https://github.com/QingWei-Li/docsify/compare/v3.1.0...v3.1.1) (2017-02-24) ### Bug Fixes * **render:** custom cover background image ([8f9bf29](https://github.com/QingWei-Li/docsify/commit/8f9bf29)) * **search:** don't search nameLink, fixed [#102](https://github.com/QingWei-Li/docsify/issues/102) ([507d9e8](https://github.com/QingWei-Li/docsify/commit/507d9e8)) * **tpl:** extra character, fixed [#101](https://github.com/QingWei-Li/docsify/issues/101) ([d67d25f](https://github.com/QingWei-Li/docsify/commit/d67d25f)) # [3.1.0](https://github.com/QingWei-Li/docsify/compare/v3.0.5...v3.1.0) (2017-02-22) ### Bug Fixes * **search:** incorrect anchor link, fixed [#90](https://github.com/QingWei-Li/docsify/issues/90) ([b8a3d8f](https://github.com/QingWei-Li/docsify/commit/b8a3d8f)) * **sw:** update white list ([f2975a5](https://github.com/QingWei-Li/docsify/commit/f2975a5)) ### Features * **emoji:** add emoji plugin ([855c450](https://github.com/QingWei-Li/docsify/commit/855c450)) ## [3.0.5](https://github.com/QingWei-Li/docsify/compare/v3.0.4...v3.0.5) (2017-02-21) ### Bug Fixes * **event:** highlight sidebar when clicked, fixed [#86](https://github.com/QingWei-Li/docsify/issues/86) ([2a1157a](https://github.com/QingWei-Li/docsify/commit/2a1157a)) * **gen-tree:** cache toc list, fixed [#88](https://github.com/QingWei-Li/docsify/issues/88) ([3394ebb](https://github.com/QingWei-Li/docsify/commit/3394ebb)) * **layout.css:** loading style ([42b2dba](https://github.com/QingWei-Li/docsify/commit/42b2dba)) ### Features * **pwa:** add sw.js ([f7111b5](https://github.com/QingWei-Li/docsify/commit/f7111b5)) ## [3.0.4](https://github.com/QingWei-Li/docsify/compare/v3.0.3...v3.0.4) (2017-02-20) ### Bug Fixes * **render:** disable rendering sub list when loadSidebar is false ([35dd2e1](https://github.com/QingWei-Li/docsify/commit/35dd2e1)) * **render:** execute script ([780c1e5](https://github.com/QingWei-Li/docsify/commit/780c1e5)) ## [3.0.3](https://github.com/QingWei-Li/docsify/compare/v3.0.2...v3.0.3) (2017-02-19) ## [3.0.2](https://github.com/QingWei-Li/docsify/compare/v3.0.1...v3.0.2) (2017-02-19) ### Bug Fixes * **compiler:** link ([3b127a1](https://github.com/QingWei-Li/docsify/commit/3b127a1)) * **search:** add lazy input ([bf593a7](https://github.com/QingWei-Li/docsify/commit/bf593a7)) ## [3.0.1](https://github.com/QingWei-Li/docsify/compare/v3.0.0...v3.0.1) (2017-02-19) ### Bug Fixes * **route:** empty alias ([cd99b52](https://github.com/QingWei-Li/docsify/commit/cd99b52)) # [3.0.0](https://github.com/QingWei-Li/docsify/compare/v2.4.3...v3.0.0) (2017-02-19) ### Bug Fixes * **compiler:** link ([c7e09c3](https://github.com/QingWei-Li/docsify/commit/c7e09c3)) * **render:** support html file ([7b6a2ac](https://github.com/QingWei-Li/docsify/commit/7b6a2ac)) * **search:** escape html ([fcb66e8](https://github.com/QingWei-Li/docsify/commit/fcb66e8)) * **search:** fix default config ([2efd859](https://github.com/QingWei-Li/docsify/commit/2efd859)) ### Features * **front-matter:** add front matter[WIP] ([dbb9278](https://github.com/QingWei-Li/docsify/commit/dbb9278)) * **render:** add auto header ([b7768b1](https://github.com/QingWei-Li/docsify/commit/b7768b1)) * **search:** Localization for search placeholder, close [#80](https://github.com/QingWei-Li/docsify/issues/80) ([2351c3e](https://github.com/QingWei-Li/docsify/commit/2351c3e)) * **themes:** add loading info ([86594a3](https://github.com/QingWei-Li/docsify/commit/86594a3)) ## [2.4.3](https://github.com/QingWei-Li/docsify/compare/v2.4.2...v2.4.3) (2017-02-15) ## [2.4.2](https://github.com/QingWei-Li/docsify/compare/v2.4.1...v2.4.2) (2017-02-14) ### Bug Fixes * **index:** load file path error ([dc536a3](https://github.com/QingWei-Li/docsify/commit/dc536a3)) ## [2.4.1](https://github.com/QingWei-Li/docsify/compare/v2.4.0...v2.4.1) (2017-02-13) ### Bug Fixes * **index:** cover page ([dd0c84b](https://github.com/QingWei-Li/docsify/commit/dd0c84b)) # [2.4.0](https://github.com/QingWei-Li/docsify/compare/v2.3.0...v2.4.0) (2017-02-13) ### Features * **hook:** add doneEach ([c6f7602](https://github.com/QingWei-Li/docsify/commit/c6f7602)) # [2.3.0](https://github.com/QingWei-Li/docsify/compare/v2.2.1...v2.3.0) (2017-02-13) ### Bug Fixes * **event:** has no effect on a FF mobile browser, fixed [#67](https://github.com/QingWei-Li/docsify/issues/67) ([0ff36c2](https://github.com/QingWei-Li/docsify/commit/0ff36c2)) * **render:** custom marked renderer ([bf559b4](https://github.com/QingWei-Li/docsify/commit/bf559b4)) * **render:** fix render link ([a866744](https://github.com/QingWei-Li/docsify/commit/a866744)) * **render:** image url ([6f87529](https://github.com/QingWei-Li/docsify/commit/6f87529)) * **render:** render link ([38ea660](https://github.com/QingWei-Li/docsify/commit/38ea660)) * **src:** fix route ([324301a](https://github.com/QingWei-Li/docsify/commit/324301a)) * **src:** get alias ([784173e](https://github.com/QingWei-Li/docsify/commit/784173e)) * **src:** get alias ([ce99a04](https://github.com/QingWei-Li/docsify/commit/ce99a04)) * **themes:** fix navbar style ([fa54b52](https://github.com/QingWei-Li/docsify/commit/fa54b52)) * **themes:** update navbar style ([4864d1b](https://github.com/QingWei-Li/docsify/commit/4864d1b)) ### Features * **hook:** support custom plugin ([9e81a59](https://github.com/QingWei-Li/docsify/commit/9e81a59)) * **src:** add alias feature ([24412cd](https://github.com/QingWei-Li/docsify/commit/24412cd)) * **src:** dynamic title and fix sidebar style ([6b30eb6](https://github.com/QingWei-Li/docsify/commit/6b30eb6)) ## [2.2.1](https://github.com/QingWei-Li/docsify/compare/v2.2.0...v2.2.1) (2017-02-11) ### Bug Fixes * **event:** scroll active sidebar ([50f5fc2](https://github.com/QingWei-Li/docsify/commit/50f5fc2)) * **search:** crash when not content, fixed [#68](https://github.com/QingWei-Li/docsify/issues/68) ([9d3cc89](https://github.com/QingWei-Li/docsify/commit/9d3cc89)) * **search:** not work in mobile ([3941304](https://github.com/QingWei-Li/docsify/commit/3941304)) # [2.2.0](https://github.com/QingWei-Li/docsify/compare/v2.1.0...v2.2.0) (2017-02-09) ### Features * **plugins:** add Google Analytics plugin ([#66](https://github.com/QingWei-Li/docsify/issues/66)) ([ac61bb0](https://github.com/QingWei-Li/docsify/commit/ac61bb0)) # [2.1.0](https://github.com/QingWei-Li/docsify/compare/v2.0.3...v2.1.0) (2017-02-09) ### Bug Fixes * render name ([12e2479](https://github.com/QingWei-Li/docsify/commit/12e2479)) * **vue.css:** update sidebar style ([fc140ef](https://github.com/QingWei-Li/docsify/commit/fc140ef)) ### Features * add search, close [#43](https://github.com/QingWei-Li/docsify/issues/43) ([eb5ff3e](https://github.com/QingWei-Li/docsify/commit/eb5ff3e)) ## [2.0.3](https://github.com/QingWei-Li/docsify/compare/v2.0.2...v2.0.3) (2017-02-07) ### Bug Fixes * css var polyfill ([8cd386a](https://github.com/QingWei-Li/docsify/commit/8cd386a)) * css var polyfill ([cbaee21](https://github.com/QingWei-Li/docsify/commit/cbaee21)) * rendering emojis ([8c7e4d7](https://github.com/QingWei-Li/docsify/commit/8c7e4d7)) ## [2.0.2](https://github.com/QingWei-Li/docsify/compare/v2.0.1...v2.0.2) (2017-02-05) ### Bug Fixes * button style in cover page ([4470855](https://github.com/QingWei-Li/docsify/commit/4470855)) ## [2.0.1](https://github.com/QingWei-Li/docsify/compare/v2.0.0...v2.0.1) (2017-02-05) # [2.0.0](https://github.com/QingWei-Li/docsify/compare/v1.10.5...v2.0.0) (2017-02-05) ### Features * customize the theme color ([5cc9f05](https://github.com/QingWei-Li/docsify/commit/5cc9f05)) ## [1.10.5](https://github.com/QingWei-Li/docsify/compare/v1.10.4...v1.10.5) (2017-01-28) ## [1.10.4](https://github.com/QingWei-Li/docsify/compare/v1.10.3...v1.10.4) (2017-01-27) ## [1.10.3](https://github.com/QingWei-Li/docsify/compare/v1.10.2...v1.10.3) (2017-01-27) ## [1.10.2](https://github.com/QingWei-Li/docsify/compare/v1.10.1...v1.10.2) (2017-01-25) ## [1.10.1](https://github.com/QingWei-Li/docsify/compare/v1.10.0...v1.10.1) (2017-01-25) # [1.10.0](https://github.com/QingWei-Li/docsify/compare/v1.9.0...v1.10.0) (2017-01-25) # [1.9.0](https://github.com/QingWei-Li/docsify/compare/v1.8.0...v1.9.0) (2017-01-24) # [1.8.0](https://github.com/QingWei-Li/docsify/compare/v1.7.4...v1.8.0) (2017-01-24) ## [1.7.4](https://github.com/QingWei-Li/docsify/compare/v1.7.3...v1.7.4) (2017-01-13) ## [1.7.3](https://github.com/QingWei-Li/docsify/compare/v1.7.2...v1.7.3) (2017-01-13) ## [1.7.2](https://github.com/QingWei-Li/docsify/compare/v1.7.1...v1.7.2) (2017-01-12) ## [1.7.1](https://github.com/QingWei-Li/docsify/compare/v1.7.0...v1.7.1) (2017-01-12) # [1.7.0](https://github.com/QingWei-Li/docsify/compare/v1.6.1...v1.7.0) (2017-01-12) ## [1.6.1](https://github.com/QingWei-Li/docsify/compare/v1.6.0...v1.6.1) (2017-01-10) # [1.6.0](https://github.com/QingWei-Li/docsify/compare/v1.5.2...v1.6.0) (2017-01-10) ## [1.5.2](https://github.com/QingWei-Li/docsify/compare/v1.5.1...v1.5.2) (2017-01-10) ## [1.5.1](https://github.com/QingWei-Li/docsify/compare/v1.5.0...v1.5.1) (2017-01-09) # [1.5.0](https://github.com/QingWei-Li/docsify/compare/v1.4.3...v1.5.0) (2017-01-04) ### Features * Markdown parser is configurable, [#42](https://github.com/QingWei-Li/docsify/issues/42) ([8b1000a](https://github.com/QingWei-Li/docsify/commit/8b1000a)) ## [1.4.3](https://github.com/QingWei-Li/docsify/compare/v1.4.2...v1.4.3) (2017-01-01) ## [1.4.2](https://github.com/QingWei-Li/docsify/compare/v1.4.1...v1.4.2) (2016-12-31) ## [1.4.1](https://github.com/QingWei-Li/docsify/compare/v1.4.0...v1.4.1) (2016-12-31) # [1.4.0](https://github.com/QingWei-Li/docsify/compare/v1.3.5...v1.4.0) (2016-12-31) ## [1.3.5](https://github.com/QingWei-Li/docsify/compare/v1.3.4...v1.3.5) (2016-12-25) ## [1.3.4](https://github.com/QingWei-Li/docsify/compare/v1.3.3...v1.3.4) (2016-12-25) ## [1.3.3](https://github.com/QingWei-Li/docsify/compare/v1.3.2...v1.3.3) (2016-12-23) ## [1.3.2](https://github.com/QingWei-Li/docsify/compare/v1.3.1...v1.3.2) (2016-12-22) ## [1.3.1](https://github.com/QingWei-Li/docsify/compare/v1.3.0...v1.3.1) (2016-12-22) # [1.3.0](https://github.com/QingWei-Li/docsify/compare/v1.2.0...v1.3.0) (2016-12-21) # [1.2.0](https://github.com/QingWei-Li/docsify/compare/v1.1.7...v1.2.0) (2016-12-20) ## [1.1.7](https://github.com/QingWei-Li/docsify/compare/v1.1.6...v1.1.7) (2016-12-19) ## [1.1.6](https://github.com/QingWei-Li/docsify/compare/v1.1.5...v1.1.6) (2016-12-18) ## [1.1.5](https://github.com/QingWei-Li/docsify/compare/v1.1.4...v1.1.5) (2016-12-18) ## [1.1.4](https://github.com/QingWei-Li/docsify/compare/v1.1.3...v1.1.4) (2016-12-17) ## [1.1.3](https://github.com/QingWei-Li/docsify/compare/v1.1.2...v1.1.3) (2016-12-17) ## [1.1.2](https://github.com/QingWei-Li/docsify/compare/v1.1.1...v1.1.2) (2016-12-17) ## [1.1.1](https://github.com/QingWei-Li/docsify/compare/v1.1.0...v1.1.1) (2016-12-17) # [1.1.0](https://github.com/QingWei-Li/docsify/compare/v1.0.3...v1.1.0) (2016-12-16) ## [1.0.3](https://github.com/QingWei-Li/docsify/compare/v1.0.2...v1.0.3) (2016-12-13) ## [1.0.2](https://github.com/QingWei-Li/docsify/compare/v1.0.1...v1.0.2) (2016-12-13) ## [1.0.1](https://github.com/QingWei-Li/docsify/compare/v1.0.0...v1.0.1) (2016-12-08) # [1.0.0](https://github.com/QingWei-Li/docsify/compare/v0.7.0...v1.0.0) (2016-12-08) # [0.7.0](https://github.com/QingWei-Li/docsify/compare/v0.6.1...v0.7.0) (2016-11-30) ## [0.6.1](https://github.com/QingWei-Li/docsify/compare/v0.6.0...v0.6.1) (2016-11-29) # [0.6.0](https://github.com/QingWei-Li/docsify/compare/v0.5.0...v0.6.0) (2016-11-29) # [0.5.0](https://github.com/QingWei-Li/docsify/compare/v0.4.2...v0.5.0) (2016-11-28) ## [0.4.2](https://github.com/QingWei-Li/docsify/compare/v0.4.1...v0.4.2) (2016-11-28) ## [0.4.1](https://github.com/QingWei-Li/docsify/compare/v0.4.0...v0.4.1) (2016-11-28) # [0.4.0](https://github.com/QingWei-Li/docsify/compare/v0.3.1...v0.4.0) (2016-11-27) ### Features * custom sidebar, [#4](https://github.com/QingWei-Li/docsify/issues/4) ([#5](https://github.com/QingWei-Li/docsify/issues/5)) ([37e7984](https://github.com/QingWei-Li/docsify/commit/37e7984)) ## [0.3.1](https://github.com/QingWei-Li/docsify/compare/v0.3.0...v0.3.1) (2016-11-27) # [0.3.0](https://github.com/QingWei-Li/docsify/compare/v0.2.1...v0.3.0) (2016-11-27) ## [0.2.1](https://github.com/QingWei-Li/docsify/compare/v0.2.0...v0.2.1) (2016-11-26) # [0.2.0](https://github.com/QingWei-Li/docsify/compare/v0.1.0...v0.2.0) (2016-11-26) # [0.1.0](https://github.com/QingWei-Li/docsify/compare/v0.0.5...v0.1.0) (2016-11-26) ## [0.0.5](https://github.com/QingWei-Li/docsify/compare/v0.0.4...v0.0.5) (2016-11-24) ## [0.0.4](https://github.com/QingWei-Li/docsify/compare/v0.0.3...v0.0.4) (2016-11-22) ## [0.0.3](https://github.com/QingWei-Li/docsify/compare/v0.0.2...v0.0.3) (2016-11-20) ## [0.0.2](https://github.com/QingWei-Li/docsify/compare/v0.0.1...v0.0.2) (2016-11-20) ## 0.0.1 (2016-11-20) ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2016 - present Docsify Contributors (https://github.com/docsifyjs/docsify/graphs/contributors) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================

docsify

A magical documentation site generator.

Backers on Open Collective Sponsors on Open Collective Build & Test npm Join Discord community and chat about Docsify Gitpod Ready-to-Code

Gold Sponsor via Open Collective

Docsify turns one or more Markdown files into a Website, with no build process required. ## Features - No statically built HTML files - Simple and lightweight - Smart full-text search plugin - Multiple themes - Useful plugin API - Emoji support ## Quick Start Get going fast by using a static web server or GitHub Pages with this ready-to-use [Docsify Template](https://github.com/docsifyjs/docsify-template), review the [quick start tutorial](https://docsify.js.org/#/quickstart) or jump right into a CodeSandbox example site with the button below. [![Edit 307qqv236](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/307qqv236) ## Showcase A large collection of showcase projects are included in [awesome-docsify](https://github.com/docsifyjs/awesome-docsify#showcase). ## Links - [Documentation](https://docsify.js.org) - [Docsify CLI (Command Line Interface)](https://github.com/docsifyjs/docsify-cli) - CDN: [UNPKG](https://unpkg.com/docsify/) | [jsDelivr](https://cdn.jsdelivr.net/npm/docsify/) | [cdnjs](https://cdnjs.com/libraries/docsify) - [`develop` branch preview](https://docsify-preview.vercel.app/) - [Awesome docsify](https://github.com/docsifyjs/awesome-docsify) - [Community chat](https://discord.gg/3NwKFyR) ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md). ## Backers Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/docsify/contribute)] ## Sponsors Thank you for supporting this project! ❤️ [[Become a sponsor](https://opencollective.com/docsify/contribute)] ## Contributors This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. ## License [MIT](LICENSE) ================================================ FILE: SECURITY.md ================================================ # Security Policy If you believe you have found a security vulnerability in docsify, please report it to us asap. ## Reporting a Vulnerability **Please do not report security vulnerabilities through our public GitHub issues.** Send email to us via :email: maintainers@docsifyjs.org. Please include as much of the following information as possible to help us better understand the possible issue: - Type of issue (e.g. cross-site scripting) - Full paths of source file(s) related to the manifestation of the issue - The location of the affected source code (tag/branch/commit or direct URL) - Any special configuration required to reproduce the issue - Step-by-step instructions to reproduce the issue - Proof-of-concept or exploit code - Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. Thank you in advance. ================================================ FILE: babel.config.json ================================================ { "presets": [ [ "@babel/preset-env", { "targets": "defaults" } ] ] } ================================================ FILE: build/cover.js ================================================ import fs from 'fs'; import { relative } from './util.js'; const read = fs.readFileSync; const write = fs.writeFileSync; const pkgPath = relative(import.meta, '..', 'package.json'); const pkg = JSON.parse(read(pkgPath).toString()); const version = process.env.VERSION || pkg.version; const file = relative(import.meta, '..', 'docs', '_coverpage.md'); let cover = read(file, 'utf8').toString(); console.log('Replace version number in cover page...'); cover = cover.replace( /(\S+)?<\/small>/g, /* html */ `${version}`, ); write(file, cover); ================================================ FILE: build/emoji.js ================================================ import fs from 'fs'; import path from 'path'; import axios from 'axios'; const filePaths = { emojiMarkdown: path.resolve(process.cwd(), 'docs', 'emoji.md'), emojiJS: path.resolve( process.cwd(), 'src', 'core', 'render', 'emoji-data.js', ), }; async function getEmojiData() { const emojiDataURL = 'https://api.github.com/emojis'; console.info(`- Fetching emoji data from ${emojiDataURL}`); const response = await axios.get(emojiDataURL); const baseURL = Object.values(response.data) .find(url => /unicode\//) .split('unicode/')[0]; const data = { ...response.data }; // Remove base URL from emoji URLs Object.entries(data).forEach( ([key, value]) => (data[key] = value.replace(baseURL, '')), ); console.info(`- Retrieved ${Object.keys(data).length} emoji entries`); return { baseURL, data, }; } function writeEmojiPage(emojiData) { const isExistingPage = fs.existsSync(filePaths.emojiMarkdown); const emojiPage = (isExistingPage && fs.readFileSync(filePaths.emojiMarkdown, 'utf8')) || '\n\n'; const emojiRegEx = /(\n)([\s\S]*)(\n)/; const emojiMatch = emojiPage.match(emojiRegEx); const emojiMarkdownStart = emojiMatch[1].trim(); const emojiMarkdown = emojiMatch[2].trim(); const emojiMarkdownEnd = emojiMatch[3].trim(); const newEmojiMarkdown = Object.keys(emojiData.data) .reduce( (preVal, curVal) => (preVal += `:${curVal}: ` + '`' + `:${curVal}:` + '`' + '\n\n'), '', ) .trim(); if (emojiMarkdown !== newEmojiMarkdown) { const newEmojiPage = emojiPage.replace( emojiMatch[0], `${emojiMarkdownStart}\n\n${newEmojiMarkdown}\n\n${emojiMarkdownEnd}`, ); fs.writeFileSync(filePaths.emojiMarkdown, newEmojiPage); console.info( `- ${!isExistingPage ? 'Created' : 'Updated'}: ${filePaths.emojiMarkdown}`, ); } else { console.info(`- No changes: ${filePaths.emojiMarkdown}`); } } function writeEmojiJS(emojiData) { const isExistingPage = fs.existsSync(filePaths.emojiJS); const emojiJS = isExistingPage && fs.readFileSync(filePaths.emojiJS, 'utf8'); const newEmojiJS = [ '// =============================================================================', '// DO NOT EDIT: This file is auto-generated by an /build/emoji.js', '// =============================================================================\n', `export default ${JSON.stringify(emojiData, {}, 2)}`, ].join('\n'); if (!emojiJS || emojiJS !== newEmojiJS) { fs.writeFileSync(filePaths.emojiJS, newEmojiJS); console.info( `- ${!isExistingPage ? 'Created' : 'Updated'}: ${filePaths.emojiJS}`, ); } else { console.info(`- No changes: ${filePaths.emojiJS}`); } } console.info('Build emoji'); const emojiData = await getEmojiData(); writeEmojiPage(emojiData); writeEmojiJS(emojiData); console.info('Finish update'); ================================================ FILE: build/release.sh ================================================ set -e if [[ -z $1 ]]; then echo "Enter new version: " read VERSION else VERSION=$1 fi RELEASE_TAG=${RELEASE_TAG:-""} if [[ -n "$RELEASE_TAG" && "$VERSION" != *"$RELEASE_TAG"* ]]; then RELEASE_MSG="$VERSION ($RELEASE_TAG)" else RELEASE_MSG="$VERSION" fi read -p "Releasing $VERSION $RELEASE_TAG - are you sure? (y/n) " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then echo "Releasing $VERSION ..." # Update version (don't commit or tag yet) npm --no-git-tag-version version "$VERSION" # Build and test npm run build npm run test:update:snapshot npm run test git stash npm run build:v4 # builds legacy v4 lib/ and themes/ folders for backwards compat while people transition to v5. git stash pop # Changelog npx conventional-changelog -p angular -i CHANGELOG.md -s # Commit all changes git add -A git commit -m "[release] $RELEASE_MSG" # Tag and push git tag "v$VERSION" git push origin "v$VERSION" git push # Publish to npm if [[ -z $RELEASE_TAG ]]; then npm publish else npm publish --tag "$RELEASE_TAG" fi npm run clean:v4 # clean up legacy v4 build files fi ================================================ FILE: build/util.js ================================================ import url from 'url'; import path from 'path'; /** Get a new path relative to the current module (pass import.meta). */ export const relative = (meta, ...to) => path.resolve(path.dirname(url.fileURLToPath(meta.url)), ...to); ================================================ FILE: docs/.nojekyll ================================================ ================================================ FILE: docs/CNAME ================================================ docsify.js.org ================================================ FILE: docs/README.md ================================================ # docsify > A magical documentation site generator. ## What it is Docsify turns your Markdown files into a documentation website instantly. Unlike most other documentation site generator tools, it doesn't need to build HTML files. Instead, it dynamically loads and parses your Markdown files and displays them as a website. To get started, create an `index.html` file and [deploy it on GitHub Pages](deploy.md) (for more details see the [Quick start](quickstart.md) guide). ## Features - No statically built HTML files - Simple and lightweight - Smart full-text search plugin - Multiple themes - Useful plugin API - Emoji support ## Examples Check out the [Showcase](https://github.com/docsifyjs/awesome-docsify#showcase) to see docsify in use. ## Donate Please consider donating if you think docsify is helpful to you or that my work is valuable. I am happy if you can help me [buy a cup of coffee](https://github.com/QingWei-Li/donate). :heart: ## Community Users and the development team are usually in the [Discord server](https://discord.gg/3NwKFyR). ================================================ FILE: docs/_coverpage.md ================================================ ![logo](_media/icon.svg) # docsify 5.0.0-rc.4 :id=docsify > A magical documentation site generator - Simple and lightweight - No statically built HTML files - Multiple themes [Get Started](#docsify) [GitHub](https://github.com/docsifyjs/docsify/) ================================================ FILE: docs/_media/example-with-yaml.md ================================================ --- author: John Smith date: 2020-1-1 --- > This is from the `example-with-yaml.md` ================================================ FILE: docs/_media/example.html ================================================

Example HTML Page

================================================ FILE: docs/_media/example.js ================================================ import fetch from 'fetch'; const URL = 'https://example.com'; const PORT = 8080; /// [demo] const result = fetch(`${URL}:${PORT}`) .then(response => { return response.json(); }) .then(myJson => { console.log(JSON.stringify(myJson)); }); /// [demo] result.then(console.log).catch(console.error); ================================================ FILE: docs/_media/example.md ================================================ > This is from the `example.md` ================================================ FILE: docs/_navbar.md ================================================ - Translations - [English](/) - [简体中文](/zh-cn/) ================================================ FILE: docs/_sidebar.md ================================================ - Getting started - [Quick start](quickstart.md) - [Adding pages](adding-pages.md) - [Cover page](cover.md) - [Custom navbar](custom-navbar.md) - Customization - [Configuration](configuration.md) - [Themes](themes.md) - [List of Plugins](plugins.md) - [Write a Plugin](write-a-plugin.md) - [Markdown configuration](markdown.md) - [Language highlighting](language-highlight.md) - [Emoji](emoji.md) - Guide - [Deploy](deploy.md) - [Helpers](helpers.md) - [Vue compatibility](vue.md) - [CDN](cdn.md) - [Offline Mode (PWA)](pwa.md) - [Embed Files](embed-files.md) - [UI Kit](ui-kit.md) - Upgrading - [v4 to v5](v5-upgrade.md) * [Awesome docsify](awesome.md) * [Changelog](changelog.md) ================================================ FILE: docs/adding-pages.md ================================================ # Adding pages If you need more pages, you can simply create more markdown files in your docsify directory. If you create a file named `guide.md`, then it is accessible via `/#/guide`. For example, the directory structure is as follows: ```text . └── docs ├── README.md ├── guide.md └── zh-cn ├── README.md └── guide.md ``` Matching routes ```text docs/README.md => http://domain.com docs/guide.md => http://domain.com/#/guide docs/zh-cn/README.md => http://domain.com/#/zh-cn/ docs/zh-cn/guide.md => http://domain.com/#/zh-cn/guide ``` ## Sidebar In order to have a sidebar, you can create your own `_sidebar.md` (see [this documentation's sidebar](https://github.com/docsifyjs/docsify/blob/main/docs/_sidebar.md) for an example): First, you need to set `loadSidebar` to **true**. Details are available in the [configuration paragraph](configuration#loadsidebar). ```html ``` Create the `_sidebar.md`: ```markdown - [Home](/) - [Page 1](page-1.md) ``` To create section headers: ```markdown - Section Header 1 - [Home](/) - [Page 1](page-1.md) - Section Header 2 - [Page 2](page-2.md) - [Page 3](page-3.md) ``` You need to create a `.nojekyll` in `./docs` to prevent GitHub Pages from ignoring files that begin with an underscore. > [!IMPORTANT] Docsify only looks for `_sidebar.md` in the current folder, and uses that, otherwise it falls back to the one configured using `window.$docsify.loadSidebar` config. Example file structure: ```text └── docs/ ├── _sidebar.md ├── index.md ├── getting-started.md └── running-services.md ``` ## Nested Sidebars You may want the sidebar to update after navigation to reflect the current directory. This can be done by adding a `_sidebar.md` file to each folder. `_sidebar.md` is loaded from each level directory. If the current directory doesn't have `_sidebar.md`, it will fall back to the parent directory. If, for example, the current path is `/guide/quick-start`, the `_sidebar.md` will be loaded from `/guide/_sidebar.md`. You can specify `alias` to avoid unnecessary fallback. ```html ``` > [!IMPORTANT] You can create a `README.md` file in a subdirectory to use it as the landing page for the route. ## Set Page Titles from Sidebar Selection A page's `title` tag is generated from the _selected_ sidebar item name. For better SEO, you can customize the title by specifying a string after the filename. ```markdown - [Home](/) - [Guide](guide.md 'The greatest guide in the world') ``` ## Table of Contents Once you've created `_sidebar.md`, the sidebar content is automatically generated based on the headers in the markdown files. A custom sidebar can also automatically generate a table of contents by setting a `subMaxLevel`, compare [subMaxLevel configuration](configuration#submaxlevel). ```html ``` ## Ignoring Subheaders When `subMaxLevel` is set, each header is automatically added to the table of contents by default. If you want to ignore a specific header, add `` to it. ```markdown # Getting Started ## Header This header won't appear in the sidebar table of contents. ``` To ignore all headers on a specific page, you can use `` on the first header of the page. ```markdown # Getting Started ## Header This header won't appear in the sidebar table of contents. ``` Both `` and `` will not be rendered on the page when used. And the `{docsify-ignore}` and `{docsify-ignore-all}` can do the samething as well. ================================================ FILE: docs/cdn.md ================================================ # CDN The docsify [npm package](https://www.npmjs.com/package/docsify) is auto-published to CDNs with each release. The contents can be viewed on each CDN. Docsify recommends [jsDelivr](//cdn.jsdelivr.net) as its preferred CDN: - https://cdn.jsdelivr.net/npm/docsify/ Other CDNs are available and may be required in locations where jsDelivr is not available: - https://cdnjs.com/libraries/docsify - https://unpkg.com/browse/docsify/ - https://www.bootcdn.cn/docsify/ ## Specifying versions Note the `@` version lock in the CDN URLs below. This allows specifying the latest major, minor, patch, or specific [semver](https://semver.org) version number. - MAJOR versions include breaking changes
`1.0.0` → `2.0.0` - MINOR versions include non-breaking new functionality
`1.0.0` → `1.1.0` - PATCH versions include non-breaking bug fixes
`1.0.0` → `1.0.1` Uncompressed resources are available by omitting the `.min` from the filename. ## Latest "major" version Specifying the latest major version allows your site to receive all non-breaking enhancements ("minor" updates) and bug fixes ("patch" updates) as they are released. This is good option for those who prefer a zero-maintenance way of keeping their site up to date with minimal risk as new versions are published. > [!TIP] When a new major version is released, you will need to manually update the major version number after the `@` symbol in your CDN URLs. ```html ``` ## Specific version Specifying an exact version prevents any future updates from affecting your site. This is good option for those who prefer to manually update their resources as new versions are published. > [!TIP] When a new version is released, you will need to manually update the version number after the `@` symbol in your CDN URLs. ```html ``` ================================================ FILE: docs/configuration.md ================================================ # Configuration You can configure Docsify by defining `window.$docsify` as an object: ```html ``` The config can also be defined as a function, in which case the first argument is the Docsify `vm` instance. The function should return a config object. This can be useful for referencing `vm` in places like the markdown configuration: ```html ``` ## alias - Type: `Object` Set the route alias. You can freely manage routing rules. Supports RegExp. Do note that order matters! If a route can be matched by multiple aliases, the one you declared first takes precedence. ```js window.$docsify = { alias: { '/foo/(.*)': '/bar/$1', // supports regexp '/zh-cn/changelog': '/changelog', '/changelog': 'https://raw.githubusercontent.com/docsifyjs/docsify/main/CHANGELOG', // You may need this if you use routerMode:'history'. '/.*/_sidebar.md': '/_sidebar.md', // See #301 }, }; ``` > **Note** If you change [`routerMode`](#routermode) to `'history'`, you may > want to configure an alias for your `_sidebar.md` and `_navbar.md` files. ## auto2top - Type: `Boolean` - Default: `false` Scrolls to the top of the screen when the route is changed. ```js window.$docsify = { auto2top: true, }; ``` ## autoHeader - Type: `Boolean` - Default: `false` If `loadSidebar` and `autoHeader` are both enabled, for each link in `_sidebar.md`, prepend a header to the page before converting it to HTML — but only if the page does not already contain an H1 heading. For more details, see [#78](https://github.com/docsifyjs/docsify/issues/78). ```js window.$docsify = { loadSidebar: true, autoHeader: true, }; ``` ## basePath - Type: `String` Base path of the website. You can set it to another directory or another domain name. ```js window.$docsify = { basePath: '/path/', // Load the files from another site basePath: 'https://docsify.js.org/', // Even can load files from other repo basePath: 'https://raw.githubusercontent.com/ryanmcdermott/clean-code-javascript/master/', }; ``` ## catchPluginErrors - Type: `Boolean` - Default: `true` Determines if Docsify should handle uncaught _synchronous_ plugin errors automatically. This can prevent plugin errors from affecting docsify's ability to properly render live site content. ## cornerExternalLinkTarget - Type: `String` - Default: `'_blank'` Target to open external link at the top right corner. Default `'_blank'` (new window/tab) ```js window.$docsify = { cornerExternalLinkTarget: '_self', // default: '_blank' }; ``` ## coverpage - Type: `Boolean|String|String[]|Object` - Default: `false` Activate the [cover feature](cover.md). If true, it will load from `_coverpage.md`. ```js window.$docsify = { coverpage: true, // Custom file name coverpage: 'cover.md', // multiple covers coverpage: ['/', '/zh-cn/'], // multiple covers and custom file name coverpage: { '/': 'cover.md', '/zh-cn/': 'cover.md', }, }; ``` ## el - Type: `String` - Default: `'#app'` The DOM element to be mounted on initialization. It can be a CSS selector string or an actual [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement). ```js window.$docsify = { el: '#app', }; ``` ## executeScript - Type: `Boolean` - Default: `null` Execute the script on the page. Only parses the first script tag ([demo](themes)). If Vue is detected, this is `true` by default. ```js window.$docsify = { executeScript: true, }; ``` ```markdown ## This is test ``` Note that if you are running an external script, e.g. an embedded jsfiddle demo, make sure to include the [external-script](plugins.md?id=external-script) plugin. ## ext - Type: `String` - Default: `'.md'` Request file extension. ```js window.$docsify = { ext: '.md', }; ``` ## externalLinkRel - Type: `String` - Default: `'noopener'` Default `'noopener'` (no opener) prevents the newly opened external page (when [externalLinkTarget](#externallinktarget) is `'_blank'`) from having the ability to control our page. No `rel` is set when it's not `'_blank'`. See [this post](https://mathiasbynens.github.io/rel-noopener/) for more information about why you may want to use this option. ```js window.$docsify = { externalLinkRel: '', // default: 'noopener' }; ``` ## externalLinkTarget - Type: `String` - Default: `'_blank'` Target to open external links inside the markdown. Default `'_blank'` (new window/tab) ```js window.$docsify = { externalLinkTarget: '_self', // default: '_blank' }; ``` ## fallbackLanguages - Type: `Array` List of languages that will fallback to the default language when a page is requested and it doesn't exist for the given locale. Example: - try to fetch the page of `/de/overview`. If this page exists, it'll be displayed. - then try to fetch the default page `/overview` (depending on the default language). If this page exists, it'll be displayed. - then display the 404 page. ```js window.$docsify = { fallbackLanguages: ['fr', 'de'], }; ``` ## fallbackDefaultLanguage - Type: `String` - Default: `''` When a page is requested and it doesn't exist for the given locale, Docsify will fallback to the language specified by this option. For example, in the scenario described above, if `/de/overview` does not exist and `fallbackDefaultLanguage` is configured as `zh-cn`, Docsify will fetch `/zh-cn/overview` instead of `/overview`. ```js window.$docsify = { fallbackLanguages: ['fr', 'de'], fallbackDefaultLanguage: 'zh-cn', // default: '' }; ``` ## formatUpdated - Type: `String|Function` We can display the file update date through **{docsify-updated}** variable. And format it by `formatUpdated`. See https://github.com/lukeed/tinydate#patterns ```js window.$docsify = { formatUpdated: '{MM}/{DD} {HH}:{mm}', formatUpdated(time) { // ... return time; }, }; ``` ## hideSidebar - Type : `Boolean` - Default: `false` This option will completely hide your sidebar and won't render any content on the side. ```js window.$docsify = { hideSidebar: true, }; ``` ## homepage - Type: `String` - Default: `'README.md'` `README.md` in your docs folder will be treated as the homepage for your website, but sometimes you may need to serve another file as your homepage. ```js window.$docsify = { // Change to /home.md homepage: 'home.md', // Or use the readme in your repo homepage: 'https://raw.githubusercontent.com/docsifyjs/docsify/main/README.md', }; ``` ## keyBindings - Type: `Boolean|Object` - Default: `Object` - \\ Toggle the sidebar menu - / Focus on [search](plugins#full-text-search) field. Also supports alt / ctrl + k. Binds key combination(s) to a custom callback function. Key `bindings` are defined as case insensitive string values separated by `+`. Modifier key values include `alt`, `ctrl`, `meta`, and `shift`. Non-modifier key values should match the keyboard event's [key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) or [code](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code) value. The `callback` function receive a [keydown event](https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event) as an argument. > [!IMPORTANT] Let site visitors know your custom key bindings are available! If a binding is associated with a DOM element, consider inserting a `` element as a visual cue (e.g., alt + a) or adding [title](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title) and [aria-keyshortcuts](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-keyshortcuts) attributes for hover/focus hints. ```js window.$docsify = { keyBindings: { // Custom key binding myCustomBinding: { bindings: ['alt+a', 'shift+a'], callback(event) { alert('Hello, World!'); }, }, }, }; ``` Key bindings can be disabled entirely or individually by setting the binding configuration to `false`. ```js window.$docsify = { // Disable all key bindings keyBindings: false, }; ``` ```js window.$docsify = { keyBindings: { // Disable individual key bindings focusSearch: false, toggleSidebar: false, }, }; ``` ## loadNavbar - Type: `Boolean|String` - Default: `false` Loads navbar from the Markdown file `_navbar.md` if **true**, else loads it from the path specified. ```js window.$docsify = { // load from _navbar.md loadNavbar: true, // load from nav.md loadNavbar: 'nav.md', }; ``` ## loadSidebar - Type: `Boolean|String` - Default: `false` Loads sidebar from the Markdown file `_sidebar.md` if **true**, else loads it from the path specified. ```js window.$docsify = { // load from _sidebar.md loadSidebar: true, // load from summary.md loadSidebar: 'summary.md', }; ``` ## logo - Type: `String` Website logo as it appears in the sidebar. You can resize it using CSS. > [!IMPORTANT] Logo will only be visible if `name` prop is also set. See [name](#name) configuration. ```js window.$docsify = { logo: '/_media/icon.svg', }; ``` ## markdown - Type: `Function` See [Markdown configuration](markdown.md). ```js window.$docsify = { // object markdown: { smartypants: true, renderer: { link() { // ... }, }, }, // function markdown(marked, renderer) { // ... return marked; }, }; ``` ## maxLevel - Type: `Number` - Default: `6` Maximum Table of content level. ```js window.$docsify = { maxLevel: 4, }; ``` ## mergeNavbar - Type: `Boolean` - Default: `false` Navbar will be merged with the sidebar on smaller screens. ```js window.$docsify = { mergeNavbar: true, }; ``` ## name - Type: `Boolean | String` Website name as it appears in the sidebar. ```js window.$docsify = { name: 'docsify', }; ``` The name field can also contain custom HTML for easier customization: ```js window.$docsify = { name: 'docsify', }; ``` If `true`, the website name will be inferred from the document's `` tag. ```js window.$docsify = { name: true, }; ``` If `false` or empty, no name will be displayed. ```js window.$docsify = { name: false, }; ``` ## nameLink - Type: `String` - Default: `'window.location.pathname'` The URL that the website `name` links to. ```js window.$docsify = { nameLink: '/', // For each route nameLink: { '/zh-cn/': '#/zh-cn/', '/': '#/', }, }; ``` ## nativeEmoji - Type: `Boolean` - Default: `false` Render emoji shorthand codes using GitHub-style emoji images or native emoji characters. ```js window.$docsify = { nativeEmoji: true, }; ``` ```markdown :smile: :partying_face: :joy: :+1: :-1: ``` GitHub-style images when `false`: <output data-lang="output"> <img class="emoji" src="https://github.githubassets.com/images/icons/emoji/unicode/1f604.png" alt="smile"> <img class="emoji" src="https://github.githubassets.com/images/icons/emoji/unicode/1f973.png" alt="partying_face"> <img class="emoji" src="https://github.githubassets.com/images/icons/emoji/unicode/1f602.png" alt="joy"> <img class="emoji" src="https://github.githubassets.com/images/icons/emoji/unicode/1f44d.png" alt="+1"> <img class="emoji" src="https://github.githubassets.com/images/icons/emoji/unicode/1f44e.png" alt="-1"> </output> Native characters when `true`: <output data-lang="output"> <span class="emoji">😄︎</span> <span class="emoji">🥳︎</span> <span class="emoji">😂︎</span> <span class="emoji">👍︎</span> <span class="emoji">👎︎</span> </output> To render shorthand codes as text, replace `:` characters with the `:` HTML entity. ```markdown :100: ``` <output data-lang="output"> :100: </output> ## noCompileLinks - Type: `Array<string>` Sometimes we do not want docsify to handle our links. See [#203](https://github.com/docsifyjs/docsify/issues/203). We can skip compiling of certain links by specifying an array of strings. Each string is converted into to a regular expression (`RegExp`) and the _whole_ href of a link is matched against it. ```js window.$docsify = { noCompileLinks: ['/foo', '/bar/.*'], }; ``` ## noEmoji - Type: `Boolean` - Default: `false` Disabled emoji parsing and render all emoji shorthand as text. ```js window.$docsify = { noEmoji: true, }; ``` ```markdown :100: ``` <output data-lang="output"> :100: </output> To disable emoji parsing of individual shorthand codes, replace `:` characters with the `:` HTML entity. ```markdown :100: :100: ``` <output data-lang="output"> :100: :100: </output> ## notFoundPage - Type: `Boolean|String|Object` - Default: `false` Display default "404 - Not Found" message: ```js window.$docsify = { notFoundPage: false, }; ``` Load the `_404.md` file: ```js window.$docsify = { notFoundPage: true, }; ``` Load the customized path of the 404 page: ```js window.$docsify = { notFoundPage: 'my404.md', }; ``` Load the right 404 page according to the localization: ```js window.$docsify = { notFoundPage: { '/': '_404.md', '/de': 'de/_404.md', }, }; ``` > Note: The options for fallbackLanguages don't work with the `notFoundPage` options. ## onlyCover - Type: `Boolean` - Default: `false` Only coverpage is loaded when visiting the home page. ```js window.$docsify = { onlyCover: false, }; ``` ## plugins See [Plugins](./plugins.md). ## relativePath - Type: `Boolean` - Default: `false` If **true**, links are relative to the current context. For example, the directory structure is as follows: ```text . └── docs ├── README.md ├── guide.md └── zh-cn ├── README.md ├── guide.md └── config └── example.md ``` With relative path **enabled** and current URL `http://domain.com/zh-cn/README`, given links will resolve to: ```text guide.md => http://domain.com/zh-cn/guide config/example.md => http://domain.com/zh-cn/config/example ../README.md => http://domain.com/README /README.md => http://domain.com/README ``` ```js window.$docsify = { // Relative path enabled relativePath: true, // Relative path disabled (default value) relativePath: false, }; ``` ## repo - Type: `String` Configure the repository url, or a string of `username/repo`, to add the [GitHub Corner](http://tholman.com/github-corners/) widget in the top right corner of the site. ```js window.$docsify = { repo: 'docsifyjs/docsify', // or repo: 'https://github.com/docsifyjs/docsify/', }; ``` If undefined or empty, no GitHub corner will be displayed. ## requestHeaders - Type: `Object` Set the request resource headers. ```js window.$docsify = { requestHeaders: { 'x-token': 'xxx', }, }; ``` Such as setting the cache ```js window.$docsify = { requestHeaders: { 'cache-control': 'max-age=600', }, }; ``` ## routerMode Configure the URL format that the paths of your site will use. - Type: `String` - Default: `'hash'` ```js window.$docsify = { routerMode: 'history', // default: 'hash' }; ``` For statically-deployed sites (f.e. on GitHub Pages) hash-based routing is simpler to set up. For websites that can re-write URLs, the history-based format is better (especially for search-engine optimization, hash-based routing is not so search-engine friendly) Hash-based routing means all URL paths will be prefixed with `/#/` in the address bar. This is a trick that allows the site to load `/index.html`, then it uses the path that follows the `#` to determine what markdown files to load. For example, a complete hash-based URL may look like this: `https://example.com/#/path/to/page`. The browser will actually load `https://example.com` (assuming your static server serves `index.html` by default, as most do), and then the Docsify JavaScript code will look at the `/#/...` and determine the markdown file to load and render. Additionally, when clicking on a link, the Docsify router will change the content after the hash dynamically. The value of `location.pathname` will still be `/` no matter what. The parts of a hash path are _not_ sent to the server when visiting such a URL in a browser. On the other hand, history-based routing means the Docsify JavaScript will use the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to dynamically change the URL without using a `#`. This means that all URLs will be considered "real" by search engines, and the full path will be sent to the server when visiting the URL in your browser. For example, a URL may look like `https://example.com/path/to/page`. The browser will try to load that full URL directly from the server, not just `https://example.com`. The upside of this is that these types of URLs are much more friendly for search engines, and can be indexed (yay!). The downside, however, is that your server, or the place where you host your site files, has to be able to handle these URLs. Various static website hosting services allow "rewrite rules" to be configured, such that a server can be configured to always send back `/index.html` no matter what path is visited. The value of `location.pathname` will show `/path/to/page`, because it was actually sent to the server. TLDR: start with `hash` routing (the default). If you feel adventurous, learn how to configure a server, then switch to `history` mode for better experience without the `#` in the URL and SEO optimization. > **Note** If you use `routerMode: 'history'`, you may want to add an > [`alias`](#alias) to make your `_sidebar.md` and `_navbar.md` files always be > loaded no matter which path is being visited. > > ```js > window.$docsify = { > routerMode: 'history', > alias: { > '/.*/_sidebar.md': '/_sidebar.md', > '/.*/_navbar.md': '/_navbar.md', > }, > }; > ``` ## routes - Type: `Object` Define "virtual" routes that can provide content dynamically. A route is a map between the expected path, to either a string or a function. If the mapped value is a string, it is treated as markdown and parsed accordingly. If it is a function, it is expected to return markdown content. A route function receives up to three parameters: 1. `route` - the path of the route that was requested (e.g. `/bar/baz`) 2. `matched` - the [`RegExpMatchArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match) that was matched by the route (e.g. for `/bar/(.+)`, you get `['/bar/baz', 'baz']`) 3. `next` - this is a callback that you may call when your route function is async Do note that order matters! Routes are matched the same order you declare them in, which means that in cases where you have overlapping routes, you might want to list the more specific ones first. ```js window.$docsify = { routes: { // Basic match w/ return string '/foo': '# Custom Markdown', // RegEx match w/ synchronous function '/bar/(.*)'(route, matched) { return '# Custom Markdown'; }, // RegEx match w/ asynchronous function '/baz/(.*)'(route, matched, next) { fetch('/api/users?id=12345') .then(response => { next('# Custom Markdown'); }) .catch(err => { // Handle error... }); }, }, }; ``` Other than strings, route functions can return a falsy value (`null` \ `undefined`) to indicate that they ignore the current request: ```js window.$docsify = { routes: { // accepts everything other than dogs (synchronous) '/pets/(.+)'(route, matched) { if (matched[0] === 'dogs') { return null; } else { return 'I like all pets but dogs'; } } // accepts everything other than cats (asynchronous) '/pets/(.*)'(route, matched, next) { if (matched[0] === 'cats') { next(); } else { // Async task(s)... next('I like all pets but cats'); } } } } ``` Finally, if you have a specific path that has a real markdown file (and therefore should not be matched by your route), you can opt it out by returning an explicit `false` value: ```js window.$docsify = { routes: { // if you look up /pets/cats, docsify will skip all routes and look for "pets/cats.md" '/pets/cats'(route, matched) { return false; } // but any other pet should generate dynamic content right here '/pets/(.+)'(route, matched) { const pet = matched[0]; return `your pet is ${pet} (but not a cat)`; } } } ``` ## skipLink - Type: `Boolean|String|Object` - Default: `'Skip to main content'` Determines if/how the site's [skip navigation link](https://webaim.org/techniques/skipnav/) will be rendered. ```js // Render skip link for all routes window.$docsify = { skipLink: 'Skip to content', }; ``` ```js // Render localized skip links based on route paths window.$docsify = { skipLink: { '/es/': 'Saltar al contenido principal', '/de-de/': 'Ga naar de hoofdinhoud', '/ru-ru/': 'Перейти к основному содержанию', '/zh-cn/': '跳到主要内容', }, }; ``` ```js // Do not render skip link window.$docsify = { skipLink: false, }; ``` ```js // Use default window.$docsify = { skipLink: true, // "Skip to main content" }; ``` ## subMaxLevel - Type: `Number` - Default: `0` Add table of contents (TOC) in custom sidebar. ```js window.$docsify = { subMaxLevel: 2, }; ``` If you have a link to the homepage in the sidebar and want it to be shown as active when accessing the root url, make sure to update your sidebar accordingly: ```markdown - Sidebar - [Home](/) - [Another page](another.md) ``` For more details, see [#1131](https://github.com/docsifyjs/docsify/issues/1131). ## themeColor ⚠️ :id=themecolor > [!IMPORTANT] Deprecated as of v5. Use the `--theme-color` [theme property](themes#theme-properties) to [customize](themes#customization) your theme color. - Type: `String` Customize the theme color. ```js window.$docsify = { themeColor: '#3F51B5', }; ``` ## topMargin ⚠️ :id=topmargin > [!IMPORTANT] Deprecated as of v5. Use the `--scroll-padding-top` [theme property](themes#theme-properties) to specify a scroll margin when using a sticky navbar. - Type: `Number|String` - Default: `0` Adds scroll padding to the top of the viewport. This is useful when you have added a sticky or "fixed" element and would like auto scrolling to align with the bottom of your element. ```js window.$docsify = { topMargin: 90, // 90, '90px', '2rem', etc. }; ``` ## vueComponents - Type: `Object` Creates and registers global [Vue](https://vuejs.org/guide/essentials/component-basics.html). Components are specified using the component name as the key with an object containing Vue options as the value. Component `data` is unique for each instance and will not persist as users navigate the site. ```js window.$docsify = { vueComponents: { 'button-counter': { template: ` <button @click="count += 1"> You clicked me {{ count }} times </button> `, data() { return { count: 0, }; }, }, }, }; ``` ```markdown <button-counter></button-counter> ``` <output data-lang="output"> <button-counter></button-counter> </output> ## vueGlobalOptions - Type: `Object` Specifies global Vue options for use with Vue content not explicitly mounted with [vueMounts](#mounting-dom-elements), [vueComponents](#components), or a [markdown script](#markdown-script). Changes to global `data` will persist and be reflected anywhere global references are used. ```js window.$docsify = { vueGlobalOptions: { data() { return { count: 0, }; }, }, }; ``` ```markdown <p> <button @click="count -= 1">-</button> {{ count }} <button @click="count += 1">+</button> </p> ``` <output data-lang="output"> <p> <button @click="count -= 1">-</button> {{ count }} <button @click="count += 1">+</button> </p> </output> ## vueMounts - Type: `Object` Specifies DOM elements to mount as Vue instances and their associated options. Mount elements are specified using a [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) as the key with an object containing Vue options as their value. Docsify will mount the first matching element in the main content area each time a new page is loaded. Mount element `data` is unique for each instance and will not persist as users navigate the site. ```js window.$docsify = { vueMounts: { '#counter': { data() { return { count: 0, }; }, }, }, }; ``` ```markdown <div id="counter"> <button @click="count -= 1">-</button> {{ count }} <button @click="count += 1">+</button> </div> ``` <output id="counter"> <button @click="count -= 1">-</button> {{ count }} <button @click="count += 1">+</button> </output> ================================================ FILE: docs/cover.md ================================================ # Cover Activate the cover feature by setting `coverpage` to **true**. See [coverpage configuration](configuration#coverpage). ## Basic usage Set `coverpage` to **true**, and create a `_coverpage.md`: ```js window.$docsify = { coverpage: true, }; ``` ```markdown <!-- _coverpage.md --> ![logo](_media/icon.svg) # docsify > A magical documentation site generator - Simple and lightweight - No statically built HTML files - Multiple themes [GitHub](https://github.com/docsifyjs/docsify/) [Get Started](#docsify) ``` ## Customization The cover page can be customized using [theme properties](themes#theme-properties): <!-- prettier-ignore --> ```css :root { --cover-bg : url('path/to/image.png'); --cover-bg-overlay : rgba(0, 0, 0, 0.5); --cover-color : #fff; --cover-title-color: var(--theme-color); --cover-title-font : 600 var(--font-size-xxxl) var(--font-family); } ``` Alternatively, a background color or image can be specified in the cover page markdown. ```markdown <!-- background color --> ![color](#f0f0f0) ``` ```markdown <!-- background image --> ![](_media/bg.png) ``` ## Coverpage as homepage Normally, the coverpage and the homepage appear at the same time. Of course, you can also separate the coverpage by [`onlyCover`](configuration#onlycover) option. ## Multiple covers If your docs site is in more than one language, it may be useful to set multiple covers. For example, your docs structure is like this ```text . └── docs ├── README.md ├── guide.md ├── _coverpage.md └── zh-cn ├── README.md └── guide.md └── _coverpage.md ``` Now, you can set ```js window.$docsify = { coverpage: ['/', '/zh-cn/'], }; ``` Or a special file name ```js window.$docsify = { coverpage: { '/': 'cover.md', '/zh-cn/': 'cover.md', }, }; ``` ================================================ FILE: docs/custom-navbar.md ================================================ # Custom navbar ## HTML If you need custom navigation, you can create a HTML-based navigation bar. > [!IMPORTANT] Note that documentation links begin with `#/`. ```html <!-- index.html --> <body> <nav> <a href="#/">EN</a> <a href="#/zh-cn/">简体中文</a> </nav> <div id="app"></div> </body> ``` ## Markdown Alternatively, you can create a custom markdown-based navigation file by setting `loadNavbar` to **true** and creating `_navbar.md`, compare [loadNavbar configuration](configuration#loadnavbar). ```html <!-- index.html --> <script> window.$docsify = { loadNavbar: true, }; </script> <script src="//cdn.jsdelivr.net/npm/docsify@5/dist/docsify.min.js"></script> ``` ```markdown <!-- _navbar.md --> - [En](/) - [chinese](/zh-cn/) ``` To create drop-down menus: ```markdown <!-- _navbar.md --> - Translations - [En](/) - [chinese](/zh-cn/) ``` > [!IMPORTANT] You need to create a `.nojekyll` in `./docs` to prevent GitHub Pages from ignoring files that begin with an underscore. `_navbar.md` is loaded from each level directory. If the current directory doesn't have `_navbar.md`, it will fall back to the parent directory. If, for example, the current path is `/guide/quick-start`, the `_navbar.md` will be loaded from `/guide/_navbar.md`. ## Nesting You can create sub-lists by indenting items that are under a certain parent. ```markdown <!-- _navbar.md --> - Getting started - [Quick start](quickstart.md) - [Writing more pages](more-pages.md) - [Custom navbar](custom-navbar.md) - [Cover page](cover.md) - Configuration - [Configuration](configuration.md) - [Themes](themes.md) - [Using plugins](plugins.md) - [Markdown configuration](markdown.md) - [Language highlight](language-highlight.md) ``` renders as ![Nesting navbar](_images/nested-navbar.png 'Nesting navbar') ## Combining custom navbars with the emoji plugin If you use the [emoji plugin](plugins#emoji): ```html <!-- index.html --> <script> window.$docsify = { // ... }; </script> <script src="//cdn.jsdelivr.net/npm/docsify@5/dist/docsify.min.js"></script> <script src="//cdn.jsdelivr.net/npm/docsify@5/dist/plugins/emoji.min.js"></script> ``` you could, for example, use flag emojis in your custom navbar Markdown file: ```markdown <!-- _navbar.md --> - [:us:, :uk:](/) - [:cn:](/zh-cn/) ``` ================================================ FILE: docs/deploy.md ================================================ # Deploy Similar to [GitBook](https://www.gitbook.com), you can deploy files to GitHub Pages, GitLab Pages or VPS. ## GitHub Pages There are three places to populate your docs for your GitHub repository: - `docs/` folder - main branch - gh-pages branch It is recommended that you save your files to the `./docs` subfolder of the `main` branch of your repository. Then select `main branch /docs folder` as your GitHub Pages source in your repository's settings page. ![GitHub Pages](_images/deploy-github-pages.png) > [!IMPORTANT] You can also save files in the root directory and select `main branch`. > You'll need to place a `.nojekyll` file in the deploy location (such as `/docs` or the gh-pages branch) ## GitLab Pages If you are deploying your master branch, create a `.gitlab-ci.yml` with the following script: > [!TIP] The `.public` workaround is so `cp` doesn't also copy `public/` to itself in an infinite loop. ```YAML pages: stage: deploy script: - mkdir .public - cp -r * .public - mv .public public artifacts: paths: - public only: - master ``` > [!IMPORTANT] You can replace script with `- cp -r docs/. public`, if `./docs` is your Docsify subfolder. ## Firebase Hosting > [!IMPORTANT] You'll need to install the Firebase CLI using `npm i -g firebase-tools` after signing into the [Firebase Console](https://console.firebase.google.com) using a Google Account. Using a terminal, determine and navigate to the directory for your Firebase Project. This could be `~/Projects/Docs`, etc. From there, run `firebase init` and choose `Hosting` from the menu (use **space** to select, **arrow keys** to change options and **enter** to confirm). Follow the setup instructions. Your `firebase.json` file should look similar to this (I changed the deployment directory from `public` to `site`): ```json { "hosting": { "public": "site", "ignore": ["firebase.json", "**/.*", "**/node_modules/**"] } } ``` Once finished, build the starting template by running `docsify init ./site` (replacing site with the deployment directory you determined when running `firebase init` - public by default). Add/edit the documentation, then run `firebase deploy` from the root project directory. ## VPS Use the following nginx config. ```nginx server { listen 80; server_name your.domain.com; location / { alias /path/to/dir/of/docs/; index index.html; } } ``` ## Netlify 1. Login to your [Netlify](https://www.netlify.com/) account. 2. In the [dashboard](https://app.netlify.com/) page, click **Add New Site**. 3. Select GitHub. 4. Choose the repository where you store your docs, in the **Base Directory** add the subfolder where the files are stored. For example, it should be `docs`. 5. In the **Build Command** area leave it blank. 6. In the **Publish directory** area, if you have added the `docs` in the **Base Directory** you will see the publish directory populated with `docs/` 7. Netlify is smart enough to look for the the `index.html` file inside the `docs/` folder. ### HTML5 router When using the HTML5 router, you need to set up redirect rules that redirect all requests to your `index.html`. It's pretty simple when you're using Netlify. Just create a file named `_redirects` in the docs directory, add this snippet to the file, and you're all set: ```sh /* /index.html 200 ``` ## Vercel 1. Install [Vercel CLI](https://vercel.com/download), `npm i -g vercel` 2. Change directory to your docsify website, for example `cd docs` 3. Deploy with a single command, `vercel` ## AWS Amplify 1. Set the routerMode in the Docsify project `index.html` to _history_ mode. ```html <script> window.$docsify = { loadSidebar: true, routerMode: 'history', }; </script> ``` 2. Login to your [AWS Console](https://aws.amazon.com). 3. Go to the [AWS Amplify Dashboard](https://aws.amazon.com/amplify). 4. Choose the **Deploy** route to setup your project. 5. When prompted, keep the build settings empty if you're serving your docs within the root directory. If you're serving your docs from a different directory, customise your amplify.yml ```yml version: 0.1 frontend: phases: build: commands: - echo "Nothing to build" artifacts: baseDirectory: /docs files: - '**/*' cache: paths: [] ``` 6. Add the following Redirect rules in their displayed order. Note that the second record is a PNG image where you can change it with any image format you are using. | Source address | Target address | Type | | -------------- | -------------- | ------------- | | /<\*>.md | /<\*>.md | 200 (Rewrite) | | /<\*>.png | /<\*>.png | 200 (Rewrite) | | /<\*> | /index.html | 200 (Rewrite) | ## Stormkit 1. Login to your [Stormkit](https://www.stormkit.io) account. 2. Using the user interface, import your docsify project from one of the three supported Git providers (GitHub, GitLab, or Bitbucket). 3. Navigate to the project’s production environment in Stormkit or create a new environment if needed. 4. Verify the build command in your Stormkit configuration. By default, Stormkit CI will run `npm run build` but you can specify a custom build command on this page. 5. Set output folder to `docs` 6. Click the “Deploy Now” button to deploy your site. Read more in the [Stormkit Documentation](https://stormkit.io/docs). ## Docker - Create docsify files You need prepare the initial files instead of making them inside the container. See the [Quickstart](https://docsify.js.org/#/quickstart) section for instructions on how to create these files manually or using [docsify-cli](https://github.com/docsifyjs/docsify-cli). ```sh index.html README.md ``` - Create Dockerfile ```Dockerfile FROM node:latest LABEL description="A demo Dockerfile for build Docsify." WORKDIR /docs RUN npm install -g docsify-cli@latest EXPOSE 3000/tcp ENTRYPOINT docsify serve . ``` The current directory structure should be this: ```sh index.html README.md Dockerfile ``` - Build docker image ```sh docker build -f Dockerfile -t docsify/demo . ``` - Run docker image ```sh docker run -itp 3000:3000 --name=docsify -v $(pwd):/docs docsify/demo ``` ## Kinsta Static Site Hosting You can deploy **Docsify** as a Static Site on [Kinsta](https://kinsta.com/static-site-hosting/). 1. Login or create an account to view your [MyKinsta](https://my.kinsta.com/) dashboard. 2. Authorize Kinsta with your Git provider. 3. Select **Static Sites** from the left sidebar and press **Add sites**. 4. Select the repository and branch you want to deploy. 5. During the build settings, Kinsta will automatically try to fill out the **Build command**, **Node version**, and **Publish directory**. If it won't, fill out the following: - Build command: leave empty - Node version: leave on default selection or a specific version (e.g. `18.16.0`) - Publish directory: `docs` 6. Click the **Create site**. ## DeployHQ [DeployHQ](https://www.deployhq.com/) is a deployment automation platform that deploys your code to SSH/SFTP servers, FTP servers, cloud storage (Amazon S3, Cloudflare R2), and modern hosting platforms (Netlify, Heroku). > [!IMPORTANT] DeployHQ does not host your site. It automates deploying your Docsify files to your chosen hosting provider or server. To deploy your Docsify site using DeployHQ: 1. Sign up for a [DeployHQ account](https://www.deployhq.com/) and verify your email. 2. Create your first project by clicking on **Projects** and **New Project**. Connect your Git repository (GitHub, GitLab, Bitbucket, or any private repository). Authorize DeployHQ to access your repository. 3. Add a server and enter your server details: - Give your server a name - Select your protocol (SSH/SFTP, FTP, or cloud platform) - Enter your server hostname, username, and password/SSH key - Set **Deployment Path** to your web root (e.g., `public_html/`) 4. Since Docsify doesn't require a build step, you can deploy your files directly. If your Docsify files are in a `docs/` folder, configure the **Source Path** in your server settings to `docs/`. 5. Click **Deploy Project**, then select your server and click **Deploy** to start your first deployment. Your Docsify site will be deployed to your server. You can enable automatic deployments to deploy on every Git push, or schedule deployments for specific times. For more information on advanced deployment features, see [DeployHQ's documentation](https://www.deployhq.com/support). ================================================ FILE: docs/embed-files.md ================================================ # Embed files With docsify 4.6 it is now possible to embed any type of file. You can embed these files as video, audio, iframes, or code blocks, and even Markdown files can even be embedded directly into the document. For example, here is an embedded Markdown file. You only need to do this: ```markdown [filename](_media/example.md ':include') ``` Then the content of `example.md` will be displayed directly here: [filename](_media/example.md ':include') You can check the original content for [example.md](_media/example.md ':ignore'). Normally, this will be compiled into a link, but in docsify, if you add `:include` it will be embedded. You can use single or double quotation marks around as you like. External links can be used too - just replace the target. If you want to use a gist URL, see [Embed a gist](#embed-a-gist) section. ## Embedded file type Currently, file extensions are automatically recognized and embedded in different ways. These types are supported: - **iframe** `.html`, `.htm` - **markdown** `.markdown`, `.md` - **audio** `.mp3` - **video** `.mp4`, `.ogg` - **code** other file extension Of course, you can force the specified type. For example, a Markdown file can be embedded as a code block by setting `:type=code`. ```markdown [filename](_media/example.md ':include :type=code') ``` You will get: [filename](_media/example.md ':include :type=code') ## Markdown with YAML Front Matter Front Matter, commonly utilized in blogging systems like Jekyll, serves to define metadata for a document. The [front-matter.js](https://www.npmjs.com/package/front-matter) package facilitates the extraction of metadata (front matter) from documents. When using Markdown, YAML front matter will be stripped from the rendered content. The attributes cannot be used in this case. ```markdown [filename](_media/example-with-yaml.md ':include') ``` You will get just the content [filename](_media/example-with-yaml.md ':include') ## Embedded code fragments Sometimes you don't want to embed a whole file. Maybe because you need just a few lines but you want to compile and test the file in CI. ```markdown [filename](_media/example.js ':include :type=code :fragment=demo') ``` In your code file you need to surround the fragment between `/// [demo]` lines (before and after the fragment). Alternatively you can use `### [demo]`. By default, only identifiers are omitted. To omit the entire line containing the identifier in the fragment output, add the `:omitFragmentLine` option. This is useful if your code fragment is e.g. HTML and you want to hide the Docsify fragment identifier from showing in your HTML source. `<!-- /// [demo] -->` in your source file and `:omitFragmentLine` will make the `-->` not show up in your Docsify code fragment section. Example: In the source file \_media/example.js, `/// [demo]` identifiers have been included: ```markdown [filename](_media/example.js ':include :type=code') ``` [filename](_media/example.js ':include :type=code') Adding the `:fragment=demo` results in the following: ```markdown [filename](_media/example.js ':include :type=code :fragment=demo') ``` [filename](_media/example.js ':include :type=code :fragment=demo') ## Tag attribute If you embed the file as `iframe`, `audio` and `video`, then you may need to set the attributes of these tags. > [!TIP] Note, for the `audio` and `video` types, docsify adds the `controls` attribute by default. When you want add more attributes, the `controls` attribute need to be added manually if need be. ```md [filename](_media/example.mp4 ':include :type=video controls width=100%') ``` ```markdown [cinwell website](https://cinwell.com ':include :type=iframe width=100% height=400px') ``` [cinwell website](https://cinwell.com ':include :type=iframe width=100% height=400px') Did you see it? You only need to write directly. You can check [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe) for these attributes. ## The code block highlight Embedding any type of source code file, you can specify the highlighted language or automatically identify. ```markdown [](_media/example.html ':include :type=code text') ``` ⬇️ [](_media/example.html ':include :type=code text') > [!TIP] How to set highlight? You can see [here](language-highlight.md). ## Embed a gist You can embed a gist as markdown content or as a code block - this is based on the approach at the start of [Embed Files](#embed-files) section, but uses a raw gist URL as the target. > [!TIP] **No** plugin or app config change is needed here to make this work. In fact, the "Embed" `script` tag that is copied from a gist will _not_ load even if you make plugin or config changes to allow an external script. ### Identify the gist's metadata Start by viewing a gist on `gist.github.com`. For the purposes of this guide, we use this gist: - https://gist.github.com/anikethsaha/f88893bb563bb7229d6e575db53a8c15 Identify the following items from the gist: | Field | Example | Description | | ------------ | ---------------------------------- | -------------------------------------------------------------------------------------------------- | | **Username** | `anikethsaha` | The gist's owner. | | **Gist ID** | `c2bece08f27c4277001f123898d16a7c` | Identifier for the gist. This is fixed for the gist's lifetime. | | **Filename** | `content.md` | Select a name of a file in the gist. This needed even on a single-file gist for embedding to work. | You will need those to build the _raw gist URL_ for the target file. This has the following format: - `https://gist.githubusercontent.com/USERNAME/GIST_ID/raw/FILENAME` Here are two examples based on the sample gist: - https://gist.githubusercontent.com/anikethsaha/f88893bb563bb7229d6e575db53a8c15/raw/content.md - https://gist.githubusercontent.com/anikethsaha/f88893bb563bb7229d6e575db53a8c15/raw/script.js > [!TIP] Alternatively, you can get a raw URL directly clicking the _Raw_ button on a gist file. But, if you use that approach, just be sure to **remove** the revision number between `raw/` and the filename so that the URL matches the pattern above instead. Otherwise your embedded gist will **not** show the latest content when the gist is updated. Continue with one of the sections below to embed the gist on a Docsify page. ### Render markdown content from a gist This is a great way to embed content **seamlessly** in your docs, without sending someone to an external link. This approach is well-suited to reusing a gist of say installation instructions across doc sites of multiple repos. This approach works equally well with a gist owned by your account or by another user. Here is the format: ```markdown [LABEL](https://gist.githubusercontent.com/USERNAME/GIST_ID/raw/FILENAME ':include') ``` For example: ```markdown [gist: content.md](https://gist.githubusercontent.com/anikethsaha/f88893bb563bb7229d6e575db53a8c15/raw/content.md ':include') ``` Which renders as: [gist: content.md](https://gist.githubusercontent.com/anikethsaha/f88893bb563bb7229d6e575db53a8c15/raw/content.md ':include') The `LABEL` can be any text you want. It acts as a _fallback_ message if the link is broken - so it is useful to repeat the filename here in case you need to fix a broken link. It also makes an embedded element easy to read at a glance. ### Render a codeblock from a gist The format is the same as the previous section, but with `:type=code` added to the alt text. As with the [Embedded file type](#embedded-file-type) section, the syntax highlighting will be **inferred** from the extension (e.g. `.js` or `.py`), so you can leave the `type` set as `code`. Here is the format: ```markdown [LABEL](https://gist.githubusercontent.com/USERNAME/GIST_ID/raw/FILENAME ':include :type=code') ``` For example: ```markdown [gist: script.js](https://gist.githubusercontent.com/anikethsaha/f88893bb563bb7229d6e575db53a8c15/raw/script.js ':include :type=code') ``` Which renders as: [gist: script.js](https://gist.githubusercontent.com/anikethsaha/f88893bb563bb7229d6e575db53a8c15/raw/script.js ':include :type=code') ================================================ FILE: docs/emoji.md ================================================ # Emoji Below is a complete list of emoji shorthand codes. Docsify can be configured to render emoji using GitHub-style emoji images or native emoji characters using the [`nativeEmoji`](configuration#nativeemoji) configuration option. <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(15em, 1fr));"> <!-- START: Auto-generated content (/build/emoji.js) --> :100: `:100:` :1234: `:1234:` :+1: `:+1:` :-1: `:-1:` :1st_place_medal: `:1st_place_medal:` :2nd_place_medal: `:2nd_place_medal:` :3rd_place_medal: `:3rd_place_medal:` :8ball: `:8ball:` :a: `:a:` :ab: `:ab:` :abacus: `:abacus:` :abc: `:abc:` :abcd: `:abcd:` :accept: `:accept:` :accessibility: `:accessibility:` :accordion: `:accordion:` :adhesive_bandage: `:adhesive_bandage:` :adult: `:adult:` :aerial_tramway: `:aerial_tramway:` :afghanistan: `:afghanistan:` :airplane: `:airplane:` :aland_islands: `:aland_islands:` :alarm_clock: `:alarm_clock:` :albania: `:albania:` :alembic: `:alembic:` :algeria: `:algeria:` :alien: `:alien:` :ambulance: `:ambulance:` :american_samoa: `:american_samoa:` :amphora: `:amphora:` :anatomical_heart: `:anatomical_heart:` :anchor: `:anchor:` :andorra: `:andorra:` :angel: `:angel:` :anger: `:anger:` :angola: `:angola:` :angry: `:angry:` :anguilla: `:anguilla:` :anguished: `:anguished:` :ant: `:ant:` :antarctica: `:antarctica:` :antigua_barbuda: `:antigua_barbuda:` :apple: `:apple:` :aquarius: `:aquarius:` :argentina: `:argentina:` :aries: `:aries:` :armenia: `:armenia:` :arrow_backward: `:arrow_backward:` :arrow_double_down: `:arrow_double_down:` :arrow_double_up: `:arrow_double_up:` :arrow_down: `:arrow_down:` :arrow_down_small: `:arrow_down_small:` :arrow_forward: `:arrow_forward:` :arrow_heading_down: `:arrow_heading_down:` :arrow_heading_up: `:arrow_heading_up:` :arrow_left: `:arrow_left:` :arrow_lower_left: `:arrow_lower_left:` :arrow_lower_right: `:arrow_lower_right:` :arrow_right: `:arrow_right:` :arrow_right_hook: `:arrow_right_hook:` :arrow_up: `:arrow_up:` :arrow_up_down: `:arrow_up_down:` :arrow_up_small: `:arrow_up_small:` :arrow_upper_left: `:arrow_upper_left:` :arrow_upper_right: `:arrow_upper_right:` :arrows_clockwise: `:arrows_clockwise:` :arrows_counterclockwise: `:arrows_counterclockwise:` :art: `:art:` :articulated_lorry: `:articulated_lorry:` :artificial_satellite: `:artificial_satellite:` :artist: `:artist:` :aruba: `:aruba:` :ascension_island: `:ascension_island:` :asterisk: `:asterisk:` :astonished: `:astonished:` :astronaut: `:astronaut:` :athletic_shoe: `:athletic_shoe:` :atm: `:atm:` :atom: `:atom:` :atom_symbol: `:atom_symbol:` :australia: `:australia:` :austria: `:austria:` :auto_rickshaw: `:auto_rickshaw:` :avocado: `:avocado:` :axe: `:axe:` :azerbaijan: `:azerbaijan:` :b: `:b:` :baby: `:baby:` :baby_bottle: `:baby_bottle:` :baby_chick: `:baby_chick:` :baby_symbol: `:baby_symbol:` :back: `:back:` :bacon: `:bacon:` :badger: `:badger:` :badminton: `:badminton:` :bagel: `:bagel:` :baggage_claim: `:baggage_claim:` :baguette_bread: `:baguette_bread:` :bahamas: `:bahamas:` :bahrain: `:bahrain:` :balance_scale: `:balance_scale:` :bald_man: `:bald_man:` :bald_woman: `:bald_woman:` :ballet_shoes: `:ballet_shoes:` :balloon: `:balloon:` :ballot_box: `:ballot_box:` :ballot_box_with_check: `:ballot_box_with_check:` :bamboo: `:bamboo:` :banana: `:banana:` :bangbang: `:bangbang:` :bangladesh: `:bangladesh:` :banjo: `:banjo:` :bank: `:bank:` :bar_chart: `:bar_chart:` :barbados: `:barbados:` :barber: `:barber:` :baseball: `:baseball:` :basecamp: `:basecamp:` :basecampy: `:basecampy:` :basket: `:basket:` :basketball: `:basketball:` :basketball_man: `:basketball_man:` :basketball_woman: `:basketball_woman:` :bat: `:bat:` :bath: `:bath:` :bathtub: `:bathtub:` :battery: `:battery:` :beach_umbrella: `:beach_umbrella:` :beans: `:beans:` :bear: `:bear:` :bearded_person: `:bearded_person:` :beaver: `:beaver:` :bed: `:bed:` :bee: `:bee:` :beer: `:beer:` :beers: `:beers:` :beetle: `:beetle:` :beginner: `:beginner:` :belarus: `:belarus:` :belgium: `:belgium:` :belize: `:belize:` :bell: `:bell:` :bell_pepper: `:bell_pepper:` :bellhop_bell: `:bellhop_bell:` :benin: `:benin:` :bento: `:bento:` :bermuda: `:bermuda:` :beverage_box: `:beverage_box:` :bhutan: `:bhutan:` :bicyclist: `:bicyclist:` :bike: `:bike:` :biking_man: `:biking_man:` :biking_woman: `:biking_woman:` :bikini: `:bikini:` :billed_cap: `:billed_cap:` :biohazard: `:biohazard:` :bird: `:bird:` :birthday: `:birthday:` :bison: `:bison:` :biting_lip: `:biting_lip:` :black_bird: `:black_bird:` :black_cat: `:black_cat:` :black_circle: `:black_circle:` :black_flag: `:black_flag:` :black_heart: `:black_heart:` :black_joker: `:black_joker:` :black_large_square: `:black_large_square:` :black_medium_small_square: `:black_medium_small_square:` :black_medium_square: `:black_medium_square:` :black_nib: `:black_nib:` :black_small_square: `:black_small_square:` :black_square_button: `:black_square_button:` :blond_haired_man: `:blond_haired_man:` :blond_haired_person: `:blond_haired_person:` :blond_haired_woman: `:blond_haired_woman:` :blonde_woman: `:blonde_woman:` :blossom: `:blossom:` :blowfish: `:blowfish:` :blue_book: `:blue_book:` :blue_car: `:blue_car:` :blue_heart: `:blue_heart:` :blue_square: `:blue_square:` :blueberries: `:blueberries:` :blush: `:blush:` :boar: `:boar:` :boat: `:boat:` :bolivia: `:bolivia:` :bomb: `:bomb:` :bone: `:bone:` :book: `:book:` :bookmark: `:bookmark:` :bookmark_tabs: `:bookmark_tabs:` :books: `:books:` :boom: `:boom:` :boomerang: `:boomerang:` :boot: `:boot:` :bosnia_herzegovina: `:bosnia_herzegovina:` :botswana: `:botswana:` :bouncing_ball_man: `:bouncing_ball_man:` :bouncing_ball_person: `:bouncing_ball_person:` :bouncing_ball_woman: `:bouncing_ball_woman:` :bouquet: `:bouquet:` :bouvet_island: `:bouvet_island:` :bow: `:bow:` :bow_and_arrow: `:bow_and_arrow:` :bowing_man: `:bowing_man:` :bowing_woman: `:bowing_woman:` :bowl_with_spoon: `:bowl_with_spoon:` :bowling: `:bowling:` :bowtie: `:bowtie:` :boxing_glove: `:boxing_glove:` :boy: `:boy:` :brain: `:brain:` :brazil: `:brazil:` :bread: `:bread:` :breast_feeding: `:breast_feeding:` :bricks: `:bricks:` :bride_with_veil: `:bride_with_veil:` :bridge_at_night: `:bridge_at_night:` :briefcase: `:briefcase:` :british_indian_ocean_territory: `:british_indian_ocean_territory:` :british_virgin_islands: `:british_virgin_islands:` :broccoli: `:broccoli:` :broken_heart: `:broken_heart:` :broom: `:broom:` :brown_circle: `:brown_circle:` :brown_heart: `:brown_heart:` :brown_square: `:brown_square:` :brunei: `:brunei:` :bubble_tea: `:bubble_tea:` :bubbles: `:bubbles:` :bucket: `:bucket:` :bug: `:bug:` :building_construction: `:building_construction:` :bulb: `:bulb:` :bulgaria: `:bulgaria:` :bullettrain_front: `:bullettrain_front:` :bullettrain_side: `:bullettrain_side:` :burkina_faso: `:burkina_faso:` :burrito: `:burrito:` :burundi: `:burundi:` :bus: `:bus:` :business_suit_levitating: `:business_suit_levitating:` :busstop: `:busstop:` :bust_in_silhouette: `:bust_in_silhouette:` :busts_in_silhouette: `:busts_in_silhouette:` :butter: `:butter:` :butterfly: `:butterfly:` :cactus: `:cactus:` :cake: `:cake:` :calendar: `:calendar:` :call_me_hand: `:call_me_hand:` :calling: `:calling:` :cambodia: `:cambodia:` :camel: `:camel:` :camera: `:camera:` :camera_flash: `:camera_flash:` :cameroon: `:cameroon:` :camping: `:camping:` :canada: `:canada:` :canary_islands: `:canary_islands:` :cancer: `:cancer:` :candle: `:candle:` :candy: `:candy:` :canned_food: `:canned_food:` :canoe: `:canoe:` :cape_verde: `:cape_verde:` :capital_abcd: `:capital_abcd:` :capricorn: `:capricorn:` :car: `:car:` :card_file_box: `:card_file_box:` :card_index: `:card_index:` :card_index_dividers: `:card_index_dividers:` :caribbean_netherlands: `:caribbean_netherlands:` :carousel_horse: `:carousel_horse:` :carpentry_saw: `:carpentry_saw:` :carrot: `:carrot:` :cartwheeling: `:cartwheeling:` :cat: `:cat:` :cat2: `:cat2:` :cayman_islands: `:cayman_islands:` :cd: `:cd:` :central_african_republic: `:central_african_republic:` :ceuta_melilla: `:ceuta_melilla:` :chad: `:chad:` :chains: `:chains:` :chair: `:chair:` :champagne: `:champagne:` :chart: `:chart:` :chart_with_downwards_trend: `:chart_with_downwards_trend:` :chart_with_upwards_trend: `:chart_with_upwards_trend:` :checkered_flag: `:checkered_flag:` :cheese: `:cheese:` :cherries: `:cherries:` :cherry_blossom: `:cherry_blossom:` :chess_pawn: `:chess_pawn:` :chestnut: `:chestnut:` :chicken: `:chicken:` :child: `:child:` :children_crossing: `:children_crossing:` :chile: `:chile:` :chipmunk: `:chipmunk:` :chocolate_bar: `:chocolate_bar:` :chopsticks: `:chopsticks:` :christmas_island: `:christmas_island:` :christmas_tree: `:christmas_tree:` :church: `:church:` :cinema: `:cinema:` :circus_tent: `:circus_tent:` :city_sunrise: `:city_sunrise:` :city_sunset: `:city_sunset:` :cityscape: `:cityscape:` :cl: `:cl:` :clamp: `:clamp:` :clap: `:clap:` :clapper: `:clapper:` :classical_building: `:classical_building:` :climbing: `:climbing:` :climbing_man: `:climbing_man:` :climbing_woman: `:climbing_woman:` :clinking_glasses: `:clinking_glasses:` :clipboard: `:clipboard:` :clipperton_island: `:clipperton_island:` :clock1: `:clock1:` :clock10: `:clock10:` :clock1030: `:clock1030:` :clock11: `:clock11:` :clock1130: `:clock1130:` :clock12: `:clock12:` :clock1230: `:clock1230:` :clock130: `:clock130:` :clock2: `:clock2:` :clock230: `:clock230:` :clock3: `:clock3:` :clock330: `:clock330:` :clock4: `:clock4:` :clock430: `:clock430:` :clock5: `:clock5:` :clock530: `:clock530:` :clock6: `:clock6:` :clock630: `:clock630:` :clock7: `:clock7:` :clock730: `:clock730:` :clock8: `:clock8:` :clock830: `:clock830:` :clock9: `:clock9:` :clock930: `:clock930:` :closed_book: `:closed_book:` :closed_lock_with_key: `:closed_lock_with_key:` :closed_umbrella: `:closed_umbrella:` :cloud: `:cloud:` :cloud_with_lightning: `:cloud_with_lightning:` :cloud_with_lightning_and_rain: `:cloud_with_lightning_and_rain:` :cloud_with_rain: `:cloud_with_rain:` :cloud_with_snow: `:cloud_with_snow:` :clown_face: `:clown_face:` :clubs: `:clubs:` :cn: `:cn:` :coat: `:coat:` :cockroach: `:cockroach:` :cocktail: `:cocktail:` :coconut: `:coconut:` :cocos_islands: `:cocos_islands:` :coffee: `:coffee:` :coffin: `:coffin:` :coin: `:coin:` :cold_face: `:cold_face:` :cold_sweat: `:cold_sweat:` :collision: `:collision:` :colombia: `:colombia:` :comet: `:comet:` :comoros: `:comoros:` :compass: `:compass:` :computer: `:computer:` :computer_mouse: `:computer_mouse:` :confetti_ball: `:confetti_ball:` :confounded: `:confounded:` :confused: `:confused:` :congo_brazzaville: `:congo_brazzaville:` :congo_kinshasa: `:congo_kinshasa:` :congratulations: `:congratulations:` :construction: `:construction:` :construction_worker: `:construction_worker:` :construction_worker_man: `:construction_worker_man:` :construction_worker_woman: `:construction_worker_woman:` :control_knobs: `:control_knobs:` :convenience_store: `:convenience_store:` :cook: `:cook:` :cook_islands: `:cook_islands:` :cookie: `:cookie:` :cool: `:cool:` :cop: `:cop:` :copilot: `:copilot:` :copyright: `:copyright:` :coral: `:coral:` :corn: `:corn:` :costa_rica: `:costa_rica:` :cote_divoire: `:cote_divoire:` :couch_and_lamp: `:couch_and_lamp:` :couple: `:couple:` :couple_with_heart: `:couple_with_heart:` :couple_with_heart_man_man: `:couple_with_heart_man_man:` :couple_with_heart_woman_man: `:couple_with_heart_woman_man:` :couple_with_heart_woman_woman: `:couple_with_heart_woman_woman:` :couplekiss: `:couplekiss:` :couplekiss_man_man: `:couplekiss_man_man:` :couplekiss_man_woman: `:couplekiss_man_woman:` :couplekiss_woman_woman: `:couplekiss_woman_woman:` :cow: `:cow:` :cow2: `:cow2:` :cowboy_hat_face: `:cowboy_hat_face:` :crab: `:crab:` :crayon: `:crayon:` :credit_card: `:credit_card:` :crescent_moon: `:crescent_moon:` :cricket: `:cricket:` :cricket_game: `:cricket_game:` :croatia: `:croatia:` :crocodile: `:crocodile:` :croissant: `:croissant:` :crossed_fingers: `:crossed_fingers:` :crossed_flags: `:crossed_flags:` :crossed_swords: `:crossed_swords:` :crown: `:crown:` :crutch: `:crutch:` :cry: `:cry:` :crying_cat_face: `:crying_cat_face:` :crystal_ball: `:crystal_ball:` :cuba: `:cuba:` :cucumber: `:cucumber:` :cup_with_straw: `:cup_with_straw:` :cupcake: `:cupcake:` :cupid: `:cupid:` :curacao: `:curacao:` :curling_stone: `:curling_stone:` :curly_haired_man: `:curly_haired_man:` :curly_haired_woman: `:curly_haired_woman:` :curly_loop: `:curly_loop:` :currency_exchange: `:currency_exchange:` :curry: `:curry:` :cursing_face: `:cursing_face:` :custard: `:custard:` :customs: `:customs:` :cut_of_meat: `:cut_of_meat:` :cyclone: `:cyclone:` :cyprus: `:cyprus:` :czech_republic: `:czech_republic:` :dagger: `:dagger:` :dancer: `:dancer:` :dancers: `:dancers:` :dancing_men: `:dancing_men:` :dancing_women: `:dancing_women:` :dango: `:dango:` :dark_sunglasses: `:dark_sunglasses:` :dart: `:dart:` :dash: `:dash:` :date: `:date:` :de: `:de:` :deaf_man: `:deaf_man:` :deaf_person: `:deaf_person:` :deaf_woman: `:deaf_woman:` :deciduous_tree: `:deciduous_tree:` :deer: `:deer:` :denmark: `:denmark:` :department_store: `:department_store:` :dependabot: `:dependabot:` :derelict_house: `:derelict_house:` :desert: `:desert:` :desert_island: `:desert_island:` :desktop_computer: `:desktop_computer:` :detective: `:detective:` :diamond_shape_with_a_dot_inside: `:diamond_shape_with_a_dot_inside:` :diamonds: `:diamonds:` :diego_garcia: `:diego_garcia:` :disappointed: `:disappointed:` :disappointed_relieved: `:disappointed_relieved:` :disguised_face: `:disguised_face:` :diving_mask: `:diving_mask:` :diya_lamp: `:diya_lamp:` :dizzy: `:dizzy:` :dizzy_face: `:dizzy_face:` :djibouti: `:djibouti:` :dna: `:dna:` :do_not_litter: `:do_not_litter:` :dodo: `:dodo:` :dog: `:dog:` :dog2: `:dog2:` :dollar: `:dollar:` :dolls: `:dolls:` :dolphin: `:dolphin:` :dominica: `:dominica:` :dominican_republic: `:dominican_republic:` :donkey: `:donkey:` :door: `:door:` :dotted_line_face: `:dotted_line_face:` :doughnut: `:doughnut:` :dove: `:dove:` :dragon: `:dragon:` :dragon_face: `:dragon_face:` :dress: `:dress:` :dromedary_camel: `:dromedary_camel:` :drooling_face: `:drooling_face:` :drop_of_blood: `:drop_of_blood:` :droplet: `:droplet:` :drum: `:drum:` :duck: `:duck:` :dumpling: `:dumpling:` :dvd: `:dvd:` :e-mail: `:e-mail:` :eagle: `:eagle:` :ear: `:ear:` :ear_of_rice: `:ear_of_rice:` :ear_with_hearing_aid: `:ear_with_hearing_aid:` :earth_africa: `:earth_africa:` :earth_americas: `:earth_americas:` :earth_asia: `:earth_asia:` :ecuador: `:ecuador:` :egg: `:egg:` :eggplant: `:eggplant:` :egypt: `:egypt:` :eight: `:eight:` :eight_pointed_black_star: `:eight_pointed_black_star:` :eight_spoked_asterisk: `:eight_spoked_asterisk:` :eject_button: `:eject_button:` :el_salvador: `:el_salvador:` :electric_plug: `:electric_plug:` :electron: `:electron:` :elephant: `:elephant:` :elevator: `:elevator:` :elf: `:elf:` :elf_man: `:elf_man:` :elf_woman: `:elf_woman:` :email: `:email:` :empty_nest: `:empty_nest:` :end: `:end:` :england: `:england:` :envelope: `:envelope:` :envelope_with_arrow: `:envelope_with_arrow:` :equatorial_guinea: `:equatorial_guinea:` :eritrea: `:eritrea:` :es: `:es:` :estonia: `:estonia:` :ethiopia: `:ethiopia:` :eu: `:eu:` :euro: `:euro:` :european_castle: `:european_castle:` :european_post_office: `:european_post_office:` :european_union: `:european_union:` :evergreen_tree: `:evergreen_tree:` :exclamation: `:exclamation:` :exploding_head: `:exploding_head:` :expressionless: `:expressionless:` :eye: `:eye:` :eye_speech_bubble: `:eye_speech_bubble:` :eyeglasses: `:eyeglasses:` :eyes: `:eyes:` :face_exhaling: `:face_exhaling:` :face_holding_back_tears: `:face_holding_back_tears:` :face_in_clouds: `:face_in_clouds:` :face_with_diagonal_mouth: `:face_with_diagonal_mouth:` :face_with_head_bandage: `:face_with_head_bandage:` :face_with_open_eyes_and_hand_over_mouth: `:face_with_open_eyes_and_hand_over_mouth:` :face_with_peeking_eye: `:face_with_peeking_eye:` :face_with_spiral_eyes: `:face_with_spiral_eyes:` :face_with_thermometer: `:face_with_thermometer:` :facepalm: `:facepalm:` :facepunch: `:facepunch:` :factory: `:factory:` :factory_worker: `:factory_worker:` :fairy: `:fairy:` :fairy_man: `:fairy_man:` :fairy_woman: `:fairy_woman:` :falafel: `:falafel:` :falkland_islands: `:falkland_islands:` :fallen_leaf: `:fallen_leaf:` :family: `:family:` :family_man_boy: `:family_man_boy:` :family_man_boy_boy: `:family_man_boy_boy:` :family_man_girl: `:family_man_girl:` :family_man_girl_boy: `:family_man_girl_boy:` :family_man_girl_girl: `:family_man_girl_girl:` :family_man_man_boy: `:family_man_man_boy:` :family_man_man_boy_boy: `:family_man_man_boy_boy:` :family_man_man_girl: `:family_man_man_girl:` :family_man_man_girl_boy: `:family_man_man_girl_boy:` :family_man_man_girl_girl: `:family_man_man_girl_girl:` :family_man_woman_boy: `:family_man_woman_boy:` :family_man_woman_boy_boy: `:family_man_woman_boy_boy:` :family_man_woman_girl: `:family_man_woman_girl:` :family_man_woman_girl_boy: `:family_man_woman_girl_boy:` :family_man_woman_girl_girl: `:family_man_woman_girl_girl:` :family_woman_boy: `:family_woman_boy:` :family_woman_boy_boy: `:family_woman_boy_boy:` :family_woman_girl: `:family_woman_girl:` :family_woman_girl_boy: `:family_woman_girl_boy:` :family_woman_girl_girl: `:family_woman_girl_girl:` :family_woman_woman_boy: `:family_woman_woman_boy:` :family_woman_woman_boy_boy: `:family_woman_woman_boy_boy:` :family_woman_woman_girl: `:family_woman_woman_girl:` :family_woman_woman_girl_boy: `:family_woman_woman_girl_boy:` :family_woman_woman_girl_girl: `:family_woman_woman_girl_girl:` :farmer: `:farmer:` :faroe_islands: `:faroe_islands:` :fast_forward: `:fast_forward:` :fax: `:fax:` :fearful: `:fearful:` :feather: `:feather:` :feelsgood: `:feelsgood:` :feet: `:feet:` :female_detective: `:female_detective:` :female_sign: `:female_sign:` :ferris_wheel: `:ferris_wheel:` :ferry: `:ferry:` :field_hockey: `:field_hockey:` :fiji: `:fiji:` :file_cabinet: `:file_cabinet:` :file_folder: `:file_folder:` :film_projector: `:film_projector:` :film_strip: `:film_strip:` :finland: `:finland:` :finnadie: `:finnadie:` :fire: `:fire:` :fire_engine: `:fire_engine:` :fire_extinguisher: `:fire_extinguisher:` :firecracker: `:firecracker:` :firefighter: `:firefighter:` :fireworks: `:fireworks:` :first_quarter_moon: `:first_quarter_moon:` :first_quarter_moon_with_face: `:first_quarter_moon_with_face:` :fish: `:fish:` :fish_cake: `:fish_cake:` :fishing_pole_and_fish: `:fishing_pole_and_fish:` :fishsticks: `:fishsticks:` :fist: `:fist:` :fist_left: `:fist_left:` :fist_oncoming: `:fist_oncoming:` :fist_raised: `:fist_raised:` :fist_right: `:fist_right:` :five: `:five:` :flags: `:flags:` :flamingo: `:flamingo:` :flashlight: `:flashlight:` :flat_shoe: `:flat_shoe:` :flatbread: `:flatbread:` :fleur_de_lis: `:fleur_de_lis:` :flight_arrival: `:flight_arrival:` :flight_departure: `:flight_departure:` :flipper: `:flipper:` :floppy_disk: `:floppy_disk:` :flower_playing_cards: `:flower_playing_cards:` :flushed: `:flushed:` :flute: `:flute:` :fly: `:fly:` :flying_disc: `:flying_disc:` :flying_saucer: `:flying_saucer:` :fog: `:fog:` :foggy: `:foggy:` :folding_hand_fan: `:folding_hand_fan:` :fondue: `:fondue:` :foot: `:foot:` :football: `:football:` :footprints: `:footprints:` :fork_and_knife: `:fork_and_knife:` :fortune_cookie: `:fortune_cookie:` :fountain: `:fountain:` :fountain_pen: `:fountain_pen:` :four: `:four:` :four_leaf_clover: `:four_leaf_clover:` :fox_face: `:fox_face:` :fr: `:fr:` :framed_picture: `:framed_picture:` :free: `:free:` :french_guiana: `:french_guiana:` :french_polynesia: `:french_polynesia:` :french_southern_territories: `:french_southern_territories:` :fried_egg: `:fried_egg:` :fried_shrimp: `:fried_shrimp:` :fries: `:fries:` :frog: `:frog:` :frowning: `:frowning:` :frowning_face: `:frowning_face:` :frowning_man: `:frowning_man:` :frowning_person: `:frowning_person:` :frowning_woman: `:frowning_woman:` :fu: `:fu:` :fuelpump: `:fuelpump:` :full_moon: `:full_moon:` :full_moon_with_face: `:full_moon_with_face:` :funeral_urn: `:funeral_urn:` :gabon: `:gabon:` :gambia: `:gambia:` :game_die: `:game_die:` :garlic: `:garlic:` :gb: `:gb:` :gear: `:gear:` :gem: `:gem:` :gemini: `:gemini:` :genie: `:genie:` :genie_man: `:genie_man:` :genie_woman: `:genie_woman:` :georgia: `:georgia:` :ghana: `:ghana:` :ghost: `:ghost:` :gibraltar: `:gibraltar:` :gift: `:gift:` :gift_heart: `:gift_heart:` :ginger_root: `:ginger_root:` :giraffe: `:giraffe:` :girl: `:girl:` :globe_with_meridians: `:globe_with_meridians:` :gloves: `:gloves:` :goal_net: `:goal_net:` :goat: `:goat:` :goberserk: `:goberserk:` :godmode: `:godmode:` :goggles: `:goggles:` :golf: `:golf:` :golfing: `:golfing:` :golfing_man: `:golfing_man:` :golfing_woman: `:golfing_woman:` :goose: `:goose:` :gorilla: `:gorilla:` :grapes: `:grapes:` :greece: `:greece:` :green_apple: `:green_apple:` :green_book: `:green_book:` :green_circle: `:green_circle:` :green_heart: `:green_heart:` :green_salad: `:green_salad:` :green_square: `:green_square:` :greenland: `:greenland:` :grenada: `:grenada:` :grey_exclamation: `:grey_exclamation:` :grey_heart: `:grey_heart:` :grey_question: `:grey_question:` :grimacing: `:grimacing:` :grin: `:grin:` :grinning: `:grinning:` :guadeloupe: `:guadeloupe:` :guam: `:guam:` :guard: `:guard:` :guardsman: `:guardsman:` :guardswoman: `:guardswoman:` :guatemala: `:guatemala:` :guernsey: `:guernsey:` :guide_dog: `:guide_dog:` :guinea: `:guinea:` :guinea_bissau: `:guinea_bissau:` :guitar: `:guitar:` :gun: `:gun:` :guyana: `:guyana:` :hair_pick: `:hair_pick:` :haircut: `:haircut:` :haircut_man: `:haircut_man:` :haircut_woman: `:haircut_woman:` :haiti: `:haiti:` :hamburger: `:hamburger:` :hammer: `:hammer:` :hammer_and_pick: `:hammer_and_pick:` :hammer_and_wrench: `:hammer_and_wrench:` :hamsa: `:hamsa:` :hamster: `:hamster:` :hand: `:hand:` :hand_over_mouth: `:hand_over_mouth:` :hand_with_index_finger_and_thumb_crossed: `:hand_with_index_finger_and_thumb_crossed:` :handbag: `:handbag:` :handball_person: `:handball_person:` :handshake: `:handshake:` :hankey: `:hankey:` :hash: `:hash:` :hatched_chick: `:hatched_chick:` :hatching_chick: `:hatching_chick:` :headphones: `:headphones:` :headstone: `:headstone:` :health_worker: `:health_worker:` :hear_no_evil: `:hear_no_evil:` :heard_mcdonald_islands: `:heard_mcdonald_islands:` :heart: `:heart:` :heart_decoration: `:heart_decoration:` :heart_eyes: `:heart_eyes:` :heart_eyes_cat: `:heart_eyes_cat:` :heart_hands: `:heart_hands:` :heart_on_fire: `:heart_on_fire:` :heartbeat: `:heartbeat:` :heartpulse: `:heartpulse:` :hearts: `:hearts:` :heavy_check_mark: `:heavy_check_mark:` :heavy_division_sign: `:heavy_division_sign:` :heavy_dollar_sign: `:heavy_dollar_sign:` :heavy_equals_sign: `:heavy_equals_sign:` :heavy_exclamation_mark: `:heavy_exclamation_mark:` :heavy_heart_exclamation: `:heavy_heart_exclamation:` :heavy_minus_sign: `:heavy_minus_sign:` :heavy_multiplication_x: `:heavy_multiplication_x:` :heavy_plus_sign: `:heavy_plus_sign:` :hedgehog: `:hedgehog:` :helicopter: `:helicopter:` :herb: `:herb:` :hibiscus: `:hibiscus:` :high_brightness: `:high_brightness:` :high_heel: `:high_heel:` :hiking_boot: `:hiking_boot:` :hindu_temple: `:hindu_temple:` :hippopotamus: `:hippopotamus:` :hocho: `:hocho:` :hole: `:hole:` :honduras: `:honduras:` :honey_pot: `:honey_pot:` :honeybee: `:honeybee:` :hong_kong: `:hong_kong:` :hook: `:hook:` :horse: `:horse:` :horse_racing: `:horse_racing:` :hospital: `:hospital:` :hot_face: `:hot_face:` :hot_pepper: `:hot_pepper:` :hotdog: `:hotdog:` :hotel: `:hotel:` :hotsprings: `:hotsprings:` :hourglass: `:hourglass:` :hourglass_flowing_sand: `:hourglass_flowing_sand:` :house: `:house:` :house_with_garden: `:house_with_garden:` :houses: `:houses:` :hugs: `:hugs:` :hungary: `:hungary:` :hurtrealbad: `:hurtrealbad:` :hushed: `:hushed:` :hut: `:hut:` :hyacinth: `:hyacinth:` :ice_cream: `:ice_cream:` :ice_cube: `:ice_cube:` :ice_hockey: `:ice_hockey:` :ice_skate: `:ice_skate:` :icecream: `:icecream:` :iceland: `:iceland:` :id: `:id:` :identification_card: `:identification_card:` :ideograph_advantage: `:ideograph_advantage:` :imp: `:imp:` :inbox_tray: `:inbox_tray:` :incoming_envelope: `:incoming_envelope:` :index_pointing_at_the_viewer: `:index_pointing_at_the_viewer:` :india: `:india:` :indonesia: `:indonesia:` :infinity: `:infinity:` :information_desk_person: `:information_desk_person:` :information_source: `:information_source:` :innocent: `:innocent:` :interrobang: `:interrobang:` :iphone: `:iphone:` :iran: `:iran:` :iraq: `:iraq:` :ireland: `:ireland:` :isle_of_man: `:isle_of_man:` :israel: `:israel:` :it: `:it:` :izakaya_lantern: `:izakaya_lantern:` :jack_o_lantern: `:jack_o_lantern:` :jamaica: `:jamaica:` :japan: `:japan:` :japanese_castle: `:japanese_castle:` :japanese_goblin: `:japanese_goblin:` :japanese_ogre: `:japanese_ogre:` :jar: `:jar:` :jeans: `:jeans:` :jellyfish: `:jellyfish:` :jersey: `:jersey:` :jigsaw: `:jigsaw:` :jordan: `:jordan:` :joy: `:joy:` :joy_cat: `:joy_cat:` :joystick: `:joystick:` :jp: `:jp:` :judge: `:judge:` :juggling_person: `:juggling_person:` :kaaba: `:kaaba:` :kangaroo: `:kangaroo:` :kazakhstan: `:kazakhstan:` :kenya: `:kenya:` :key: `:key:` :keyboard: `:keyboard:` :keycap_ten: `:keycap_ten:` :khanda: `:khanda:` :kick_scooter: `:kick_scooter:` :kimono: `:kimono:` :kiribati: `:kiribati:` :kiss: `:kiss:` :kissing: `:kissing:` :kissing_cat: `:kissing_cat:` :kissing_closed_eyes: `:kissing_closed_eyes:` :kissing_heart: `:kissing_heart:` :kissing_smiling_eyes: `:kissing_smiling_eyes:` :kite: `:kite:` :kiwi_fruit: `:kiwi_fruit:` :kneeling_man: `:kneeling_man:` :kneeling_person: `:kneeling_person:` :kneeling_woman: `:kneeling_woman:` :knife: `:knife:` :knot: `:knot:` :koala: `:koala:` :koko: `:koko:` :kosovo: `:kosovo:` :kr: `:kr:` :kuwait: `:kuwait:` :kyrgyzstan: `:kyrgyzstan:` :lab_coat: `:lab_coat:` :label: `:label:` :lacrosse: `:lacrosse:` :ladder: `:ladder:` :lady_beetle: `:lady_beetle:` :lantern: `:lantern:` :laos: `:laos:` :large_blue_circle: `:large_blue_circle:` :large_blue_diamond: `:large_blue_diamond:` :large_orange_diamond: `:large_orange_diamond:` :last_quarter_moon: `:last_quarter_moon:` :last_quarter_moon_with_face: `:last_quarter_moon_with_face:` :latin_cross: `:latin_cross:` :latvia: `:latvia:` :laughing: `:laughing:` :leafy_green: `:leafy_green:` :leaves: `:leaves:` :lebanon: `:lebanon:` :ledger: `:ledger:` :left_luggage: `:left_luggage:` :left_right_arrow: `:left_right_arrow:` :left_speech_bubble: `:left_speech_bubble:` :leftwards_arrow_with_hook: `:leftwards_arrow_with_hook:` :leftwards_hand: `:leftwards_hand:` :leftwards_pushing_hand: `:leftwards_pushing_hand:` :leg: `:leg:` :lemon: `:lemon:` :leo: `:leo:` :leopard: `:leopard:` :lesotho: `:lesotho:` :level_slider: `:level_slider:` :liberia: `:liberia:` :libra: `:libra:` :libya: `:libya:` :liechtenstein: `:liechtenstein:` :light_blue_heart: `:light_blue_heart:` :light_rail: `:light_rail:` :link: `:link:` :lion: `:lion:` :lips: `:lips:` :lipstick: `:lipstick:` :lithuania: `:lithuania:` :lizard: `:lizard:` :llama: `:llama:` :lobster: `:lobster:` :lock: `:lock:` :lock_with_ink_pen: `:lock_with_ink_pen:` :lollipop: `:lollipop:` :long_drum: `:long_drum:` :loop: `:loop:` :lotion_bottle: `:lotion_bottle:` :lotus: `:lotus:` :lotus_position: `:lotus_position:` :lotus_position_man: `:lotus_position_man:` :lotus_position_woman: `:lotus_position_woman:` :loud_sound: `:loud_sound:` :loudspeaker: `:loudspeaker:` :love_hotel: `:love_hotel:` :love_letter: `:love_letter:` :love_you_gesture: `:love_you_gesture:` :low_battery: `:low_battery:` :low_brightness: `:low_brightness:` :luggage: `:luggage:` :lungs: `:lungs:` :luxembourg: `:luxembourg:` :lying_face: `:lying_face:` :m: `:m:` :macau: `:macau:` :macedonia: `:macedonia:` :madagascar: `:madagascar:` :mag: `:mag:` :mag_right: `:mag_right:` :mage: `:mage:` :mage_man: `:mage_man:` :mage_woman: `:mage_woman:` :magic_wand: `:magic_wand:` :magnet: `:magnet:` :mahjong: `:mahjong:` :mailbox: `:mailbox:` :mailbox_closed: `:mailbox_closed:` :mailbox_with_mail: `:mailbox_with_mail:` :mailbox_with_no_mail: `:mailbox_with_no_mail:` :malawi: `:malawi:` :malaysia: `:malaysia:` :maldives: `:maldives:` :male_detective: `:male_detective:` :male_sign: `:male_sign:` :mali: `:mali:` :malta: `:malta:` :mammoth: `:mammoth:` :man: `:man:` :man_artist: `:man_artist:` :man_astronaut: `:man_astronaut:` :man_beard: `:man_beard:` :man_cartwheeling: `:man_cartwheeling:` :man_cook: `:man_cook:` :man_dancing: `:man_dancing:` :man_facepalming: `:man_facepalming:` :man_factory_worker: `:man_factory_worker:` :man_farmer: `:man_farmer:` :man_feeding_baby: `:man_feeding_baby:` :man_firefighter: `:man_firefighter:` :man_health_worker: `:man_health_worker:` :man_in_manual_wheelchair: `:man_in_manual_wheelchair:` :man_in_motorized_wheelchair: `:man_in_motorized_wheelchair:` :man_in_tuxedo: `:man_in_tuxedo:` :man_judge: `:man_judge:` :man_juggling: `:man_juggling:` :man_mechanic: `:man_mechanic:` :man_office_worker: `:man_office_worker:` :man_pilot: `:man_pilot:` :man_playing_handball: `:man_playing_handball:` :man_playing_water_polo: `:man_playing_water_polo:` :man_scientist: `:man_scientist:` :man_shrugging: `:man_shrugging:` :man_singer: `:man_singer:` :man_student: `:man_student:` :man_teacher: `:man_teacher:` :man_technologist: `:man_technologist:` :man_with_gua_pi_mao: `:man_with_gua_pi_mao:` :man_with_probing_cane: `:man_with_probing_cane:` :man_with_turban: `:man_with_turban:` :man_with_veil: `:man_with_veil:` :mandarin: `:mandarin:` :mango: `:mango:` :mans_shoe: `:mans_shoe:` :mantelpiece_clock: `:mantelpiece_clock:` :manual_wheelchair: `:manual_wheelchair:` :maple_leaf: `:maple_leaf:` :maracas: `:maracas:` :marshall_islands: `:marshall_islands:` :martial_arts_uniform: `:martial_arts_uniform:` :martinique: `:martinique:` :mask: `:mask:` :massage: `:massage:` :massage_man: `:massage_man:` :massage_woman: `:massage_woman:` :mate: `:mate:` :mauritania: `:mauritania:` :mauritius: `:mauritius:` :mayotte: `:mayotte:` :meat_on_bone: `:meat_on_bone:` :mechanic: `:mechanic:` :mechanical_arm: `:mechanical_arm:` :mechanical_leg: `:mechanical_leg:` :medal_military: `:medal_military:` :medal_sports: `:medal_sports:` :medical_symbol: `:medical_symbol:` :mega: `:mega:` :melon: `:melon:` :melting_face: `:melting_face:` :memo: `:memo:` :men_wrestling: `:men_wrestling:` :mending_heart: `:mending_heart:` :menorah: `:menorah:` :mens: `:mens:` :mermaid: `:mermaid:` :merman: `:merman:` :merperson: `:merperson:` :metal: `:metal:` :metro: `:metro:` :mexico: `:mexico:` :microbe: `:microbe:` :micronesia: `:micronesia:` :microphone: `:microphone:` :microscope: `:microscope:` :middle_finger: `:middle_finger:` :military_helmet: `:military_helmet:` :milk_glass: `:milk_glass:` :milky_way: `:milky_way:` :minibus: `:minibus:` :minidisc: `:minidisc:` :mirror: `:mirror:` :mirror_ball: `:mirror_ball:` :mobile_phone_off: `:mobile_phone_off:` :moldova: `:moldova:` :monaco: `:monaco:` :money_mouth_face: `:money_mouth_face:` :money_with_wings: `:money_with_wings:` :moneybag: `:moneybag:` :mongolia: `:mongolia:` :monkey: `:monkey:` :monkey_face: `:monkey_face:` :monocle_face: `:monocle_face:` :monorail: `:monorail:` :montenegro: `:montenegro:` :montserrat: `:montserrat:` :moon: `:moon:` :moon_cake: `:moon_cake:` :moose: `:moose:` :morocco: `:morocco:` :mortar_board: `:mortar_board:` :mosque: `:mosque:` :mosquito: `:mosquito:` :motor_boat: `:motor_boat:` :motor_scooter: `:motor_scooter:` :motorcycle: `:motorcycle:` :motorized_wheelchair: `:motorized_wheelchair:` :motorway: `:motorway:` :mount_fuji: `:mount_fuji:` :mountain: `:mountain:` :mountain_bicyclist: `:mountain_bicyclist:` :mountain_biking_man: `:mountain_biking_man:` :mountain_biking_woman: `:mountain_biking_woman:` :mountain_cableway: `:mountain_cableway:` :mountain_railway: `:mountain_railway:` :mountain_snow: `:mountain_snow:` :mouse: `:mouse:` :mouse2: `:mouse2:` :mouse_trap: `:mouse_trap:` :movie_camera: `:movie_camera:` :moyai: `:moyai:` :mozambique: `:mozambique:` :mrs_claus: `:mrs_claus:` :muscle: `:muscle:` :mushroom: `:mushroom:` :musical_keyboard: `:musical_keyboard:` :musical_note: `:musical_note:` :musical_score: `:musical_score:` :mute: `:mute:` :mx_claus: `:mx_claus:` :myanmar: `:myanmar:` :nail_care: `:nail_care:` :name_badge: `:name_badge:` :namibia: `:namibia:` :national_park: `:national_park:` :nauru: `:nauru:` :nauseated_face: `:nauseated_face:` :nazar_amulet: `:nazar_amulet:` :neckbeard: `:neckbeard:` :necktie: `:necktie:` :negative_squared_cross_mark: `:negative_squared_cross_mark:` :nepal: `:nepal:` :nerd_face: `:nerd_face:` :nest_with_eggs: `:nest_with_eggs:` :nesting_dolls: `:nesting_dolls:` :netherlands: `:netherlands:` :neutral_face: `:neutral_face:` :new: `:new:` :new_caledonia: `:new_caledonia:` :new_moon: `:new_moon:` :new_moon_with_face: `:new_moon_with_face:` :new_zealand: `:new_zealand:` :newspaper: `:newspaper:` :newspaper_roll: `:newspaper_roll:` :next_track_button: `:next_track_button:` :ng: `:ng:` :ng_man: `:ng_man:` :ng_woman: `:ng_woman:` :nicaragua: `:nicaragua:` :niger: `:niger:` :nigeria: `:nigeria:` :night_with_stars: `:night_with_stars:` :nine: `:nine:` :ninja: `:ninja:` :niue: `:niue:` :no_bell: `:no_bell:` :no_bicycles: `:no_bicycles:` :no_entry: `:no_entry:` :no_entry_sign: `:no_entry_sign:` :no_good: `:no_good:` :no_good_man: `:no_good_man:` :no_good_woman: `:no_good_woman:` :no_mobile_phones: `:no_mobile_phones:` :no_mouth: `:no_mouth:` :no_pedestrians: `:no_pedestrians:` :no_smoking: `:no_smoking:` :non-potable_water: `:non-potable_water:` :norfolk_island: `:norfolk_island:` :north_korea: `:north_korea:` :northern_mariana_islands: `:northern_mariana_islands:` :norway: `:norway:` :nose: `:nose:` :notebook: `:notebook:` :notebook_with_decorative_cover: `:notebook_with_decorative_cover:` :notes: `:notes:` :nut_and_bolt: `:nut_and_bolt:` :o: `:o:` :o2: `:o2:` :ocean: `:ocean:` :octocat: `:octocat:` :octopus: `:octopus:` :oden: `:oden:` :office: `:office:` :office_worker: `:office_worker:` :oil_drum: `:oil_drum:` :ok: `:ok:` :ok_hand: `:ok_hand:` :ok_man: `:ok_man:` :ok_person: `:ok_person:` :ok_woman: `:ok_woman:` :old_key: `:old_key:` :older_adult: `:older_adult:` :older_man: `:older_man:` :older_woman: `:older_woman:` :olive: `:olive:` :om: `:om:` :oman: `:oman:` :on: `:on:` :oncoming_automobile: `:oncoming_automobile:` :oncoming_bus: `:oncoming_bus:` :oncoming_police_car: `:oncoming_police_car:` :oncoming_taxi: `:oncoming_taxi:` :one: `:one:` :one_piece_swimsuit: `:one_piece_swimsuit:` :onion: `:onion:` :open_book: `:open_book:` :open_file_folder: `:open_file_folder:` :open_hands: `:open_hands:` :open_mouth: `:open_mouth:` :open_umbrella: `:open_umbrella:` :ophiuchus: `:ophiuchus:` :orange: `:orange:` :orange_book: `:orange_book:` :orange_circle: `:orange_circle:` :orange_heart: `:orange_heart:` :orange_square: `:orange_square:` :orangutan: `:orangutan:` :orthodox_cross: `:orthodox_cross:` :otter: `:otter:` :outbox_tray: `:outbox_tray:` :owl: `:owl:` :ox: `:ox:` :oyster: `:oyster:` :package: `:package:` :page_facing_up: `:page_facing_up:` :page_with_curl: `:page_with_curl:` :pager: `:pager:` :paintbrush: `:paintbrush:` :pakistan: `:pakistan:` :palau: `:palau:` :palestinian_territories: `:palestinian_territories:` :palm_down_hand: `:palm_down_hand:` :palm_tree: `:palm_tree:` :palm_up_hand: `:palm_up_hand:` :palms_up_together: `:palms_up_together:` :panama: `:panama:` :pancakes: `:pancakes:` :panda_face: `:panda_face:` :paperclip: `:paperclip:` :paperclips: `:paperclips:` :papua_new_guinea: `:papua_new_guinea:` :parachute: `:parachute:` :paraguay: `:paraguay:` :parasol_on_ground: `:parasol_on_ground:` :parking: `:parking:` :parrot: `:parrot:` :part_alternation_mark: `:part_alternation_mark:` :partly_sunny: `:partly_sunny:` :partying_face: `:partying_face:` :passenger_ship: `:passenger_ship:` :passport_control: `:passport_control:` :pause_button: `:pause_button:` :paw_prints: `:paw_prints:` :pea_pod: `:pea_pod:` :peace_symbol: `:peace_symbol:` :peach: `:peach:` :peacock: `:peacock:` :peanuts: `:peanuts:` :pear: `:pear:` :pen: `:pen:` :pencil: `:pencil:` :pencil2: `:pencil2:` :penguin: `:penguin:` :pensive: `:pensive:` :people_holding_hands: `:people_holding_hands:` :people_hugging: `:people_hugging:` :performing_arts: `:performing_arts:` :persevere: `:persevere:` :person_bald: `:person_bald:` :person_curly_hair: `:person_curly_hair:` :person_feeding_baby: `:person_feeding_baby:` :person_fencing: `:person_fencing:` :person_in_manual_wheelchair: `:person_in_manual_wheelchair:` :person_in_motorized_wheelchair: `:person_in_motorized_wheelchair:` :person_in_tuxedo: `:person_in_tuxedo:` :person_red_hair: `:person_red_hair:` :person_white_hair: `:person_white_hair:` :person_with_crown: `:person_with_crown:` :person_with_probing_cane: `:person_with_probing_cane:` :person_with_turban: `:person_with_turban:` :person_with_veil: `:person_with_veil:` :peru: `:peru:` :petri_dish: `:petri_dish:` :philippines: `:philippines:` :phone: `:phone:` :pick: `:pick:` :pickup_truck: `:pickup_truck:` :pie: `:pie:` :pig: `:pig:` :pig2: `:pig2:` :pig_nose: `:pig_nose:` :pill: `:pill:` :pilot: `:pilot:` :pinata: `:pinata:` :pinched_fingers: `:pinched_fingers:` :pinching_hand: `:pinching_hand:` :pineapple: `:pineapple:` :ping_pong: `:ping_pong:` :pink_heart: `:pink_heart:` :pirate_flag: `:pirate_flag:` :pisces: `:pisces:` :pitcairn_islands: `:pitcairn_islands:` :pizza: `:pizza:` :placard: `:placard:` :place_of_worship: `:place_of_worship:` :plate_with_cutlery: `:plate_with_cutlery:` :play_or_pause_button: `:play_or_pause_button:` :playground_slide: `:playground_slide:` :pleading_face: `:pleading_face:` :plunger: `:plunger:` :point_down: `:point_down:` :point_left: `:point_left:` :point_right: `:point_right:` :point_up: `:point_up:` :point_up_2: `:point_up_2:` :poland: `:poland:` :polar_bear: `:polar_bear:` :police_car: `:police_car:` :police_officer: `:police_officer:` :policeman: `:policeman:` :policewoman: `:policewoman:` :poodle: `:poodle:` :poop: `:poop:` :popcorn: `:popcorn:` :portugal: `:portugal:` :post_office: `:post_office:` :postal_horn: `:postal_horn:` :postbox: `:postbox:` :potable_water: `:potable_water:` :potato: `:potato:` :potted_plant: `:potted_plant:` :pouch: `:pouch:` :poultry_leg: `:poultry_leg:` :pound: `:pound:` :pouring_liquid: `:pouring_liquid:` :pout: `:pout:` :pouting_cat: `:pouting_cat:` :pouting_face: `:pouting_face:` :pouting_man: `:pouting_man:` :pouting_woman: `:pouting_woman:` :pray: `:pray:` :prayer_beads: `:prayer_beads:` :pregnant_man: `:pregnant_man:` :pregnant_person: `:pregnant_person:` :pregnant_woman: `:pregnant_woman:` :pretzel: `:pretzel:` :previous_track_button: `:previous_track_button:` :prince: `:prince:` :princess: `:princess:` :printer: `:printer:` :probing_cane: `:probing_cane:` :puerto_rico: `:puerto_rico:` :punch: `:punch:` :purple_circle: `:purple_circle:` :purple_heart: `:purple_heart:` :purple_square: `:purple_square:` :purse: `:purse:` :pushpin: `:pushpin:` :put_litter_in_its_place: `:put_litter_in_its_place:` :qatar: `:qatar:` :question: `:question:` :rabbit: `:rabbit:` :rabbit2: `:rabbit2:` :raccoon: `:raccoon:` :racehorse: `:racehorse:` :racing_car: `:racing_car:` :radio: `:radio:` :radio_button: `:radio_button:` :radioactive: `:radioactive:` :rage: `:rage:` :rage1: `:rage1:` :rage2: `:rage2:` :rage3: `:rage3:` :rage4: `:rage4:` :railway_car: `:railway_car:` :railway_track: `:railway_track:` :rainbow: `:rainbow:` :rainbow_flag: `:rainbow_flag:` :raised_back_of_hand: `:raised_back_of_hand:` :raised_eyebrow: `:raised_eyebrow:` :raised_hand: `:raised_hand:` :raised_hand_with_fingers_splayed: `:raised_hand_with_fingers_splayed:` :raised_hands: `:raised_hands:` :raising_hand: `:raising_hand:` :raising_hand_man: `:raising_hand_man:` :raising_hand_woman: `:raising_hand_woman:` :ram: `:ram:` :ramen: `:ramen:` :rat: `:rat:` :razor: `:razor:` :receipt: `:receipt:` :record_button: `:record_button:` :recycle: `:recycle:` :red_car: `:red_car:` :red_circle: `:red_circle:` :red_envelope: `:red_envelope:` :red_haired_man: `:red_haired_man:` :red_haired_woman: `:red_haired_woman:` :red_square: `:red_square:` :registered: `:registered:` :relaxed: `:relaxed:` :relieved: `:relieved:` :reminder_ribbon: `:reminder_ribbon:` :repeat: `:repeat:` :repeat_one: `:repeat_one:` :rescue_worker_helmet: `:rescue_worker_helmet:` :restroom: `:restroom:` :reunion: `:reunion:` :revolving_hearts: `:revolving_hearts:` :rewind: `:rewind:` :rhinoceros: `:rhinoceros:` :ribbon: `:ribbon:` :rice: `:rice:` :rice_ball: `:rice_ball:` :rice_cracker: `:rice_cracker:` :rice_scene: `:rice_scene:` :right_anger_bubble: `:right_anger_bubble:` :rightwards_hand: `:rightwards_hand:` :rightwards_pushing_hand: `:rightwards_pushing_hand:` :ring: `:ring:` :ring_buoy: `:ring_buoy:` :ringed_planet: `:ringed_planet:` :robot: `:robot:` :rock: `:rock:` :rocket: `:rocket:` :rofl: `:rofl:` :roll_eyes: `:roll_eyes:` :roll_of_paper: `:roll_of_paper:` :roller_coaster: `:roller_coaster:` :roller_skate: `:roller_skate:` :romania: `:romania:` :rooster: `:rooster:` :rose: `:rose:` :rosette: `:rosette:` :rotating_light: `:rotating_light:` :round_pushpin: `:round_pushpin:` :rowboat: `:rowboat:` :rowing_man: `:rowing_man:` :rowing_woman: `:rowing_woman:` :ru: `:ru:` :rugby_football: `:rugby_football:` :runner: `:runner:` :running: `:running:` :running_man: `:running_man:` :running_shirt_with_sash: `:running_shirt_with_sash:` :running_woman: `:running_woman:` :rwanda: `:rwanda:` :sa: `:sa:` :safety_pin: `:safety_pin:` :safety_vest: `:safety_vest:` :sagittarius: `:sagittarius:` :sailboat: `:sailboat:` :sake: `:sake:` :salt: `:salt:` :saluting_face: `:saluting_face:` :samoa: `:samoa:` :san_marino: `:san_marino:` :sandal: `:sandal:` :sandwich: `:sandwich:` :santa: `:santa:` :sao_tome_principe: `:sao_tome_principe:` :sari: `:sari:` :sassy_man: `:sassy_man:` :sassy_woman: `:sassy_woman:` :satellite: `:satellite:` :satisfied: `:satisfied:` :saudi_arabia: `:saudi_arabia:` :sauna_man: `:sauna_man:` :sauna_person: `:sauna_person:` :sauna_woman: `:sauna_woman:` :sauropod: `:sauropod:` :saxophone: `:saxophone:` :scarf: `:scarf:` :school: `:school:` :school_satchel: `:school_satchel:` :scientist: `:scientist:` :scissors: `:scissors:` :scorpion: `:scorpion:` :scorpius: `:scorpius:` :scotland: `:scotland:` :scream: `:scream:` :scream_cat: `:scream_cat:` :screwdriver: `:screwdriver:` :scroll: `:scroll:` :seal: `:seal:` :seat: `:seat:` :secret: `:secret:` :see_no_evil: `:see_no_evil:` :seedling: `:seedling:` :selfie: `:selfie:` :senegal: `:senegal:` :serbia: `:serbia:` :service_dog: `:service_dog:` :seven: `:seven:` :sewing_needle: `:sewing_needle:` :seychelles: `:seychelles:` :shaking_face: `:shaking_face:` :shallow_pan_of_food: `:shallow_pan_of_food:` :shamrock: `:shamrock:` :shark: `:shark:` :shaved_ice: `:shaved_ice:` :sheep: `:sheep:` :shell: `:shell:` :shield: `:shield:` :shinto_shrine: `:shinto_shrine:` :ship: `:ship:` :shipit: `:shipit:` :shirt: `:shirt:` :shit: `:shit:` :shoe: `:shoe:` :shopping: `:shopping:` :shopping_cart: `:shopping_cart:` :shorts: `:shorts:` :shower: `:shower:` :shrimp: `:shrimp:` :shrug: `:shrug:` :shushing_face: `:shushing_face:` :sierra_leone: `:sierra_leone:` :signal_strength: `:signal_strength:` :singapore: `:singapore:` :singer: `:singer:` :sint_maarten: `:sint_maarten:` :six: `:six:` :six_pointed_star: `:six_pointed_star:` :skateboard: `:skateboard:` :ski: `:ski:` :skier: `:skier:` :skull: `:skull:` :skull_and_crossbones: `:skull_and_crossbones:` :skunk: `:skunk:` :sled: `:sled:` :sleeping: `:sleeping:` :sleeping_bed: `:sleeping_bed:` :sleepy: `:sleepy:` :slightly_frowning_face: `:slightly_frowning_face:` :slightly_smiling_face: `:slightly_smiling_face:` :slot_machine: `:slot_machine:` :sloth: `:sloth:` :slovakia: `:slovakia:` :slovenia: `:slovenia:` :small_airplane: `:small_airplane:` :small_blue_diamond: `:small_blue_diamond:` :small_orange_diamond: `:small_orange_diamond:` :small_red_triangle: `:small_red_triangle:` :small_red_triangle_down: `:small_red_triangle_down:` :smile: `:smile:` :smile_cat: `:smile_cat:` :smiley: `:smiley:` :smiley_cat: `:smiley_cat:` :smiling_face_with_tear: `:smiling_face_with_tear:` :smiling_face_with_three_hearts: `:smiling_face_with_three_hearts:` :smiling_imp: `:smiling_imp:` :smirk: `:smirk:` :smirk_cat: `:smirk_cat:` :smoking: `:smoking:` :snail: `:snail:` :snake: `:snake:` :sneezing_face: `:sneezing_face:` :snowboarder: `:snowboarder:` :snowflake: `:snowflake:` :snowman: `:snowman:` :snowman_with_snow: `:snowman_with_snow:` :soap: `:soap:` :sob: `:sob:` :soccer: `:soccer:` :socks: `:socks:` :softball: `:softball:` :solomon_islands: `:solomon_islands:` :somalia: `:somalia:` :soon: `:soon:` :sos: `:sos:` :sound: `:sound:` :south_africa: `:south_africa:` :south_georgia_south_sandwich_islands: `:south_georgia_south_sandwich_islands:` :south_sudan: `:south_sudan:` :space_invader: `:space_invader:` :spades: `:spades:` :spaghetti: `:spaghetti:` :sparkle: `:sparkle:` :sparkler: `:sparkler:` :sparkles: `:sparkles:` :sparkling_heart: `:sparkling_heart:` :speak_no_evil: `:speak_no_evil:` :speaker: `:speaker:` :speaking_head: `:speaking_head:` :speech_balloon: `:speech_balloon:` :speedboat: `:speedboat:` :spider: `:spider:` :spider_web: `:spider_web:` :spiral_calendar: `:spiral_calendar:` :spiral_notepad: `:spiral_notepad:` :sponge: `:sponge:` :spoon: `:spoon:` :squid: `:squid:` :sri_lanka: `:sri_lanka:` :st_barthelemy: `:st_barthelemy:` :st_helena: `:st_helena:` :st_kitts_nevis: `:st_kitts_nevis:` :st_lucia: `:st_lucia:` :st_martin: `:st_martin:` :st_pierre_miquelon: `:st_pierre_miquelon:` :st_vincent_grenadines: `:st_vincent_grenadines:` :stadium: `:stadium:` :standing_man: `:standing_man:` :standing_person: `:standing_person:` :standing_woman: `:standing_woman:` :star: `:star:` :star2: `:star2:` :star_and_crescent: `:star_and_crescent:` :star_of_david: `:star_of_david:` :star_struck: `:star_struck:` :stars: `:stars:` :station: `:station:` :statue_of_liberty: `:statue_of_liberty:` :steam_locomotive: `:steam_locomotive:` :stethoscope: `:stethoscope:` :stew: `:stew:` :stop_button: `:stop_button:` :stop_sign: `:stop_sign:` :stopwatch: `:stopwatch:` :straight_ruler: `:straight_ruler:` :strawberry: `:strawberry:` :stuck_out_tongue: `:stuck_out_tongue:` :stuck_out_tongue_closed_eyes: `:stuck_out_tongue_closed_eyes:` :stuck_out_tongue_winking_eye: `:stuck_out_tongue_winking_eye:` :student: `:student:` :studio_microphone: `:studio_microphone:` :stuffed_flatbread: `:stuffed_flatbread:` :sudan: `:sudan:` :sun_behind_large_cloud: `:sun_behind_large_cloud:` :sun_behind_rain_cloud: `:sun_behind_rain_cloud:` :sun_behind_small_cloud: `:sun_behind_small_cloud:` :sun_with_face: `:sun_with_face:` :sunflower: `:sunflower:` :sunglasses: `:sunglasses:` :sunny: `:sunny:` :sunrise: `:sunrise:` :sunrise_over_mountains: `:sunrise_over_mountains:` :superhero: `:superhero:` :superhero_man: `:superhero_man:` :superhero_woman: `:superhero_woman:` :supervillain: `:supervillain:` :supervillain_man: `:supervillain_man:` :supervillain_woman: `:supervillain_woman:` :surfer: `:surfer:` :surfing_man: `:surfing_man:` :surfing_woman: `:surfing_woman:` :suriname: `:suriname:` :sushi: `:sushi:` :suspect: `:suspect:` :suspension_railway: `:suspension_railway:` :svalbard_jan_mayen: `:svalbard_jan_mayen:` :swan: `:swan:` :swaziland: `:swaziland:` :sweat: `:sweat:` :sweat_drops: `:sweat_drops:` :sweat_smile: `:sweat_smile:` :sweden: `:sweden:` :sweet_potato: `:sweet_potato:` :swim_brief: `:swim_brief:` :swimmer: `:swimmer:` :swimming_man: `:swimming_man:` :swimming_woman: `:swimming_woman:` :switzerland: `:switzerland:` :symbols: `:symbols:` :synagogue: `:synagogue:` :syria: `:syria:` :syringe: `:syringe:` :t-rex: `:t-rex:` :taco: `:taco:` :tada: `:tada:` :taiwan: `:taiwan:` :tajikistan: `:tajikistan:` :takeout_box: `:takeout_box:` :tamale: `:tamale:` :tanabata_tree: `:tanabata_tree:` :tangerine: `:tangerine:` :tanzania: `:tanzania:` :taurus: `:taurus:` :taxi: `:taxi:` :tea: `:tea:` :teacher: `:teacher:` :teapot: `:teapot:` :technologist: `:technologist:` :teddy_bear: `:teddy_bear:` :telephone: `:telephone:` :telephone_receiver: `:telephone_receiver:` :telescope: `:telescope:` :tennis: `:tennis:` :tent: `:tent:` :test_tube: `:test_tube:` :thailand: `:thailand:` :thermometer: `:thermometer:` :thinking: `:thinking:` :thong_sandal: `:thong_sandal:` :thought_balloon: `:thought_balloon:` :thread: `:thread:` :three: `:three:` :thumbsdown: `:thumbsdown:` :thumbsup: `:thumbsup:` :ticket: `:ticket:` :tickets: `:tickets:` :tiger: `:tiger:` :tiger2: `:tiger2:` :timer_clock: `:timer_clock:` :timor_leste: `:timor_leste:` :tipping_hand_man: `:tipping_hand_man:` :tipping_hand_person: `:tipping_hand_person:` :tipping_hand_woman: `:tipping_hand_woman:` :tired_face: `:tired_face:` :tm: `:tm:` :togo: `:togo:` :toilet: `:toilet:` :tokelau: `:tokelau:` :tokyo_tower: `:tokyo_tower:` :tomato: `:tomato:` :tonga: `:tonga:` :tongue: `:tongue:` :toolbox: `:toolbox:` :tooth: `:tooth:` :toothbrush: `:toothbrush:` :top: `:top:` :tophat: `:tophat:` :tornado: `:tornado:` :tr: `:tr:` :trackball: `:trackball:` :tractor: `:tractor:` :traffic_light: `:traffic_light:` :train: `:train:` :train2: `:train2:` :tram: `:tram:` :transgender_flag: `:transgender_flag:` :transgender_symbol: `:transgender_symbol:` :triangular_flag_on_post: `:triangular_flag_on_post:` :triangular_ruler: `:triangular_ruler:` :trident: `:trident:` :trinidad_tobago: `:trinidad_tobago:` :tristan_da_cunha: `:tristan_da_cunha:` :triumph: `:triumph:` :troll: `:troll:` :trolleybus: `:trolleybus:` :trollface: `:trollface:` :trophy: `:trophy:` :tropical_drink: `:tropical_drink:` :tropical_fish: `:tropical_fish:` :truck: `:truck:` :trumpet: `:trumpet:` :tshirt: `:tshirt:` :tulip: `:tulip:` :tumbler_glass: `:tumbler_glass:` :tunisia: `:tunisia:` :turkey: `:turkey:` :turkmenistan: `:turkmenistan:` :turks_caicos_islands: `:turks_caicos_islands:` :turtle: `:turtle:` :tuvalu: `:tuvalu:` :tv: `:tv:` :twisted_rightwards_arrows: `:twisted_rightwards_arrows:` :two: `:two:` :two_hearts: `:two_hearts:` :two_men_holding_hands: `:two_men_holding_hands:` :two_women_holding_hands: `:two_women_holding_hands:` :u5272: `:u5272:` :u5408: `:u5408:` :u55b6: `:u55b6:` :u6307: `:u6307:` :u6708: `:u6708:` :u6709: `:u6709:` :u6e80: `:u6e80:` :u7121: `:u7121:` :u7533: `:u7533:` :u7981: `:u7981:` :u7a7a: `:u7a7a:` :uganda: `:uganda:` :uk: `:uk:` :ukraine: `:ukraine:` :umbrella: `:umbrella:` :unamused: `:unamused:` :underage: `:underage:` :unicorn: `:unicorn:` :united_arab_emirates: `:united_arab_emirates:` :united_nations: `:united_nations:` :unlock: `:unlock:` :up: `:up:` :upside_down_face: `:upside_down_face:` :uruguay: `:uruguay:` :us: `:us:` :us_outlying_islands: `:us_outlying_islands:` :us_virgin_islands: `:us_virgin_islands:` :uzbekistan: `:uzbekistan:` :v: `:v:` :vampire: `:vampire:` :vampire_man: `:vampire_man:` :vampire_woman: `:vampire_woman:` :vanuatu: `:vanuatu:` :vatican_city: `:vatican_city:` :venezuela: `:venezuela:` :vertical_traffic_light: `:vertical_traffic_light:` :vhs: `:vhs:` :vibration_mode: `:vibration_mode:` :video_camera: `:video_camera:` :video_game: `:video_game:` :vietnam: `:vietnam:` :violin: `:violin:` :virgo: `:virgo:` :volcano: `:volcano:` :volleyball: `:volleyball:` :vomiting_face: `:vomiting_face:` :vs: `:vs:` :vulcan_salute: `:vulcan_salute:` :waffle: `:waffle:` :wales: `:wales:` :walking: `:walking:` :walking_man: `:walking_man:` :walking_woman: `:walking_woman:` :wallis_futuna: `:wallis_futuna:` :waning_crescent_moon: `:waning_crescent_moon:` :waning_gibbous_moon: `:waning_gibbous_moon:` :warning: `:warning:` :wastebasket: `:wastebasket:` :watch: `:watch:` :water_buffalo: `:water_buffalo:` :water_polo: `:water_polo:` :watermelon: `:watermelon:` :wave: `:wave:` :wavy_dash: `:wavy_dash:` :waxing_crescent_moon: `:waxing_crescent_moon:` :waxing_gibbous_moon: `:waxing_gibbous_moon:` :wc: `:wc:` :weary: `:weary:` :wedding: `:wedding:` :weight_lifting: `:weight_lifting:` :weight_lifting_man: `:weight_lifting_man:` :weight_lifting_woman: `:weight_lifting_woman:` :western_sahara: `:western_sahara:` :whale: `:whale:` :whale2: `:whale2:` :wheel: `:wheel:` :wheel_of_dharma: `:wheel_of_dharma:` :wheelchair: `:wheelchair:` :white_check_mark: `:white_check_mark:` :white_circle: `:white_circle:` :white_flag: `:white_flag:` :white_flower: `:white_flower:` :white_haired_man: `:white_haired_man:` :white_haired_woman: `:white_haired_woman:` :white_heart: `:white_heart:` :white_large_square: `:white_large_square:` :white_medium_small_square: `:white_medium_small_square:` :white_medium_square: `:white_medium_square:` :white_small_square: `:white_small_square:` :white_square_button: `:white_square_button:` :wilted_flower: `:wilted_flower:` :wind_chime: `:wind_chime:` :wind_face: `:wind_face:` :window: `:window:` :wine_glass: `:wine_glass:` :wing: `:wing:` :wink: `:wink:` :wireless: `:wireless:` :wolf: `:wolf:` :woman: `:woman:` :woman_artist: `:woman_artist:` :woman_astronaut: `:woman_astronaut:` :woman_beard: `:woman_beard:` :woman_cartwheeling: `:woman_cartwheeling:` :woman_cook: `:woman_cook:` :woman_dancing: `:woman_dancing:` :woman_facepalming: `:woman_facepalming:` :woman_factory_worker: `:woman_factory_worker:` :woman_farmer: `:woman_farmer:` :woman_feeding_baby: `:woman_feeding_baby:` :woman_firefighter: `:woman_firefighter:` :woman_health_worker: `:woman_health_worker:` :woman_in_manual_wheelchair: `:woman_in_manual_wheelchair:` :woman_in_motorized_wheelchair: `:woman_in_motorized_wheelchair:` :woman_in_tuxedo: `:woman_in_tuxedo:` :woman_judge: `:woman_judge:` :woman_juggling: `:woman_juggling:` :woman_mechanic: `:woman_mechanic:` :woman_office_worker: `:woman_office_worker:` :woman_pilot: `:woman_pilot:` :woman_playing_handball: `:woman_playing_handball:` :woman_playing_water_polo: `:woman_playing_water_polo:` :woman_scientist: `:woman_scientist:` :woman_shrugging: `:woman_shrugging:` :woman_singer: `:woman_singer:` :woman_student: `:woman_student:` :woman_teacher: `:woman_teacher:` :woman_technologist: `:woman_technologist:` :woman_with_headscarf: `:woman_with_headscarf:` :woman_with_probing_cane: `:woman_with_probing_cane:` :woman_with_turban: `:woman_with_turban:` :woman_with_veil: `:woman_with_veil:` :womans_clothes: `:womans_clothes:` :womans_hat: `:womans_hat:` :women_wrestling: `:women_wrestling:` :womens: `:womens:` :wood: `:wood:` :woozy_face: `:woozy_face:` :world_map: `:world_map:` :worm: `:worm:` :worried: `:worried:` :wrench: `:wrench:` :wrestling: `:wrestling:` :writing_hand: `:writing_hand:` :x: `:x:` :x_ray: `:x_ray:` :yarn: `:yarn:` :yawning_face: `:yawning_face:` :yellow_circle: `:yellow_circle:` :yellow_heart: `:yellow_heart:` :yellow_square: `:yellow_square:` :yemen: `:yemen:` :yen: `:yen:` :yin_yang: `:yin_yang:` :yo_yo: `:yo_yo:` :yum: `:yum:` :zambia: `:zambia:` :zany_face: `:zany_face:` :zap: `:zap:` :zebra: `:zebra:` :zero: `:zero:` :zimbabwe: `:zimbabwe:` :zipper_mouth_face: `:zipper_mouth_face:` :zombie: `:zombie:` :zombie_man: `:zombie_man:` :zombie_woman: `:zombie_woman:` :zzz: `:zzz:` <!-- END: Auto-generated content (/build/emoji.js) --> </div> ================================================ FILE: docs/helpers.md ================================================ # Doc helper docsify extends Markdown syntax to make your documents more readable. > Note: For the special code syntax cases, it's better to put them within code backticks to avoid any conflict from configurations or emojis. ## Callouts Docsify supports [GitHub style](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) callouts (also known as "admonitions" or "alerts"). <!-- prettier-ignore --> > [!CAUTION] > **Caution** callouts communicate negative potential consequences of an action. <!-- prettier-ignore --> > [!IMPORTANT] > **Important** callouts communicate information necessary for users to succeed. <!-- prettier-ignore --> > [!NOTE] > **Note** callouts communicate information that users should take into account. <!-- prettier-ignore --> > [!TIP] > **Tip** callouts communicate optional information to help a user be more successful. <!-- prettier-ignore --> > [!WARNING] > **Warning** callouts communicate potential risks user should be aware of. **Markdown** <!-- prettier-ignore --> ```markdown > [!CAUTION] > **Caution** callouts communicate negative potential consequences of an action. > [!IMPORTANT] > **Important** callouts communicate information necessary for users to succeed. > [!NOTE] > **Note** callouts communicate information that users should take into account. > [!TIP] > **Tip** callouts communicate optional information to help a user be more successful. > [!WARNING] > **Warning** callouts communicate potential risks user should be aware of. ``` ### Legacy Style ⚠️ The following Docsify v4 callout syntax has been deprecated and will be removed in a future version. !> Legacy **Important** callouts are deprecated. ?> Legacy **Tip** callouts are deprecated. **Markdown** ```markdown !> Legacy **Important** callouts are deprecated. ?> Legacy **Tip** callouts are deprecated. ``` ## Link attributes ### disabled ```markdown [link](/demo ':disabled') ``` ### href Sometimes we will use some other relative path for the link, and we have to tell docsify that we don't need to compile this link. For example: ```markdown [link](/demo/) ``` It will be compiled to `<a href="/#/demo/">link</a>` and will load `/demo/README.md`. Maybe you want to jump to `/demo/index.html`. Now you can do that ```markdown [link](/demo/ ':ignore') ``` You will get `<a href="/demo/">link</a>`html. Do not worry, you can still set the title for the link. ```markdown [link](/demo/ ':ignore title') <a href="/demo/" title="title">link</a> ``` ### target ```markdown [link](/demo ':target=_blank') [link](/demo2 ':target=_self') ``` ## Task lists ```markdown - [ ] foo - bar - [x] baz - [] bam <~ not working - [ ] bim - [ ] lim ``` - [ ] foo - bar - [x] baz - [] bam <~ not working - [ ] bim - [ ] lim ## Images ### Class names ```markdown ![logo](https://docsify.js.org/_media/icon.svg ':class=someCssClass') <!-- Multiple class names --> ![logo](https://docsify.js.org/_media/icon.svg ':class=someCssClass :class=anotherCssClass') ``` ### IDs ```markdown ![logo](https://docsify.js.org/_media/icon.svg ':id=someCssId') ``` ### Sizes ```markdown ![logo](https://docsify.js.org/_media/icon.svg ':size=WIDTHxHEIGHT') ![logo](https://docsify.js.org/_media/icon.svg ':size=50x100') ![logo](https://docsify.js.org/_media/icon.svg ':size=100') <!-- Support percentage --> ![logo](https://docsify.js.org/_media/icon.svg ':size=10%') ``` ![logo](https://docsify.js.org/_media/icon.svg ':size=50x100') ![logo](https://docsify.js.org/_media/icon.svg ':size=100') ![logo](https://docsify.js.org/_media/icon.svg ':size=10%') ## Heading IDs ```markdown ### Hello, world! :id=hello-world ``` ## Markdown + HTML You need to insert a space between the html and markdown content. This is useful for rendering markdown content in the details element. ```markdown <details> <summary>Self-assessment (Click to expand)</summary> - Abc - Abc </details> ``` <details> <summary>Self-assessment (Click to expand)</summary> - Abc - Abc </details> Markdown content can also be wrapped in html tags. ```markdown <div style='color: red'> - listitem - listitem - listitem </div> ``` <div style='color: red'> - listitem - listitem - listitem </div> ================================================ FILE: docs/index.html ================================================ <!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <title>docsify
================================================ FILE: docs/language-highlight.md ================================================ # Language highlighting ## Prism Docsify uses [Prism](https://prismjs.com) for syntax highlighting within code blocks. Prism supports the following languages by default (additional [language support](#language-support) also available): - Markup: HTML, XML, SVG, MathML, SSML, Atom, RSS - CSS - C-like - JavaScript To enable syntax highlighting, create a markdown codeblock using backticks (` ``` `) with a [language](https://prismjs.com/#supported-languages) specified on the first line (e.g., `html`, `css`, `js`): ````text ```html

This is a paragraph

Docsify ``` ```` ````text ```css p { color: red; } ``` ```` ````text ```js function add(a, b) { return a + b; } ``` ```` The above markdown will be rendered as: ```html

This is a paragraph

Docsify ``` ```css p { color: red; } ``` ```js function add(a, b) { return a + b; } ``` ## Language support Support for additional [languages](https://prismjs.com/#supported-languages) is available by loading the Prism [grammar files](https://cdn.jsdelivr.net/npm/prismjs@1/components/): > [!IMPORTANT] Prism grammar files must be loaded after Docsify. ```html ``` ## Theme support Docsify's official [themes](themes) are compatible with Prism syntax highlighting themes. > [!IMPORTANT] Prism themes must be loaded after Docsify themes. ```html ``` Themes can be applied in light and/or dark mode ```html ``` The following Docsify [theme properties](themes#theme-properties) will override Prism theme styles by default: ```text --border-radius --font-family-mono --font-size-mono ``` To use the values specified in the Prism theme, set the desired theme property to `unset`: ```html ``` ## Dynamic content Dynamically generated Code blocks can be highlighted using Prism's [`highlightElement()`](https://prismjs.com/docs/Prism.html#.highlightElement) method: ```js const code = document.createElement('code'); code.innerHTML = "console.log('Hello World!')"; code.setAttribute('class', 'language-javascript'); Prism.highlightElement(code); ``` ================================================ FILE: docs/markdown.md ================================================ # Markdown configuration **docsify** uses [marked](https://github.com/markedjs/marked) as its Markdown parser. You can customize how it renders your Markdown content to HTML by customizing `renderer`: ```js window.$docsify = { markdown: { smartypants: true, renderer: { link() { // ... }, }, }, }; ``` > [!TIP] Configuration Options Reference: [marked documentation](https://marked.js.org/#/USING_ADVANCED.md) You can completely customize the parsing rules. ```js window.$docsify = { markdown(marked, renderer) { // ... return marked; }, }; ``` ## Supports mermaid > [!IMPORTANT] Currently, docsify doesn't support the async mermaid render (the latest mermaid version supported is `v9.3.0`). ```js // // let num = 0; mermaid.initialize({ startOnLoad: false }); window.$docsify = { markdown: { renderer: { code({ text, lang }) { if (lang === 'mermaid') { return /* html */ `
${mermaid.render( 'mermaid-svg-' + num++, text, )}
`; } return this.origin.code.apply(this, arguments); }, }, }, }; ``` ================================================ FILE: docs/plugins.md ================================================ # List of Plugins These are built-in and external plugins for Docsify. See also how to [Write a Plugin](./write-a-plugin.md). ## Full text search By default, the hyperlink on the current page is recognized and the content is saved in `IndexedDB`. You can also specify the path to the files. ```html ``` This plugin ignores diacritical marks when performing a full text search (e.g., "cafe" will also match "café"). ## Google Analytics > Google's Universal Analytics service will no longer process new data in standard properties beginning July 1, 2023. Prepare now by setting up and switching over to a Google Analytics 4 property and docsify's gtag.js plugin. Install the plugin and configure the track id. ```html ``` Configure by `data-ga`. ```html ``` ## Google Analytics 4 (GA4) Install the plugin and configure the track id. ```html ``` ## Emoji Renders a larger collection of emoji shorthand codes. Without this plugin, Docsify is able to render only a limited number of emoji shorthand codes. > [!IMPORTANT] Deprecated as of v4.13. Docsify no longer requires this plugin for full emoji support. ```html ``` ## External Script If the script on the page is an external one (imports a js file via `src` attribute), you'll need this plugin to make it work. ```html ``` ## Zoom image Medium's image zoom. Based on [medium-zoom](https://github.com/francoischalifour/medium-zoom). ```html ``` Exclude the special image ```markdown ![](image.png ':no-zoom') ``` ## Edit on github Add `Edit on github` button on every pages. Provided by [@njleonzhang](https://github.com/njleonzhang), see this [document](https://github.com/njleonzhang/docsify-edit-on-github) ## Demo code with instant preview and jsfiddle integration With this plugin, sample code can be rendered on the page instantly, so that the readers can see the preview immediately. When readers expand the demo box, the source code and description are shown there. if they click the button `Try in Jsfiddle`, `jsfiddle.net` will be open with the code of this sample, which allow readers to revise the code and try on their own. [Vue](https://njleonzhang.github.io/docsify-demo-box-vue/) and [React](https://njleonzhang.github.io/docsify-demo-box-react/) are both supported. ## Copy to Clipboard Add a simple `Click to copy` button to all preformatted code blocks to effortlessly allow users to copy example code from your docs. Provided by [@jperasmus](https://github.com/jperasmus) ```html ``` See [here](https://github.com/jperasmus/docsify-copy-code/blob/master/README.md) for more details. ## Disqus Disqus comments. https://disqus.com/ ```html ``` ## Gitalk [Gitalk](https://github.com/gitalk/gitalk) is a modern comment component based on GitHub Issue and Preact. ```html ``` ## Pagination Pagination for docsify. By [@imyelo](https://github.com/imyelo) ```html ``` Click [here](https://github.com/imyelo/docsify-pagination#readme) to get more information. ## Tabs A docsify.js plugin for displaying tabbed content from markdown. - [Documentation & Demos](https://jhildenbiddle.github.io/docsify-tabs) Provided by [@jhildenbiddle](https://github.com/jhildenbiddle/docsify-tabs). ## More plugins See [awesome-docsify](awesome?id=plugins) ================================================ FILE: docs/pwa.md ================================================ # Offline Mode [Progressive Web Apps](https://developers.google.com/web/progressive-web-apps/) (PWA) are experiences that combine the best of the web with the best of apps. We can enhance our website with service workers to work **offline** or on low-quality networks. It is also very easy to use. ## Create serviceWorker Create a `sw.js` file in your project's root directory and copy the following code: _sw.js_ ```js /* =========================================================== * docsify sw.js * =========================================================== * Copyright 2016 @huxpro * Licensed under Apache 2.0 * Register service worker. * ========================================================== */ const RUNTIME = 'docsify'; const HOSTNAME_WHITELIST = [ self.location.hostname, 'fonts.gstatic.com', 'fonts.googleapis.com', 'cdn.jsdelivr.net', ]; // The Util Function to hack URLs of intercepted requests const getFixedUrl = req => { const now = Date.now(); const url = new URL(req.url); // 1. fixed http URL // Just keep syncing with location.protocol // fetch(httpURL) belongs to active mixed content. // And fetch(httpRequest) is not supported yet. url.protocol = self.location.protocol; // 2. add query for caching-busting. // GitHub Pages served with Cache-Control: max-age=600 // max-age on mutable content is error-prone, with SW life of bugs can even extend. // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string. // Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190 if (url.hostname === self.location.hostname) { url.search += (url.search ? '&' : '?') + 'cache-bust=' + now; } return url.href; }; /** * @Lifecycle Activate * New one activated when old isnt being used. * * waitUntil(): activating ====> activated */ self.addEventListener('activate', event => { event.waitUntil(self.clients.claim()); }); /** * @Functional Fetch * All network requests are being intercepted here. * * void respondWith(Promise r) */ self.addEventListener('fetch', event => { // Skip some of cross-origin requests, like those for Google Analytics. if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) { // Stale-while-revalidate // similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale // Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1 const cached = caches.match(event.request); const fixedUrl = getFixedUrl(event.request); const fetched = fetch(fixedUrl, { cache: 'no-store' }); const fetchedCopy = fetched.then(resp => resp.clone()); // Call respondWith() with whatever we get first. // If the fetch fails (e.g disconnected), wait for the cache. // If there’s nothing in cache, wait for the fetch. // If neither yields a response, return offline pages. event.respondWith( Promise.race([fetched.catch(_ => cached), cached]) .then(resp => resp || fetched) .catch(_ => { /* eat any errors */ }), ); // Update the cache with the version we fetched (only for ok status) event.waitUntil( Promise.all([fetchedCopy, caches.open(RUNTIME)]) .then( ([response, cache]) => response.ok && cache.put(event.request, response), ) .catch(_ => { /* eat any errors */ }), ); } }); ``` ## Register Now, register it in your `index.html`. It only works on some modern browsers, so we need to check: _index.html_ ```html ``` ## Enjoy it Release your website and start experiencing the magical offline feature. ================================================ FILE: docs/quickstart.md ================================================ # Quick start It is recommended to install `docsify-cli` globally, which helps initializing and previewing the website locally. ```bash npm i docsify-cli -g ``` ## Initialize If you want to write the documentation in the `./docs` subdirectory, you can use the `init` command. ```bash docsify init ./docs ``` ## Writing content After the `init` is complete, you can see the file list in the `./docs` subdirectory. - `index.html` as the entry file - `README.md` as the home page - `.nojekyll` prevents GitHub Pages from ignoring files that begin with an underscore You can easily update the documentation in `./docs/README.md`, and of course you can add [more pages](adding-pages.md). ## Preview your site Run the local server with `docsify serve`. You can preview your site in your browser on `http://localhost:3000`. ```bash docsify serve docs ``` > [!TIP] For more use cases of `docsify-cli`, head over to the [docsify-cli documentation](https://github.com/docsifyjs/docsify-cli). ## Manual initialization Download or create an `index.html` template using the following markup:
Download Template ```html
```
### Specifying docsify versions > [!TIP] Note that in both of the examples below, docsify URLs will need to be > manually updated when a new major version of docsify is released (e.g. `v5.x.x` > => `v6.x.x`). Check the docsify website periodically to see if a new major > version has been released. Specifying a major version in the URL (`@5`) will allow your site to receive non-breaking enhancements (i.e. "minor" updates) and bug fixes (i.e. "patch" updates) automatically. This is the recommended way to load docsify resources. ```html ``` If you'd like to ensure absolutely no possibility of changes that will break your site (non-major updates can unintentionally introduce breaking changes despite that they aim not to), specify the full version after the `@` symbol in the URL. This is the safest way to ensure your site will look and behave the same way regardless of any changes made to future versions of docsify. ```html ``` JSDelivr supports [npm-compatible semver ranges](https://docs.npmjs.com/cli/v11/configuring-npm/package-json#dependencies), so you can also use version syntax such as `@^5.0.0` for the latest v5 release, `@5.0.x` for the latest v5.0 patch release (f.e. you will receive 5.0.4 but not 5.1.0), `@5.x` for the latest v5 minor and patch releases (which is effectively the same as `@5` and `@^5.0.0`), etc. ### Manually preview your site If you have Python installed on your system, you can easily use it to run a static server to preview your site instead of using `docsify serve` from `docsify-cli`. ```python # Python 2 cd docs && python -m SimpleHTTPServer 3000 ``` ```python # Python 3 cd docs && python -m http.server 3000 ``` ================================================ FILE: docs/themes.md ================================================ # Themes ## Core theme The Docsify "core" theme contains all of the styles and [theme properties](#theme-properties) needed to render a Docsify site. This theme is designed to serve as a minimalist theme on its own, in combination with [theme add-ons](#theme-add-ons), modified using core [classes](#classes), and as a starting point for [customization](#customization). ```html ``` ## Theme add-ons Theme add-ons are used in combination with the [core theme](#core-theme). Add-ons contain CSS rules that modify [theme properties](#theme-properties) values and/or add custom style declarations. They can often (but not always) be used with other add-ons. > [!IMPORTANT] Theme add-ons must be loaded after the [core theme](#core-theme). ```html ``` ### Core Dark (Add-on) Dark mode styles for the [core theme](#core-theme). Styles can applied only when an operating system's dark mode is active by specifying a `media` attribute.
```html ``` ```html ``` ### Vue theme (Add-on) The popular Docsify v4 theme. ```html ``` ## Classes The [core theme](#core-theme) provides several CSS classes for customizing your Docsify site. These classes should be applied to the `` element within your `index.html` page. ```html ``` ### Loading Display a loading animation while waiting for Docsify to initialize. ```html ```
### Sidebar chevrons Display expand/collapse icons on page links in the sidebar.
```html ``` ```html ``` To prevent chevrons from displaying for specific page links, add a `no-chevron` class as follows: ```md [My Page](page.md ':class=no-chevron') ``` **Theme properties** ```css :root { --sidebar-chevron-collapsed-color: var(--color-mono-3); --sidebar-chevron-expanded-color : var(--theme-color); } ``` ### Sidebar groups Add visual distinction between groups of links in the sidebar.
```html ``` ```html ``` ### Sidebar link clamp Limit multi-line sidebar links to a single line followed by an ellipses. ```html ``` ### Sidebar toggle Display a "hamburger" icon (three lines) in the sidebar toggle button instead of the default "kebab" icon.
```html ``` ```html ``` ## Customization Docsify provides [theme properties](#theme-properties) for simplified customization of frequently modified styles. 1. Add a ` ``` Theme properties can also be set on a per-page basis in markdown. ```markdown # My Heading Hello, World! ``` 2. Set custom [theme properties](#theme-properties) within a `:root` declaration. ```css :root { --theme-color: red; --font-size : 15px; --line-height: 1.5; } ``` Custom [theme properties](#theme-properties) can be conditionally applied in light and/or dark mode. ```css /* Light and dark mode */ :root { --theme-color: pink; } /* Light mode only */ @media (prefers-color-scheme: light) { :root { --color-bg : #eee; --color-text: #444; } } /* Dark mode only */ @media screen and (prefers-color-scheme: dark) { :root { --color-bg : #222; --color-text: #ddd; } } ``` 3. Custom fonts can be used by adding web font resources and modifying `--font-family` properties as needed: ```css /* Fonts: Noto Sans, Noto Emoji, Noto Mono */ @import url('https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&family=Noto+Sans+Mono:wght@100..900&family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap'); :root { --font-family : 'Noto Sans', sans-serif; --font-family-emoji: 'Noto Color Emoji', sans-serif; --font-family-mono : 'Noto Sans Mono', monospace; } ``` > [!TIP] **Theme authors**: Consider providing instructions for loading your recommended web fonts manually instead of including them in your theme using `@import`. This allows users who prefer a different font to avoid loading your recommended web font(s) unnecessarily. 4. Advanced styling may require custom CSS declarations. This is expected, however custom CSS declarations may break when new versions of Docsify are released. When possible, leverage [theme properties](#theme-properties) instead of custom declarations or lock your [CDN](cdn) URLs to a [specific version](cdn#specific-version) to avoid potential issues when using custom CSS declarations. ```css .sidebar li.active > a { border-right: 3px solid var(--theme-color); } ``` ## Theme properties The following properties are available in all official Docsify themes. Default values for the "Core" theme are shown. > [!TIP] **Theme and plugin authors**: We encourage you to leverage these custom theme properties and to offer similar customization options in your projects. ### Common Below are the most commonly modified theme properties. [Advanced](#advanced) theme properties are also available for use but typically do not need to be modified. [\_vars.css](https://raw.githubusercontent.com/docsifyjs/docsify/refs/heads/develop/src/themes/shared/_vars.css ':include') ### Advanced Advanced theme properties are also available for use but typically do not need to be modified. Values derived from [common](#common) theme properties but can be set explicitly if preferred. [\_vars-advanced.css](https://raw.githubusercontent.com/docsifyjs/docsify/refs/heads/develop/src/themes/shared/_vars-advanced.css ':include') ## Community See [Awesome Docsify](awesome) for additional community themes. ================================================ FILE: docs/ui-kit.md ================================================ # UI Kit
View the markdown source for this page
[ui-kit.md](ui-kit.md ':include :type=code')
## Blockquotes > Cras aliquet nulla quis metus tincidunt, sed placerat enim cursus. Etiam > turpis nisl, posuere eu condimentum ut, interdum a risus. Sed non luctus mi. > Quisque malesuada risus sit amet tortor aliquet, a posuere ex iaculis. Vivamus > ultrices enim dui, eleifend porttitor elit aliquet sed. > > _- Quote Source_ #### Nested > Level 1 > > Level 2 > > > Level 3 ## Buttons #### Default #### Basic Link Button #### Primary Link Button #### Secondary Link Button ## Callouts > [!CAUTION] > **Caution** callout with `inline code`. > [!IMPORTANT] > **Important** callout with `inline code`. > [!NOTE] > **Note** callout with `inline code`. > [!TIP] > **Tip** callout with `inline code`. > [!WARNING] > **Warning** callout with `inline code`. **Multi Line** > [!NOTE] > - List item 1 > - List item 2 > > Text > > ```html >

Hello, World!

> ``` **Nested** > [!NOTE] > Level 1 > > [!NOTE] > > Level 2 > > > [!NOTE] > > > Level 3 **Legacy Style** !> Legacy **Important** callout with `inline code`. ?> Legacy **Tip** with `inline code`. ## Code This is `inline code` ```javascript const add = (num1, num2) => num1 + num2; const total = add(1, 2); console.log(total); // 3 ``` ```html

Hello

``` ## Colors #### Theme
1
2
3
4
Theme Color
5
6
7
8
#### Monochromatic
Min
1
2
3
4
5
6
7
8
9
Max
## Details
Details (click to open) Suscipit nemo aut ex suscipit voluptatem laboriosam odio velit. Ipsum eveniet labore sequi non optio vel. Ut culpa ad accusantium est aut harum ipsam voluptatum. Velit eum incidunt non sint. Et molestiae veniam natus autem vel assumenda ut numquam esse. Non nisi id qui vero corrupti quos et.
Details (open by default) Suscipit nemo aut ex suscipit voluptatem laboriosam odio velit. Ipsum eveniet labore sequi non optio vel. Ut culpa ad accusantium est aut harum ipsam voluptatum. Velit eum incidunt non sint. Et molestiae veniam natus autem vel assumenda ut numquam esse. Non nisi id qui vero corrupti quos et.
## Form Elements ### Fieldset
Legend

### Input #### Checkbox


#### Datalist
#### Radio


#### Text
#### Toggles
Checkbox (multi-select)

Radio (single-select)

### Select
### Textarea ## Headings # Heading 1 {docsify-ignore} Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse luctus nulla eu ex varius, a varius elit tincidunt. Aenean arcu magna, gravida id purus a, interdum convallis turpis. Aenean id ipsum eu tortor sollicitudin scelerisque in quis elit. ## Heading 2 {docsify-ignore} Vestibulum lobortis laoreet nunc vel vulputate. In et augue non lectus pellentesque molestie et ac justo. Sed sed turpis ut diam gravida sagittis nec at neque. Vivamus id tellus est. Nam ac dignissim mi. Vestibulum nec sem convallis, condimentum augue at, commodo diam. ### Heading 3 {docsify-ignore} Suspendisse sit amet tincidunt nibh, ac interdum velit. Ut orci diam, dignissim at enim sit amet, placerat rutrum magna. Mauris consectetur nibh eget sem feugiat, sit amet congue quam laoreet. Curabitur sed massa metus. #### Heading 4 {docsify-ignore} Donec odio orci, facilisis ac vehicula in, vestibulum ut urna. Ut bibendum ullamcorper risus, ac euismod leo maximus sed. In pulvinar sagittis rutrum. Morbi quis cursus diam. Cras ac laoreet nulla, rhoncus sodales dui. ##### Heading 5 {docsify-ignore} Commodo sit veniam nulla cillum labore ullamco aliquip quis. Consequat nulla fugiat consequat ex duis proident. Adipisicing excepteur tempor exercitation ad. Consectetur voluptate Lorem sint elit exercitation ullamco dolor. ###### Heading 6 {docsify-ignore} Ipsum ea amet dolore mollit incididunt fugiat nulla laboris est sint voluptate. Ex culpa id amet ipsum amet pariatur ipsum officia sit laborum irure ullamco deserunt. Consequat qui tempor occaecat nostrud proident. ## Horizontal Rule Text before rule. --- Text after rule. ## IFrame [Example](_media/example.html ':include height=200px') ## Images #### Inline-style ![Docsify Logo](/_media/icon.svg 'This is the Docsify logo!') #### Reference-style ![Docsify Logo][logo] [logo]: /_media/icon.svg 'This is the Docsify logo!' #### Light / Dark Theme BinaryTree ## Keyboard #### Default CtrlAltDel ⌃ Control⌥ Alt⌫ Delete #### Alternate CtrlAltDel ⌃ Control⌥ Alt⌫ Delete #### Entities
Arrow Up
Arrow Down
Arrow Left
Arrow Right
Caps Lock
Command
Control
Delete
Delete (Forward)
End
Enter
Escape
Home
Page Up
Page Down
Option, Alt
Return
Shift
Space
Tab
Tab + Shift
## Links [Inline link](https://google.com) [Inline link with title](https://google.com 'Google') [Reference link by name][link1] [Reference link by number][1] [Reference link by self] [link1]: https://google.com [1]: https://google.com [Reference link by self]: https://google.com ## Lists ### Ordered List 1. Ordered 1. Ordered 1. Nested 1. Nested (Wrapping): Similique tempora et. Voluptatem consequuntur ut. Rerum minus et sed beatae. Consequatur ut nemo laboriosam quo architecto quia qui. Corrupti aut omnis velit. 1. Ordered (Wrapping): Error minima modi rem sequi facere voluptatem. Est nihil veritatis doloribus et corporis ipsam. Pariatur eos ipsam qui odit labore est voluptatem enim. Veritatis est qui ut pariatur inventore. ### Unordered List - Unordered - Unordered - Nested - Nested (Wrapping): Quia consectetur sint vel ut excepturi ipsa voluptatum suscipit hic. Ipsa error qui molestiae harum laboriosam. Rerum non amet illo voluptatem odio pariatur. Ut minus enim. - Unordered (Wrapping): Fugiat qui tempore ratione amet repellendus repudiandae non. Rerum nisi officia enim. Itaque est alias voluptatibus id molestiae accusantium. Cupiditate sequi qui omnis sed facere aliquid quia ut. ### Task List - [x] Task - [ ] Task - [ ] Subtask - [ ] Subtask - [x] Subtask - [ ] Task (Wrapping): Earum consequuntur itaque numquam sunt error omnis ipsum repudiandae. Est assumenda neque eum quia quisquam laborum beatae autem ad. Fuga fugiat perspiciatis harum quia dignissimos molestiae. Officia quo eveniet tempore modi voluptates consequatur. Eum odio adipisci labore. - [x] Subtask (Wrapping): Vel possimus eaque laborum. Voluptates qui debitis quaerat atque molestiae quia explicabo doloremque. Reprehenderit perspiciatis a aut impedit temporibus aut quasi quia. Incidunt sed recusandae vitae asperiores sit in. ## Output

Et cum fugiat nesciunt voluptates. A atque quos doloribus dolorem quo. Et dignissimos omnis nam. Recusandae voluptatem nam. Tenetur veniam et qui consequatur. Aut sequi atque fuga itaque iusto eum nihil quod iure.

  1. Item
  2. Item
  3. Item
## Tables ### Alignment | Left Align | Center Align | Right Align | Non‑Breaking Header | | ---------- | :----------: | ----------: | ------------------------------ | | A1 | A2 | A3 | A4 | | B1 | B2 | B3 | B4 | | C1 | C2 | C3 | C4 | ### Headerless | | | | | | --- | --- | --- | --- | | A1 | A2 | A3 | A4 | | B1 | B2 | B3 | B4 | | C1 | C2 | C3 | C4 | ### Scrolling | Header | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Dicta in nobis dolor adipisci qui. Accusantium voluptates est dolor laboriosam qui voluptatibus. Veritatis eos aspernatur iusto et dicta quas. Fugit voluptatem dolorum qui quisquam. nihil | | Aut praesentium officia aut delectus. Quas atque reprehenderit saepe. Et voluptatibus qui dolores rem facere in dignissimos id aut. Debitis excepturi delectus et quos numquam magnam. | | Sed eum atque at laborum aut et repellendus ullam dolor. Cupiditate saepe voluptatibus odit est pariatur qui. Hic sunt nihil optio enim eum laudantium. Repellendus voluptate. | ## Text Elements Marked text
Preformatted text
Sample Output Small Text This is subscript This is superscript Underlined Text ## Text Styles Body text **Bold text** _Italic text_ **_Bold and italic text_** ~~Strikethrough~~ ================================================ FILE: docs/v5-upgrade.md ================================================ # Upgrading v4 to v5 The main changes when upgrading a Docsify v4 site to v5 involve updating CDN URLs and theme files. Your configuration settings remain mostly the same, so the upgrade is fairly straightforward. ## Before You Begin Some older Docsify sites may use non-version-locked URLs like: ```html ``` If your site uses URLs without `@4` or a specific version number, follow the same steps below. You'll need to update both the version specifier and the path structure. ## Step-by-Step Instructions ### 1. Update the Theme CSS **Replace the theme (v4):** ```html ``` **With this (v5):** ```html ``` **Note:** If you were using a different v4 theme (buble, dark, pure), the v5 core theme replaces these, though Vue and Dark themes are available as add-ons if preferred. View [Themes](themes.md) for more details. ### 2. Add Optional Body Class (for styling) **Update your opening body tag:** ```html ``` This adds a chevron indicator to the sidebar. You can omit this if you prefer. View [Theme Classes](themes.md?id=classes) for more details. ### 3. Update the Main Docsify Script **Change:** ```html ``` **To:** ```html ``` ### 4. Update Plugin URLs **Search Plugin:** ```html ``` **Zoom Plugin:** ```html ``` **Note:** If you're using additional Docsify plugins (such as emoji, external-script, front-matter, etc.), you'll need to update those URLs as well following the same pattern: - Change `/lib/plugins/` to `/dist/plugins/` - Update version from `@4` (or non-versioned) to `@5` - Example: `//cdn.jsdelivr.net/npm/docsify/lib/plugins/emoji.min.js` becomes `//cdn.jsdelivr.net/npm/docsify@5/dist/plugins/emoji.min.js` ## Key Differences Summary - **CDN Path**: Changed from `/lib/` to `/dist/` - **Version**: Updated from `@4` to `@5` - **Themes**: v5 uses a core theme (with optional add-ons available) - **Plugin Names**: `zoom-image` → `zoom` ## Additional Notes - Your configuration in `window.$docsify` stays the same - All your markdown content remains unchanged - The upgrade is non-breaking for most sites (however, legacy browsers like Internet Explorer 11 are no longer supported) - To maintain the same visual styling as Docsify v4, the [Vue theme (Add-on)](themes.md?id=vue-theme-add-on) is available - Custom CSS targeting v4 theme-specific classes or elements may need to be updated for v5 - The v5 core theme can be customized using CSS variables - view [Theme Customization](themes.md?id=customization) for more details That's it! Your Docsify site should now be running on v5. ================================================ FILE: docs/vue.md ================================================ # Vue compatibility Docsify allows [Vue.js](https://vuejs.org) content to be added directly to your markdown pages. This can greatly simplify working with data and adding reactivity to your site. Vue [template syntax](https://vuejs.org/guide/essentials/template-syntax) can be used to add dynamic content to your pages. Vue content becomes more interesting when [data](#data), [computed properties](#computed-properties), [methods](#methods), and [lifecycle hooks](#lifecycle-hooks) are used. These options can be specified as [global options](#global-options) or within DOM [mounts](#mounts) and [components](#components). ## Setup To get started, add Vue.js to your `index.html` file. Choose the production version for your live site or the development version for helpful console warnings and [Vue.js devtools](https://github.com/vuejs/vue-devtools) support. ```html ``` ## Template syntax Vue [template syntax](https://vuejs.org/guide/essentials/template-syntax) offers several useful features like support for [JavaScript expressions](https://vuejs.org/guide/essentials/template-syntax.html#using-javascript-expressions) and Vue [directives](https://vuejs.org/guide/essentials/template-syntax.html#directives) for loops and conditional rendering. ```markdown

Text for GitHub

  • Item {{ i }}

2 + 2 = {{ 2 + 2 }}

```

Text for GitHub

  • Item {{ i }}

2 + 2 = {{ 2 + 2 }}

[View output on GitHub](https://github.com/docsifyjs/docsify/blob/develop/docs/vue.md#template-syntax) ## Code Blocks Docsify ignores Vue template syntax within code blocks by default: ````markdown ``` {{ message }} ``` ```` To process Vue template syntax within a code block, wrap the code block in an element with a `v-template` attribute: ````markdown
``` {{ message }} ```
```` ## Data ```js { data() { return { message: 'Hello, World!' }; } } ``` ```markdown {{ message }}

```

{{ message }}

[View output on GitHub](https://github.com/docsifyjs/docsify/blob/develop/docs/vue.md#data) ## Computed properties ```js { computed: { timeOfDay() { const date = new Date(); const hours = date.getHours(); if (hours < 12) { return 'morning'; } else if (hours < 18) { return 'afternoon'; } else { return 'evening' } } }, } ``` ```markdown Good {{ timeOfDay }}! ``` Good {{ timeOfDay }}! ## Methods ```js { data() { return { message: 'Hello, World!' }; }, methods: { hello() { alert(this.message); } }, } ``` ```markdown ```

## Lifecycle Hooks ```js { data() { return { images: null, }; }, created() { fetch('https://api.domain.com/') .then(response => response.json()) .then(data => (this.images = data)) .catch(err => console.log(err)); } } // API response: // [ // { title: 'Image 1', url: 'https://domain.com/1.jpg' }, // { title: 'Image 2', url: 'https://domain.com/2.jpg' }, // { title: 'Image 3', url: 'https://domain.com/3.jpg' }, // ]; ``` ```markdown
{{ image.title }}
```
{{ image.title }}
## Global options Use `vueGlobalOptions` to specify global Vue options for use with Vue content not explicitly mounted with [vueMounts](#mounts), [vueComponents](#components), or a [markdown script](#markdown-script). Changes to global `data` will persist and be reflected anywhere global references are used. ```js window.$docsify = { vueGlobalOptions: { data() { return { count: 0, }; }, }, }; ``` ```markdown

{{ count }}

```

{{ count }}

Notice the behavior when multiple global counters are rendered:

{{ count }}

Changes made to one counter affect the both counters. This is because both instances reference the same global `count` value. Now, navigate to a new page and return to this section to see how changes made to global data persist between page loads. ## Mounts Use `vueMounts` to specify DOM elements to mount as Vue instances and their associated options. Mount elements are specified using a [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) as the key with an object containing Vue options as their value. Docsify will mount the first matching element in the main content area each time a new page is loaded. Mount element `data` is unique for each instance and will not persist as users navigate the site. ```js window.$docsify = { vueMounts: { '#counter': { data() { return { count: 0, }; }, }, }, }; ``` ```markdown
{{ count }}
``` {{ count }} ## Components Use `vueComponents` to create and register global [Vue components](https://vuejs.org/guide/essentials/component-basics.html). Components are specified using the component name as the key with an object containing Vue options as the value. Component `data` is unique for each instance and will not persist as users navigate the site. ```js window.$docsify = { vueComponents: { 'button-counter': { template: ` `, data() { return { count: 0, }; }, }, }, }; ``` ```markdown ``` ## Markdown script Vue content can mounted using a ` ``` ## Technical Notes - Docsify processes Vue content in the following order on each page load: 1. Execute markdown ` ``` ## Template Below is a plugin template with placeholders for all available lifecycle hooks. 1. Copy the template 1. Modify the `myPlugin` name as appropriate 1. Add your plugin logic 1. Remove unused lifecycle hooks 1. Save the file as `docsify-plugin-[name].js` 1. Load your plugin using a standard `
================================================ FILE: test/consume-types/package.json ================================================ { "scripts": { "typecheck": "tsc --noEmit", "serve": "five-server . --open=false --ignorePattern=node_modules" }, "dependencies": { "docsify": "file:../../" }, "devDependencies": { "five-server": "^0.4.5", "typescript": "^5.9.3" } } ================================================ FILE: test/consume-types/prism.js ================================================ // Small ESM wrapper, used in the importmap for 'prismjs', to import PrismJS by // tricking its CommonJS format to see module.exports. const __prismCjs = await fetch('/node_modules/prismjs/prism.js').then(res => res.text(), ); // Emulate CommonJS environment const module = { exports: {} }; // eslint-disable-next-line no-unused-vars const exports = module.exports; // Evaluate the CommonJS code with module.exports in scope eval(__prismCjs); // Export the Prism object const _Prism = module.exports; export default _Prism; // Also make Prism global because Docsify expects it by importing from // 'prismjs/components/prism-markup-templating.js' in the compiler. // @ts-expect-error FIXME get rid of this ugly global dependency hack in Docsify. window.Prism = _Prism; // @ts-expect-error // eslint-disable-next-line no-undef console.log('Prism loaded:', Prism); ================================================ FILE: test/consume-types/register-sw.js ================================================ // This whole thing is ugly. It is only so that we can fix improper import // statements in libraries from node_modules. See sw.js for how we re-map the // import URLs. // The convoluted code here is so that we will force the app to use a new // service worker if in dev mode we update the service worker code in sw.js let reloadQueued = false; function queueReload() { if (reloadQueued) { return; } reloadQueued = true; // Use location.reload() after a so any late controllerchange still settles. window.location.reload(); } // Register first. const registration = await navigator.serviceWorker.register('/sw.js', { scope: '/', type: 'module', updateViaCache: 'none', }); // If there is already a waiting worker, wait for it to claim this client. if (registration.waiting) { const sw = registration.waiting; await new Promise(resolve => { sw.addEventListener('statechange', () => { // Wait until activated. if (sw.state === 'activated') { if (sw === navigator.serviceWorker.controller) { resolve(void 0); } else { // If the new SW activated but is not controlling yet, it changed? Not sure if this can actuall happen. queueReload(); } } }); }); } if (navigator.serviceWorker.controller) { navigator.serviceWorker.addEventListener('controllerchange', () => { queueReload(); }); } else { // First-ever load: wait for controllerchange await new Promise(resolve => { navigator.serviceWorker.addEventListener('controllerchange', resolve, { once: true, }); }); } // Track new installs. registration.addEventListener('updatefound', () => { const sw = registration.installing; if (!sw) { return; } sw.addEventListener('statechange', () => { // When installed AND there is already a controller, a reload will let new SW control. if (sw.state === 'installed' && navigator.serviceWorker.controller) { // If skipWaiting ran inside new SW, controllerchange may fire soon; still queue. queueReload(); } }); }); // Force update check after listeners. // For purposes of testing the example, force update on page load so we // always test the latest service worker. await registration.update(); // Wait until there is an active worker (first load). await navigator.serviceWorker.ready; if (reloadQueued) { await new Promise(() => {}); } console.log( 'Service worker ready and controlling. Continue app bootstrap here.', ); export {}; ================================================ FILE: test/consume-types/sw.js ================================================ // The purpose of this service worker is to help with loading // node_modules modules when using ES modules in the browser. // // Specifically, this service worker helps with non-standard module paths // that do not include file extensions, such as: // // /node_modules/some-lib/foo/bar // /node_modules/some-lib/foo/bar/ // // In these cases, the service worker will try to resolve them to actual files // by appending ".js" or "/index.js" as needed. // // This service worker only handles requests under /node_modules/. // All other requests are passed through unmodified. export {}; const scope = /** @type {ServiceWorkerGlobalScope} */ ( /** @type {any} */ (self) ); scope.addEventListener('install', event => { // Always activate worker immediately, for purposes of testing. event.waitUntil(scope.skipWaiting()); }); scope.addEventListener('activate', event => { // Always activate worker immediately, for purposes of testing. event.waitUntil(scope.clients.claim()); }); scope.addEventListener('fetch', event => { const url = new URL(event.request.url); // Don't handle non-node_modules paths if (!url.pathname.startsWith('/node_modules/')) { event.respondWith(fetch(url.href)); return; } // 6 // Special handling for non-standard module paths in node_modules const parts = url.pathname.split('/'); const fileName = /** @type {string} */ (parts.pop()); const ext = fileName.includes('.') ? fileName.split('.').pop() : ''; // Handle imports like 'some-lib/foo/bar' without an extension. if (fileName !== '' && ext === '') { event.respondWith( // eslint-disable-next-line no-async-promise-executor new Promise(async resolve => { try { // First try adding .js const response = await tryJs(); const mimeType = response.headers.get('Content-Type') || ''; if (response.ok && mimeType.includes('javascript')) { resolve(response); } else { throw new Error('Not JS'); } } catch { // If that fails, try adding /index.js resolve(await tryIndexJs()); } async function tryJs() { const tryJs = new URL(url); tryJs.href += '.js'; const response = await fetch(tryJs); return response; } async function tryIndexJs() { const tryIndexJs = new URL(url); tryIndexJs.href += '/index.js'; const response = await fetch(tryIndexJs); return response; } }), ); return; } // Handle imports like 'some-lib/foo/bar/' (ending with a slash). if (fileName === '') { // adding index.js const tryIndexJs = new URL(url); tryIndexJs.href += 'index.js'; event.respondWith(fetch(tryIndexJs)); return; } // For all other cases, just fetch normally. event.respondWith(fetch(url)); }); ================================================ FILE: test/consume-types/tsconfig.json ================================================ { "compilerOptions": { "allowJs": true, "checkJs": true, "module": "esnext", "moduleResolution": "node", "target": "esnext", "strict": true, "noEmit": true, "lib": ["DOM", "ESNext", "WebWorker"], "skipLibCheck": true, "skipDefaultLibCheck": true } } ================================================ FILE: test/e2e/configuration.test.js ================================================ /* global fail */ import docsifyInit from '../helpers/docsify-init.js'; import { test, expect } from './fixtures/docsify-init-fixture.js'; test.describe('Configuration options', () => { test.describe('catchPluginErrors', () => { test('true (handles uncaught errors)', async ({ page }) => { let consoleMsg, errorMsg; page.on('console', msg => (consoleMsg = msg.text())); page.on('pageerror', err => (errorMsg = err.message)); await docsifyInit({ config: { catchPluginErrors: true, plugins: [ function (hook, vm) { hook.init(function () { fail(); }); hook.beforeEach(markdown => { return `${markdown}\n\nbeforeEach`; }); }, ], }, markdown: { homepage: '# Hello World', }, // _logHTML: true, }); const mainElm = page.locator('#main'); expect(errorMsg).toBeUndefined(); expect(consoleMsg).toContain('Docsify plugin error'); await expect(mainElm).toContainText('Hello World'); await expect(mainElm).toContainText('beforeEach'); }); test('false (throws uncaught errors)', async ({ page }) => { let consoleMsg, errorMsg; page.on('console', msg => { const text = msg.text(); if (text.startsWith('DEPRECATION:')) { return; } // ignore expected deprecation warnings consoleMsg = text; }); page.on('pageerror', err => (errorMsg = err.message)); await docsifyInit({ config: { catchPluginErrors: false, plugins: [ function (hook, vm) { hook.ready(function () { fail(); }); }, ], }, markdown: { homepage: '# Hello World', }, // _logHTML: true, }); expect(consoleMsg).toBeUndefined(); expect(errorMsg).toContain('fail'); }); }); test.describe('notFoundPage', () => { test.describe('renders default error content', () => { test.beforeEach(async ({ page }) => { await page.route('README.md', async route => { await route.fulfill({ status: 500, }); }); }); test('false', async ({ page }) => { await docsifyInit({ config: { notFoundPage: false, }, }); await expect(page.locator('#main')).toContainText('500'); }); test('true with non-404 error', async ({ page }) => { await docsifyInit({ config: { notFoundPage: true, }, routes: { '_404.md': '', }, }); await expect(page.locator('#main')).toContainText('500'); }); test('string with non-404 error', async ({ page }) => { await docsifyInit({ config: { notFoundPage: '404.md', }, routes: { '404.md': '', }, }); await expect(page.locator('#main')).toContainText('500'); }); }); test('true: renders _404.md page', async ({ page }) => { const expectText = 'Pass'; await docsifyInit({ config: { notFoundPage: true, }, routes: { '_404.md': expectText, }, }); await page.evaluate(() => (window.location.hash = '#/fail')); await expect(page.locator('#main')).toContainText(expectText); }); test('string: renders specified 404 page', async ({ page }) => { const expectText = 'Pass'; await docsifyInit({ config: { notFoundPage: '404.md', }, routes: { '404.md': expectText, }, }); await page.evaluate(() => (window.location.hash = '#/fail')); await expect(page.locator('#main')).toContainText(expectText); }); }); }); test.describe('keyBindings', () => { test('handles toggleSidebar binding (default)', async ({ page }) => { const docsifyInitConfig = { markdown: { homepage: ` # Heading 1 `, }, }; await docsifyInit(docsifyInitConfig); const sidebarElm = page.locator('.sidebar'); await expect(sidebarElm).toHaveClass(/show/); await page.keyboard.press('\\'); await expect(sidebarElm).not.toHaveClass(/show/); }); test('handles custom binding', async ({ page }) => { const docsifyInitConfig = { config: { keyBindings: { customBinding: { bindings: 'z', callback(e) { const elm = document.querySelector('main input[type="text"]'); elm.value = 'foo'; }, }, }, }, markdown: { homepage: ` `, }, }; const inputElm = page.locator('main input[type="text"]'); await docsifyInit(docsifyInitConfig); await expect(inputElm).toHaveValue(''); await page.keyboard.press('z'); await expect(inputElm).toHaveValue('foo'); }); test('ignores event when focused on text input elements', async ({ page, }) => { const docsifyInitConfig = { config: { keyBindings: { customBinding: { bindings: 'z', callback(e) { document.body.setAttribute('data-foo', ''); }, }, }, }, markdown: { homepage: ` `, }, }; const bodyElm = page.locator('body'); const inputElm = page.locator('input[type="text"]'); const selectElm = page.locator('select'); const textareaElm = page.locator('textarea'); await docsifyInit(docsifyInitConfig); await inputElm.focus(); await expect(inputElm).toHaveValue(''); await page.keyboard.press('z'); await expect(inputElm).toHaveValue('z'); await inputElm.blur(); await textareaElm.focus(); await expect(textareaElm).toHaveValue(''); await page.keyboard.press('z'); await expect(textareaElm).toHaveValue('z'); await textareaElm.blur(); await selectElm.focus(); await page.keyboard.press('z'); await expect(selectElm).toHaveValue('z'); await selectElm.blur(); await expect(bodyElm).not.toHaveAttribute('data-foo'); await page.keyboard.press('z'); await expect(bodyElm).toHaveAttribute('data-foo'); }); }); ================================================ FILE: test/e2e/example.test.js ================================================ import docsifyInit from '../helpers/docsify-init.js'; import { test, expect } from './fixtures/docsify-init-fixture.js'; test.describe('Creating a Docsify site (e2e tests in Playwright)', () => { test('manual docsify site using playwright methods', async ({ page }) => { // Add docsify target element await page.setContent('
'); // Set docsify configuration await page.evaluate(() => { window.$docsify = { el: '#app', themeColor: 'red', }; }); // Inject docsify theme await page.addStyleTag({ url: '/dist/themes/core.css' }); // Inject docsify.js await page.addScriptTag({ url: '/dist/docsify.js' }); // Wait for docsify to initialize await page.locator('#main').waitFor(); // Create handle for JavaScript object in browser const $docsify = await page.evaluate(() => window.$docsify); // const $docsify = await page.evaluateHandle(() => window.$docsify); // Test object property and value expect($docsify).toHaveProperty('themeColor', 'red'); }); test('Docsify /docs/ site using docsifyInit()', async ({ page }) => { // Load custom docsify // (See ./helpers/docsifyInit.js for details) await docsifyInit({ // _logHTML: true, }); // Verify docsifyInitConfig.markdown content was rendered const mainElm = page.locator('#main'); await expect(mainElm).toHaveCount(1); await expect(mainElm).toContainText( 'A magical documentation site generator', ); }); test('custom docsify site using docsifyInit()', async ({ page }) => { const docsifyInitConfig = { config: { name: 'Docsify Name', themeColor: 'red', }, markdown: { coverpage: ` # Docsify Test > Testing a magical documentation site generator [GitHub](https://github.com/docsifyjs/docsify/) `, homepage: ` # Hello World This is the homepage. `, navbar: ` - [docsify.js.org](https://docsify.js.org/#/) `, sidebar: ` - [Test Page](test) `, }, routes: { 'test.md': ` # Test Page This is a custom route. `, 'data-test-scripturls.js': ` document.body.setAttribute('data-test-scripturls', 'pass'); `, }, script: ` document.body.setAttribute('data-test-script', 'pass'); `, scriptURLs: [ // docsifyInit() route 'data-test-scripturls.js', // Server route '/dist/plugins/search.js', ], style: ` body { background: red !important; } `, styleURLs: ['/dist/themes/core.css'], }; await docsifyInit({ ...docsifyInitConfig, // _logHTML: true, }); const $docsify = await page.evaluate(() => window.$docsify); // Verify config options expect(typeof $docsify).toEqual('object'); expect($docsify).toHaveProperty('themeColor', 'red'); await expect(page.locator('.app-name')).toHaveText('Docsify Name'); // Verify docsifyInitConfig.markdown content was rendered await expect(page.locator('section.cover h1')).toHaveText('Docsify Test'); // Coverpage await expect(page.locator('nav.app-nav')).toHaveText('docsify.js.org'); // Navbar await expect(page.locator('aside.sidebar')).toContainText('Test Page'); // Sidebar await expect(page.locator('#main')).toContainText('This is the homepage'); // Homepage // Verify docsifyInitConfig.scriptURLs were added to the DOM for (const scriptURL of docsifyInitConfig.scriptURLs) { await expect(page.locator(`script[src$="${scriptURL}"]`)).toHaveCount(1); } // Verify docsifyInitConfig.scriptURLs were executed await expect(page.locator('body[data-test-scripturls]')).toHaveCount(1); await expect(page.locator('.search input[type="search"]')).toHaveCount(1); // Verify docsifyInitConfig.script was added to the DOM expect( await page.evaluate( scriptText => { return [...document.querySelectorAll('script')].some( elm => elm.textContent.replace(/\s+/g, '') === scriptText, ); }, docsifyInitConfig.script.replace(/\s+/g, ''), ), ).toBe(true); // Verify docsifyInitConfig.script was executed await expect(page.locator('body[data-test-script]')).toHaveCount(1); // Verify docsifyInitConfig.styleURLs were added to the DOM for (const styleURL of docsifyInitConfig.styleURLs) { await expect( page.locator(`link[rel*="stylesheet"][href$="${styleURL}"]`), ).toHaveCount(1); } // Verify docsifyInitConfig.style was added to the DOM expect( await page.evaluate( styleText => { return [...document.querySelectorAll('style')].some( elm => elm.textContent.replace(/\s+/g, '') === styleText, ); }, docsifyInitConfig.style.replace(/\s+/g, ''), ), ).toBe(true); // Verify docsify navigation and docsifyInitConfig.routes await page.click('a[href="#/test"]'); expect(page.url()).toMatch(/\/test$/); await expect(page.locator('#main')).toContainText('This is a custom route'); }); // test.fixme('image snapshots', async ({ page }) => { // await docsifyInit({ // config: { // name: 'Docsify Test', // }, // markdown: { // homepage: ` // # The Cosmos Awaits // [Carl Sagan](https://en.wikipedia.org/wiki/Carl_Sagan) // Cosmic ocean take root and flourish decipherment hundreds of thousands // dream of the mind's eye courage of our questions. At the edge of forever // network of wormholes ship of the imagination two ghostly white figures // in coveralls and helmets are softly dancing are creatures of the cosmos // the only home we've ever known? How far away emerged into consciousness // bits of moving fluff gathered by gravity with pretty stories for which // there's little good evidence vanquish the impossible. // The ash of stellar alchemy permanence of the stars shores of the cosmic // ocean billions upon billions Drake Equation finite but unbounded. // Hundreds of thousands cosmic ocean hearts of the stars Hypatia invent // the universe hearts of the stars? Realm of the galaxies muse about dream // of the mind's eye hundreds of thousands the only home we've ever known // how far away. Extraordinary claims require extraordinary evidence // citizens of distant epochs invent the universe as a patch of light the // carbon in our apple pies gathered by gravity. // Billions upon billions gathered by gravity white dwarf intelligent // beings vanquish the impossible descended from astronomers. A still more // glorious dawn awaits cosmic ocean star stuff harvesting star light the // sky calls to us kindling the energy hidden in matter rich in heavy // atoms. A mote of dust suspended in a sunbeam across the centuries the // only home we've ever known bits of moving fluff a very small stage in a // vast cosmic arena courage of our questions. // Euclid the only home we've ever known realm of the galaxies trillion // radio telescope Apollonius of Perga. The carbon in our apple pies invent // the universe muse about stirred by starlight great turbulent clouds // emerged into consciousness? Invent the universe vastness is bearable // only through love a still more glorious dawn awaits descended from // astronomers as a patch of light the sky calls to us. Great turbulent // clouds citizens of distant epochs invent the universe two ghostly white // figures in coveralls and helmets are softly dancing courage of our // questions rich in heavy atoms and billions upon billions upon billions // upon billions upon billions upon billions upon billions. // `, // }, // styleURLs: [`/dist/themes/core.css`], // // _logHTML: true, // }); // // Viewport screenshot // const viewportShot = await page.screenshot(); // expect(viewportShot).toMatchSnapshot('viewport.png'); // // Element screenshot // const elmHandle = await page.locator('h1').first(); // const elmShot = await elmHandle.screenshot(); // expect(elmShot).toMatchSnapshot('element.png'); // }); }); ================================================ FILE: test/e2e/fixtures/docsify-init-fixture.js ================================================ import { test as _test, expect as _expect } from '@playwright/test'; export const test = _test.extend({ page: async ({ page }, use) => { global.page = page; // Navigate to a real URL by default // Playwright tests are executed on "about:blank" by default, which will // cause operations that require the window location to be a valid URL to // fail (e.g. AJAX requests). Navigating to a blank document with a real // URL solved this problem. await page.goto('/_blank.html'); await use(page); }, }); export const expect = _expect; ================================================ FILE: test/e2e/gtag.test.js ================================================ import docsifyInit from '../helpers/docsify-init.js'; import { test, expect } from './fixtures/docsify-init-fixture.js'; const gtagList = [ 'AW-YYYYYY', // Google Ads 'DC-ZZZZZZ', // Floodlight 'G-XXXXXX', // Google Analytics 4 (GA4) 'UA-XXXXXX', // Google Universal Analytics (GA3) ]; test.describe('Gtag Plugin Tests', () => { // page request listened, print collect url function pageRequestListened(page) { page.on('request', request => { if (request.url().indexOf('www.google-analytics.com') !== -1) { // console.log(request.url()); } }); page.on('response', response => { const request = response.request(); // googleads.g.doubleclick.net // www.google-analytics.com // www.googletagmanager.com const reg = /googleads\.g\.doubleclick\.net|www\.google-analytics\.com|www\.googletagmanager\.com/g; if (request.url().match(reg)) { // console.log(request.url(), response.status()); } }); } test('single gtag', async ({ page }) => { pageRequestListened(page); const docsifyInitConfig = { config: { gtag: gtagList[0], }, scriptURLs: ['/dist/plugins/gtag.js'], styleURLs: ['/dist/themes/core.css'], }; await docsifyInit({ ...docsifyInitConfig, }); const $docsify = await page.evaluate(() => window.$docsify); // Verify config options expect(typeof $docsify).toEqual('object'); // console.log($docsify.gtag, $docsify.gtag === ''); // Tests expect($docsify.gtag).not.toEqual(''); }); test('multi gtag', async ({ page }) => { pageRequestListened(page); const docsifyInitConfig = { config: { gtag: gtagList, }, scriptURLs: ['/dist/plugins/gtag.js'], styleURLs: ['/dist/themes/core.css'], }; await docsifyInit({ ...docsifyInitConfig, }); const $docsify = await page.evaluate(() => window.$docsify); // Verify config options expect(typeof $docsify).toEqual('object'); // console.log($docsify.gtag, $docsify.gtag === ''); // Tests expect($docsify.gtag).not.toEqual(''); }); }); ================================================ FILE: test/e2e/index-file.test.js ================================================ import docsifyInit from '../helpers/docsify-init.js'; import { test, expect } from './fixtures/docsify-init-fixture.js'; test.describe('Index file hosting', () => { const sharedOptions = { config: { basePath: '/index.html#/', }, testURL: '/index.html#/', }; test('should serve from index file', async ({ page }) => { await docsifyInit(sharedOptions); await expect(page.locator('#main')).toContainText( 'A magical documentation site generator', ); expect(page.url()).toMatch(/index\.html#\/$/); }); test('should use index file links in sidebar from index file hosting', async ({ page, }) => { await docsifyInit(sharedOptions); await page.click('a[href="#/quickstart"]'); await expect(page.locator('#main')).toContainText('Quick start'); expect(page.url()).toMatch(/index\.html#\/quickstart$/); }); }); ================================================ FILE: test/e2e/plugins.test.js ================================================ import docsifyInit from '../helpers/docsify-init.js'; import { waitForFunction } from '../helpers/wait-for.js'; import { test, expect } from './fixtures/docsify-init-fixture.js'; test.describe('Plugins', () => { test('Hook order', async ({ page }) => { const consoleMsgs = []; const expectedMsgs = [ 'init', 'mounted', 'beforeEach-async', 'beforeEach', 'afterEach-async', 'afterEach', 'doneEach', 'ready', ]; page.on('console', msg => { const text = msg.text(); if (text.startsWith('DEPRECATION:')) { return; } // ignore expected deprecation warnings consoleMsgs.push(text); }); await docsifyInit({ config: { plugins: [ function (hook, vm) { hook.init(() => { console.log('init'); }); hook.mounted(() => { console.log('mounted'); }); hook.beforeEach((markdown, next) => { setTimeout(() => { console.log('beforeEach-async'); next(markdown); }, 100); }); hook.beforeEach(markdown => { console.log('beforeEach'); return markdown; }); hook.afterEach((html, next) => { setTimeout(() => { console.log('afterEach-async'); next(html); }, 100); }); hook.afterEach(html => { console.log('afterEach'); return html; }); hook.doneEach(() => { console.log('doneEach'); }); hook.ready(() => { console.log('ready'); }); }, ], }, markdown: { homepage: '# Hello World', }, // _logHTML: true, }); expect(consoleMsgs).toEqual(expectedMsgs); }); test.describe('beforeEach()', () => { test('return value', async ({ page }) => { await docsifyInit({ config: { plugins: [ function (hook, vm) { hook.beforeEach(markdown => { return 'beforeEach'; }); }, ], }, // _logHTML: true, }); await expect(page.locator('#main')).toContainText('beforeEach'); }); test('async return value', async ({ page }) => { await docsifyInit({ config: { plugins: [ function (hook, vm) { hook.beforeEach((markdown, next) => { setTimeout(() => { next('beforeEach'); }, 100); }); }, ], }, markdown: { homepage: '# Hello World', }, // _logHTML: true, }); await expect(page.locator('#main')).toContainText('beforeEach'); }); }); test.describe('afterEach()', () => { test('return value', async ({ page }) => { await docsifyInit({ config: { plugins: [ function (hook, vm) { hook.afterEach(html => { return '

afterEach

'; }); }, ], }, markdown: { homepage: '# Hello World', }, // _logHTML: true, }); await expect(page.locator('#main')).toContainText('afterEach'); }); test('async return value', async ({ page }) => { await docsifyInit({ config: { plugins: [ function (hook, vm) { hook.afterEach((html, next) => { setTimeout(() => { next('

afterEach

'); }, 100); }); }, ], }, markdown: { homepage: '# Hello World', }, // _logHTML: true, }); await expect(page.locator('#main')).toContainText('afterEach'); }); }); test.describe('route data accessible to plugins', () => { let routeData = null; test.beforeEach(async ({ page }) => { // Store route data set via plugin hook (below) page.on('console', async msg => { for (const arg of msg.args()) { const val = await arg.jsonValue(); if (typeof val === 'string' && val.startsWith('DEPRECATION:')) { continue; } try { const obj = typeof val === 'string' ? JSON.parse(val) : val; obj && obj.response && (routeData = obj); } catch { // ignore non-JSON console messages } } }); }); test.afterEach(async ({ page }) => { routeData = null; }); test('success (200)', async ({ page }) => { await docsifyInit({ config: { plugins: [ function (hook, vm) { hook.doneEach(html => { console.log(JSON.stringify(vm.route)); }); }, ], }, }); expect(routeData).toHaveProperty('response'); expect(routeData.response).toHaveProperty('ok', true); expect(routeData.response).toHaveProperty('status', 200); expect(routeData.response).toHaveProperty('statusText', 'OK'); }); test('fail (404)', async ({ page }) => { const link404Elm = page.locator('a[href*="404"]'); await docsifyInit({ markdown: { sidebar: ` - [404](404.md) `, }, config: { plugins: [ function (hook, vm) { hook.doneEach(html => { console.log(JSON.stringify(vm.route)); }); }, ], }, }); await link404Elm.click(); await waitForFunction(() => routeData?.response?.status === 404); expect(routeData).toHaveProperty('response'); expect(routeData.response).toHaveProperty('ok', false); expect(routeData.response).toHaveProperty('status', 404); expect(routeData.response).toHaveProperty('statusText', 'Not Found'); }); }); }); ================================================ FILE: test/e2e/search.test.js ================================================ import docsifyInit from '../helpers/docsify-init.js'; import { test, expect } from './fixtures/docsify-init-fixture.js'; test.describe('Search Plugin Tests', () => { test('search readme', async ({ page }) => { const docsifyInitConfig = { markdown: { homepage: ` # Hello World This is the homepage. `, sidebar: ` - [Test Page](test) `, }, routes: { '/test.md': ` # Test Page This is a custom route. `, }, scriptURLs: ['/dist/plugins/search.js'], }; const searchFieldElm = page.locator('input[type=search]'); const resultsHeadingElm = page.locator('.results-panel .title'); await docsifyInit(docsifyInitConfig); await searchFieldElm.fill('hello'); await expect(resultsHeadingElm).toHaveText('Hello World'); await page.click('.clear-button'); await searchFieldElm.fill('test'); await expect(resultsHeadingElm).toHaveText('Test Page'); }); test('search ignore title', async ({ page }) => { const docsifyInitConfig = { markdown: { homepage: ` # Hello World This is the homepage. `, sidebar: ` - [Home page](/) - [GitHub Pages](github) `, }, routes: { '/github.md': ` # GitHub Pages This is the GitHub Pages. ## GitHub Pages ignore1 There're three places to populate your docs for your GitHub repository1. ## GitHub Pages ignore2 {docsify-ignore} There're three places to populate your docs for your GitHub repository2. `, }, scriptURLs: ['/dist/plugins/search.js'], }; const searchFieldElm = page.locator('input[type=search]'); const resultsHeadingElm = page.locator('.results-panel .title'); await docsifyInit(docsifyInitConfig); await searchFieldElm.fill('repository1'); await expect(resultsHeadingElm).toHaveText('GitHub Pages ignore1'); await page.click('.clear-button'); await searchFieldElm.fill('repository2'); await expect(resultsHeadingElm).toHaveText('GitHub Pages ignore2'); }); test('search only one homepage', async ({ page }) => { const docsifyInitConfig = { markdown: { sidebar: ` - [README](README) - [Test Page](test) `, }, routes: { '/README.md': ` # Hello World This is the homepage. `, '/test.md': ` # Test Page This is a custom route. `, }, scriptURLs: ['/dist/plugins/search.js'], }; const searchFieldElm = page.locator('input[type=search]'); const resultsHeadingElm = page.locator('.results-panel .title'); const resultElm = page.locator('.matching-post'); await docsifyInit(docsifyInitConfig); await searchFieldElm.fill('hello'); await expect(resultElm).toHaveCount(1); await expect(resultsHeadingElm).toHaveText('Hello World'); await page.click('.clear-button'); await searchFieldElm.fill('test'); await expect(resultsHeadingElm).toHaveText('Test Page'); }); test('search ignore diacritical marks', async ({ page }) => { const docsifyInitConfig = { markdown: { homepage: ` # Qué es docsify genera su sitio web de documentación sobre la marcha. A diferencia de GitBook, no genera archivos estáticos html. En cambio, carga y analiza de forma inteligente sus archivos de Markdown y los muestra como sitio web. Todo lo que necesita hacer es crear un index.html para comenzar y desplegarlo en GitHub Pages. `, }, scriptURLs: ['/dist/plugins/search.js'], }; const searchFieldElm = page.locator('input[type=search]'); const resultsHeadingElm = page.locator('.results-panel .title'); await docsifyInit(docsifyInitConfig); await searchFieldElm.fill('documentacion'); await expect(resultsHeadingElm).toHaveText('Que es'); await page.click('.clear-button'); await searchFieldElm.fill('estáticos'); await expect(resultsHeadingElm).toHaveText('Que es'); }); test('search when there is no title', async ({ page }) => { const docsifyInitConfig = { markdown: { homepage: ` This is some description. We assume autoHeader added the # Title. A long paragraph. `, sidebar: ` - [Changelog](changelog) `, }, routes: { '/changelog.md': ` feat: Support search when there is no title ## Changelog Title hello, this is a changelog `, }, scriptURLs: ['/dist/plugins/search.js'], }; const searchFieldElm = page.locator('input[type=search]'); const resultsHeadingElm = page.locator('.results-panel .title'); await docsifyInit(docsifyInitConfig); await searchFieldElm.fill('paragraph'); await expect(resultsHeadingElm).toHaveText('Home Page'); await page.click('.clear-button'); await searchFieldElm.fill('Support'); await expect(resultsHeadingElm).toHaveText('changelog'); await page.click('.clear-button'); await searchFieldElm.fill('hello'); await expect(resultsHeadingElm).toHaveText('Changelog Title'); }); test('search when there is no body', async ({ page }) => { const docsifyInitConfig = { markdown: { homepage: ` # EmptyContent --- --- `, }, scriptURLs: ['/dist/plugins/search.js'], }; const searchFieldElm = page.locator('input[type=search]'); const resultsHeadingElm = page.locator('.results-panel .title'); await docsifyInit(docsifyInitConfig); await searchFieldElm.fill('empty'); await expect(resultsHeadingElm).toHaveText('EmptyContent'); }); test('handles default focusSearch binding', async ({ page }) => { const docsifyInitConfig = { scriptURLs: ['/dist/plugins/search.js'], }; const searchFieldElm = page.locator('input[type="search"]'); await docsifyInit(docsifyInitConfig); await expect(searchFieldElm).not.toBeFocused(); await page.keyboard.press('/'); await expect(searchFieldElm).toBeFocused(); }); test('handles custom focusSearch binding', async ({ page }) => { const docsifyInitConfig = { config: { search: { keyBindings: ['z'], }, }, scriptURLs: ['/dist/plugins/search.js'], }; const searchFieldElm = page.locator('input[type="search"]'); await docsifyInit(docsifyInitConfig); await expect(searchFieldElm).not.toBeFocused(); await page.keyboard.press('/'); await expect(searchFieldElm).not.toBeFocused(); await page.keyboard.press('z'); await expect(searchFieldElm).toBeFocused(); }); test('search result should remove markdown code block', async ({ page }) => { const docsifyInitConfig = { markdown: { homepage: ` # Hello World searchHere \`\`\`js console.log('Hello World'); \`\`\` `, }, scriptURLs: ['/dist/plugins/search.js'], }; const searchFieldElm = page.locator('input[type=search]'); const resultsHeadingElm = page.locator('.results-panel .content'); await docsifyInit(docsifyInitConfig); await searchFieldElm.fill('searchHere'); // there is a newline after searchHere and the markdown part ```js ``` it should be removed expect(await resultsHeadingElm.textContent()).toContain( "...searchHere\nconsole.log('Hello World');...", ); }); test('search result should remove file markdown and keep href attribution for files', async ({ page, }) => { const docsifyInitConfig = { markdown: { homepage: ` # Hello World ![filename](_media/example.js ':include :type=code :fragment=demo') `, }, scriptURLs: ['/dist/plugins/search.js'], }; const searchFieldElm = page.locator('input[type=search]'); const resultsHeadingElm = page.locator('.results-panel .content'); await docsifyInit(docsifyInitConfig); await searchFieldElm.fill('filename'); expect(await resultsHeadingElm.textContent()).toContain( '...filename _media/example.js :include :type=code :fragment=demo...', ); }); test('search result should remove checkbox markdown and keep related values', async ({ page, }) => { const docsifyInitConfig = { markdown: { homepage: ` # Hello World - [ ] Task 1 - [x] SearchHere - [ ] Task 3 `, }, scriptURLs: ['/dist/plugins/search.js'], }; const searchFieldElm = page.locator('input[type=search]'); const resultsHeadingElm = page.locator('.results-panel .content'); await docsifyInit(docsifyInitConfig); await searchFieldElm.fill('SearchHere'); // remove the checkbox markdown and keep the related values expect(await resultsHeadingElm.textContent()).toContain( '...Task 1 SearchHere Task 3...', ); }); test('search result should remove docsify self helper markdown and keep related values', async ({ page, }) => { const docsifyInitConfig = { markdown: { homepage: ` # Hello World !> SearchHere to check it! `, }, scriptURLs: ['/dist/plugins/search.js'], }; const searchFieldElm = page.locator('input[type=search]'); const resultsHeadingElm = page.locator('.results-panel .content'); await docsifyInit(docsifyInitConfig); await searchFieldElm.fill('SearchHere'); // remove the helper markdown and keep the related values expect(await resultsHeadingElm.textContent()).toContain( '...SearchHere to check it!...', ); }); }); ================================================ FILE: test/e2e/security.test.js ================================================ import docsifyInit from '../helpers/docsify-init.js'; import { test, expect } from './fixtures/docsify-init-fixture.js'; test.describe('Security - Cross Site Scripting (XSS)', () => { const sharedOptions = { markdown: { homepage: '# Hello World', }, routes: { 'test.md': '# Test Page', }, }; const slashStrings = ['//', '///']; for (const slashString of slashStrings) { const hash = `#${slashString}domain.com/file.md`; test(`should not load remote content from hash (${hash})`, async ({ page, }) => { const mainElm = page.locator('#main'); await docsifyInit(sharedOptions); await expect(mainElm).toContainText('Hello World'); await page.evaluate(() => (location.hash = '#/test')); await expect(mainElm).toContainText('Test Page'); await page.evaluate(newHash => { location.hash = newHash; }, hash); await expect(mainElm).toContainText('Hello World'); expect(page.url()).toMatch(/#\/$/); }); } }); ================================================ FILE: test/e2e/sidebar.test.js ================================================ import docsifyInit from '../helpers/docsify-init.js'; import { test, expect } from './fixtures/docsify-init-fixture.js'; // Suite // ----------------------------------------------------------------------------- test.describe('Sidebar Tests', () => { // Tests // --------------------------------------------------------------------------- test('Active Test', async ({ page }) => { const docsifyInitConfig = { markdown: { sidebar: ` - [Test Space](test%20space) - [Test _](test_foo) - [Test -](test-foo) - [Test .](test.foo) - [Test >](test>foo) - [Test](test) `, }, routes: { '/test space.md': ` # Test Space `, '/test_foo.md': ` # Test _ `, '/test-foo.md': ` # Test - `, '/test.foo.md': ` # Test . `, '/test>foo.md': ` # Test > `, '/test.md': ` # Test page `, }, }; const activeLinkElm = page.locator('.sidebar-nav li[class=active]'); await docsifyInit(docsifyInitConfig); await page.click('a[href="#/test"]'); await expect(activeLinkElm).toHaveText('Test'); expect(page.url()).toMatch(/\/test$/); await page.click('a[href="#/test%20space"]'); await expect(activeLinkElm).toHaveText('Test Space'); expect(page.url()).toMatch(/\/test%20space$/); await page.click('a[href="#/test_foo"]'); await expect(activeLinkElm).toHaveText('Test _'); expect(page.url()).toMatch(/\/test_foo$/); await page.click('a[href="#/test-foo"]'); await expect(activeLinkElm).toHaveText('Test -'); expect(page.url()).toMatch(/\/test-foo$/); await page.click('a[href="#/test.foo"]'); await expect(activeLinkElm).toHaveText('Test .'); expect(page.url()).toMatch(/\/test.foo$/); await page.click('a[href="#/test>foo"]'); await expect(activeLinkElm).toHaveText('Test >'); expect(page.url()).toMatch(/\/test%3Efoo$/); }); }); test.describe('Configuration: autoHeader', () => { test('autoHeader=false', async ({ page }) => { const docsifyInitConfig = { config: { loadSidebar: '_sidebar.md', autoHeader: false, }, markdown: { sidebar: ` - [QuickStartAutoHeader](quickstart.md) `, }, routes: { '/quickstart.md': ` the content of quickstart space ## In the main content there is no h1 `, }, }; await docsifyInit(docsifyInitConfig); await page.click('a[href="#/quickstart"]'); expect(page.url()).toMatch(/\/quickstart$/); // not heading await expect(page.locator('#quickstart')).toBeHidden(); }); test('autoHeader=true', async ({ page }) => { const docsifyInitConfig = { config: { loadSidebar: '_sidebar.md', autoHeader: true, }, markdown: { sidebar: ` - [QuickStartAutoHeader](quickstart.md ) `, }, routes: { '/quickstart.md': ` the content of quickstart space ## In the main content there is no h1 `, }, }; await docsifyInit(docsifyInitConfig); await page.click('a[href="#/quickstart"]'); expect(page.url()).toMatch(/\/quickstart$/); // auto generate default heading id const autoHeader = page.locator('#quickstartautoheader'); expect(await autoHeader.innerText()).toContain('QuickStartAutoHeader'); }); test('autoHeader=true and custom headingId', async ({ page }) => { const docsifyInitConfig = { config: { loadSidebar: '_sidebar.md', autoHeader: true, }, markdown: { sidebar: ` - [QuickStartAutoHeader](quickstart.md ":id=quickstartId") `, }, routes: { '/quickstart.md': ` the content of quickstart space ## In the main content there is no h1 `, }, }; await docsifyInit(docsifyInitConfig); await page.click('a[href="#/quickstart"]'); expect(page.url()).toMatch(/\/quickstart$/); // auto generate custom heading id const autoHeader = page.locator('#quickstartId'); expect(await autoHeader.innerText()).toContain('QuickStartAutoHeader'); }); }); ================================================ FILE: test/e2e/virtual-routes.test.js ================================================ import docsifyInit from '../helpers/docsify-init.js'; import { test, expect } from './fixtures/docsify-init-fixture.js'; /** * Navigate to a specific route in the site * @param {import('playwright-core').Page} page the playwright page instance from the test * @param {string} route the route you want to navigate to */ async function navigateToRoute(page, route) { await page.evaluate(r => (window.location.hash = r), route); await page.waitForLoadState('load'); } test.describe('Virtual Routes - Generate Dynamic Content via Config', () => { test.describe('Different Types of Virtual Routes', () => { test('rendering virtual routes specified as string', async ({ page }) => { const routes = { '/my-awesome-route': '# My Awesome Route', }; await docsifyInit({ config: { routes, }, }); await navigateToRoute(page, '/my-awesome-route'); const titleElm = page.locator('#main h1'); await expect(titleElm).toContainText('My Awesome Route'); }); test('rendering virtual routes specified as functions', async ({ page, }) => { const routes = { '/my-awesome-function-route': function () { return '# My Awesome Function Route'; }, }; await docsifyInit({ config: { routes, }, }); await navigateToRoute(page, '/my-awesome-function-route'); const titleElm = page.locator('#main h1'); await expect(titleElm).toContainText('My Awesome Function Route'); }); test('rendering virtual routes specified functions that use the "next" callback', async ({ page, }) => { const routes = { '/my-awesome-async-function-route': async function ( route, matched, next, ) { setTimeout(() => next('# My Awesome Function Route'), 100); }, }; await docsifyInit({ config: { routes, }, }); await navigateToRoute(page, '/my-awesome-async-function-route'); const titleElm = page.locator('#main h1'); await expect(titleElm).toContainText('My Awesome Function Route'); }); }); test.describe('Routes with Regex Matches', () => { test('rendering virtual routes with regex matches', async ({ page }) => { const routes = { '/items/(.*)': '# Item Page', }; await docsifyInit({ config: { routes, }, }); await navigateToRoute(page, '/items/banana'); const titleElm = page.locator('#main h1'); await expect(titleElm).toContainText('Item Page'); }); test('virtual route functions should get the route as first parameter', async ({ page, }) => { const routes = { '/pets/(.*)': route => '# Route: /pets/dog', }; await docsifyInit({ config: { routes, }, }); await navigateToRoute(page, '/pets/dog'); const titleElm = page.locator('#main h1'); await expect(titleElm).toContainText('Route: /pets/dog'); }); test('virtual route functions should get the matched array as second parameter', async ({ page, }) => { const routes = { '/pets/(.*)'(_, matched) { return `# Pets Page (${matched[1]})`; }, }; await docsifyInit({ config: { routes, }, }); await navigateToRoute(page, '/pets/cat'); const titleElm = page.locator('#main h1'); await expect(titleElm).toContainText('Pets Page (cat)'); }); }); test.describe('Route Matching Specifics', () => { test('routes should be exact match if no regex was passed', async ({ page, }) => { const routes = { '/my': '# Incorrect Route - only prefix', '/route': '# Incorrect Route - only postfix', '/my/route': '# Correct Route', }; await docsifyInit({ config: { routes, }, }); await navigateToRoute(page, '/my/route'); const titleElm = page.locator('#main h1'); await expect(titleElm).toContainText('Correct Route'); }); test('if there are two routes that match, the first one should be taken', async ({ page, }) => { const routes = { '/multiple/(.+)': '# First Match', '/multiple/(.*)': '# Second Match', }; await docsifyInit({ config: { routes, }, }); await navigateToRoute(page, '/multiple/matches'); const titleElm = page.locator('#main h1'); await expect(titleElm).toContainText('First Match'); }); test('prefer virtual route over a real file, if a virtual route exists', async ({ page, }) => { const routes = { '/': '# Virtual Homepage', }; await docsifyInit({ markdown: { homepage: '# Real File Homepage', }, config: { routes, }, }); const titleElm = page.locator('#main h1'); await expect(titleElm).toContainText('Virtual Homepage'); }); test('fallback to default routing if no route was matched', async ({ page, }) => { const routes = { '/a': '# A', '/b': '# B', '/c': '# C', }; await docsifyInit({ markdown: { homepage: '# Real File Homepage', }, config: { routes, }, }); await navigateToRoute(page, '/d'); const mainElm = page.locator('#main'); await expect(mainElm).toContainText('404 - Not Found'); }); test('skip routes that returned a falsy value that is not a boolean', async ({ page, }) => { const routes = { '/multiple/(.+)': () => null, '/multiple/(.*)': () => undefined, '/multiple/.+': () => 0, '/multiple/.*': () => '# Last Match', }; await docsifyInit({ config: { routes, }, }); await navigateToRoute(page, '/multiple/matches'); const titleElm = page.locator('#main h1'); await expect(titleElm).toContainText('Last Match'); }); test('abort virtual routes and not try the next one, if any matched route returned an explicit "false" boolean', async ({ page, }) => { const routes = { '/multiple/(.+)': () => false, '/multiple/(.*)': () => "# You Shouldn't See Me", }; await docsifyInit({ config: { routes, }, }); await navigateToRoute(page, '/multiple/matches'); const mainElm = page.locator('#main'); await expect(mainElm).toContainText('404 - Not Found'); }); test('skip routes that are not a valid string or function', async ({ page, }) => { const routes = { '/multiple/(.+)': 123, '/multiple/(.*)': false, '/multiple/.+': null, '/multiple/..+': [], '/multiple/..*': {}, '/multiple/.*': '# Last Match', }; await docsifyInit({ config: { routes, }, }); await navigateToRoute(page, '/multiple/matches'); const titleElm = page.locator('#main h1'); await expect(titleElm).toContainText('Last Match'); }); }); }); ================================================ FILE: test/e2e/vue.test.js ================================================ import { stripIndent } from 'common-tags'; import docsifyInit from '../helpers/docsify-init.js'; import { test, expect } from './fixtures/docsify-init-fixture.js'; const vueURL = '/node_modules/vue/dist/vue.global.js'; test.describe('Vue.js Compatibility', () => { function getSharedConfig() { const config = { config: { vueComponents: { 'button-counter': { template: ` `, data: function () { return { counter: 0, }; }, }, }, vueGlobalOptions: { data: () => ({ counter: 0, msg: 'vueglobaloptions', }), }, vueMounts: { '#vuemounts': { data() { return { counter: 0, msg: 'vuemounts', }; }, }, }, }, markdown: { homepage: stripIndent`
{{ i }}
---

---

{{ counter }}

---

{{ counter }}

---

{{ counter }}
`, }, }; return config; } // Tests // ---------------------------------------------------------------------------- test('Parse templates and render content when import Vue resources', async ({ page, }) => { const docsifyInitConfig = { config: {}, markdown: { homepage: stripIndent`
{{ i }}
`, }, }; docsifyInitConfig.scriptURLs = vueURL; await docsifyInit(docsifyInitConfig); await expect(page.locator('#vuefor')).toHaveText('12345'); }); for (const executeScript of [true, undefined]) { test(`renders content when executeScript is ${executeScript}`, async ({ page, }) => { const docsifyInitConfig = getSharedConfig(); docsifyInitConfig.config.executeScript = executeScript; docsifyInitConfig.scriptURLs = vueURL; await docsifyInit(docsifyInitConfig); // Static await expect(page.locator('#vuefor')).toHaveText('12345'); await expect(page.locator('#vuecomponent')).toHaveText('0'); await expect(page.locator('#vueglobaloptions p')).toHaveText( 'vueglobaloptions', ); await expect(page.locator('#vueglobaloptions > span')).toHaveText('0'); await expect(page.locator('#vuemounts p')).toHaveText('vuemounts'); await expect(page.locator('#vuemounts > span')).toHaveText('0'); await expect(page.locator('#vuescript p')).toHaveText('vuescript'); await expect(page.locator('#vuescript > span')).toHaveText('0'); // Reactive await page.click('#vuecomponent'); await expect(page.locator('#vuecomponent')).toHaveText('1'); await page.click('#vueglobaloptions button'); await expect(page.locator('#vueglobaloptions > span')).toHaveText('1'); await page.click('#vuemounts button'); await expect(page.locator('#vuemounts > span')).toHaveText('1'); await page.click('#vuescript button'); await expect(page.locator('#vuescript > span')).toHaveText('1'); }); } test('ignores content when Vue is not present', async ({ page }) => { const docsifyInitConfig = getSharedConfig(); await docsifyInit(docsifyInitConfig); await page.evaluate(() => 'Vue' in window === false); await expect(page.locator('#vuefor')).toHaveText('{{ i }}'); await expect(page.locator('#vuecomponent')).toHaveText('---'); await expect(page.locator('#vueglobaloptions p')).toHaveText('---'); await expect(page.locator('#vuemounts p')).toHaveText('---'); await expect(page.locator('#vuescript p')).toHaveText('---'); }); test('ignores content when vueGlobalOptions is undefined', async ({ page, }) => { const docsifyInitConfig = getSharedConfig(); docsifyInitConfig.config.vueGlobalOptions = undefined; docsifyInitConfig.scriptURLs = vueURL; await docsifyInit(docsifyInitConfig); await expect(page.locator('#vuefor')).toHaveText('12345'); await expect(page.locator('#vuecomponent')).toHaveText('0'); await expect(page.locator('#vuecomponent')).toHaveText('0'); // eslint-disable-next-line playwright/prefer-web-first-assertions expect(await page.locator('#vueglobaloptions p').innerText()).toBe(''); await expect(page.locator('#vuemounts p')).toHaveText('vuemounts'); await expect(page.locator('#vuescript p')).toHaveText('vuescript'); }); test('ignores content when vueMounts is undefined', async ({ page }) => { const docsifyInitConfig = getSharedConfig(); docsifyInitConfig.config.vueMounts['#vuemounts'] = undefined; docsifyInitConfig.scriptURLs = vueURL; await docsifyInit(docsifyInitConfig); await expect(page.locator('#vuefor')).toHaveText('12345'); await expect(page.locator('#vuecomponent')).toHaveText('0'); await expect(page.locator('#vueglobaloptions p')).toHaveText( 'vueglobaloptions', ); await expect(page.locator('#vuemounts p')).toHaveText('vueglobaloptions'); await expect(page.locator('#vuescript p')).toHaveText('vuescript'); }); test('ignores " `; exports[`Emoji Ignores emoji shorthand codes in comments 1`] = `"

Text

"`; exports[`Emoji Ignores emoji shorthand codes in html attributes 1`] = `"

"`; exports[`Emoji Ignores emoji shorthand codes in style url() values 1`] = `""`; exports[`Emoji Ignores unmatched emoji shorthand codes 1`] = `"

hh:mm

hh:mm:ss

Namespace::SubNameSpace

Namespace::SubNameSpace::Class

2014-12-29T16:11:20+00:00

"`; exports[`Emoji Renders GitHub emoji images (nativeEmoji:false) 1`] = `"

smile

smilesmile

smile smile

smilesmilesmile

smile smile smile

textsmile

smiletext

textsmiletext

"`; exports[`Emoji Renders native emoji characters (nativeEmoji:true) 1`] = `"

😄︎

😄︎😄︎

😄︎ 😄︎

😄︎😄︎😄︎

😄︎ 😄︎ 😄︎

text😄︎

😄︎text

text😄︎text

"`; ================================================ FILE: test/integration/docs.test.js ================================================ import { jest } from '@jest/globals'; import docsifyInit from '../helpers/docsify-init.js'; // Suite // ----------------------------------------------------------------------------- describe('Docs Site', function () { // Tests // --------------------------------------------------------------------------- test('coverpage renders and is unchanged', async () => { // Override Math.random implementation to prevent random gradient values // used as background image from causing test to fail const mathSpy = jest.spyOn(Math, 'random').mockReturnValue(0.5); await docsifyInit({ config: { coverpage: '_coverpage.md', }, markdown: { homepage: '# Hello World', }, waitForSelector: '.cover-main > *', }); const coverpageElm = document.querySelector('section.cover'); // Test snapshots expect(mathSpy).toHaveBeenCalled(); expect(coverpageElm).not.toBeNull(); expect(coverpageElm.outerHTML).toMatchSnapshot(); }); test('sidebar renders and is unchanged', async () => { await docsifyInit({ config: { loadSidebar: '_sidebar.md', }, markdown: { homepage: '# Hello World', }, waitForSelector: '.sidebar-nav > ul', }); const sidebarElm = document.querySelector('.sidebar'); // Test snapshots expect(sidebarElm).not.toBeNull(); expect(sidebarElm.outerHTML).toMatchSnapshot(); }); test('navbar renders and is unchanged', async () => { await docsifyInit({ config: { loadNavbar: '_navbar.md', }, markdown: { homepage: '# Hello World', }, waitForSelector: '.app-nav > ul', }); const navbarElm = document.querySelector('nav.app-nav'); // Test snapshots expect(navbarElm).not.toBeNull(); expect(navbarElm.outerHTML).toMatchSnapshot(); }); }); ================================================ FILE: test/integration/docsify.test.js ================================================ import { jest } from '@jest/globals'; import docsifyInit from '../helpers/docsify-init.js'; // Suite // ----------------------------------------------------------------------------- describe('Docsify', function () { // Tests // --------------------------------------------------------------------------- test('allows $docsify configuration to be a function', async () => { const testConfig = jest.fn(vm => { expect(vm).toBeInstanceOf(Object); expect(vm.constructor.name).toBe('Docsify'); expect(vm.$fetch).toBeInstanceOf(Function); expect(vm.route).toBeInstanceOf(Object); }); await docsifyInit({ config: testConfig, }); expect(typeof Docsify).toBe('object'); expect(testConfig).toHaveBeenCalled(); }); test('provides the hooks and vm API to plugins', async () => { const testConfig = jest.fn(vm => { const vm1 = vm; return { plugins: [ function (hook, vm2) { expect(vm1).toEqual(vm2); expect(hook.init).toBeInstanceOf(Function); expect(hook.beforeEach).toBeInstanceOf(Function); expect(hook.afterEach).toBeInstanceOf(Function); expect(hook.doneEach).toBeInstanceOf(Function); expect(hook.mounted).toBeInstanceOf(Function); expect(hook.ready).toBeInstanceOf(Function); }, ], }; }); await docsifyInit({ config: testConfig, }); expect(typeof Docsify).toBe('object'); expect(testConfig).toHaveBeenCalled(); }); }); ================================================ FILE: test/integration/emoji.test.js ================================================ import docsifyInit from '../helpers/docsify-init.js'; // Suite // ----------------------------------------------------------------------------- describe('Emoji', function () { // Tests // --------------------------------------------------------------------------- const emojiMarkdown = ` :smile: :smile::smile: :smile: :smile: :smile::smile::smile: :smile: :smile: :smile: text:smile: :smile:text text:smile:text `; test('Renders native emoji characters (nativeEmoji:true)', async () => { await docsifyInit({ config: { nativeEmoji: true, }, markdown: { homepage: emojiMarkdown, }, // _logHTML: true, }); const mainElm = document.querySelector('#main'); expect(mainElm.innerHTML).toMatchSnapshot(); }); test('Renders GitHub emoji images (nativeEmoji:false)', async () => { await docsifyInit({ config: { nativeEmoji: false, }, markdown: { homepage: emojiMarkdown, }, // _logHTML: true, }); const mainElm = document.querySelector('#main'); expect(mainElm.innerHTML).toMatchSnapshot(); }); test('Ignores all emoji shorthand codes (noEmoji:true)', async () => { await docsifyInit({ config: { noEmoji: true, }, markdown: { homepage: emojiMarkdown, }, // _logHTML: true, }); const mainElm = document.querySelector('#main'); expect(mainElm.innerHTML).toMatchSnapshot(); }); test('Ignores unmatched emoji shorthand codes', async () => { await docsifyInit({ markdown: { homepage: ` hh:mm hh:mm:ss Namespace::SubNameSpace Namespace::SubNameSpace::Class 2014-12-29T16:11:20+00:00 `, }, // _logHTML: true, }); const mainElm = document.querySelector('#main'); expect(mainElm.innerHTML).toMatchSnapshot(); }); test('Ignores emoji shorthand codes in comments', async () => { await docsifyInit({ markdown: { homepage: 'Text ', }, // _logHTML: true, }); const mainElm = document.querySelector('#main'); expect(mainElm.innerHTML).toMatchSnapshot(); }); test('Ignores emoji shorthand codes in URIs', async () => { await docsifyInit({ markdown: { homepage: 'Url https://docsify.js.org/:foo:/ http://docsify.js.org/:100:/ ftp://docsify.js.org/:smile:/', }, // _logHTML: true, }); const mainElm = document.querySelector('#main'); expect(mainElm.innerHTML).toMatchSnapshot(); }); test('Ignores emoji shorthand codes in URIs while handling anchor content', async () => { await docsifyInit({ markdown: { homepage: 'Achor tags [:100:](http://docsify.js.org/:100:/)', }, // _logHTML: true, }); const mainElm = document.querySelector('#main'); expect(mainElm.innerHTML).toMatchSnapshot(); }); test('Ignores emoji shorthand codes in html attributes', async () => { await docsifyInit({ markdown: { homepage: /* html */ ' ', }, // _logHTML: true, }); const mainElm = document.querySelector('#main'); expect(mainElm.innerHTML).toMatchSnapshot(); }); test('Ignores emoji shorthand codes in style url() values', async () => { await docsifyInit({ markdown: { homepage: /* html */ '', }, // _logHTML: true, }); const mainElm = document.querySelector('#main'); expect(mainElm.innerHTML).toMatchSnapshot(); }); test('Ignores emoji shorthand codes in code, pre, script, and template tags', async () => { await docsifyInit({ markdown: { homepage: /* html */ `
:100:
:100: `, }, // _logHTML: true, }); const mainElm = document.querySelector('#main'); expect(mainElm.innerHTML).toMatchSnapshot(); }); }); ================================================ FILE: test/integration/example.test.js ================================================ import { waitForFunction, waitForText } from '../helpers/wait-for.js'; import docsifyInit from '../helpers/docsify-init.js'; describe('Creating a Docsify site (integration tests in Jest)', function () { test('Docsify /docs/ site using docsifyInit()', async () => { await docsifyInit({ // _logHTML: true, }); // Verify config options expect(typeof window.$docsify).toBe('object'); // Verify options.markdown content was rendered expect(document.querySelector('#main').textContent).toContain( 'A magical documentation site generator', ); }); test('kitchen sink docsify site using docsifyInit()', async () => { const docsifyInitConfig = { config: { name: 'Docsify Name', }, markdown: { coverpage: ` # Docsify Test > Testing a magical documentation site generator [GitHub](https://github.com/docsifyjs/docsify/) `, homepage: ` # Hello World This is the homepage. `, navbar: ` - [docsify.js.org](https://docsify.js.org/#/) `, sidebar: ` - [Test Page](test) `, }, routes: { 'test.md': ` # Test Page This is a custom route. `, 'data-test-scripturls.js': ` document.body.setAttribute('data-test-scripturls', 'pass'); `, }, script: ` document.body.setAttribute('data-test-script', 'pass'); `, scriptURLs: [ // docsifyInit() route 'data-test-scripturls.js', ], style: ` body { background: red !important; } `, styleURLs: ['/dist/themes/core.css'], }; await docsifyInit({ ...docsifyInitConfig, // _logHTML: true, }); // Verify config options expect(typeof window.$docsify).toBe('object'); expect(document.querySelector('.app-name').textContent).toContain( 'Docsify Name', ); // Verify docsifyInitConfig.markdown content was rendered Object.entries({ 'section.cover': 'Docsify Test', // Coverpage 'nav.app-nav': 'docsify.js.org', // Navbar 'aside.sidebar': 'Test Page', // Sidebar '#main': 'This is the homepage', // Homepage }).forEach(([selector, content]) => { expect(document.querySelector(selector).textContent).toContain(content); }); // Verify docsifyInitConfig.scriptURLs were added to the DOM for (const scriptURL of docsifyInitConfig.scriptURLs) { const matchElm = document.querySelector( `script[data-src$="${scriptURL}"]`, ); expect(matchElm).toBeTruthy(); } // Verify docsifyInitConfig.scriptURLs were executed expect(document.body.hasAttribute('data-test-scripturls')).toBe(true); // Verify docsifyInitConfig.script was added to the DOM expect( [...document.querySelectorAll('script')].some( elm => elm.textContent.replace(/\s+/g, '') === docsifyInitConfig.script.replace(/\s+/g, ''), ), ).toBe(true); // Verify docsifyInitConfig.script was executed expect(document.body.hasAttribute('data-test-script')).toBe(true); // Verify docsifyInitConfig.styleURLs were added to the DOM for (const styleURL of docsifyInitConfig.styleURLs) { const matchElm = document.querySelector( `link[rel*="stylesheet"][href$="${styleURL}"]`, ); expect(matchElm).toBeTruthy(); } // Verify docsifyInitConfig.style was added to the DOM expect( [...document.querySelectorAll('style')].some( elm => elm.textContent.replace(/\s+/g, '') === docsifyInitConfig.style.replace(/\s+/g, ''), ), ).toBe(true); // Verify docsify navigation and docsifyInitConfig.routes document.querySelector('a[href="#/test"]').click(); expect( await waitForFunction(() => /#\/test$/.test(window.location.href)), ).toBeTruthy(); expect(await waitForText('#main', 'This is a custom route')).toBeTruthy(); }); test('embed file code fragment renders', async () => { await docsifyInit({ markdown: { homepage: ` # Embed Test [filename](_media/example1.js ':include :type=code :fragment=demo') `, }, routes: { '_media/example1.js': ` let myURL = 'https://api.example.com/data'; /// [demo] const result = fetch(myURL) .then(response => { return response.json(); }) .then(myJson => { console.log(JSON.stringify(myJson)); }); /// [demo] result.then(console.log).catch(console.error); `, }, }); // Wait for the embedded fragment to be fetched and rendered into #main expect( await waitForText('#main', 'console.log(JSON.stringify(myJson));'), ).toBeTruthy(); const mainText = document.querySelector('#main').textContent; expect(mainText).not.toContain('https://api.example.com/data'); expect(mainText).not.toContain( 'result.then(console.log).catch(console.error);', ); }); test('embed file full line fragment identifier', async () => { await docsifyInit({ markdown: { homepage: ` # Embed Test [filename](_media/example1.html ':include :type=code :fragment=demo :omitFragmentLine') `, }, routes: { '_media/example1.html': ` `, }, }); // Wait for the embedded fragment to be fetched and rendered into #main expect( await waitForText('#main', 'console.log(JSON.stringify(myJson));'), ).toBeTruthy(); const mainText = document.querySelector('#main').textContent; expect(mainText).not.toContain('https://api.example.com/data'); expect(mainText).not.toContain('Full line fragment identifier'); expect(mainText).not.toContain('-->'); expect(mainText).not.toContain( 'result.then(console.log).catch(console.error);', ); }); test('embed multiple file code fragments', async () => { await docsifyInit({ markdown: { homepage: ` # Embed Test [filename](_media/example1.js ':include :type=code :fragment=demo') [filename](_media/example2.js ":include :type=code :fragment=something") # Text between [filename](_media/example3.js ':include :fragment=something_else_not_code') [filename](_media/example4.js ':include :fragment=demo') # Text after `, }, routes: { '_media/example1.js': ` let example1 = 1; /// [demo] example1 += 10; /// [demo] console.log(example1);`, '_media/example2.js': ` let example1 = 1; ### [something] example2 += 10; ### [something] console.log(example2);`, '_media/example3.js': ` let example3 = 1; ### [something_else_not_code] example3 += 10; /// [something_else_not_code] console.log(example3);`, '_media/example4.js': ` let example4 = 1; ### No fragment here example4 += 10; /// No fragment here console.log(example4);`, }, }); expect(await waitForText('#main', 'example1 += 10;')).toBeTruthy(); expect(await waitForText('#main', 'example2 += 10;')).toBeTruthy(); expect(await waitForText('#main', 'example3 += 10;')).toBeTruthy(); const mainText = document.querySelector('#main').textContent; expect(mainText).toContain('Text between'); expect(mainText).toContain('Text after'); expect(mainText).not.toContain('let example1 = 1;'); expect(mainText).not.toContain('let example2 = 1;'); expect(mainText).not.toContain('let example3 = 1;'); expect(mainText).not.toContain('console.log(example1);'); expect(mainText).not.toContain('console.log(example2);'); expect(mainText).not.toContain('console.log(example3);'); expect(mainText).not.toContain('console.log(example4);'); expect(mainText).not.toContain('example4 += 10;'); expect(mainText).not.toContain('No fragment here'); }); test('embed file table cell', async () => { await docsifyInit({ markdown: { homepage: ` # Embed Test Command | Description | Parameters ---: | --- | --- **Something** | | \`do-something\` | Does something. | [include content](_media/content.md ':include') **Something else** | | \`etc.\` | Etc. | | `, }, routes: { '_media/content.md': `this is include content`, }, }); const mainText = document.querySelector('#main').textContent; expect(mainText).toContain('Something'); expect(mainText).toContain('this is include content'); }); }); ================================================ FILE: test/integration/global-apis.test.js ================================================ import initGlobalAPI from '../../src/core/global-api.js'; // Suite // ----------------------------------------------------------------------------- describe('Global APIs', function () { // Tests // --------------------------------------------------------------------------- test('APIs are available', () => { initGlobalAPI(); expect(typeof window.Docsify).toBe('object'); expect(typeof window.Docsify.util).toBe('object'); expect(typeof window.Docsify.dom).toBe('object'); expect(typeof window.Docsify.get).toBe('function'); expect(typeof window.Docsify.slugify).toBe('function'); expect(typeof window.Docsify.version).toBe('string'); expect(typeof window.DocsifyCompiler).toBe('function'); expect(typeof window.marked).toBe('function'); expect(typeof window.Prism).toBe('object'); }); }); ================================================ FILE: test/integration/render.test.js ================================================ import { stripIndent } from 'common-tags'; import docsifyInit from '../helpers/docsify-init.js'; import { waitForText } from '../helpers/wait-for.js'; // Suite // ----------------------------------------------------------------------------- describe('render', function () { // Helpers // --------------------------------------------------------------------------- describe('callouts', () => { beforeEach(async () => { await docsifyInit(); }); test('caution', () => { const output = window.marked('> [!CAUTION]\n> Text'); expect(output).toMatchInlineSnapshot(` "

Text

" `); }); test('important', () => { const output = window.marked('> [!IMPORTANT]\n> Text'); expect(output).toMatchInlineSnapshot(` "

Text

" `); }); test('note', () => { const output = window.marked('> [!NOTE]\n> Text'); expect(output).toMatchInlineSnapshot(` "

Text

" `); }); test('tip', () => { const output = window.marked('> [!TIP]\n> Text'); expect(output).toMatchInlineSnapshot(` "

Text

" `); }); test('warning', () => { const output = window.marked('> [!WARNING]\n> Text'); expect(output).toMatchInlineSnapshot(` "

Text

" `); }); test('important (legacy)', () => { const output = window.marked('!> Important content'); expect(output).toMatchInlineSnapshot( `"

Important content

"`, ); }); test('tip (legacy)', () => { const output = window.marked('?> General tip'); expect(output).toMatchInlineSnapshot( `"

General tip

"`, ); }); }); // Lists // --------------------------------------------------------------------------- describe('lists', function () { beforeEach(async () => { await docsifyInit(); }); test('as unordered task list', async function () { const output = window.marked(stripIndent` - [x] Task 1 - [ ] Task 2 - [ ] Task 3 `); expect(output).toMatchInlineSnapshot( '"
"', ); }); test('as ordered task list', async function () { const output = window.marked(stripIndent` 1. [ ] Task 1 2. [x] Task 2 `); expect(output).toMatchInlineSnapshot( '"
"', ); }); test('normal unordered', async function () { const output = window.marked(stripIndent` - [linktext](link) - just text `); expect(output).toMatchInlineSnapshot( '"
"', ); }); test('unordered with custom start', async function () { const output = window.marked(stripIndent` 1. first 2. second text 3. third `); expect(output).toMatchInlineSnapshot( '"
  1. first
  2. second

text

  1. third
"', ); }); test('nested', async function () { const output = window.marked(stripIndent` - 1 - 2 - 2 a - 2 b - 3 `); expect(output).toMatchInlineSnapshot( '"
  • 1
  • 2
    • 2 a
    • 2 b
  • 3
"', ); }); }); // Images // --------------------------------------------------------------------------- describe('images', function () { beforeEach(async () => { await docsifyInit(); }); test('regular', async function () { const output = window.marked('![alt text](http://imageUrl)'); expect(output).toMatchInlineSnapshot( '"

alt text

"', ); }); test('class', async function () { const output = window.marked( "![alt text](http://imageUrl ':class=someCssClass')", ); expect(output).toMatchInlineSnapshot( '"

alt text

"', ); }); test('id', async function () { const output = window.marked( "![alt text](http://imageUrl ':id=someCssID')", ); expect(output).toMatchInlineSnapshot( '"

alt text

"', ); }); test('no-zoom', async function () { const output = window.marked("![alt text](http://imageUrl ':no-zoom')"); expect(output).toMatchInlineSnapshot( '"

alt text

"', ); }); test('width and height', async function () { const output = window.marked( "![alt text](http://imageUrl ':size=WIDTHxHEIGHT')", ); expect(output).toMatchInlineSnapshot( '"

alt text

"', ); }); test('width', async function () { const output = window.marked("![alt text](http://imageUrl ':size=50')"); expect(output).toMatchInlineSnapshot( '"

alt text

"', ); }); }); // Headings // --------------------------------------------------------------------------- describe('headings', function () { beforeEach(async () => { await docsifyInit(); }); test('h1', async function () { const output = window.marked('# h1 tag'); expect(output).toMatchInlineSnapshot( '"

h1 tag

"', ); }); test('h2', async function () { const output = window.marked('## h2 tag'); expect(output).toMatchInlineSnapshot( '"

h2 tag

"', ); }); test('h3', async function () { const output = window.marked('### h3 tag'); expect(output).toMatchInlineSnapshot( '"

h3 tag

"', ); }); test('h4', async function () { const output = window.marked('#### h4 tag'); expect(output).toMatchInlineSnapshot( '"

h4 tag

"', ); }); test('h5', async function () { const output = window.marked('##### h5 tag'); expect(output).toMatchInlineSnapshot( '"
h5 tag
"', ); }); test('h6', async function () { const output = window.marked('###### h6 tag'); expect(output).toMatchInlineSnapshot( '"
h6 tag
"', ); }); }); // Links // --------------------------------------------------------------------------- describe('link', function () { beforeEach(async () => { await docsifyInit(); }); test('regular', async function () { const output = window.marked('[alt text](http://url)'); expect(output).toMatchInlineSnapshot( `"

alt text

"`, ); }); test('linkrel', async function () { // const { docsify } = await init('default', { // externalLinkTarget: '_blank', // externalLinkRel: 'noopener', // }); const output = window.marked('[alt text](http://www.example.com)'); expect(output).toMatchInlineSnapshot( `"

alt text

"`, ); }); test('disabled', async function () { const output = window.marked("[alt text](http://url ':disabled')"); expect(output).toMatchInlineSnapshot( `"

alt text

"`, ); }); test('target for absolute path', async function () { const output = window.marked("[alt text](http://url ':target=_self')"); expect(output).toMatchInlineSnapshot( `"

alt text

"`, ); }); test('target for relative path', async function () { const output = window.marked("[alt text](/url ':target=_blank')"); expect(output).toMatchInlineSnapshot( '"

alt text

"', ); }); test('class', async function () { const output = window.marked( "[alt text](http://url ':class=someCssClass')", ); expect(output).toMatchInlineSnapshot( `"

alt text

"`, ); }); test('multi class config', async function () { const output = window.marked( "[alt text](http://url ':class=someCssClass :class=anotherCssClass')", ); expect(output).toMatchInlineSnapshot( `"

alt text

"`, ); }); test('id', async function () { const output = window.marked("[alt text](http://url ':id=someCssID')"); expect(output).toMatchInlineSnapshot( `"

alt text

"`, ); }); }); // Skip Link // --------------------------------------------------------------------------- describe('skip link', () => { test('renders default skip link and label', async () => { await docsifyInit(); const elm = document.getElementById('skip-to-content'); const expectText = 'Skip to main content'; expect(elm.textContent).toBe(expectText); expect(elm.outerHTML).toMatchInlineSnapshot( `""`, ); }); test('renders custom label from config string', async () => { const expectText = 'test'; await docsifyInit({ config: { skipLink: expectText, }, }); const elm = document.getElementById('skip-to-content'); expect(elm.textContent).toBe(expectText); }); test('renders custom label from config object', async () => { const getSkipLinkText = () => document.getElementById('skip-to-content').textContent; await docsifyInit({ config: { skipLink: { '/dir1/dir2/': 'baz', '/dir1/': 'bar', }, }, }); window.location.hash = '/dir1/dir2/'; await waitForText('#skip-to-content', 'baz'); expect(getSkipLinkText()).toBe('baz'); window.location.hash = '/dir1/'; await waitForText('#skip-to-content', 'bar'); expect(getSkipLinkText()).toBe('bar'); // Fallback to default window.location.hash = ''; await waitForText('#skip-to-content', 'Skip to main content'); expect(getSkipLinkText()).toBe('Skip to main content'); }); test('does not render skip link when false', async () => { await docsifyInit({ config: { skipLink: false, }, }); const elm = document.getElementById('skip-to-content') || false; expect(elm).toBe(false); }); }); }); ================================================ FILE: test/integration/sidebar.test.js ================================================ import docsifyInit from '../helpers/docsify-init.js'; describe('Test sidebar render toc structure', function () { test('Render sidebar with loadSidebar=true and the _sidebar.md file', async () => { await docsifyInit({ config: { loadSidebar: '_sidebar.md', }, markdown: { homepage: '# Hello World', sidebar: ` - Getting started - [Level1](QuickStart.md) - [Level2](QuickStart2.md) `, }, waitForSelector: '.sidebar-nav > ul', }); const sidebarElm = document.querySelector('.sidebar'); /** * Expected render result * ========================== * */ const GettingStarted = document.querySelector('.sidebar-nav > ul > li'); const level1_Elm = document.querySelector( '.sidebar-nav > ul > li > ul> li', ); const level1_A_tag = level1_Elm.querySelector('a'); const level2_Elm = level1_Elm.querySelector(' ul > li '); const level2_A_tag = level2_Elm.querySelector('a'); expect(sidebarElm).not.toBeNull(); expect(GettingStarted).not.toBeNull(); expect(level1_Elm).not.toBeNull(); expect(level1_A_tag).not.toBeNull(); expect(level2_Elm).not.toBeNull(); expect(level2_A_tag).not.toBeNull(); expect(level1_A_tag.textContent).toContain('Level1'); expect(level2_A_tag.textContent).toContain('Level2'); }); test('Render sidebar with loadSidebar=false should be same to loadSidebar=true sidebar structure', async () => { await docsifyInit({ config: { loadSidebar: false, }, markdown: { homepage: ` # Getting started some thing ## Level1 foo ### Level2 bar `, }, waitForSelector: '.sidebar-nav > ul', }); const sidebarElm = document.querySelector('.sidebar'); /** * Expected render result * ========================== * */ const appSubSidebarTargetElm = document.querySelector('.sidebar-nav > ul'); expect(appSubSidebarTargetElm).not.toBeNull(); const ulClass = appSubSidebarTargetElm.className; // the sidebar-nav > ul should have the class app-sub-sidebar expect(ulClass).toContain('app-sub-sidebar'); const GettingStarted = document.querySelector('.sidebar-nav > ul > li'); const level1_Elm = document.querySelector( '.sidebar-nav > ul > li > ul> li', ); const level1_A_tag = level1_Elm.querySelector('a'); const level2_Elm = level1_Elm.querySelector(' ul > li '); const level2_A_tag = level2_Elm.querySelector('a'); expect(sidebarElm).not.toBeNull(); expect(GettingStarted).not.toBeNull(); expect(level1_Elm).not.toBeNull(); expect(level1_A_tag).not.toBeNull(); expect(level2_Elm).not.toBeNull(); expect(level2_A_tag).not.toBeNull(); expect(level1_A_tag.textContent).toContain('Level1'); expect(level2_A_tag.textContent).toContain('Level2'); }); }); ================================================ FILE: test/unit/core-util.test.js ================================================ import { isExternal } from '../../src/core/util/index.js'; // Core util // ----------------------------------------------------------------------------- describe('core/util', () => { // isExternal() // --------------------------------------------------------------------------- describe('isExternal()', () => { // cases non external test('non external local url with one /', () => { const result = isExternal(`/${location.host}/docsify/demo.md`); expect(result).toBeFalsy(); }); test('non external local url with two //', () => { const result = isExternal(`//${location.host}/docsify/demo.md`); expect(result).toBeFalsy(); }); test('non external local url with three ///', () => { const result = isExternal(`///${location.host}/docsify/demo.md`); expect(result).toBeFalsy(); }); test('non external local url with more /', () => { const result = isExternal( `//////////////////${location.host}/docsify/demo.md`, ); expect(result).toBeFalsy(); }); test('non external url with one /', () => { const result = isExternal('/example.github.io/docsify/demo.md'); expect(result).toBeFalsy(); }); // cases is external test('external url with two //', () => { const result = isExternal('/docsify/demo.md'); expect(result).toBeFalsy(); }); test('external url with three ///', () => { const result = isExternal('///example.github.io/docsify/demo.md'); expect(result).toBeTruthy(); }); test('external url with more /', () => { const result = isExternal( '//////////////////example.github.io/docsify/demo.md', ); expect(result).toBeTruthy(); }); test('external url with one \\', () => { const result = isExternal('/\\example.github.io/docsify/demo.md'); expect(result).toBeTruthy(); }); test('external url with two \\', () => { const result = isExternal('/\\\\example.github.io/docsify/demo.md'); expect(result).toBeTruthy(); }); }); }); ================================================ FILE: test/unit/render-util.test.js ================================================ import { removeAtag, getAndRemoveConfig, getAndRemoveDocsifyIgnoreConfig, } from '../../src/core/render/utils.js'; import { tree } from '../../src/core/render/tpl.js'; import { slugify } from '../../src/core/render/slugify.js'; // Suite // ----------------------------------------------------------------------------- describe('core/render/utils', () => { // removeAtag() // --------------------------------------------------------------------------- describe('removeAtag()', () => { test('removeAtag from a link', () => { const result = removeAtag('content'); expect(result).toBe('content'); }); }); // getAndRemoveDocsifyIgnoreConfig() // --------------------------------------------------------------------------- describe('getAndRemoveDocsifyIgnoreConfig()', () => { test('getAndRemoveDocsifyIgnoreConfig from ', () => { const { content, ignoreAllSubs, ignoreSubHeading } = getAndRemoveDocsifyIgnoreConfig( 'My Ignore Title', ); expect(content).toBe('My Ignore Title'); expect(ignoreSubHeading).toBeTruthy(); expect(ignoreAllSubs === undefined).toBeTruthy(); }); test('getAndRemoveDocsifyIgnoreConfig from ', () => { const { content, ignoreAllSubs, ignoreSubHeading } = getAndRemoveDocsifyIgnoreConfig( 'My Ignore Title', ); expect(content).toBe('My Ignore Title'); expect(ignoreAllSubs).toBeTruthy(); expect(ignoreSubHeading === undefined).toBeTruthy(); }); test('getAndRemoveDocsifyIgnoreConfig from {docsify-ignore}', () => { const { content, ignoreAllSubs, ignoreSubHeading } = getAndRemoveDocsifyIgnoreConfig('My Ignore Title{docsify-ignore}'); expect(content).toBe('My Ignore Title'); expect(ignoreSubHeading).toBeTruthy(); expect(ignoreAllSubs === undefined).toBeTruthy(); }); test('getAndRemoveDocsifyIgnoreConfig from {docsify-ignore-all}', () => { const { content, ignoreAllSubs, ignoreSubHeading } = getAndRemoveDocsifyIgnoreConfig('My Ignore Title{docsify-ignore-all}'); expect(content).toBe('My Ignore Title'); expect(ignoreAllSubs).toBeTruthy(); expect(ignoreSubHeading === undefined).toBeTruthy(); }); }); // getAndRemoveConfig() // --------------------------------------------------------------------------- describe('getAndRemoveConfig()', () => { test('parse simple config', () => { const result = getAndRemoveConfig( "[filename](_media/example.md ':include')", ); expect(result).toMatchObject({ config: {}, str: "[filename](_media/example.md ':include')", }); }); test('parse config with arguments', () => { const result = getAndRemoveConfig( "[filename](_media/example.md ':include :foo=bar :baz test')", ); expect(result).toMatchObject({ config: { foo: 'bar', baz: true, }, str: "[filename](_media/example.md ':include test')", }); }); test('parse config with key arguments img', () => { const result = getAndRemoveConfig( "![logo](https://docsify.js.org/_media/icon.svg ' :size=50x100 ')", ); expect(result).toMatchObject({ config: { size: '50x100', }, str: "![logo](https://docsify.js.org/_media/icon.svg ' ')", }); }); test('parse config with key arguments', () => { const result = getAndRemoveConfig( "[filename](_media/example.md ' :class=foo ')", ); expect(result).toMatchObject({ config: { class: 'foo', }, str: "[filename](_media/example.md ' ')", }); }); test('parse config with same key arguments', () => { const result = getAndRemoveConfig( "[filename](_media/example.md ' :class=foo :class=bar :bb=aa ')", ); expect(result).toMatchObject({ config: { class: ['foo', 'bar'], }, str: "[filename](_media/example.md ' ')", }); }); test('parse config with double quotes', () => { const result = getAndRemoveConfig( '[filename](_media/example.md ":include")', ); expect(result).toMatchObject({ config: {}, str: '[filename](_media/example.md ":include")', }); }); }); }); describe('core/render/tpl', () => { test('remove html tag in tree', () => { const result = tree([ { level: 2, slug: '#/cover?id=basic-usage', title: 'Basic usage', }, { level: 2, slug: '#/cover?id=custom-background', title: 'Custom background', }, { level: 2, slug: '#/cover?id=test', title: 'icoTest', }, ]); expect(result).toBe( /* html */ '', ); }); }); describe('core/render/slugify', () => { test('slugify()', () => { const htmlStrippedSlug = slugify( 'Bla bla bla ', ); expect(htmlStrippedSlug).toBe('bla-bla-bla-'); const nestedHtmlStrippedSlug = slugify( 'Another broken example', ); expect(nestedHtmlStrippedSlug).toBe('another-broken-example'); const emojiRemovedSlug = slugify('emoji test ⚠️🔥✅'); expect(emojiRemovedSlug).toBe('emoji-test-'); const multiSpaceSlug = slugify('Title with multiple spaces'); expect(multiSpaceSlug).toBe('title----with---multiple-spaces'); const numberLeadingSlug = slugify('123abc'); expect(numberLeadingSlug).toBe('_123abc'); const firstDuplicate = slugify('duplicate'); expect(firstDuplicate).toBe('duplicate'); const secondDuplicate = slugify('duplicate'); expect(secondDuplicate).toBe('duplicate-1'); const thirdDuplicate = slugify('duplicate'); expect(thirdDuplicate).toBe('duplicate-2'); const mixedCaseSlug = slugify('This Is Mixed CASE'); expect(mixedCaseSlug).toBe('this-is-mixed-case'); const chinesePreservedSlug = slugify('你好 world'); expect(chinesePreservedSlug).toBe('你好-world'); const specialCharSlug = slugify('C++ vs. Java & Python!'); expect(specialCharSlug).toBe('c-vs-java--python'); const docsifyIgnoreSlug = slugify( 'Ignore Heading ', ); expect(docsifyIgnoreSlug).toBe('ignore-heading-'); const quoteCleanedSlug = slugify('"The content"'); expect(quoteCleanedSlug).toBe('the-content'); }); }); ================================================ FILE: test/unit/router-history-base.test.js ================================================ import { History } from '../../src/core/router/history/base.js'; class MockHistory extends History { parse(path) { return { path }; } } // Suite // ----------------------------------------------------------------------------- describe('router/history/base', () => { // Setup & Teardown // --------------------------------------------------------------------------- let history; // resolvePath: true // --------------------------------------------------------------------------- describe('relativePath: true', () => { // Setup & Teardown // ------------------------------------------------------------------------- beforeEach(() => { history = new MockHistory({ relativePath: true }); }); // Tests // ------------------------------------------------------------------------- test('toURL', () => { const url = history.toURL('guide.md', {}, '/zh-ch/'); expect(url).toBe('/zh-ch/guide'); }); test('toURL with double dot', () => { const url = history.toURL('../README.md', {}, '/zh-ch/'); expect(url).toBe('/README'); }); test('toURL child path', () => { const url = history.toURL('config/example.md', {}, '/zh-ch/'); expect(url).toBe('/zh-ch/config/example'); }); test('toURL absolute path', () => { const url = history.toURL('/README', {}, '/zh-ch/'); expect(url).toBe('/README'); }); }); // resolvePath: false // --------------------------------------------------------------------------- describe('relativePath: false', () => { // Setup & Teardown // ------------------------------------------------------------------------- beforeEach(() => { history = new MockHistory({ relativePath: false }); }); // Tests // ------------------------------------------------------------------------- test('toURL', () => { const url = history.toURL('README', {}, '/zh-ch/'); expect(url).toBe('/README'); }); }); // getFile test // --------------------------------------------------------------------------- describe('getFile', () => { // Tests // ------------------------------------------------------------------------- test('path is url', () => { const file = history.getFile('https://some/raw/url/README.md'); expect(file).toBe('https://some/raw/url/README.md'); }); test('path is url, but ext is .html', () => { const file = history.getFile('https://foo.com/index.html'); expect(file).toBe('https://foo.com/index.html'); }); test('path is url, but with parameters', () => { const file = history.getFile( 'https://some/raw/url/README.md?token=Mytoken', ); expect(file).toBe('https://some/raw/url/README.md?token=Mytoken'); }); test('path is url, but ext is different', () => { history = new MockHistory({ ext: '.ext' }); const file = history.getFile('https://some/raw/url/README.md'); expect(file).toBe('https://some/raw/url/README.md.ext'); }); }); }); ================================================ FILE: test/unit/router-util.test.js ================================================ import { resolvePath } from '../../src/core/util/index.js'; // Suite // ----------------------------------------------------------------------------- describe('router/util', () => { // resolvePath() // --------------------------------------------------------------------------- describe('resolvePath()', () => { test('resolvePath with filename', () => { const result = resolvePath('hello.md'); expect(result).toBe('/hello.md'); }); test('resolvePath with ./', () => { const result = resolvePath('./hello.md'); expect(result).toBe('/hello.md'); }); test('resolvePath with ../', () => { const result = resolvePath('test/../hello.md'); expect(result).toBe('/hello.md'); }); }); }); ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "allowJs": true, "checkJs": true, "module": "esnext", "moduleResolution": "node", "target": "es2023", "lib": ["ES2023", "DOM", "DOM.AsyncIterable", "DOM.Iterable"], "declaration": true, "declarationMap": true, // This (along with also shipping src/, not only dist/), makes "Go To Definition" go to the source files in IDEs for a better DX. "emitDeclarationOnly": true, "resolveJsonModule": true, // Output declarations next to source files (the default when outDir is not // defined), rather than in dist/, to avoid conflicting with the global // build in dist/. // "outDir": "dist/", "strict": true, "skipLibCheck": true, "skipDefaultLibCheck": true, // TODO: Remove once tinydate import is refactored to non-default, or replace/delete non-standards-aligned libs. // Enables default import interop for CJS modules like tinydate "allowSyntheticDefaultImports": true, // TODO: Remove this once all implicit any errors are fixed with proper JSDoc types // Currently suppressing ~600 implicit any errors across the codebase // See: https://github.com/docsifyjs/docsify/pull/2392 "noImplicitAny": false }, "include": ["src/**/*.js", "src/core/modules.ts", "src/core/globals.ts"] } ================================================ FILE: vercel.json ================================================ { "headers": [ { "source": "/(.*)", "headers": [{ "key": "x-robots-tag", "value": "noindex" }] } ], "redirects": [{ "source": "/", "destination": "/preview/" }], "rewrites": [ { "source": "/preview/CHANGELOG.md", "destination": "/CHANGELOG.md" }, { "source": "/preview/:path*", "destination": "/docs/:path*" } ] }