jekyll-theme-zer0 1.23.0 → 1.25.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 +4 -4
- data/CHANGELOG.md +110 -0
- data/README.md +3 -3
- data/_data/authors.yml +1 -0
- data/_data/features.yml +882 -34
- data/_data/navigation/README.md +15 -5
- data/_data/navigation/main.yml +0 -8
- data/_data/roadmap.yml +1 -0
- data/_includes/README.md +10 -7
- data/_includes/analytics/google-analytics.html +1 -0
- data/_includes/analytics/google-tag-manager-body.html +1 -0
- data/_includes/analytics/google-tag-manager-head.html +1 -0
- data/_includes/analytics/posthog.html +1 -0
- data/_includes/components/README.md +2 -1
- data/_includes/components/admin-links.html +63 -0
- data/_includes/components/admin-tabs.html +1 -0
- data/_includes/components/ai-chat.html +27 -5
- data/_includes/components/author-eeat.html +1 -0
- data/_includes/components/background-customizer.html +1 -0
- data/_includes/components/background-settings.html +36 -37
- data/_includes/components/component-showcase.html +1 -0
- data/_includes/components/cookie-consent.html +1 -0
- data/_includes/components/dev-shortcuts.html +1 -8
- data/_includes/components/env-switcher.html +90 -138
- data/_includes/components/feature-card.html +16 -1
- data/_includes/components/halfmoon.html +22 -41
- data/_includes/components/info-section.html +127 -130
- data/_includes/components/mermaid.html +1 -0
- data/_includes/components/nanobar.html +1 -0
- data/_includes/components/post-type-badge.html +1 -0
- data/_includes/components/preview-image.html +1 -0
- data/_includes/components/search-modal.html +1 -0
- data/_includes/components/searchbar.html +1 -0
- data/_includes/components/shortcuts-modal.html +1 -0
- data/_includes/components/theme-info.html +21 -30
- data/_includes/components/theme-preview-gallery.html +1 -0
- data/_includes/components/zer0-env-var.html +7 -1
- data/_includes/content/backlinks.html +1 -0
- data/_includes/content/giscus.html +1 -0
- data/_includes/content/jsonld-faq.html +1 -0
- data/_includes/content/seo.html +1 -0
- data/_includes/content/sitemap.html +1 -0
- data/_includes/content/toc.html +1 -0
- data/_includes/core/color-mode-init.html +1 -0
- data/_includes/core/footer.html +1 -0
- data/_includes/core/head.html +1 -0
- data/_includes/core/header.html +5 -15
- data/_includes/navigation/admin-nav.html +1 -0
- data/_includes/navigation/breadcrumbs.html +1 -0
- data/_includes/navigation/local-graph-fab.html +3 -9
- data/_includes/navigation/local-graph.html +4 -9
- data/_includes/navigation/navbar.html +1 -0
- data/_includes/navigation/sidebar-categories.html +56 -14
- data/_includes/navigation/sidebar-config.html +115 -0
- data/_includes/navigation/sidebar-folders.html +168 -62
- data/_includes/navigation/sidebar-left.html +29 -52
- data/_includes/navigation/sidebar-nav.html +30 -0
- data/_includes/navigation/sidebar-right.html +1 -0
- data/_includes/navigation/toc-fab.html +3 -9
- data/_includes/navigation/unified-drawer.html +7 -14
- data/_includes/obsidian/full-graph.html +1 -0
- data/_includes/setup/wizard.html +1 -0
- data/_includes/stats/stats-categories.html +1 -0
- data/_includes/stats/stats-header.html +1 -0
- data/_includes/stats/stats-metrics.html +1 -0
- data/_includes/stats/stats-overview.html +1 -0
- data/_includes/stats/stats-tags.html +1 -0
- data/_layouts/admin.html +1 -0
- data/_layouts/article.html +1 -0
- data/_layouts/author.html +1 -0
- data/_layouts/authors.html +1 -0
- data/_layouts/collection.html +1 -0
- data/_layouts/default.html +14 -37
- data/_layouts/home.html +1 -0
- data/_layouts/index.html +1 -0
- data/_layouts/landing.html +1 -0
- data/_layouts/news.html +1 -0
- data/_layouts/note.html +1 -0
- data/_layouts/notebook.html +1 -0
- data/_layouts/root.html +3 -2
- data/_layouts/search.html +1 -0
- data/_layouts/section.html +1 -0
- data/_layouts/setup.html +1 -0
- data/_layouts/sitemap-collection.html +1 -0
- data/_layouts/stats.html +1 -0
- data/_layouts/tag.html +1 -0
- data/_layouts/welcome.html +1 -0
- data/_plugins/author_pages_generator.rb +1 -0
- data/_plugins/obsidian_links.rb +1 -0
- data/_plugins/preview_image_generator.rb +1 -0
- data/_plugins/search_and_sitemap_generator.rb +1 -0
- data/_plugins/theme_version.rb +1 -0
- data/_sass/components/_author.scss +12 -1
- data/_sass/components/_footer.scss +22 -0
- data/_sass/components/_ui-enhancements.scss +9 -0
- data/_sass/core/_docs-layout.scss +1 -0
- data/_sass/core/_navbar.scss +10 -27
- data/_sass/core/_obsidian.scss +2 -2
- data/_sass/core/code-copy.scss +10 -0
- data/_sass/custom.scss +1 -0
- data/_sass/notebooks.scss +1 -0
- data/_sass/theme/_backgrounds.scss +7 -2
- data/_sass/utilities/_layout.scss +11 -0
- data/assets/css/main.scss +2 -1
- data/assets/js/ai-chat.js +1 -0
- data/assets/js/author-profile.js +1 -0
- data/assets/js/auto-hide-nav.js +1 -0
- data/assets/js/back-to-top.js +1 -0
- data/assets/js/background-customizer.js +1 -0
- data/assets/js/code-copy.js +1 -0
- data/assets/js/halfmoon.js +23 -22
- data/assets/js/modules/navigation/gestures.js +1 -0
- data/assets/js/modules/navigation/keyboard.js +1 -0
- data/assets/js/modules/navigation/scroll-spy.js +1 -0
- data/assets/js/modules/navigation/sidebar-state.js +1 -0
- data/assets/js/modules/navigation/sidebar-visibility.js +1 -0
- data/assets/js/modules/navigation/smooth-scroll.js +1 -0
- data/assets/js/modules/theme/appearance.js +52 -16
- data/assets/js/nanobar-init.js +1 -0
- data/assets/js/navigation.js +1 -0
- data/assets/js/obsidian-graph.js +1 -0
- data/assets/js/obsidian-wiki-links.js +1 -0
- data/assets/js/palette-generator.js +1 -0
- data/assets/js/particles-source.js +1 -0
- data/assets/js/search-modal.js +1 -0
- data/assets/js/setup-wizard.js +1 -0
- data/assets/js/share-actions.js +1 -0
- data/assets/js/skin-editor.js +1 -0
- data/assets/js/table-copy.js +1 -0
- data/assets/js/theme-customizer.js +1 -0
- data/scripts/analyze-commits.sh +1 -0
- data/scripts/bin/giscus-discussions +1 -0
- data/scripts/bin/install +1 -0
- data/scripts/bin/validate +18 -1
- data/scripts/build +1 -0
- data/scripts/content-review.rb +1 -0
- data/scripts/convert-notebooks.sh +4 -3
- data/scripts/fix-markdown-format.sh +1 -0
- data/scripts/generate-preview-images.sh +1 -0
- data/scripts/generate-roadmap.sh +1 -0
- data/scripts/install-preview-generator.sh +1 -0
- data/scripts/release +1 -0
- data/scripts/requirements.txt +11 -0
- data/scripts/setup.sh +1 -0
- data/scripts/tag-features +136 -0
- data/scripts/validate-features.rb +149 -0
- data/scripts/vendor-install.sh +1 -0
- metadata +9 -3
- data/_plugins/admin_page_urls.rb +0 -16
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# ---------------------------------------------------------------------------
|
|
5
|
+
# validate-features.rb — canonical integrity checker for the feature registry.
|
|
6
|
+
#
|
|
7
|
+
# Single source of truth shared by `scripts/bin/validate` (preflight) and
|
|
8
|
+
# `test/test_features.sh` (the `features` test suite). Governance:
|
|
9
|
+
# .github/instructions/features.instructions.md.
|
|
10
|
+
#
|
|
11
|
+
# HARD failures (exit 1):
|
|
12
|
+
# - features/features.yml and _data/features.yml are not byte-identical
|
|
13
|
+
# - the header is missing a `# Last Updated:` line
|
|
14
|
+
# - schema violations (missing required field, bad id/version, duplicate id)
|
|
15
|
+
# - implemented:false without `removed_in:`
|
|
16
|
+
# - a reference path on an ACTIVE feature does not exist in the repo
|
|
17
|
+
# - missing/malformed `provenance:` block on an active feature (since PR B)
|
|
18
|
+
# - missing/unresolvable `tests:` linkage on an active feature (since PR C):
|
|
19
|
+
# every entry must be a real test path or `{na: reason}`
|
|
20
|
+
#
|
|
21
|
+
# WARNINGS (non-fatal unless FEATURES_STRICT=1):
|
|
22
|
+
# - header `# Version:` not tracking the gem version
|
|
23
|
+
# - id gaps (IDs are never reused, but gaps are worth a human glance)
|
|
24
|
+
#
|
|
25
|
+
# Portable across Ruby 2.6 (macOS system) and 3.x (CI/Docker).
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
require 'date'
|
|
29
|
+
require 'yaml'
|
|
30
|
+
|
|
31
|
+
MASTER = 'features/features.yml'
|
|
32
|
+
MIRROR = '_data/features.yml'
|
|
33
|
+
REQUIRED = %w[id title description implemented version link docs tags date].freeze
|
|
34
|
+
STRICT = ENV['FEATURES_STRICT'] == '1'
|
|
35
|
+
|
|
36
|
+
def die(msg)
|
|
37
|
+
warn " \e[31m✗\e[0m #{msg}"
|
|
38
|
+
exit 1
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Ruby 3.1+ defaults YAML.load_file to safe loading (rejects Date); 2.6 does
|
|
42
|
+
# not accept the keywords. Try the strict form, fall back for old Ruby.
|
|
43
|
+
def load_yaml(path)
|
|
44
|
+
YAML.load_file(path, permitted_classes: [Date, Time], aliases: true)
|
|
45
|
+
rescue ArgumentError
|
|
46
|
+
YAML.load_file(path)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def ref_paths(refs)
|
|
50
|
+
return [] unless refs.is_a?(Hash)
|
|
51
|
+
|
|
52
|
+
refs.values.flat_map { |v| v.is_a?(Array) ? v : [v] }.select { |v| v.is_a?(String) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
[MASTER, MIRROR].each { |f| die "#{f} is missing" unless File.file?(f) }
|
|
56
|
+
|
|
57
|
+
# 1. Sync contract — the two registries must be byte-identical.
|
|
58
|
+
if File.binread(MASTER) != File.binread(MIRROR)
|
|
59
|
+
die "#{MASTER} and #{MIRROR} differ — run `cp #{MASTER} #{MIRROR}` (must be byte-identical)"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
data = load_yaml(MIRROR)
|
|
63
|
+
feats = data && data['features']
|
|
64
|
+
die 'top-level `features:` list missing' unless feats.is_a?(Array)
|
|
65
|
+
|
|
66
|
+
warnings = []
|
|
67
|
+
|
|
68
|
+
# Header sanity.
|
|
69
|
+
header = File.foreach(MIRROR).first(8).join
|
|
70
|
+
gem_version = File.read('lib/jekyll-theme-zer0/version.rb')[/VERSION\s*=\s*"([^"]+)"/, 1]
|
|
71
|
+
warnings << "header `# Version:` should match gem version #{gem_version}" unless header.include?("# Version: #{gem_version}")
|
|
72
|
+
die 'header missing `# Last Updated: YYYY-MM-DD` line' unless header =~ /# Last Updated: \d{4}-\d{2}-\d{2}/
|
|
73
|
+
|
|
74
|
+
seen = {}
|
|
75
|
+
feats.each_with_index do |f, i|
|
|
76
|
+
die "entry ##{i} is not a mapping" unless f.is_a?(Hash)
|
|
77
|
+
id = f['id'] || "(index #{i})"
|
|
78
|
+
|
|
79
|
+
REQUIRED.each do |key|
|
|
80
|
+
val = f[key]
|
|
81
|
+
die "#{id}: required field `#{key}` is missing/empty" if val.nil? || (val.respond_to?(:empty?) && val.empty?)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Title/description are rendered UNwrapped into HTML on /features/; a raw
|
|
85
|
+
# angle bracket (e.g. "/authors/<key>/") becomes a stray tag that swallows
|
|
86
|
+
# every following card. Keep them plain prose.
|
|
87
|
+
%w[title description].each do |key|
|
|
88
|
+
die "#{id}: `#{key}` must not contain raw `<`/`>` (breaks HTML on /features/ — use :key or <)" if f[key].to_s =~ /[<>]/
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
die "#{id}: id must match ZER0-NNN" unless f['id'] =~ /\AZER0-\d{3}\z/
|
|
92
|
+
die "#{id}: duplicate id" if seen[f['id']]
|
|
93
|
+
seen[f['id']] = true
|
|
94
|
+
die "#{id}: version must be X.Y.Z" unless f['version'].to_s =~ /\A\d+\.\d+\.\d+/
|
|
95
|
+
die "#{id}: tags must be a non-empty list" unless f['tags'].is_a?(Array) && !f['tags'].empty?
|
|
96
|
+
|
|
97
|
+
if f['implemented'] == false
|
|
98
|
+
# Removed features keep their (now-absent) references for history but must
|
|
99
|
+
# record when they went away.
|
|
100
|
+
die "#{id}: implemented:false requires `removed_in:`" unless f['removed_in']
|
|
101
|
+
next
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# 2. Every reference path on an active feature must exist.
|
|
105
|
+
ref_paths(f['references']).each do |p|
|
|
106
|
+
ok = p.end_with?('/') ? File.directory?(p) : File.exist?(p)
|
|
107
|
+
die "#{id}: reference path does not exist: #{p}" unless ok
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# 3a. Provenance is REQUIRED on every active feature (backfilled in PR B).
|
|
111
|
+
prov = f['provenance']
|
|
112
|
+
die "#{id}: missing `provenance:` block" unless prov.is_a?(Hash)
|
|
113
|
+
die "#{id}: provenance.introduced_in must be a version string" unless prov['introduced_in'].is_a?(String) && prov['introduced_in'] =~ /\A\d+\.\d+\.\d+/
|
|
114
|
+
die "#{id}: provenance.commit must be a short git hash" unless prov['commit'].is_a?(String) && prov['commit'] =~ /\A[0-9a-f]{7,40}\z/
|
|
115
|
+
die "#{id}: provenance.pr must be an integer or null" unless prov['pr'].nil? || prov['pr'].is_a?(Integer)
|
|
116
|
+
die "#{id}: provenance.issue must be an integer or null" unless prov['issue'].nil? || prov['issue'].is_a?(Integer)
|
|
117
|
+
|
|
118
|
+
# 3b. Test linkage is REQUIRED on every active feature (backfilled in PR C).
|
|
119
|
+
tests = f['tests']
|
|
120
|
+
die "#{id}: missing/empty `tests:` (a real test path or `{na: reason}`)" unless tests.is_a?(Array) && !tests.empty?
|
|
121
|
+
tests.each do |t|
|
|
122
|
+
if t.is_a?(String)
|
|
123
|
+
die "#{id}: tests entry does not exist: #{t}" unless File.exist?(t)
|
|
124
|
+
elsif t.is_a?(Hash)
|
|
125
|
+
die "#{id}: tests `na:` must carry a non-empty reason" unless t['na'].is_a?(String) && !t['na'].strip.empty?
|
|
126
|
+
else
|
|
127
|
+
die "#{id}: tests entries must be a path string or `{na: reason}`"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Sequential-id sanity (gaps allowed — never reuse an ID — but flagged).
|
|
133
|
+
nums = seen.keys.map { |k| k.split('-').last.to_i }.sort
|
|
134
|
+
gaps = (1..nums.last).to_a - nums
|
|
135
|
+
warnings << "id gaps (verify intentional): #{gaps.map { |n| format('ZER0-%03d', n) }.join(', ')}" unless gaps.empty?
|
|
136
|
+
|
|
137
|
+
unless warnings.empty?
|
|
138
|
+
verbose = STRICT || ENV['FEATURES_VERBOSE'] == '1'
|
|
139
|
+
prov = warnings.count { |w| w.include?('provenance') }
|
|
140
|
+
tests = warnings.count { |w| w.include?('`tests:`') }
|
|
141
|
+
other = warnings.length - prov - tests
|
|
142
|
+
warn " \e[33m⚠\e[0m feature registry: #{warnings.length} warning(s) — " \
|
|
143
|
+
"provenance:#{prov} tests:#{tests} other:#{other} (FEATURES_VERBOSE=1 for the full list)"
|
|
144
|
+
warnings.each { |w| warn " - #{w}" } if verbose
|
|
145
|
+
die "#{warnings.length} warning(s) treated as errors (FEATURES_STRICT=1)" if STRICT
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
active = feats.count { |f| f['implemented'] != false }
|
|
149
|
+
puts "Feature registry valid: #{feats.length} entries (#{active} active), refs resolved, master/_data in sync"
|
data/scripts/vendor-install.sh
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jekyll-theme-zer0
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.25.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Amr Abdel
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-07-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jekyll
|
|
@@ -114,6 +114,7 @@ files:
|
|
|
114
114
|
- _includes/analytics/google-tag-manager-head.html
|
|
115
115
|
- _includes/analytics/posthog.html
|
|
116
116
|
- _includes/components/README.md
|
|
117
|
+
- _includes/components/admin-links.html
|
|
117
118
|
- _includes/components/admin-tabs.html
|
|
118
119
|
- _includes/components/ai-chat.html
|
|
119
120
|
- _includes/components/analytics-dashboard.html
|
|
@@ -191,8 +192,10 @@ files:
|
|
|
191
192
|
- _includes/navigation/navbar.html
|
|
192
193
|
- _includes/navigation/section-sidebar.html
|
|
193
194
|
- _includes/navigation/sidebar-categories.html
|
|
195
|
+
- _includes/navigation/sidebar-config.html
|
|
194
196
|
- _includes/navigation/sidebar-folders.html
|
|
195
197
|
- _includes/navigation/sidebar-left.html
|
|
198
|
+
- _includes/navigation/sidebar-nav.html
|
|
196
199
|
- _includes/navigation/sidebar-right.html
|
|
197
200
|
- _includes/navigation/toc-fab.html
|
|
198
201
|
- _includes/navigation/unified-drawer.html
|
|
@@ -227,7 +230,6 @@ files:
|
|
|
227
230
|
- _layouts/stats.html
|
|
228
231
|
- _layouts/tag.html
|
|
229
232
|
- _layouts/welcome.html
|
|
230
|
-
- _plugins/admin_page_urls.rb
|
|
231
233
|
- _plugins/author_pages_generator.rb
|
|
232
234
|
- _plugins/content_statistics_generator.rb
|
|
233
235
|
- _plugins/obsidian_links.rb
|
|
@@ -282,6 +284,7 @@ files:
|
|
|
282
284
|
- _sass/tokens/_spacing.scss
|
|
283
285
|
- _sass/tokens/_typography.scss
|
|
284
286
|
- _sass/utilities/_focus.scss
|
|
287
|
+
- _sass/utilities/_layout.scss
|
|
285
288
|
- _sass/utilities/_motion.scss
|
|
286
289
|
- assets/css/custom.css
|
|
287
290
|
- assets/css/main.scss
|
|
@@ -498,11 +501,13 @@ files:
|
|
|
498
501
|
- scripts/platform/setup-wsl.sh
|
|
499
502
|
- scripts/post-template-setup.sh
|
|
500
503
|
- scripts/release
|
|
504
|
+
- scripts/requirements.txt
|
|
501
505
|
- scripts/setup.sh
|
|
502
506
|
- scripts/sync-backlog.rb
|
|
503
507
|
- scripts/sync-backlog.sh
|
|
504
508
|
- scripts/sync-plan.rb
|
|
505
509
|
- scripts/sync-plan.sh
|
|
510
|
+
- scripts/tag-features
|
|
506
511
|
- scripts/test-auto-version.sh
|
|
507
512
|
- scripts/test-mermaid.sh
|
|
508
513
|
- scripts/test-notebook-conversion.sh
|
|
@@ -526,6 +531,7 @@ files:
|
|
|
526
531
|
- scripts/utils/fix-markdown
|
|
527
532
|
- scripts/utils/setup
|
|
528
533
|
- scripts/validate
|
|
534
|
+
- scripts/validate-features.rb
|
|
529
535
|
- scripts/vendor-install.sh
|
|
530
536
|
homepage: https://github.com/bamr87/zer0-mistakes
|
|
531
537
|
licenses:
|
data/_plugins/admin_page_urls.rb
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Populates site.data['admin_page_urls'] once per build with a pipe-delimited
|
|
4
|
-
# string of /about/ page URLs (e.g. "|/about/config/|/about/settings/theme/|").
|
|
5
|
-
# Templates use a `contains` check against this string instead of re-running
|
|
6
|
-
# `site.html_pages | where_exp | map | join` on every page render.
|
|
7
|
-
Jekyll::Hooks.register :site, :pre_render do |site|
|
|
8
|
-
admin_urls = site.pages
|
|
9
|
-
.select { |p| p.output_ext == '.html' && p.url.include?('/about/') }
|
|
10
|
-
.map(&:url)
|
|
11
|
-
.sort
|
|
12
|
-
.join('|')
|
|
13
|
-
admin_urls = "|#{admin_urls}|" unless admin_urls.empty?
|
|
14
|
-
site.data ||= {}
|
|
15
|
-
site.data['admin_page_urls'] = admin_urls
|
|
16
|
-
end
|