jekyll-theme-zer0 0.22.5 → 0.22.20

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.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +196 -0
  3. data/README.md +66 -19
  4. data/_data/navigation/admin.yml +53 -0
  5. data/_data/theme_backgrounds.yml +121 -0
  6. data/_includes/components/admin-tabs.html +59 -0
  7. data/_includes/components/analytics-dashboard.html +232 -0
  8. data/_includes/components/background-customizer.html +159 -0
  9. data/_includes/components/background-settings.html +137 -0
  10. data/_includes/components/collection-manager.html +151 -0
  11. data/_includes/components/component-showcase.html +452 -0
  12. data/_includes/components/config-editor.html +207 -0
  13. data/_includes/components/config-viewer.html +479 -0
  14. data/_includes/components/cookie-consent.html +35 -100
  15. data/_includes/components/env-dashboard.html +154 -0
  16. data/_includes/components/feature-card.html +94 -0
  17. data/_includes/components/info-section.html +172 -149
  18. data/_includes/components/js-cdn.html +4 -1
  19. data/_includes/components/nanobar.html +117 -0
  20. data/_includes/components/nav-editor.html +99 -0
  21. data/_includes/components/setup-banner.html +28 -0
  22. data/_includes/components/setup-check.html +53 -0
  23. data/_includes/components/svg-background.html +42 -0
  24. data/_includes/components/theme-customizer.html +46 -0
  25. data/_includes/content/seo.html +68 -135
  26. data/_includes/core/footer.html +10 -12
  27. data/_includes/core/head.html +7 -27
  28. data/_includes/core/header.html +30 -17
  29. data/_includes/landing/landing-install-cards.html +18 -7
  30. data/_includes/navigation/admin-nav.html +95 -0
  31. data/_includes/navigation/navbar.html +45 -6
  32. data/_includes/navigation/sidebar-left.html +1 -1
  33. data/_includes/setup/wizard.html +330 -0
  34. data/_layouts/admin.html +166 -0
  35. data/_layouts/landing.html +15 -9
  36. data/_layouts/root.html +12 -6
  37. data/_layouts/setup.html +73 -0
  38. data/_plugins/preview_image_generator.rb +26 -12
  39. data/_sass/core/_navbar.scss +8 -2
  40. data/_sass/custom.scss +65 -10
  41. data/_sass/theme/_background-mixins.scss +95 -0
  42. data/_sass/theme/_backgrounds.scss +156 -0
  43. data/_sass/theme/_color-modes.scss +2 -1
  44. data/assets/backgrounds/gradients/air.svg +15 -0
  45. data/assets/backgrounds/gradients/aqua.svg +15 -0
  46. data/assets/backgrounds/gradients/contrast.svg +15 -0
  47. data/assets/backgrounds/gradients/dark.svg +15 -0
  48. data/assets/backgrounds/gradients/dirt.svg +15 -0
  49. data/assets/backgrounds/gradients/mint.svg +15 -0
  50. data/assets/backgrounds/gradients/neon.svg +15 -0
  51. data/assets/backgrounds/gradients/plum.svg +15 -0
  52. data/assets/backgrounds/gradients/sunrise.svg +15 -0
  53. data/assets/backgrounds/noise/air.svg +8 -0
  54. data/assets/backgrounds/noise/aqua.svg +8 -0
  55. data/assets/backgrounds/noise/contrast.svg +8 -0
  56. data/assets/backgrounds/noise/dark.svg +8 -0
  57. data/assets/backgrounds/noise/dirt.svg +8 -0
  58. data/assets/backgrounds/noise/mint.svg +8 -0
  59. data/assets/backgrounds/noise/neon.svg +8 -0
  60. data/assets/backgrounds/noise/plum.svg +8 -0
  61. data/assets/backgrounds/noise/sunrise.svg +8 -0
  62. data/assets/backgrounds/patterns/air.svg +7 -0
  63. data/assets/backgrounds/patterns/aqua.svg +7 -0
  64. data/assets/backgrounds/patterns/contrast.svg +4 -0
  65. data/assets/backgrounds/patterns/dark.svg +5 -0
  66. data/assets/backgrounds/patterns/dirt.svg +5 -0
  67. data/assets/backgrounds/patterns/mint.svg +6 -0
  68. data/assets/backgrounds/patterns/neon.svg +6 -0
  69. data/assets/backgrounds/patterns/plum.svg +6 -0
  70. data/assets/backgrounds/patterns/sunrise.svg +5 -0
  71. data/assets/js/background-customizer.js +73 -0
  72. data/assets/js/code-copy.js +18 -47
  73. data/assets/js/config-utility.js +307 -0
  74. data/assets/js/nanobar-init.js +63 -0
  75. data/assets/js/nav-editor.js +39 -0
  76. data/assets/js/palette-generator.js +415 -0
  77. data/assets/js/search-modal.js +31 -11
  78. data/assets/js/setup-wizard.js +306 -0
  79. data/assets/js/skin-editor.js +645 -0
  80. data/assets/js/theme-customizer.js +102 -0
  81. data/assets/js/ui-enhancements.js +2 -24
  82. data/assets/vendor/bootstrap/css/bootstrap.min.css +1 -0
  83. data/assets/vendor/bootstrap/js/bootstrap.bundle.min.js +1 -0
  84. data/scripts/README.md +45 -0
  85. data/scripts/features/generate-preview-images +297 -7
  86. data/scripts/features/install-preview-generator +51 -33
  87. data/scripts/fork-cleanup.sh +92 -19
  88. data/scripts/github-setup.sh +284 -0
  89. data/scripts/init_setup.sh +0 -1
  90. data/scripts/lib/frontmatter.sh +543 -0
  91. data/scripts/lib/migrate.sh +265 -0
  92. data/scripts/lib/preview_generator.py +607 -32
  93. data/scripts/lint-pages +508 -0
  94. data/scripts/migrate.sh +201 -0
  95. data/scripts/platform/setup-linux.sh +244 -0
  96. data/scripts/platform/setup-macos.sh +187 -0
  97. data/scripts/platform/setup-wsl.sh +196 -0
  98. metadata +73 -6
