jekyll-vitepress-theme 1.4.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2bc27b4f32b6d0a84f9515ccf5fb9c60d13abb2f743841b5df36252fe0ea239a
4
- data.tar.gz: 1d1892da5c64967cbdc0b9d384a4983502402b67904ae4c7c5edb4f107681181
3
+ metadata.gz: 39b278e0794a067003ad3429adb3ff6069023850168c8fea94a41c97e0e02aaa
4
+ data.tar.gz: c19f3340ff9d10dbd175daff50bfcbe6800ada67f8922eac7217b2882e853d75
5
5
  SHA512:
6
- metadata.gz: 3a179de1a473aede9d9d67805e6597e5bc47a4fe0fd1bfa23ff5763cba04cf46070e6988a06c97d313ea3a95d0a06210d55c422dfc943b54a290c277671c826b
7
- data.tar.gz: 6dc2a1bd0d0d353e7f43b378461adedd44d623b7cb9bf56d8e0fe1cac64bf6d3defb4e892e81d3a716094ce894fefd6c9b1e6b58953ebc78e30c52c3e6214d69
6
+ metadata.gz: 6fe07bde949fe99f69ea54a28736b0a3742e4608c548e34d1969df70f80119c4833437696c0bc22ada0e8b9d37ff089f36db52c15db64aba2a69c2a043043ebf
7
+ data.tar.gz: cc5f979de2a06f80e598cc1b98fbd4767e4a15be238a638f5932e0f8d799757169431043ab7922709c51a0503d1cd294203251fac2582d5c5639a7966a0cb5c7
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # Jekyll VitePress Theme
6
6
 
7
- <strong>Ruby deserves beautiful docs. Jekyll VitePress Theme brings VitePress-level polish to Jekyll.</strong>
7
+ <strong>Ruby deserves beautiful docs. Jekyll VitePress Theme brings VitePress-level polish to Jekyll, then goes beyond it.</strong>
8
8
 
