jekyll-vitepress-theme 1.2.0 → 1.2.2
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 +4 -4
- data/README.md +1 -0
- data/assets/css/vitepress-overrides.css +13 -2
- data/assets/js/vitepress-theme.js +61 -5
- data/lib/jekyll/vitepress_theme/hooks.rb +127 -23
- data/lib/jekyll/vitepress_theme/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 87a44689ffa884d7e6d6e6a7ab80c6919ecd5e09d828307d16a94816176fed28
|
|
4
|
+
data.tar.gz: 0b1346f4a809c05492379ac6464b958b251d4977e4a86fd7bfee6b758916856a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 137bbe877456978ccfd6d4565a3c05f6efa686f6361b8970ea21eb0aa93d659db5f286e460dd2d398ccab8b39e09f0c33c1b77e50a01a966ee161f459db467f9
|
|
7
|
+
data.tar.gz: 9ce78c0cc99df03cb514841771d83b8de04a058418dcb7694c69b631dcb2e1a3b1b38f3b4bf86e159f2babc1ab50ae9700f59fe031910f3713fcf193e8e390a0
|
data/README.md
CHANGED
|
@@ -7,6 +7,7 @@ A reusable Jekyll theme gem that reproduces the VitePress default docs experienc
|
|
|
7
7
|
- VitePress-style layout structure (top nav, sidebar, outline, doc footer)
|
|
8
8
|
- Appearance toggle with `auto -> dark -> light`
|
|
9
9
|
- Local search modal (`/`, `Ctrl/Cmd+K`, `Cmd+K`)
|
|
10
|
+
- Automatic `/search.json` generation for the home page and sidebar collections
|
|
10
11
|
- Optional GitHub star button with live count (`jekyll_vitepress.github_star`)
|
|
11
12
|
- Code block copy button, language labels, file-title bars and icons
|
|
12
13
|
- Page-level "Copy page" split button with raw Markdown copy + plain `.md` view (`jekyll_vitepress.copy_page`, enabled by default)
|
|
@@ -1700,15 +1700,26 @@ html.dark .only-light {
|
|
|
1700
1700
|
}
|
|
1701
1701
|
|
|
1702
1702
|
/* Copy as Markdown button group */
|
|
1703
|
-
.vp-doc-header
|
|
1703
|
+
.vp-doc-header,
|
|
1704
|
+
.vp-doc-title-row {
|
|
1704
1705
|
display: flex;
|
|
1705
1706
|
align-items: center;
|
|
1706
1707
|
justify-content: space-between;
|
|
1707
1708
|
gap: 12px;
|
|
1709
|
+
flex-wrap: wrap;
|
|
1708
1710
|
}
|
|
1709
1711
|
|
|
1710
|
-
.vp-doc-header h1
|
|
1712
|
+
.vp-doc-header h1,
|
|
1713
|
+
.vp-doc-title-row h1 {
|
|
1711
1714
|
margin-top: 0;
|
|
1715
|
+
margin-bottom: 0;
|
|
1716
|
+
flex: 1 1 320px;
|
|
1717
|
+
min-width: 0;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
.vp-doc-header .copy-md-group,
|
|
1721
|
+
.vp-doc-title-row .copy-md-group {
|
|
1722
|
+
margin-left: auto;
|
|
1712
1723
|
}
|
|
1713
1724
|
|
|
1714
1725
|
/* Copy page icon defs */
|
|
@@ -1164,16 +1164,72 @@
|
|
|
1164
1164
|
addCopyButtons();
|
|
1165
1165
|
|
|
1166
1166
|
// Copy page as Markdown - split button with dropdown
|
|
1167
|
+
function markdownStartsWithH1(markdown) {
|
|
1168
|
+
var value = String(markdown || '').replace(/^\s+/, '');
|
|
1169
|
+
return /^#\s+\S/.test(value) || /^<h1(?:\s|>)/i.test(value) || /^[^\n]+\n=+\s*(?:\n|$)/.test(value);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
function resolveCopyPageTitleHeading() {
|
|
1173
|
+
var doc = document.querySelector('.vp-doc');
|
|
1174
|
+
if (!doc) {
|
|
1175
|
+
return null;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
var headerHeading = doc.querySelector('.vp-doc-header h1');
|
|
1179
|
+
if (headerHeading) {
|
|
1180
|
+
return headerHeading;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
var headings = Array.from(doc.querySelectorAll('h1'));
|
|
1184
|
+
for (var i = 0; i < headings.length; i += 1) {
|
|
1185
|
+
if (!headings[i].closest('.vp-doc-header')) {
|
|
1186
|
+
return headings[i];
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
return null;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
function alignCopyPageButtonWithHeading() {
|
|
1194
|
+
var header = document.querySelector('.vp-doc-header');
|
|
1195
|
+
var group = header && header.querySelector('.copy-md-group');
|
|
1196
|
+
if (!header || !group || header.querySelector('h1')) {
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
var titleHeading = resolveCopyPageTitleHeading();
|
|
1201
|
+
if (!titleHeading) {
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
var titleRow = titleHeading.closest('.vp-doc-title-row');
|
|
1206
|
+
if (!titleRow) {
|
|
1207
|
+
titleRow = document.createElement('div');
|
|
1208
|
+
titleRow.className = 'vp-doc-title-row';
|
|
1209
|
+
titleHeading.parentNode.insertBefore(titleRow, titleHeading);
|
|
1210
|
+
titleRow.appendChild(titleHeading);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
titleRow.appendChild(group);
|
|
1214
|
+
|
|
1215
|
+
if (!header.querySelector('*')) {
|
|
1216
|
+
header.hidden = true;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
alignCopyPageButtonWithHeading();
|
|
1221
|
+
|
|
1167
1222
|
function copyPageMarkdown(btn) {
|
|
1223
|
+
if (!btn) return;
|
|
1224
|
+
|
|
1168
1225
|
var textarea = document.querySelector('.vp-raw-markdown');
|
|
1169
1226
|
if (!textarea) return;
|
|
1170
1227
|
|
|
1171
1228
|
var md = textarea.value;
|
|
1172
|
-
if (
|
|
1173
|
-
var
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
var titleText = autoH1.textContent.trim();
|
|
1229
|
+
if (!markdownStartsWithH1(md)) {
|
|
1230
|
+
var titleHeading = resolveCopyPageTitleHeading();
|
|
1231
|
+
if (titleHeading) {
|
|
1232
|
+
var titleText = titleHeading.textContent.trim();
|
|
1177
1233
|
if (titleText) {
|
|
1178
1234
|
md = '# ' + titleText + '\n\n' + md;
|
|
1179
1235
|
}
|
|
@@ -3,6 +3,75 @@ require 'rouge'
|
|
|
3
3
|
|
|
4
4
|
module Jekyll
|
|
5
5
|
module VitePressTheme
|
|
6
|
+
module SearchIndex
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
TEMPLATE = <<~LIQUID.freeze
|
|
10
|
+
[
|
|
11
|
+
{% assign first = true %}
|
|
12
|
+
{% assign home_page = site.pages | where: 'url', '/' | first %}
|
|
13
|
+
{% if home_page %}
|
|
14
|
+
{% assign home_excerpt = home_page.content | markdownify | strip_html | strip_newlines | replace: ' ', ' ' | strip | truncate: 2400, '' %}
|
|
15
|
+
{
|
|
16
|
+
"title": {{ home_page.title | default: site.title | strip_html | strip | jsonify }},
|
|
17
|
+
"url": {{ home_page.url | relative_url | jsonify }},
|
|
18
|
+
"content": {{ home_excerpt | jsonify }}
|
|
19
|
+
}
|
|
20
|
+
{% assign first = false %}
|
|
21
|
+
{% endif %}
|
|
22
|
+
{% assign sidebar_groups = site.data.sidebar %}
|
|
23
|
+
{% for group in sidebar_groups %}
|
|
24
|
+
{% assign docs = site[group.collection] | sort: 'nav_order' %}
|
|
25
|
+
{% for doc in docs %}
|
|
26
|
+
{% if doc.title and doc.url %}
|
|
27
|
+
{% unless first %},{% endunless %}
|
|
28
|
+
{% assign excerpt = doc.content | markdownify | strip_html | strip_newlines | replace: ' ', ' ' | strip | truncate: 2400, '' %}
|
|
29
|
+
{
|
|
30
|
+
"title": {{ doc.title | strip_html | strip | jsonify }},
|
|
31
|
+
"url": {{ doc.url | relative_url | jsonify }},
|
|
32
|
+
"content": {{ excerpt | jsonify }}
|
|
33
|
+
}
|
|
34
|
+
{% assign first = false %}
|
|
35
|
+
{% endif %}
|
|
36
|
+
{% endfor %}
|
|
37
|
+
{% endfor %}
|
|
38
|
+
]
|
|
39
|
+
LIQUID
|
|
40
|
+
|
|
41
|
+
class GeneratedPage < Jekyll::PageWithoutAFile
|
|
42
|
+
def initialize(site)
|
|
43
|
+
super(site, site.source, '', 'search.json')
|
|
44
|
+
|
|
45
|
+
self.content = TEMPLATE
|
|
46
|
+
data['layout'] = nil
|
|
47
|
+
data['permalink'] = '/search.json'
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def apply(site)
|
|
52
|
+
return if custom_page?(site)
|
|
53
|
+
|
|
54
|
+
site.pages << GeneratedPage.new(site)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def custom_page?(site)
|
|
58
|
+
site.pages.any? { |page| search_index_path?(page.path) || search_index_url?(page.url) } ||
|
|
59
|
+
site.static_files.any? { |file| search_index_path?(file.path) || search_index_url?(file.relative_path) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def search_index_path?(value)
|
|
63
|
+
return false unless value
|
|
64
|
+
|
|
65
|
+
Pathname.new(value.to_s).basename.to_s == 'search.json'
|
|
66
|
+
rescue ArgumentError
|
|
67
|
+
false
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def search_index_url?(value)
|
|
71
|
+
value.to_s.strip == '/search.json'
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
6
75
|
module LastUpdated
|
|
7
76
|
module_function
|
|
8
77
|
|
|
@@ -104,46 +173,81 @@ module Jekyll
|
|
|
104
173
|
value.to_s.strip.casecmp(AUTO_VALUE).zero?
|
|
105
174
|
end
|
|
106
175
|
end
|
|
176
|
+
|
|
177
|
+
module CopyPage
|
|
178
|
+
module_function
|
|
179
|
+
|
|
180
|
+
def enabled?(item)
|
|
181
|
+
theme_config = item.site.config['jekyll_vitepress']
|
|
182
|
+
copy_page_disabled = theme_config.is_a?(Hash) &&
|
|
183
|
+
theme_config['copy_page'].is_a?(Hash) &&
|
|
184
|
+
theme_config['copy_page']['enabled'] == false
|
|
185
|
+
|
|
186
|
+
unless copy_page_disabled
|
|
187
|
+
page_theme = item.data['jekyll_vitepress']
|
|
188
|
+
copy_page_disabled = page_theme == false || (page_theme.is_a?(Hash) && page_theme['copy_page'] == false)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
!copy_page_disabled
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def resolved_markdown(item, payload)
|
|
195
|
+
raw = item.content.to_s
|
|
196
|
+
return raw unless item.respond_to?(:render_with_liquid?) && item.render_with_liquid?
|
|
197
|
+
|
|
198
|
+
item.renderer.render_liquid(raw, payload, liquid_render_info(item, payload), item.path)
|
|
199
|
+
rescue StandardError => e
|
|
200
|
+
relative_path = item.respond_to?(:relative_path) ? item.relative_path : item.path
|
|
201
|
+
Jekyll.logger.warn('jekyll-vitepress-theme', "Copy page markdown capture failed for #{relative_path}: #{e.message}")
|
|
202
|
+
raw
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def liquid_render_info(item, payload)
|
|
206
|
+
liquid_options = item.site.config['liquid'] || {}
|
|
207
|
+
|
|
208
|
+
{
|
|
209
|
+
registers: { site: item.site, page: payload['page'] },
|
|
210
|
+
strict_filters: liquid_options['strict_filters'],
|
|
211
|
+
strict_variables: liquid_options['strict_variables']
|
|
212
|
+
}
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def leading_h1?(markdown)
|
|
216
|
+
return false if markdown.nil?
|
|
217
|
+
|
|
218
|
+
stripped = markdown.lstrip
|
|
219
|
+
return false if stripped.empty?
|
|
220
|
+
|
|
221
|
+
stripped.match?(/\A#\s+\S/) ||
|
|
222
|
+
stripped.match?(/\A<h1(?:\s|>)/i) ||
|
|
223
|
+
stripped.match?(/\A[^\n]+\n=+\s*(?:\n|$)/)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
107
226
|
end
|
|
108
227
|
end
|
|
109
228
|
|
|
110
229
|
Jekyll::Hooks.register :site, :post_read do |site|
|
|
111
230
|
Jekyll::VitePressTheme::VersionLabel.apply(site)
|
|
112
231
|
Jekyll::VitePressTheme::RougeStyles.apply(site)
|
|
232
|
+
Jekyll::VitePressTheme::SearchIndex.apply(site)
|
|
113
233
|
end
|
|
114
234
|
|
|
115
|
-
Jekyll::Hooks.register :documents, :pre_render do |document|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
theme_config['copy_page'].is_a?(Hash) &&
|
|
119
|
-
theme_config['copy_page']['enabled'] == false
|
|
120
|
-
|
|
121
|
-
unless copy_page_disabled
|
|
122
|
-
page_theme = document.data['jekyll_vitepress']
|
|
123
|
-
copy_page_disabled = page_theme == false || (page_theme.is_a?(Hash) && page_theme['copy_page'] == false)
|
|
235
|
+
Jekyll::Hooks.register :documents, :pre_render do |document, payload|
|
|
236
|
+
if Jekyll::VitePressTheme::CopyPage.enabled?(document)
|
|
237
|
+
document.data['_raw_markdown'] = Jekyll::VitePressTheme::CopyPage.resolved_markdown(document, payload)
|
|
124
238
|
end
|
|
125
239
|
|
|
126
|
-
document.data['_raw_markdown'] = document.content unless copy_page_disabled
|
|
127
|
-
|
|
128
240
|
next if document.data.key?('last_updated_at')
|
|
129
241
|
|
|
130
242
|
updated_at = Jekyll::VitePressTheme::LastUpdated.source_file_time(document.site, document.path)
|
|
131
243
|
document.data['last_updated_at'] = updated_at if updated_at
|
|
132
244
|
end
|
|
133
245
|
|
|
134
|
-
Jekyll::Hooks.register :pages, :pre_render do |page|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
theme_config['copy_page'].is_a?(Hash) &&
|
|
138
|
-
theme_config['copy_page']['enabled'] == false
|
|
139
|
-
|
|
140
|
-
unless copy_page_disabled
|
|
141
|
-
page_theme = page.data['jekyll_vitepress']
|
|
142
|
-
copy_page_disabled = page_theme == false || (page_theme.is_a?(Hash) && page_theme['copy_page'] == false)
|
|
246
|
+
Jekyll::Hooks.register :pages, :pre_render do |page, payload|
|
|
247
|
+
if Jekyll::VitePressTheme::CopyPage.enabled?(page)
|
|
248
|
+
page.data['_raw_markdown'] = Jekyll::VitePressTheme::CopyPage.resolved_markdown(page, payload)
|
|
143
249
|
end
|
|
144
250
|
|
|
145
|
-
page.data['_raw_markdown'] = page.content unless copy_page_disabled
|
|
146
|
-
|
|
147
251
|
next if page.data.key?('last_updated_at')
|
|
148
252
|
|
|
149
253
|
updated_at = Jekyll::VitePressTheme::LastUpdated.source_file_time(page.site, page.path)
|
|
@@ -171,7 +275,7 @@ Jekyll::Hooks.register :site, :post_write do |site|
|
|
|
171
275
|
md_path = '/index.md' if item.url == '/'
|
|
172
276
|
|
|
173
277
|
title = item.data['title']
|
|
174
|
-
body = if title && !title.empty? && !
|
|
278
|
+
body = if title && !title.empty? && !Jekyll::VitePressTheme::CopyPage.leading_h1?(raw)
|
|
175
279
|
"# #{title}\n\n#{raw}"
|
|
176
280
|
else
|
|
177
281
|
raw
|