@@ -0,0 +1,307 @@
1
+ /**
2
+ * ===================================================================
3
+ * Configuration Utility — Interactive Config Viewer & Editor
4
+ * ===================================================================
5
+ *
6
+ * Powers the /about/config/ page with:
7
+ * - Search / filter across all config keys
8
+ * - Copy individual values or entire sections
9
+ * - Live YAML preview from the editor form
10
+ * - Import / export configuration
11
+ *
12
+ * Dependencies: Bootstrap 5 (already on page)
13
+ * ===================================================================
14
+ */
15
+
16
+ (function () {
17
+ 'use strict';
18
+
19
+ /* ── helpers ──────────────────────────────────────────────────────── */
20
+
21
+ /** Safe clipboard write with visual feedback on a button. */
22
+ function copyToClipboard(text, btn) {
23
+ if (!btn) return;
24
+ var orig = btn.innerHTML;
25
+ navigator.clipboard.writeText(text).then(function () {
26
+ btn.innerHTML = '<i class="bi bi-check-lg"></i> Copied!';
27
+ btn.classList.replace('btn-outline-secondary', 'btn-success');
28
+ setTimeout(function () {
29
+ btn.innerHTML = orig;
30
+ btn.classList.replace('btn-success', 'btn-outline-secondary');
31
+ }, 1800);
32
+ }).catch(function () {
33
+ /* fallback */
34
+ var ta = document.createElement('textarea');
35
+ ta.value = text;
36
+ ta.style.cssText = 'position:fixed;opacity:0';
37
+ document.body.appendChild(ta);
38
+ ta.select();
39
+ try { document.execCommand('copy'); } catch (_) { /* noop */ }
40
+ document.body.removeChild(ta);
41
+ btn.innerHTML = '<i class="bi bi-check-lg"></i> Copied!';
42
+ setTimeout(function () { btn.innerHTML = orig; }, 1800);
43
+ });
44
+ }
45
+
46
+ /* ── search / filter ─────────────────────────────────────────────── */
47
+
48
+ function initSearch() {
49
+ var input = document.getElementById('cfg-search');
50
+ if (!input) return;
51
+
52
+ input.addEventListener('input', function () {
53
+ var q = input.value.toLowerCase().trim();
54
+ var rows = document.querySelectorAll('.cfg-row');
55
+ var sections = document.querySelectorAll('.cfg-section');
56
+
57
+ if (!q) {
58
+ rows.forEach(function (r) { r.classList.remove('d-none'); });
59
+ sections.forEach(function (s) { s.classList.remove('d-none'); });
60
+ return;
61
+ }
62
+
63
+ rows.forEach(function (r) {
64
+ var key = (r.getAttribute('data-key') || '').toLowerCase();
65
+ var val = (r.getAttribute('data-value') || '').toLowerCase();
66
+ var match = key.indexOf(q) !== -1 || val.indexOf(q) !== -1;
67
+ r.classList.toggle('d-none', !match);
68
+ });
69
+
70
+ /* hide sections that have zero visible rows */
71
+ sections.forEach(function (s) {
72
+ var visible = s.querySelectorAll('.cfg-row:not(.d-none)');
73
+ s.classList.toggle('d-none', !visible.length);
74
+ });
75
+ });
76
+
77
+ /* clear button */
78
+ var clearBtn = document.getElementById('cfg-search-clear');
79
+ if (clearBtn) {
80
+ clearBtn.addEventListener('click', function () {
81
+ input.value = '';
82
+ input.dispatchEvent(new Event('input'));
83
+ input.focus();
84
+ });
85
+ }
86
+ }
87
+
88
+ /* ── copy individual value buttons ───────────────────────────────── */
89
+
90
+ function initCopyButtons() {
91
+ document.querySelectorAll('.cfg-copy-val').forEach(function (btn) {
92
+ btn.addEventListener('click', function () {
93
+ var val = btn.getAttribute('data-value') || '';
94
+ copyToClipboard(val, btn);
95
+ });
96
+ });
97
+ }
98
+
99
+ /* ── copy section YAML ───────────────────────────────────────────── */
100
+
101
+ function initSectionCopy() {
102
+ document.querySelectorAll('.cfg-copy-section').forEach(function (btn) {
103
+ btn.addEventListener('click', function () {
104
+ var sectionId = btn.getAttribute('data-section');
105
+ var section = document.getElementById(sectionId);
106
+ if (!section) return;
107
+ var rows = section.querySelectorAll('.cfg-row');
108
+ var lines = [];
109
+ rows.forEach(function (r) {
110
+ var key = r.getAttribute('data-key') || '';
111
+ var val = r.getAttribute('data-value') || '';
112
+ lines.push(key + ': ' + val);
113
+ });
114
+ copyToClipboard(lines.join('\n'), btn);
115
+ });
116
+ });
117
+ }
118
+
119
+ /* ── copy full config ────────────────────────────────────────────── */
120
+
121
+ function initFullCopy() {
122
+ var btn = document.getElementById('cfg-copy-full');
123
+ if (!btn) return;
124
+ btn.addEventListener('click', function () {
125
+ var pre = document.getElementById('cfg-full-yaml');
126
+ if (pre) copyToClipboard(pre.textContent, btn);
127
+ });
128
+ }
129
+
130
+ /* ── expand / collapse all ───────────────────────────────────────── */
131
+
132
+ function initExpandCollapse() {
133
+ var expandBtn = document.getElementById('cfg-expand-all');
134
+ var collapseBtn = document.getElementById('cfg-collapse-all');
135
+
136
+ if (expandBtn) {
137
+ expandBtn.addEventListener('click', function () {
138
+ document.querySelectorAll('#configAccordion .accordion-collapse').forEach(function (el) {
139
+ el.classList.add('show');
140
+ });
141
+ document.querySelectorAll('#configAccordion .accordion-button').forEach(function (el) {
142
+ el.classList.remove('collapsed');
143
+ el.setAttribute('aria-expanded', 'true');
144
+ });
145
+ });
146
+ }
147
+
148
+ if (collapseBtn) {
149
+ collapseBtn.addEventListener('click', function () {
150
+ document.querySelectorAll('#configAccordion .accordion-collapse').forEach(function (el) {
151
+ el.classList.remove('show');
152
+ });
153
+ document.querySelectorAll('#configAccordion .accordion-button').forEach(function (el) {
154
+ el.classList.add('collapsed');
155
+ el.setAttribute('aria-expanded', 'false');
156
+ });
157
+ });
158
+ }
159
+ }
160
+
161
+ /* ── editor: YAML builder ────────────────────────────────────────── */
162
+
163
+ function yamlEscape(val) {
164
+ if (val === '' || val === null || val === undefined) return '""';
165
+ if (val === true || val === false) return String(val);
166
+ var s = String(val).replace(/\r\n|\r|\n/g, ' ').replace(/\s{2,}/g, ' ').trim();
167
+ if (s === '') return '""';
168
+ if (s === 'true' || s === 'false') return s;
169
+ if (/^-?[0-9]+(\.[0-9]+)?$/.test(s)) return s;
170
+ if (/[:#{}[\],&*?|>!%@`'"]/.test(s)) {
171
+ return '"' + s.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
172
+ }
173
+ return '"' + s + '"';
174
+ }
175
+
176
+ function pad(k, w) { w = w || 26; return (k + ' '.repeat(w)).slice(0, w); }
177
+
178
+ function buildEditorYAML() {
179
+ var f = {};
180
+ document.querySelectorAll('#configEditor .edit-field').forEach(function (el) {
181
+ var key = el.getAttribute('data-key');
182
+ if (!key) return;
183
+ f[key] = el.type === 'checkbox' ? el.checked : el.value.trim();
184
+ });
185
+
186
+ var lines = [];
187
+ lines.push('# ===========================================================================');
188
+ lines.push('# _config.yml — generated by Configuration Utility');
189
+ lines.push('# ===========================================================================');
190
+ lines.push('');
191
+
192
+ lines.push('# ── Site Identity ─────────────────────────────────────────────────');
193
+ lines.push(pad('title') + ': ' + yamlEscape(f.title || ''));
194
+ lines.push(pad('founder') + ': ' + yamlEscape(f.founder || ''));
195
+ lines.push(pad('email') + ': ' + yamlEscape(f.email || ''));
196
+ lines.push(pad('description') + ': ' + yamlEscape(f.description || ''));
197
+ lines.push('');
198
+
199
+ lines.push('# ── GitHub ────────────────────────────────────────────────────────');
200
+ lines.push(pad('github_user') + ': ' + yamlEscape(f.github_user || ''));
201
+ lines.push(pad('repository_name') + ': ' + yamlEscape(f.repository_name || ''));
202
+ lines.push(pad('branch') + ': ' + yamlEscape(f.branch || 'main'));
203
+ lines.push('');
204
+
205
+ lines.push('# ── URLs & Deployment ─────────────────────────────────────────────');
206
+ lines.push(pad('url') + ': ' + yamlEscape(f.url || ''));
207
+ lines.push(pad('baseurl') + ': ' + yamlEscape(f.baseurl || ''));
208
+ lines.push(pad('remote_theme') + ': ' + yamlEscape(f.remote_theme || 'bamr87/zer0-mistakes'));
209
+ lines.push(pad('port') + ': ' + (f.port || '4000'));
210
+ lines.push(pad('permalink') + ': ' + (f.permalink || 'pretty'));
211
+ lines.push('');
212
+
213
+ lines.push('# ── Personalization ───────────────────────────────────────────────');
214
+ lines.push(pad('locale') + ': ' + yamlEscape(f.locale || 'en-US'));
215
+ lines.push(pad('theme_skin') + ': ' + yamlEscape(f.theme_skin || 'dark'));
216
+ if (f.logo) lines.push(pad('logo') + ': ' + yamlEscape(f.logo));
217
+ lines.push('');
218
+
219
+ lines.push('# ── Analytics ─────────────────────────────────────────────────────');
220
+ if (f.google_analytics) {
221
+ lines.push(pad('google_analytics') + ': ' + yamlEscape(f.google_analytics));
222
+ }
223
+ lines.push('posthog:');
224
+ lines.push(' enabled: ' + (f['posthog.enabled'] ? 'true' : 'false'));
225
+ if (f['posthog.api_key']) {
226
+ lines.push(' api_key: ' + yamlEscape(f['posthog.api_key']));
227
+ }
228
+ lines.push('');
229
+
230
+ lines.push('# ── Build ─────────────────────────────────────────────────────────');
231
+ lines.push(pad('markdown') + ': kramdown');
232
+ lines.push(pad('highlighter') + ': rouge');
233
+ lines.push('');
234
+
235
+ lines.push('# ── Plugins ───────────────────────────────────────────────────────');
236
+ lines.push('plugins:');
237
+ lines.push(' - github-pages');
238
+ lines.push(' - jekyll-remote-theme');
239
+ lines.push(' - jekyll-feed');
240
+ lines.push(' - jekyll-sitemap');
241
+ lines.push(' - jekyll-seo-tag');
242
+
243
+ return lines.join('\n');
244
+ }
245
+
246
+ function initEditor() {
247
+ var preview = document.getElementById('editor-yaml-preview');
248
+ if (!preview) return;
249
+
250
+ function update() { preview.textContent = buildEditorYAML(); }
251
+
252
+ document.querySelectorAll('#configEditor .edit-field').forEach(function (el) {
253
+ el.addEventListener('input', update);
254
+ el.addEventListener('change', update);
255
+ });
256
+
257
+ /* download */
258
+ var dlBtn = document.getElementById('editor-download');
259
+ if (dlBtn) {
260
+ dlBtn.addEventListener('click', function () {
261
+ var yaml = buildEditorYAML();
262
+ var blob = new Blob([yaml], { type: 'text/yaml;charset=utf-8' });
263
+ var a = document.createElement('a');
264
+ a.href = URL.createObjectURL(blob);
265
+ a.download = '_config.yml';
266
+ document.body.appendChild(a);
267
+ a.click();
268
+ document.body.removeChild(a);
269
+ });
270
+ }
271
+
272
+ /* copy */
273
+ var cpBtn = document.getElementById('editor-copy');
274
+ if (cpBtn) {
275
+ cpBtn.addEventListener('click', function () {
276
+ copyToClipboard(buildEditorYAML(), cpBtn);
277
+ });
278
+ }
279
+
280
+ /* initial render */
281
+ update();
282
+ }
283
+
284
+ /* ── description char counter ────────────────────────────────────── */
285
+
286
+ function initCharCounters() {
287
+ document.querySelectorAll('[data-char-counter]').forEach(function (el) {
288
+ var counter = document.getElementById(el.getAttribute('data-char-counter'));
289
+ if (!counter) return;
290
+ function upd() { counter.textContent = el.value.length; }
291
+ el.addEventListener('input', upd);
292
+ upd();
293
+ });
294
+ }
295
+
296
+ /* ── wiring ──────────────────────────────────────────────────────── */
297
+
298
+ document.addEventListener('DOMContentLoaded', function () {
299
+ initSearch();
300
+ initCopyButtons();
301
+ initSectionCopy();
302
+ initFullCopy();
303
+ initExpandCollapse();
304
+ initEditor();
305
+ initCharCounters();
306
+ });
307
+ })();
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Nanobar Initialization — config-driven page-load progress bar.
3
+ *
4
+ * Reads settings from `window.zer0Nanobar` (injected by the
5
+ * _includes/components/nanobar.html Liquid template) and instantiates the
6
+ * Nanobar library that was loaded via <script defer>.
7
+ *
8
+ * Placement modes (site.nanobar.position):
9
+ * "top" – fixed bar at the top of the viewport (default)
10
+ * "bottom" – fixed bar at the bottom of the viewport
11
+ * "navbar" – inline strip mounted inside #top-progress-target under header
12
+ *
13
+ * @see _config.yml → nanobar section
14
+ * @see _includes/components/nanobar.html
15
+ * @see _includes/core/header.html → #top-progress-target mount point
16
+ */
17
+ (function () {
18
+ 'use strict';
19
+
20
+ document.addEventListener('DOMContentLoaded', function () {
21
+ if (typeof Nanobar !== 'function') { return; }
22
+
23
+ var cfg = window.zer0Nanobar || {};
24
+ var classname = cfg.classname || 'nanobar';
25
+
26
+ // ----- Resolve mount target -----
27
+ // Priority: explicit selector → position "navbar" → none (fixed to viewport)
28
+ var targetEl = null;
29
+ if (cfg.target) {
30
+ targetEl = document.querySelector(cfg.target);
31
+ } else if (cfg.position === 'navbar') {
32
+ targetEl = document.getElementById('top-progress-target');
33
+ }
34
+
35
+ // ----- Position modifier class -----
36
+ var positionMod = '';
37
+ if (cfg.position === 'bottom') { positionMod = classname + '--bottom'; }
38
+ if (cfg.position === 'navbar') { positionMod = classname + '--navbar'; }
39
+
40
+ // ----- Create the Nanobar instance -----
41
+ var nanobar = new Nanobar({
42
+ classname: classname,
43
+ id: cfg.id,
44
+ target: targetEl || undefined
45
+ });
46
+
47
+ if (positionMod && nanobar.el && nanobar.el.classList) {
48
+ nanobar.el.classList.add(positionMod);
49
+ }
50
+
51
+ // ----- Animate progress steps -----
52
+ var steps = (cfg.steps && cfg.steps.length) ? cfg.steps : [30, 76, 100];
53
+ var delay = cfg.stepDelay || 0;
54
+
55
+ if (delay > 0) {
56
+ steps.forEach(function (pct, i) {
57
+ setTimeout(function () { nanobar.go(pct); }, i * delay);
58
+ });
59
+ } else {
60
+ steps.forEach(function (pct) { nanobar.go(pct); });
61
+ }
62
+ });
63
+ })();
@@ -0,0 +1,39 @@
1
+ /**
2
+ * nav-editor.js
3
+ * Powers the Navigation Editor admin page.
4
+ * - Copy YAML output from nav data rendered in the overview tab
5
+ */
6
+
7
+ document.addEventListener('DOMContentLoaded', function () {
8
+ 'use strict';
9
+
10
+ var copyBtn = document.getElementById('nav-copy-yaml');
11
+ if (copyBtn) {
12
+ copyBtn.addEventListener('click', function () {
13
+ var text = document.getElementById('nav-yaml-output').textContent;
14
+ navigator.clipboard.writeText(text).then(function () {
15
+ copyBtn.innerHTML = '<i class="bi bi-check me-1"></i> Copied';
16
+ setTimeout(function () { copyBtn.innerHTML = '<i class="bi bi-clipboard me-1"></i> Copy'; }, 2000);
17
+ });
18
+ });
19
+ }
20
+
21
+ /* Populate YAML preview when nav file selector changes */
22
+ var fileSelect = document.getElementById('nav-file-select');
23
+ if (fileSelect) {
24
+ fileSelect.addEventListener('change', function () {
25
+ var selected = this.value;
26
+ var output = document.getElementById('nav-yaml-output');
27
+ if (output) {
28
+ output.textContent = '# _data/navigation/' + selected + '.yml\n# View the Overview tab for the rendered tree structure.\n# Edit the YAML file directly in your repository.';
29
+ }
30
+
31
+ // Expand the matching accordion section
32
+ var accBtn = document.querySelector('#navAcc-' + selected);
33
+ if (accBtn) {
34
+ var bsCollapse = bootstrap.Collapse.getOrCreateInstance(accBtn, { toggle: false });
35
+ bsCollapse.show();
36
+ }
37
+ });
38
+ }
39
+ });