9
9
  [![Gem Version](https://img.shields.io/gem/v/jekyll-vitepress-theme.svg)](https://rubygems.org/gems/jekyll-vitepress-theme)
10
10
  [![CI](https://github.com/crmne/jekyll-vitepress-theme/actions/workflows/main.yml/badge.svg)](https://github.com/crmne/jekyll-vitepress-theme/actions/workflows/main.yml)
@@ -17,16 +17,16 @@
17
17
 
18
18
  Ruby projects should not need to leave Ruby just to get documentation that feels designed.
19
19
 
20
- `jekyll-vitepress-theme` brings VitePress-level documentation polish to Jekyll: a product-like homepage, familiar navigation, sidebars, outlines, search, dark mode, code blocks, callouts, and doc footers, packaged as a Ruby gem.
20
+ `jekyll-vitepress-theme` brings VitePress-level documentation polish to Jekyll: a product-like homepage, familiar navigation, sidebars, outlines, search, dark mode, code blocks, callouts, and doc footers, packaged as a Ruby gem. It targets [VitePress parity](https://jekyll-vitepress.dev/vitepress-parity/) for the core docs experience and adds [extensions to VitePress](https://jekyll-vitepress.dev/extensions-to-vitepress/) for Jekyll-first workflows.
21
21
 
22
22
  The unusual part is navigation. Jekyll VitePress uses Turbo Frames like a Rails app, swapping only the content frame while the nav, sidebar, and shell stay in place. Page changes feel instant, while the output remains plain Jekyll: Markdown, Liquid, YAML, Ruby, and static files.
23
23
 
24
24
  ## Why Use It
25
25
 
26
- - **VitePress polish for Jekyll:** give Ruby projects a docs homepage, sidebar, outline, search, dark mode, callouts, code blocks, and doc footers that feel designed.
26
+ - **[VitePress polish for Jekyll](https://jekyll-vitepress.dev/vitepress-parity/):** match VitePress for the docs homepage, sidebar, outline, search, dark mode, callouts, code blocks, and doc footers.
27
27
  - **Jekyll-native setup:** keep your Markdown, Liquid, YAML, and static hosting. Add the gem, set a few options, and publish.
28
28
  - **Fast docs navigation:** Turbo Frames update the content area while the nav, sidebar, and shell stay mounted.
29
- - **More than VitePress:** add GitHub Star and Sponsor buttons, versions, generated local search, and Copy Page/View as Markdown for LLM workflows.
29
+ - **[More than VitePress](https://jekyll-vitepress.dev/extensions-to-vitepress/):** add GitHub Star and Sponsor buttons, RubyGems downloads, versions, labels, generated local search, and Copy Page/View as Markdown for LLM workflows.
30
30
  - **Static Ruby output:** build with Jekyll and deploy the generated HTML to GitHub Pages, any CDN, or any static host.
31
31
 
32
32
  ## Quick Start
@@ -95,6 +95,8 @@ Start here:
95
95
  - [Navigation and Layout](https://jekyll-vitepress.dev/navigation-layout/)
96
96
  - [Search and Outline](https://jekyll-vitepress.dev/search-and-outline/)
97
97
  - [Configuration Reference](https://jekyll-vitepress.dev/configuration-reference/)
98
+ - [VitePress Parity](https://jekyll-vitepress.dev/vitepress-parity/)
99
+ - [Extensions to VitePress](https://jekyll-vitepress.dev/extensions-to-vitepress/)
98
100
 
99
101
  ## Development
100
102
 
@@ -34,12 +34,20 @@
34
34
  {% assign last_updated_text = last_updated_config.text %}
35
35
  {% endif %}
36
36
 
37
- {% assign last_updated_format = '%-d %b %Y, %H:%M' %}
37
+ {% assign last_updated_format = 'vitepress' %}
38
38
  {% if last_updated_config and last_updated_config.format %}
39
39
  {% assign last_updated_format = last_updated_config.format %}
40
40
  {% endif %}
41
+ {% assign last_updated_uses_vitepress_format = false %}
42
+ {% if last_updated_format == 'vitepress' %}
43
+ {% assign last_updated_uses_vitepress_format = true %}
44
+ {% assign last_updated_format = '%b %-d, %Y, %-I:%M:%S %p' %}
45
+ {% endif %}
41
46
 
42
- {% assign last_updated_at = page_theme.last_updated_at | default: nil %}
47
+ {% assign last_updated_at = page.last_updated_at | default: nil %}
48
+ {% if page_theme.last_updated_at %}
49
+ {% assign last_updated_at = page_theme.last_updated_at %}
50
+ {% endif %}
43
51
  {% assign show_last_updated = false %}
44
52
  {% if last_updated_enabled and last_updated_at %}
45
53
  {% assign show_last_updated = true %}
@@ -175,7 +183,7 @@
175
183
  {% endif %}
176
184
  {% if show_last_updated %}
177
185
  <div class="last-updated">
178
- <p class="VPLastUpdated">{{ last_updated_text }}: <time datetime="{{ last_updated_at | date_to_xmlschema }}" lang="{{ site.lang | default: 'en-US' }}">{{ last_updated_at | date: last_updated_format }}</time></p>
186
+ <p class="VPLastUpdated">{{ last_updated_text }}: <time datetime="{{ last_updated_at | date_to_xmlschema }}" lang="{{ site.lang | default: 'en-US' }}"{% if last_updated_uses_vitepress_format %} data-vp-last-updated{% endif %}>{{ last_updated_at | date: last_updated_format }}</time></p>
179
187
  </div>
180
188
  {% endif %}
181
189
  </div>
@@ -0,0 +1,45 @@
1
+ {% assign theme = site.jekyll_vitepress %}
2
+ {% assign config = theme.github_sponsor %}
3
+ {% assign explicit_sponsor_user = include.user | default: include.username | default: include.account %}
4
+ {% assign explicit_button_url = include.url %}
5
+ {% assign sponsor_user = explicit_sponsor_user %}
6
+ {% assign button_url = explicit_button_url %}
7
+ {% assign button_text = include.text %}
8
+ {% assign button_label = include.label %}
9
+ {% assign button_enabled = true %}
10
+ {% if config and config != false %}
11
+ {% if config.enabled == false %}
12
+ {% unless explicit_sponsor_user or explicit_button_url %}
13
+ {% assign button_enabled = false %}
14
+ {% endunless %}
15
+ {% endif %}
16
+ {% unless sponsor_user %}
17
+ {% assign sponsor_user = config.user | default: config.username | default: config.account %}
18
+ {% endunless %}
19
+ {% unless button_url %}
20
+ {% assign button_url = config.url %}
21
+ {% endunless %}
22
+ {% unless button_text %}
23
+ {% assign button_text = config.text %}
24
+ {% endunless %}
25
+ {% unless button_label %}
26
+ {% assign button_label = config.label %}
27
+ {% endunless %}
28
+ {% endif %}
29
+ {% assign button_text = button_text | default: 'Sponsor' %}
30
+ {% assign button_label = button_label | default: 'Sponsor on GitHub' %}
31
+ {% unless button_url %}
32
+ {% if sponsor_user %}
33
+ {% assign button_url = 'https://github.com/sponsors/' | append: sponsor_user %}
34
+ {% endif %}
35
+ {% endunless %}
36
+ {% if button_enabled and button_url %}
37
+ <a class="VPMetricButton VPMetricButtonSponsor no-icon" href="{{ button_url }}" aria-label="{{ button_label | escape }}" target="_blank" rel="noreferrer noopener">
38
+ <span class="VPMetricButtonIcon VPMetricButtonSponsorIcon" aria-hidden="true">
39
+ <svg viewBox="0 0 16 16" focusable="false">
40
+ <path d="M7.655 14.916v-.001h-.002l-.006-.003-.018-.01a22.066 22.066 0 0 1-3.744-2.584C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.044 5.231-3.886 6.818a22.094 22.094 0 0 1-3.433 2.414 7.152 7.152 0 0 1-.31.17l-.018.01-.008.004a.75.75 0 0 1-.69 0Z"></path>
41
+ </svg>
42
+ </span>
43
+ <span class="VPMetricButtonText">{{ button_text }}</span>
44
+ </a>
45
+ {% endif %}
@@ -0,0 +1,41 @@
1
+ {% assign theme = site.jekyll_vitepress %}
2
+ {% assign config = theme.github_star %}
3
+ {% assign explicit_repository = include.repository | default: include.repo %}
4
+ {% assign explicit_button_url = include.url %}
5
+ {% assign repository = explicit_repository %}
6
+ {% assign button_text = include.text %}
7
+ {% assign show_count = include.show_count %}
8
+ {% assign button_enabled = true %}
9
+ {% if config and config != false %}
10
+ {% if config.enabled == false %}
11
+ {% unless explicit_repository or explicit_button_url %}
12
+ {% assign button_enabled = false %}
13
+ {% endunless %}
14
+ {% endif %}
15
+ {% unless repository %}
16
+ {% assign repository = config.repository | default: config.repo %}
17
+ {% endunless %}
18
+ {% unless button_text %}
19
+ {% assign button_text = config.text %}
20
+ {% endunless %}
21
+ {% if show_count == nil %}
22
+ {% assign show_count = config.show_count %}
23
+ {% endif %}
24
+ {% endif %}
25
+ {% assign button_text = button_text | default: 'Star' %}
26
+ {% if show_count == nil %}
27
+ {% assign show_count = true %}
28
+ {% endif %}
29
+ {% assign button_url = explicit_button_url %}
30
+ {% unless button_url %}
31
+ {% if repository %}
32
+ {% assign button_url = 'https://github.com/' | append: repository %}
33
+ {% endif %}
34
+ {% endunless %}
35
+ {% if button_enabled and repository and button_url %}
36
+ <a class="VPMetricButton VPMetricButtonGithub no-icon" href="{{ button_url }}" aria-label="Star {{ repository }} on GitHub" target="_blank" rel="noreferrer noopener" data-github-star-repo="{{ repository | escape }}" data-github-star-show-count="{{ show_count }}">
37
+ <span class="VPMetricButtonIcon vpi-social-github" aria-hidden="true"></span>
38
+ <span class="VPMetricButtonText">{{ button_text }}</span>
39
+ <span class="VPMetricButtonCount" data-vp-github-star-count aria-hidden="true"{% unless show_count %} hidden{% endunless %}>{% if show_count %}...{% endif %}</span>
40
+ </a>
41
+ {% endif %}
@@ -7,11 +7,11 @@
7
7
  </button>
8
8
 
9
9
  <div class="VPLocalNavOutlineDropdown" id="vp-local-outline-dropdown">
10
- <button type="button" id="vp-local-outline-button">
10
+ <button type="button" id="vp-local-outline-button" aria-expanded="false" aria-controls="vp-local-outline-items">
11
11
  <span class="menu-text">{{ labels.outline | default: 'On this page' }}</span>
12
12
  <span class="vpi-chevron-right icon"></span>
13
13
  </button>
14
- <div class="items" hidden>
14
+ <div class="items" id="vp-local-outline-items" hidden>
15
15
  <div class="header">
16
16
  <a class="top-link" href="#top" id="vp-local-top-link">{{ labels.return_to_top | default: 'Return to top' }}</a>
17
17
  </div>
data/_includes/nav.html CHANGED
@@ -168,7 +168,7 @@
168
168
  {% if github_star_enabled %}
169
169
  <div class="VPNavBarStar">
170
170
  <a class="VPLink link no-icon VPNavBarStarLink" href="{{ github_star_url }}" aria-label="Star {{ github_star_repository }} on GitHub" target="_blank" rel="noreferrer noopener" data-github-star-repo="{{ github_star_repository | escape }}" data-github-star-show-count="{{ github_star_show_count }}">
171
- <span class="vpi-social-github" aria-hidden="true"></span>
171
+ <span class="VPNavBarStarIcon vpi-social-github" aria-hidden="true"></span>
172
172
  <span class="VPNavBarStarText">{{ github_star_text }}</span>
173
173
  <span class="VPNavBarStarCount" aria-hidden="true"{% unless github_star_show_count %} hidden{% endunless %}>{% if github_star_show_count %}...{% endif %}</span>
174
174
  </a>
@@ -177,9 +177,9 @@
177
177
  {% if github_sponsor_enabled %}
178
178
  <div class="VPNavBarStar VPNavBarSponsor">
179
179
  <a class="VPLink link no-icon VPNavBarStarLink VPNavBarSponsorLink" href="{{ github_sponsor_url }}" aria-label="{{ github_sponsor_label | escape }}" target="_blank" rel="noreferrer noopener">
180
- <span class="VPNavBarSponsorIcon" aria-hidden="true">
180
+ <span class="VPNavBarStarIcon VPNavBarSponsorIcon" aria-hidden="true">
181
181
  <svg viewBox="0 0 16 16" focusable="false">
182
- <path d="M8 13.25l-.7-.64C3.1 8.79 1 6.88 1 4.5 1 2.56 2.56 1 4.5 1c1.1 0 2.15.51 2.83 1.31A3.76 3.76 0 0 1 10.5 1C12.44 1 14 2.56 14 4.5c0 2.38-2.1 4.29-6.3 8.11l-.7.64z"></path>
182
+ <path d="M7.655 14.916v-.001h-.002l-.006-.003-.018-.01a22.066 22.066 0 0 1-3.744-2.584C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.044 5.231-3.886 6.818a22.094 22.094 0 0 1-3.433 2.414 7.152 7.152 0 0 1-.31.17l-.018.01-.008.004a.75.75 0 0 1-.69 0Z"></path>
183
183
  </svg>
184
184
  </span>
185
185
  <span class="VPNavBarStarText">{{ github_sponsor_text }}</span>
@@ -225,7 +225,7 @@
225
225
  {% if github_star_enabled %}
226
226
  <div class="VPNavBarStar" id="vp-nav-star">
227
227
  <a class="VPLink link no-icon VPNavBarStarLink" href="{{ github_star_url }}" aria-label="Star {{ github_star_repository }} on GitHub" target="_blank" rel="noreferrer noopener" data-github-star-repo="{{ github_star_repository | escape }}" data-github-star-show-count="{{ github_star_show_count }}">
228
- <span class="vpi-social-github" aria-hidden="true"></span>
228
+ <span class="VPNavBarStarIcon vpi-social-github" aria-hidden="true"></span>
229
229
  <span class="VPNavBarStarText">{{ github_star_text }}</span>
230
230
  <span class="VPNavBarStarCount" aria-hidden="true"{% unless github_star_show_count %} hidden{% endunless %}>{% if github_star_show_count %}...{% endif %}</span>
231
231
  </a>
@@ -234,9 +234,9 @@
234
234
  {% if github_sponsor_enabled %}
235
235
  <div class="VPNavBarStar VPNavBarSponsor" id="vp-nav-sponsor">
236
236
  <a class="VPLink link no-icon VPNavBarStarLink VPNavBarSponsorLink" href="{{ github_sponsor_url }}" aria-label="{{ github_sponsor_label | escape }}" target="_blank" rel="noreferrer noopener">
237
- <span class="VPNavBarSponsorIcon" aria-hidden="true">
237
+ <span class="VPNavBarStarIcon VPNavBarSponsorIcon" aria-hidden="true">
238
238
  <svg viewBox="0 0 16 16" focusable="false">
239
- <path d="M8 13.25l-.7-.64C3.1 8.79 1 6.88 1 4.5 1 2.56 2.56 1 4.5 1c1.1 0 2.15.51 2.83 1.31A3.76 3.76 0 0 1 10.5 1C12.44 1 14 2.56 14 4.5c0 2.38-2.1 4.29-6.3 8.11l-.7.64z"></path>
239
+ <path d="M7.655 14.916v-.001h-.002l-.006-.003-.018-.01a22.066 22.066 0 0 1-3.744-2.584C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.044 5.231-3.886 6.818a22.094 22.094 0 0 1-3.433 2.414 7.152 7.152 0 0 1-.31.17l-.018.01-.008.004a.75.75 0 0 1-.69 0Z"></path>
240
240
  </svg>
241
241
  </span>
242
242
  <span class="VPNavBarStarText">{{ github_sponsor_text }}</span>
@@ -345,7 +345,7 @@
345
345
  {% if github_star_enabled %}
346
346
  <div class="VPNavBarStar VPNavScreenStar">
347
347
  <a class="VPLink link no-icon VPNavBarStarLink VPNavScreenStarLink" href="{{ github_star_url }}" aria-label="Star {{ github_star_repository }} on GitHub" target="_blank" rel="noreferrer noopener" data-github-star-repo="{{ github_star_repository | escape }}" data-github-star-show-count="{{ github_star_show_count }}">
348
- <span class="vpi-social-github" aria-hidden="true"></span>
348
+ <span class="VPNavBarStarIcon vpi-social-github" aria-hidden="true"></span>
349
349
  <span class="VPNavBarStarText">{{ github_star_text }}</span>
350
350
  <span class="VPNavBarStarCount" aria-hidden="true"{% unless github_star_show_count %} hidden{% endunless %}>{% if github_star_show_count %}...{% endif %}</span>
351
351
  </a>
@@ -354,9 +354,9 @@
354
354
  {% if github_sponsor_enabled %}
355
355
  <div class="VPNavBarStar VPNavScreenStar VPNavBarSponsor">
356
356
  <a class="VPLink link no-icon VPNavBarStarLink VPNavScreenStarLink VPNavBarSponsorLink" href="{{ github_sponsor_url }}" aria-label="{{ github_sponsor_label | escape }}" target="_blank" rel="noreferrer noopener">
357
- <span class="VPNavBarSponsorIcon" aria-hidden="true">
357
+ <span class="VPNavBarStarIcon VPNavBarSponsorIcon" aria-hidden="true">
358
358
  <svg viewBox="0 0 16 16" focusable="false">
359
- <path d="M8 13.25l-.7-.64C3.1 8.79 1 6.88 1 4.5 1 2.56 2.56 1 4.5 1c1.1 0 2.15.51 2.83 1.31A3.76 3.76 0 0 1 10.5 1C12.44 1 14 2.56 14 4.5c0 2.38-2.1 4.29-6.3 8.11l-.7.64z"></path>
359
+ <path d="M7.655 14.916v-.001h-.002l-.006-.003-.018-.01a22.066 22.066 0 0 1-3.744-2.584C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.044 5.231-3.886 6.818a22.094 22.094 0 0 1-3.433 2.414 7.152 7.152 0 0 1-.31.17l-.018.01-.008.004a.75.75 0 0 1-.69 0Z"></path>
360
360
  </svg>
361
361
  </span>
362
362
  <span class="VPNavBarStarText">{{ github_sponsor_text }}</span>
@@ -0,0 +1,53 @@
1
+ {% assign theme = site.jekyll_vitepress %}
2
+ {% assign config = theme.gem_downloads %}
3
+ {% assign explicit_gem_name = include.gem | default: include.name %}
4
+ {% assign explicit_button_url = include.url %}
5
+ {% assign gem_name = explicit_gem_name %}
6
+ {% assign button_url = explicit_button_url %}
7
+ {% assign button_text = include.text %}
8
+ {% assign button_label = include.label %}
9
+ {% assign show_count = include.show_count %}
10
+ {% assign button_enabled = true %}
11
+ {% if config and config != false %}
12
+ {% if config.enabled == false %}
13
+ {% unless explicit_gem_name or explicit_button_url %}
14
+ {% assign button_enabled = false %}
15
+ {% endunless %}
16
+ {% endif %}
17
+ {% unless gem_name %}
18
+ {% assign gem_name = config.gem | default: config.name %}
19
+ {% endunless %}
20
+ {% unless button_url %}
21
+ {% assign button_url = config.url %}
22
+ {% endunless %}
23
+ {% unless button_text %}
24
+ {% assign button_text = config.text %}
25
+ {% endunless %}
26
+ {% unless button_label %}
27
+ {% assign button_label = config.label %}
28
+ {% endunless %}
29
+ {% if show_count == nil %}
30
+ {% assign show_count = config.show_count %}
31
+ {% endif %}
32
+ {% endif %}
33
+ {% assign button_text = button_text | default: 'Downloads' %}
34
+ {% assign button_label = button_label | default: 'View gem downloads' %}
35
+ {% if show_count == nil %}
36
+ {% assign show_count = true %}
37
+ {% endif %}
38
+ {% unless button_url %}
39
+ {% if gem_name %}
40
+ {% assign button_url = 'https://rubygems.org/gems/' | append: gem_name %}
41
+ {% endif %}
42
+ {% endunless %}
43
+ {% if button_enabled and gem_name and button_url %}
44
+ <a class="VPMetricButton VPMetricButtonDownloads no-icon" href="{{ button_url }}" aria-label="{{ button_label | escape }}" target="_blank" rel="noreferrer noopener" data-rubygems-downloads-gem="{{ gem_name | escape }}" data-rubygems-downloads-show-count="{{ show_count }}" data-rubygems-downloads-label="{{ button_label | escape }}">
45
+ <span class="VPMetricButtonIcon VPMetricButtonDownloadsIcon" aria-hidden="true">
46
+ <svg viewBox="0 0 16 16" focusable="false">
47
+ <path d="M8 1.75a.75.75 0 0 1 .75.75v5.19l1.72-1.72a.75.75 0 1 1 1.06 1.06l-3 3a.75.75 0 0 1-1.06 0l-3-3a.75.75 0 0 1 1.06-1.06l1.72 1.72V2.5A.75.75 0 0 1 8 1.75ZM3.75 11a.75.75 0 0 1 .75.75v1h7v-1a.75.75 0 0 1 1.5 0v1.5a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-1.5a.75.75 0 0 1 .75-.75Z"></path>
48
+ </svg>
49
+ </span>
50
+ <span class="VPMetricButtonText"{% if show_count %} hidden{% endif %}>{{ button_text }}</span>
51
+ <strong class="VPMetricButtonCount" data-vp-rubygems-downloads-count{% unless show_count %} hidden{% endunless %}>{% if show_count %}... downloads{% endif %}</strong>
52
+ </a>
53
+ {% endif %}
@@ -2,7 +2,7 @@
2
2
 
3
3
  html,
4
4
  body {
5
- overflow-x: hidden;
5
+ overflow-x: clip;
6
6
  }
7
7
 
8
8
  /* Keep appearance transitions visually consistent across key UI surfaces. */
@@ -296,16 +296,14 @@ body.vp-nav-screen-open .VPNav {
296
296
  transition: color 0.25s;
297
297
  }
298
298
 
299
- .VPNavBarStarLink .vpi-social-github {
300
- width: 20px;
301
- height: 20px;
302
- color: currentcolor;
303
- }
304
-
305
- .VPNavBarSponsorIcon {
299
+ .VPNavBarStarIcon {
306
300
  display: inline-flex;
301
+ flex: 0 0 auto;
302
+ align-items: center;
303
+ justify-content: center;
307
304
  width: 20px;
308
305
  height: 20px;
306
+ line-height: 0;
309
307
  color: currentcolor;
310
308
  }
311
309
 
@@ -347,12 +345,7 @@ body.vp-nav-screen-open .VPNav {
347
345
  background: var(--vp-c-bg-elv);
348
346
  }
349
347
 
350
- .VPNavBarStarLink .vpi-social-github {
351
- width: 14px;
352
- height: 14px;
353
- }
354
-
355
- .VPNavBarSponsorIcon {
348
+ .VPNavBarStarIcon {
356
349
  width: 14px;
357
350
  height: 14px;
358
351
  }
@@ -512,12 +505,7 @@ body.vp-nav-screen-open .VPNav {
512
505
  background: var(--vp-c-bg-elv);
513
506
  }
514
507
 
515
- .VPNavScreenSocialLinks .VPNavScreenStarLink .vpi-social-github {
516
- width: 14px;
517
- height: 14px;
518
- }
519
-
520
- .VPNavScreenSocialLinks .VPNavBarSponsorIcon {
508
+ .VPNavScreenSocialLinks .VPNavBarStarIcon {
521
509
  width: 14px;
522
510
  height: 14px;
523
511
  }
@@ -643,21 +631,14 @@ body.vp-search-open {
643
631
  color: var(--vp-c-text-2);
644
632
  }
645
633
 
646
- .VPDocFooter .last-updated {
647
- margin-top: 8px;
648
- }
649
-
650
634
  .VPDocFooter .VPLastUpdated {
651
635
  margin: 0;
652
- line-height: 22px;
653
- font-size: 13px;
636
+ line-height: 24px;
637
+ font-size: 14px;
638
+ font-weight: 500;
654
639
  color: var(--vp-c-text-2);
655
640
  }
656
641
 
657
- .VPDocFooter .VPLastUpdated time {
658
- color: var(--vp-c-text-1);
659
- }
660
-
661
642
  .VPFooter .footer-brand-links {
662
643
  display: inline-flex;
663
644
  align-items: center;
@@ -698,8 +679,10 @@ body.vp-search-open {
698
679
  }
699
680
 
700
681
  @media (min-width: 640px) {
701
- .VPDocFooter .last-updated {
702
- margin-top: 0;
682
+ .VPDocFooter .VPLastUpdated {
683
+ line-height: 32px;
684
+ font-size: 14px;
685
+ font-weight: 500;
703
686
  }
704
687
  }
705
688
 
@@ -1085,6 +1068,66 @@ body.vp-search-open {
1085
1068
  background-color: var(--vp-button-sponsor-active-bg);
1086
1069
  }
1087
1070
 
1071
+ .VPMetricButton {
1072
+ display: inline-flex;
1073
+ align-items: center;
1074
+ gap: 8px;
1075
+ min-height: 30px;
1076
+ border: 1px solid var(--vp-c-divider);
1077
+ border-radius: 999px;
1078
+ padding: 0 12px;
1079
+ color: var(--vp-c-text-2);
1080
+ background: var(--vp-c-bg-soft);
1081
+ font-size: 13px;
1082
+ font-weight: 600;
1083
+ line-height: 1;
1084
+ white-space: nowrap;
1085
+ text-decoration: none !important;
1086
+ transition: color 0.25s, border-color 0.25s, background-color 0.25s;
1087
+ vertical-align: middle;
1088
+ }
1089
+
1090
+ .VPMetricButton:hover {
1091
+ border-color: var(--vp-c-brand-1);
1092
+ color: var(--vp-c-text-1);
1093
+ background: var(--vp-c-bg-elv);
1094
+ }
1095
+
1096
+ .VPMetricButtonIcon {
1097
+ display: inline-flex;
1098
+ flex: 0 0 auto;
1099
+ align-items: center;
1100
+ justify-content: center;
1101
+ width: 14px;
1102
+ height: 14px;
1103
+ color: currentcolor;
1104
+ line-height: 0;
1105
+ }
1106
+
1107
+ .VPMetricButtonIcon svg {
1108
+ display: block;
1109
+ width: 100%;
1110
+ height: 100%;
1111
+ fill: currentcolor;
1112
+ }
1113
+
1114
+ .VPMetricButtonCount {
1115
+ color: var(--vp-c-text-1);
1116
+ font-weight: 600;
1117
+ font-variant-numeric: tabular-nums;
1118
+ }
1119
+
1120
+ .VPMetricButtonText[hidden],
1121
+ .VPMetricButtonCount[hidden] {
1122
+ display: none !important;
1123
+ }
1124
+
1125
+ .vp-doc p:has(> .VPMetricButton) {
1126
+ display: inline-block;
1127
+ margin: 8px 8px 8px 0;
1128
+ line-height: 1;
1129
+ }
1130
+
1088
1131
  .VPFeatures {
1089
1132
  position: relative;
1090
1133
  padding: 0 24px;
@@ -1695,6 +1738,149 @@ html.dark .only-light {
1695
1738
  background: var(--vp-c-danger-soft);
1696
1739
  }
1697
1740
 
1741
+ .vp-doc .d-inline-block {
1742
+ display: inline-block;
1743
+ }
1744
+
1745
+ .vp-doc :is(h1, h2, h3, h4, h5, h6).d-inline-block {
1746
+ margin-bottom: 0;
1747
+ vertical-align: middle;
1748
+ }
1749
+
1750
+ .vp-doc :is(p, span).label {
1751
+ display: inline-block;
1752
+ margin: 0 0 0 8px;
1753
+ border-radius: 999px;
1754
+ padding: 0 10px;
1755
+ min-height: 22px;
1756
+ color: #fff;
1757
+ background: var(--vp-c-brand-1);
1758
+ font-size: 13px;
1759
+ font-weight: 700;
1760
+ line-height: 22px;
1761
+ vertical-align: middle;
1762
+ }
1763
+
1764
+ .vp-doc .label.label-blue {
1765
+ background: var(--vp-c-brand-1);
1766
+ }
1767
+
1768
+ .vp-doc .label.label-green {
1769
+ background: var(--vp-c-green-1);
1770
+ }
1771
+
1772
+ .vp-doc .label.label-purple {
1773
+ background: var(--vp-c-indigo-1);
1774
+ }
1775
+
1776
+ .vp-doc .label.label-yellow {
1777
+ color: var(--vp-c-text-1);
1778
+ background: var(--vp-c-warning-2);
1779
+ }
1780
+
1781
+ .vp-doc .label.label-red {
1782
+ background: var(--vp-c-danger-1);
1783
+ }
1784
+
1785
+ .vp-doc :is(h1, h2, h3, h4, h5, h6).d-inline-block + p.label {
1786
+ margin-left: 8px;
1787
+ }
1788
+
1789
+ .vp-doc :is(p, blockquote):is(.info, .note, .tip, .important, .warning, .danger, .caution) {
1790
+ border: 1px solid transparent;
1791
+ border-radius: 8px;
1792
+ padding: 16px;
1793
+ line-height: 24px;
1794
+ font-size: var(--vp-custom-block-font-size);
1795
+ }
1796
+
1797
+ .vp-doc :is(p, blockquote):is(.info, .note, .tip, .important, .warning, .danger, .caution)::before {
1798
+ display: block;
1799
+ margin-bottom: 8px;
1800
+ font-weight: 600;
1801
+ }
1802
+
1803
+ .vp-doc :is(p, blockquote).info {
1804
+ border-color: var(--vp-custom-block-info-border);
1805
+ color: var(--vp-custom-block-info-text);
1806
+ background-color: var(--vp-custom-block-info-bg);
1807
+ }
1808
+
1809
+ .vp-doc :is(p, blockquote).info::before {
1810
+ content: "INFO";
1811
+ }
1812
+
1813
+ .vp-doc :is(p, blockquote).note {
1814
+ border-color: var(--vp-custom-block-note-border);
1815
+ color: var(--vp-custom-block-note-text);
1816
+ background-color: var(--vp-custom-block-note-bg);
1817
+ }
1818
+
1819
+ .vp-doc :is(p, blockquote).note::before {
1820
+ content: "NOTE";
1821
+ }
1822
+
1823
+ .vp-doc :is(p, blockquote).tip {
1824
+ border-color: var(--vp-custom-block-tip-border);
1825
+ color: var(--vp-custom-block-tip-text);
1826
+ background-color: var(--vp-custom-block-tip-bg);
1827
+ }
1828
+
1829
+ .vp-doc :is(p, blockquote).tip::before {
1830
+ content: "TIP";
1831
+ }
1832
+
1833
+ .vp-doc :is(p, blockquote).important {
1834
+ border-color: var(--vp-custom-block-important-border);
1835
+ color: var(--vp-custom-block-important-text);
1836
+ background-color: var(--vp-custom-block-important-bg);
1837
+ }
1838
+
1839
+ .vp-doc :is(p, blockquote).important::before {
1840
+ content: "IMPORTANT";
1841
+ }
1842
+
1843
+ .vp-doc :is(p, blockquote).warning {
1844
+ border-color: var(--vp-custom-block-warning-border);
1845
+ color: var(--vp-custom-block-warning-text);
1846
+ background-color: var(--vp-custom-block-warning-bg);
1847
+ }
1848
+
1849
+ .vp-doc :is(p, blockquote).warning::before {
1850
+ content: "WARNING";
1851
+ }
1852
+
1853
+ .vp-doc :is(p, blockquote).danger {
1854
+ border-color: var(--vp-custom-block-danger-border);
1855
+ color: var(--vp-custom-block-danger-text);
1856
+ background-color: var(--vp-custom-block-danger-bg);
1857
+ }
1858
+
1859
+ .vp-doc :is(p, blockquote).danger::before {
1860
+ content: "DANGER";
1861
+ }
1862
+
1863
+ .vp-doc :is(p, blockquote).caution {
1864
+ border-color: var(--vp-custom-block-caution-border);
1865
+ color: var(--vp-custom-block-caution-text);
1866
+ background-color: var(--vp-custom-block-caution-bg);
1867
+ }
1868
+
1869
+ .vp-doc :is(p, blockquote).caution::before {
1870
+ content: "CAUTION";
1871
+ }
1872
+
1873
+ .vp-doc blockquote:is(.info, .note, .tip, .important, .warning, .danger, .caution) > p {
1874
+ margin: 0;
1875
+ color: inherit;
1876
+ line-height: 24px;
1877
+ font-size: var(--vp-custom-block-font-size);
1878
+ }
1879
+
1880
+ .vp-doc :is(p, blockquote):is(.info, .note, .tip, .important, .warning, .danger, .caution) a {
1881
+ font-weight: 600;
1882
+ }
1883
+
1698
1884
 
1699
1885
  @media (max-width: 1279px) {
1700
1886
  .VPDoc.has-aside .content-container {
@@ -112,6 +112,31 @@
112
112
  });
113
113
  }
114
114
 
115
+ function formatLastUpdatedTimes(rootElement) {
116
+ var rootNode = rootElement || document;
117
+ var language = navigator.language || document.documentElement.getAttribute('lang') || undefined;
118
+
119
+ rootNode.querySelectorAll('.VPLastUpdated time[data-vp-last-updated][datetime]').forEach(function (time) {
120
+ var date = new Date(time.getAttribute('datetime'));
121
+ if (Number.isNaN(date.getTime())) {
122
+ return;
123
+ }
124
+
125
+ try {
126
+ time.textContent = new Intl.DateTimeFormat(language, {
127
+ dateStyle: 'medium',
128
+ timeStyle: 'medium'
129
+ }).format(date);
130
+
131
+ if (language) {
132
+ time.setAttribute('lang', language);
133
+ }
134
+ } catch (error) {
135
+ // Keep the server-rendered fallback if Intl formatting is unavailable.
136
+ }
137
+ });
138
+ }
139
+
115
140
  window.__vpAppearance = {
116
141
  getMode: function () {
117
142
  return mode;
@@ -240,6 +265,14 @@
240
265
  });
241
266
  }
242
267
 
268
+ function formatExactCount(value) {
269
+ try {
270
+ return new Intl.NumberFormat('en-US').format(value);
271
+ } catch (error) {
272
+ return String(value);
273
+ }
274
+ }
275
+
243
276
  function formatGitHubStarCount(value) {
244
277
  var count = Number(value);
245
278
  if (!Number.isFinite(count) || count < 0) {
@@ -264,7 +297,7 @@
264
297
  return;
265
298
  }
266
299
 
267
- var countElement = button.querySelector('.VPNavBarStarCount');
300
+ var countElement = button.querySelector('.VPNavBarStarCount, .VPMetricButtonCount[data-vp-github-star-count]');
268
301
  if (!countElement) {
269
302
  return;
270
303
  }
@@ -276,9 +309,20 @@
276
309
 
277
310
  countElement.textContent = formatted;
278
311
  countElement.hidden = false;
312
+
313
+ if (button.classList.contains('VPMetricButton')) {
314
+ var repository = button.getAttribute('data-github-star-repo');
315
+ var exactCount = formatExactCount(value);
316
+ button.title = exactCount + ' GitHub stars';
317
+ if (repository) {
318
+ button.setAttribute('aria-label', 'Star ' + repository + ' on GitHub: ' + exactCount + ' stars');
319
+ }
320
+ }
279
321
  }
280
322
 
281
323
  var githubStarCacheTtlMs = 6 * 60 * 60 * 1000;
324
+ var rubygemsDownloadsCacheTtlMs = 6 * 60 * 60 * 1000;
325
+ var pendingRubyGemsDownloads = {};
282
326
 
283
327
  function readCachedGitHubStarCount(repository) {
284
328
  if (!repository || !window.localStorage) {
@@ -374,9 +418,169 @@
374
418
  });
375
419
  }
376
420
 
377
- document.querySelectorAll('.VPNavBarStarLink[data-github-star-repo]').forEach(function (button) {
378
- loadGitHubStarCount(button);
379
- });
421
+ function formatRubyGemsDownloads(value) {
422
+ var count = Number(value);
423
+ if (!Number.isFinite(count) || count < 0) {
424
+ return null;
425
+ }
426
+
427
+ if (count >= 1000000) {
428
+ var millions = (count / 1000000).toFixed(count >= 10000000 ? 0 : 1);
429
+ return millions.replace(/\.0$/, '') + 'M';
430
+ }
431
+
432
+ if (count >= 1000) {
433
+ var thousands = (count / 1000).toFixed(count >= 100000 ? 0 : 1);
434
+ return thousands.replace(/\.0$/, '') + 'K';
435
+ }
436
+
437
+ return String(Math.round(count));
438
+ }
439
+
440
+ function setRubyGemsDownloads(button, value) {
441
+ if (!button) {
442
+ return;
443
+ }
444
+
445
+ var countElement = button.querySelector('.VPMetricButtonCount[data-vp-rubygems-downloads-count]');
446
+ if (!countElement) {
447
+ return;
448
+ }
449
+
450
+ var formatted = formatRubyGemsDownloads(value);
451
+ if (!formatted) {
452
+ return;
453
+ }
454
+
455
+ var exactCount = formatExactCount(value);
456
+ var textElement = button.querySelector('.VPMetricButtonText');
457
+ countElement.textContent = formatted + ' downloads';
458
+ countElement.hidden = false;
459
+ if (textElement) {
460
+ textElement.hidden = true;
461
+ }
462
+
463
+ var label = button.getAttribute('data-rubygems-downloads-label') || 'View gem downloads';
464
+ button.title = exactCount + ' downloads on RubyGems';
465
+ button.setAttribute('aria-label', label + ': ' + exactCount + ' downloads');
466
+ }
467
+
468
+ function readCachedRubyGemsDownloads(gemName) {
469
+ if (!gemName || !window.localStorage) {
470
+ return null;
471
+ }
472
+
473
+ try {
474
+ var valueKey = 'vp-rubygems-downloads:value:' + gemName;
475
+ var tsKey = 'vp-rubygems-downloads:ts:' + gemName;
476
+
477
+ var rawValue = localStorage.getItem(valueKey);
478
+ var rawTs = localStorage.getItem(tsKey);
479
+ if (!rawValue || !rawTs) {
480
+ return null;
481
+ }
482
+
483
+ var downloads = Number(rawValue);
484
+ var cachedAt = Number(rawTs);
485
+ if (!Number.isFinite(downloads) || !Number.isFinite(cachedAt)) {
486
+ return null;
487
+ }
488
+
489
+ if (Date.now() - cachedAt > rubygemsDownloadsCacheTtlMs) {
490
+ return null;
491
+ }
492
+
493
+ return { downloads: downloads, cachedAt: cachedAt };
494
+ } catch (error) {
495
+ return null;
496
+ }
497
+ }
498
+
499
+ function writeCachedRubyGemsDownloads(gemName, downloads) {
500
+ if (!gemName || !window.localStorage) {
501
+ return;
502
+ }
503
+
504
+ try {
505
+ var valueKey = 'vp-rubygems-downloads:value:' + gemName;
506
+ var tsKey = 'vp-rubygems-downloads:ts:' + gemName;
507
+ localStorage.setItem(valueKey, String(downloads));
508
+ localStorage.setItem(tsKey, String(Date.now()));
509
+ } catch (error) {
510
+ // Ignore storage errors.
511
+ }
512
+ }
513
+
514
+ function fetchRubyGemsDownloads(gemName) {
515
+ if (!pendingRubyGemsDownloads[gemName]) {
516
+ pendingRubyGemsDownloads[gemName] = fetch('https://rubygems.org/api/v1/gems/' + encodeURIComponent(gemName) + '.json', {
517
+ credentials: 'omit',
518
+ headers: { Accept: 'application/json' }
519
+ })
520
+ .then(function (response) {
521
+ if (!response.ok) {
522
+ throw new Error('RubyGems API request failed');
523
+ }
524
+ return response.json();
525
+ })
526
+ .then(function (data) {
527
+ var downloads = Number(data && data.downloads);
528
+ if (!Number.isFinite(downloads)) {
529
+ throw new Error('RubyGems response missing downloads');
530
+ }
531
+ writeCachedRubyGemsDownloads(gemName, downloads);
532
+ return downloads;
533
+ })
534
+ .catch(function () {
535
+ return null;
536
+ });
537
+ }
538
+
539
+ return pendingRubyGemsDownloads[gemName];
540
+ }
541
+
542
+ function loadRubyGemsDownloads(button) {
543
+ if (!button) {
544
+ return;
545
+ }
546
+
547
+ var showCount = button.getAttribute('data-rubygems-downloads-show-count') !== 'false';
548
+ if (!showCount) {
549
+ return;
550
+ }
551
+
552
+ var gemName = button.getAttribute('data-rubygems-downloads-gem');
553
+ if (!gemName) {
554
+ return;
555
+ }
556
+
557
+ var cached = readCachedRubyGemsDownloads(gemName);
558
+ if (cached && typeof cached.downloads === 'number') {
559
+ setRubyGemsDownloads(button, cached.downloads);
560
+ return;
561
+ }
562
+
563
+ fetchRubyGemsDownloads(gemName).then(function (downloads) {
564
+ if (downloads !== null) {
565
+ setRubyGemsDownloads(button, downloads);
566
+ }
567
+ });
568
+ }
569
+
570
+ function loadMetricButtons(rootElement) {
571
+ var rootNode = rootElement || document;
572
+
573
+ rootNode.querySelectorAll('.VPNavBarStarLink[data-github-star-repo], .VPMetricButton[data-github-star-repo]').forEach(function (button) {
574
+ loadGitHubStarCount(button);
575
+ });
576
+
577
+ rootNode.querySelectorAll('.VPMetricButton[data-rubygems-downloads-gem]').forEach(function (button) {
578
+ loadRubyGemsDownloads(button);
579
+ });
580
+ }
581
+
582
+ loadMetricButtons(document);
583
+ formatLastUpdatedTimes(document);
380
584
 
381
585
  function syncMobileMenus() {
382
586
  var anyOpen = navScreenOpen || sidebarOpen;
@@ -1478,20 +1682,90 @@
1478
1682
  }
1479
1683
 
1480
1684
  var localOutlineOpen = false;
1685
+ var localOutlineTransitionTimer = null;
1686
+
1687
+ function clearLocalOutlineTransition() {
1688
+ if (localOutlineTransitionTimer) {
1689
+ window.clearTimeout(localOutlineTransitionTimer);
1690
+ localOutlineTransitionTimer = null;
1691
+ }
1481
1692
 
1482
- function closeLocalOutline() {
1483
- localOutlineOpen = false;
1484
1693
  if (localOutlineItems) {
1485
- localOutlineItems.hidden = true;
1694
+ localOutlineItems.classList.remove(
1695
+ 'flyout-enter-active',
1696
+ 'flyout-enter-from',
1697
+ 'flyout-leave-active',
1698
+ 'flyout-leave-to'
1699
+ );
1486
1700
  }
1701
+ }
1702
+
1703
+ function setLocalOutlineOpen(open, animate) {
1704
+ localOutlineOpen = open;
1705
+
1487
1706
  if (localOutlineButton) {
1488
- localOutlineButton.classList.remove('open');
1489
- localOutlineButton.setAttribute('aria-expanded', 'false');
1707
+ localOutlineButton.classList.toggle('open', open);
1708
+ localOutlineButton.setAttribute('aria-expanded', String(open));
1709
+ }
1710
+
1711
+ if (!localOutlineItems) {
1712
+ return;
1490
1713
  }
1714
+
1715
+ clearLocalOutlineTransition();
1716
+
1717
+ if (open) {
1718
+ localOutlineItems.hidden = false;
1719
+ if (animate !== false) {
1720
+ localOutlineItems.classList.add('flyout-enter-active', 'flyout-enter-from');
1721
+ window.requestAnimationFrame(function () {
1722
+ if (!localOutlineOpen || !localOutlineItems) {
1723
+ return;
1724
+ }
1725
+
1726
+ localOutlineItems.classList.remove('flyout-enter-from');
1727
+ localOutlineTransitionTimer = window.setTimeout(function () {
1728
+ if (localOutlineItems) {
1729
+ localOutlineItems.classList.remove('flyout-enter-active');
1730
+ }
1731
+ localOutlineTransitionTimer = null;
1732
+ }, 200);
1733
+ });
1734
+ }
1735
+ return;
1736
+ }
1737
+
1738
+ if (animate === false || localOutlineItems.hidden) {
1739
+ localOutlineItems.hidden = true;
1740
+ return;
1741
+ }
1742
+
1743
+ localOutlineItems.classList.add('flyout-leave-active');
1744
+ window.requestAnimationFrame(function () {
1745
+ if (localOutlineOpen || !localOutlineItems) {
1746
+ return;
1747
+ }
1748
+
1749
+ localOutlineItems.classList.add('flyout-leave-to');
1750
+ localOutlineTransitionTimer = window.setTimeout(function () {
1751
+ if (localOutlineItems && !localOutlineOpen) {
1752
+ localOutlineItems.hidden = true;
1753
+ localOutlineItems.classList.remove('flyout-leave-active', 'flyout-leave-to');
1754
+ }
1755
+ localOutlineTransitionTimer = null;
1756
+ }, 150);
1757
+ });
1758
+ }
1759
+
1760
+ function closeLocalOutline() {
1761
+ setLocalOutlineOpen(false, true);
1491
1762
  }
1492
1763
 
1493
1764
  if (localOutlineButton) {
1494
1765
  localOutlineButton.setAttribute('aria-expanded', 'false');
1766
+ if (localOutlineItems && localOutlineItems.id) {
1767
+ localOutlineButton.setAttribute('aria-controls', localOutlineItems.id);
1768
+ }
1495
1769
 
1496
1770
  localOutlineButton.addEventListener('click', function () {
1497
1771
  if (!hasOutline) {
@@ -1499,12 +1773,7 @@
1499
1773
  return;
1500
1774
  }
1501
1775
 
1502
- localOutlineOpen = !localOutlineOpen;
1503
- if (localOutlineItems) {
1504
- localOutlineItems.hidden = !localOutlineOpen;
1505
- }
1506
- localOutlineButton.classList.toggle('open', localOutlineOpen);
1507
- localOutlineButton.setAttribute('aria-expanded', String(localOutlineOpen));
1776
+ setLocalOutlineOpen(!localOutlineOpen, true);
1508
1777
  });
1509
1778
  }
1510
1779
 
@@ -1799,6 +2068,8 @@
1799
2068
  return value || '/';
1800
2069
  }
1801
2070
 
2071
+ var pendingDocFrameHash = null;
2072
+
1802
2073
  function shouldTargetDocFrameLink(link) {
1803
2074
  if (!link) {
1804
2075
  return false;
@@ -1839,6 +2110,52 @@
1839
2110
  return true;
1840
2111
  }
1841
2112
 
2113
+ function rememberDocFrameHash(event) {
2114
+ if (event.defaultPrevented || (typeof event.button === 'number' && event.button !== 0) ||
2115
+ event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
2116
+ return;
2117
+ }
2118
+
2119
+ var link = event.currentTarget;
2120
+ if (!shouldTargetDocFrameLink(link)) {
2121
+ return;
2122
+ }
2123
+
2124
+ var url;
2125
+ try {
2126
+ url = new URL(link.getAttribute('href'), window.location.href);
2127
+ } catch (error) {
2128
+ pendingDocFrameHash = null;
2129
+ return;
2130
+ }
2131
+
2132
+ pendingDocFrameHash = url.hash || null;
2133
+ }
2134
+
2135
+ function consumePendingDocFrameHash() {
2136
+ var hash = pendingDocFrameHash;
2137
+ pendingDocFrameHash = null;
2138
+
2139
+ if (!hash || !getHashTarget(hash)) {
2140
+ return null;
2141
+ }
2142
+
2143
+ return hash;
2144
+ }
2145
+
2146
+ function restoreLocationHash(hash) {
2147
+ if (!hash || window.location.hash === hash) {
2148
+ return;
2149
+ }
2150
+
2151
+ if (window.history && typeof window.history.replaceState === 'function') {
2152
+ window.history.replaceState(window.history.state, '', window.location.pathname + window.location.search + hash);
2153
+ return;
2154
+ }
2155
+
2156
+ window.location.hash = hash;
2157
+ }
2158
+
1842
2159
  function enhanceDocFrameLinks() {
1843
2160
  document.querySelectorAll('.vp-doc a[href], .VPDocFooter .pager-link').forEach(function (link) {
1844
2161
  if (!shouldTargetDocFrameLink(link)) {
@@ -1847,6 +2164,10 @@
1847
2164
  link.setAttribute('data-turbo', 'true');
1848
2165
  link.setAttribute('data-turbo-frame', 'vp-content-frame');
1849
2166
  link.setAttribute('data-turbo-action', 'advance');
2167
+ if (!link.hasAttribute('data-vp-frame-hash-bound')) {
2168
+ link.setAttribute('data-vp-frame-hash-bound', 'true');
2169
+ link.addEventListener('click', rememberDocFrameHash);
2170
+ }
1850
2171
  });
1851
2172
  }
1852
2173
 
@@ -1971,6 +2292,7 @@
1971
2292
  marker = document.getElementById('vp-outline-marker');
1972
2293
  hasOutline = headings.length > 0;
1973
2294
  localOutlineOpen = false;
2295
+ syncLocalOutlineViewportHeight();
1974
2296
 
1975
2297
  if (!hasOutline) {
1976
2298
  replaceOutline('.VPDocAsideOutline .VPDocOutlineItem.root', [], true);
@@ -2021,13 +2343,10 @@
2021
2343
  replaceOutline('#vp-local-outline-dropdown .outline .VPDocOutlineItem', tree, true);
2022
2344
  }
2023
2345
 
2024
- if (localOutlineButton) {
2025
- localOutlineButton.classList.remove('open');
2026
- localOutlineButton.setAttribute('aria-expanded', 'false');
2027
- }
2346
+ setLocalOutlineOpen(false, false);
2028
2347
 
2029
- if (localOutlineItems) {
2030
- localOutlineItems.hidden = true;
2348
+ if (localOutlineButton && localOutlineItems && localOutlineItems.id) {
2349
+ localOutlineButton.setAttribute('aria-controls', localOutlineItems.id);
2031
2350
  }
2032
2351
 
2033
2352
  bindHashLinkHandlers();
@@ -2063,8 +2382,14 @@
2063
2382
 
2064
2383
  syncPersistentNavState();
2065
2384
  refreshDocPageState();
2066
-
2067
- if (window.location.hash) {
2385
+ loadMetricButtons(event.target);
2386
+ formatLastUpdatedTimes(event.target);
2387
+
2388
+ var pendingHash = consumePendingDocFrameHash();
2389
+ if (pendingHash) {
2390
+ restoreLocationHash(pendingHash);
2391
+ scrollToHash(pendingHash, false);
2392
+ } else if (window.location.hash) {
2068
2393
  scrollToHash(window.location.hash, false);
2069
2394
  } else {
2070
2395
  window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
@@ -1,5 +1,5 @@
1
1
  module Jekyll
2
2
  module VitePressTheme
3
- VERSION = "1.4.3".freeze
3
+ VERSION = "1.5.0".freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-vitepress-theme
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.3
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carmine Paolino
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-26 00:00:00.000000000 Z
11
+ date: 2026-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -63,6 +63,8 @@ files:
63
63
  - _includes/alert.html
64
64
  - _includes/copy_page_button.html
65
65
  - _includes/doc_footer.html
66
+ - _includes/github_sponsor_button.html
67
+ - _includes/github_star_button.html
66
68
  - _includes/head.html
67
69
  - _includes/home.html
68
70
  - _includes/jekyll_vitepress/doc_footer_end.html
@@ -70,6 +72,7 @@ files:
70
72
  - _includes/jekyll_vitepress/layout_end.html
71
73
  - _includes/local_nav.html
72
74
  - _includes/nav.html
75
+ - _includes/rubygems_downloads_button.html
73
76
  - _includes/search.html
74
77
  - _includes/sidebar.html
75
78
  - _layouts/default.html