jekyll-theme-zer0 1.24.0 → 1.26.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.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +124 -0
  3. data/README.md +4 -4
  4. data/_data/authors.yml +1 -0
  5. data/_data/backlog.yml +26 -1
  6. data/_data/features.yml +73 -45
  7. data/_data/feedback_types.yml +148 -0
  8. data/_data/navigation/README.md +16 -6
  9. data/_data/navigation/main.yml +0 -8
  10. data/_data/roadmap.yml +1 -0
  11. data/_includes/README.md +10 -7
  12. data/_includes/analytics/google-analytics.html +1 -0
  13. data/_includes/analytics/google-tag-manager-body.html +1 -0
  14. data/_includes/analytics/google-tag-manager-head.html +1 -0
  15. data/_includes/analytics/posthog.html +1 -0
  16. data/_includes/components/README.md +3 -1
  17. data/_includes/components/admin-links.html +63 -0
  18. data/_includes/components/admin-tabs.html +1 -0
  19. data/_includes/components/ai-chat.html +27 -5
  20. data/_includes/components/author-eeat.html +1 -0
  21. data/_includes/components/background-customizer.html +1 -0
  22. data/_includes/components/background-settings.html +36 -37
  23. data/_includes/components/component-showcase.html +1 -0
  24. data/_includes/components/cookie-consent.html +1 -0
  25. data/_includes/components/dev-shortcuts.html +1 -8
  26. data/_includes/components/env-switcher.html +90 -138
  27. data/_includes/components/feature-card.html +1 -1
  28. data/_includes/components/halfmoon.html +22 -41
  29. data/_includes/components/info-section.html +127 -130
  30. data/_includes/components/mermaid.html +1 -0
  31. data/_includes/components/nanobar.html +1 -0
  32. data/_includes/components/page-feedback.html +252 -0
  33. data/_includes/components/post-type-badge.html +1 -0
  34. data/_includes/components/preview-image.html +1 -0
  35. data/_includes/components/search-modal.html +1 -0
  36. data/_includes/components/searchbar.html +1 -0
  37. data/_includes/components/shortcuts-modal.html +1 -0
  38. data/_includes/components/theme-info.html +21 -30
  39. data/_includes/components/theme-preview-gallery.html +1 -0
  40. data/_includes/content/backlinks.html +1 -0
  41. data/_includes/content/giscus.html +1 -0
  42. data/_includes/content/intro.html +20 -62
  43. data/_includes/content/jsonld-faq.html +1 -0
  44. data/_includes/content/seo.html +1 -0
  45. data/_includes/content/sitemap.html +1 -0
  46. data/_includes/content/toc.html +1 -0
  47. data/_includes/core/color-mode-init.html +1 -0
  48. data/_includes/core/console-capture.html +89 -0
  49. data/_includes/core/footer.html +1 -0
  50. data/_includes/core/head.html +5 -0
  51. data/_includes/core/header.html +5 -15
  52. data/_includes/navigation/admin-nav.html +1 -0
  53. data/_includes/navigation/breadcrumbs.html +1 -0
  54. data/_includes/navigation/local-graph-fab.html +3 -9
  55. data/_includes/navigation/local-graph.html +4 -9
  56. data/_includes/navigation/navbar.html +1 -0
  57. data/_includes/navigation/sidebar-categories.html +56 -14
  58. data/_includes/navigation/sidebar-config.html +115 -0
  59. data/_includes/navigation/sidebar-folders.html +168 -62
  60. data/_includes/navigation/sidebar-left.html +29 -52
  61. data/_includes/navigation/sidebar-nav.html +30 -0
  62. data/_includes/navigation/sidebar-right.html +1 -0
  63. data/_includes/navigation/toc-fab.html +3 -9
  64. data/_includes/navigation/unified-drawer.html +7 -14
  65. data/_includes/obsidian/full-graph.html +1 -0
  66. data/_includes/setup/wizard.html +1 -0
  67. data/_includes/stats/stats-categories.html +1 -0
  68. data/_includes/stats/stats-header.html +1 -0
  69. data/_includes/stats/stats-metrics.html +1 -0
  70. data/_includes/stats/stats-overview.html +1 -0
  71. data/_includes/stats/stats-tags.html +1 -0
  72. data/_layouts/admin.html +1 -0
  73. data/_layouts/article.html +1 -0
  74. data/_layouts/author.html +1 -0
  75. data/_layouts/authors.html +1 -0
  76. data/_layouts/collection.html +1 -0
  77. data/_layouts/default.html +14 -37
  78. data/_layouts/home.html +1 -0
  79. data/_layouts/index.html +1 -0
  80. data/_layouts/landing.html +1 -0
  81. data/_layouts/news.html +1 -0
  82. data/_layouts/note.html +1 -0
  83. data/_layouts/notebook.html +1 -0
  84. data/_layouts/root.html +6 -2
  85. data/_layouts/search.html +1 -0
  86. data/_layouts/section.html +1 -0
  87. data/_layouts/setup.html +1 -0
  88. data/_layouts/sitemap-collection.html +1 -0
  89. data/_layouts/stats.html +1 -0
  90. data/_layouts/tag.html +1 -0
  91. data/_layouts/welcome.html +1 -0
  92. data/_plugins/author_pages_generator.rb +1 -0
  93. data/_plugins/obsidian_links.rb +1 -0
  94. data/_plugins/preview_image_generator.rb +1 -0
  95. data/_plugins/search_and_sitemap_generator.rb +1 -0
  96. data/_plugins/theme_version.rb +1 -0
  97. data/_sass/components/_author.scss +12 -1
  98. data/_sass/components/_footer.scss +22 -0
  99. data/_sass/components/_page-feedback.scss +276 -0
  100. data/_sass/components/_ui-enhancements.scss +9 -0
  101. data/_sass/core/_docs-layout.scss +3 -78
  102. data/_sass/core/_navbar.scss +10 -27
  103. data/_sass/core/_obsidian.scss +2 -2
  104. data/_sass/core/code-copy.scss +10 -0
  105. data/_sass/custom.scss +1 -0
  106. data/_sass/notebooks.scss +1 -0
  107. data/_sass/theme/_backgrounds.scss +7 -2
  108. data/_sass/tokens/_layers.scss +4 -0
  109. data/_sass/utilities/_layout.scss +11 -0
  110. data/assets/css/main.scss +3 -1
  111. data/assets/js/ai-chat.js +1 -0
  112. data/assets/js/author-profile.js +1 -0
  113. data/assets/js/auto-hide-nav.js +1 -0
  114. data/assets/js/back-to-top.js +1 -0
  115. data/assets/js/background-customizer.js +1 -0
  116. data/assets/js/code-copy.js +1 -0
  117. data/assets/js/halfmoon.js +23 -22
  118. data/assets/js/modules/navigation/gestures.js +1 -0
  119. data/assets/js/modules/navigation/keyboard.js +1 -0
  120. data/assets/js/modules/navigation/scroll-spy.js +1 -0
  121. data/assets/js/modules/navigation/sidebar-state.js +1 -0
  122. data/assets/js/modules/navigation/sidebar-visibility.js +1 -0
  123. data/assets/js/modules/navigation/smooth-scroll.js +1 -0
  124. data/assets/js/modules/theme/appearance.js +52 -16
  125. data/assets/js/nanobar-init.js +1 -0
  126. data/assets/js/navigation.js +1 -0
  127. data/assets/js/obsidian-graph.js +1 -0
  128. data/assets/js/obsidian-wiki-links.js +1 -0
  129. data/assets/js/page-feedback.js +660 -0
  130. data/assets/js/palette-generator.js +1 -0
  131. data/assets/js/particles-source.js +1 -0
  132. data/assets/js/search-modal.js +1 -0
  133. data/assets/js/setup-wizard.js +1 -0
  134. data/assets/js/share-actions.js +1 -0
  135. data/assets/js/skin-editor.js +1 -0
  136. data/assets/js/table-copy.js +1 -0
  137. data/assets/js/theme-customizer.js +1 -0
  138. data/scripts/analyze-commits.sh +1 -0
  139. data/scripts/bin/giscus-discussions +1 -0
  140. data/scripts/bin/install +1 -0
  141. data/scripts/bin/validate +1 -0
  142. data/scripts/build +1 -0
  143. data/scripts/content-review.rb +1 -0
  144. data/scripts/convert-notebooks.sh +4 -3
  145. data/scripts/fix-markdown-format.sh +1 -0
  146. data/scripts/generate-preview-images.sh +1 -0
  147. data/scripts/generate-roadmap.sh +1 -0
  148. data/scripts/install-preview-generator.sh +1 -0
  149. data/scripts/release +1 -0
  150. data/scripts/requirements.txt +11 -0
  151. data/scripts/setup.sh +1 -0
  152. data/scripts/tag-features +136 -0
  153. data/scripts/validate-features.rb +7 -0
  154. data/scripts/vendor-install.sh +1 -0
  155. metadata +13 -4
  156. data/_data/prompts.yml +0 -302
  157. data/_plugins/admin_page_urls.rb +0 -16
@@ -1,26 +1,30 @@
1
+ {% comment %} Feature: ZER0-021 {% endcomment %}
1
2
  <!--
2
3
  ===================================================================
3
4
  THEME INFO - Compact Theme and Build Information
4
5
  ===================================================================
5
-
6
+
6
7
  File: theme-info.html
7
8
  Path: _includes/components/theme-info.html
8
9
  Purpose: Display compact theme version and system information
9
-
10
+
10
11
  Dependencies:
11
12
  - site.remote_theme or site.theme configuration
12
13
  - Jekyll's site.github metadata
14
+
15
+ Note: environment (Prod/Dev) is shown by env-switcher.html in the
16
+ same Site tab — not duplicated here.
13
17
  ===================================================================
14
18
  -->
15
19
 
16
20
  <div class="theme-info-compact">
17
21
  <div class="row g-2">
18
-
22
+
19
23
  <!-- Theme -->
20
24
  <div class="col-12">
21
25
  <div class="d-flex align-items-center justify-content-between p-2 bg-body-tertiary rounded">
22
26
  <div class="d-flex align-items-center">
23
- <i class="bi bi-palette text-primary me-2"></i>
27
+ <i class="bi bi-palette text-primary me-2" aria-hidden="true"></i>
24
28
  <span class="fw-medium small">Theme</span>
25
29
  </div>
26
30
  <div>
@@ -28,7 +32,7 @@
28
32
  {% assign theme_parts = site.remote_theme | split: "/" %}
29
33
  <a href="https://github.com/{{ site.remote_theme }}" target="_blank" rel="noopener" class="text-decoration-none">
30
34
  <code class="text-info">{{ theme_parts[1] }}</code>
31
- <i class="bi bi-box-arrow-up-right ms-1 small"></i>
35
+ <i class="bi bi-box-arrow-up-right ms-1 small" aria-hidden="true"></i>
32
36
  </a>
33
37
  {% elsif site.theme %}
34
38
  <code>{{ site.theme }}</code>
@@ -39,8 +43,8 @@
39
43
  </div>
40
44
  </div>
41
45
  </div>
42
-
43
- <!-- Jekyll & Environment -->
46
+
47
+ <!-- Jekyll & Last Build -->
44
48
  <div class="col-6">
45
49
  <div class="p-2 bg-body-tertiary rounded text-center">
46
50
  <small class="text-body-secondary d-block">Jekyll</small>
@@ -49,56 +53,43 @@
49
53
  </div>
50
54
  <div class="col-6">
51
55
  <div class="p-2 bg-body-tertiary rounded text-center">
52
- <small class="text-body-secondary d-block">Environment</small>
53
- <span class="badge {% if jekyll.environment == 'production' %}bg-success{% else %}bg-warning text-dark{% endif %}">
54
- {{ jekyll.environment | capitalize }}
55
- </span>
56
- </div>
57
- </div>
58
-
59
- <!-- Build Time -->
60
- <div class="col-12">
61
- <div class="d-flex align-items-center justify-content-between p-2 bg-body-tertiary rounded">
62
- <div class="d-flex align-items-center">
63
- <i class="bi bi-clock-history text-secondary me-2"></i>
64
- <span class="small text-body-secondary">Last Build</span>
65
- </div>
66
- <code class="small">{{ site.time | date: "%Y-%m-%d %H:%M %Z" }}</code>
56
+ <small class="text-body-secondary d-block">Last Build</small>
57
+ <code class="small">{{ site.time | date: "%b %d, %H:%M" }}</code>
67
58
  </div>
68
59
  </div>
69
-
60
+
70
61
  {% if site.repository %}
71
62
  <!-- Repository -->
72
63
  <div class="col-12">
73
64
  <div class="d-flex align-items-center justify-content-between p-2 bg-body-tertiary rounded">
74
65
  <div class="d-flex align-items-center">
75
- <i class="bi bi-git text-danger me-2"></i>
66
+ <i class="bi bi-git text-danger me-2" aria-hidden="true"></i>
76
67
  <span class="small text-body-secondary">Repository</span>
77
68
  </div>
78
69
  <a href="https://github.com/{{ site.repository }}" target="_blank" rel="noopener" class="text-decoration-none small">
79
70
  {{ site.repository }}
80
- <i class="bi bi-box-arrow-up-right ms-1"></i>
71
+ <i class="bi bi-box-arrow-up-right ms-1" aria-hidden="true"></i>
81
72
  </a>
82
73
  </div>
83
74
  </div>
84
75
  {% endif %}
85
-
76
+
86
77
  <!-- Quick Links -->
87
78
  <div class="col-12 mt-2">
88
79
  <div class="d-flex flex-wrap gap-2 justify-content-center">
89
80
  {% if site.remote_theme %}
90
81
  <a href="https://github.com/{{ site.remote_theme }}/blob/main/CHANGELOG.md" target="_blank" rel="noopener" class="btn btn-sm btn-outline-secondary">
91
- <i class="bi bi-journal-text me-1"></i>Changelog
82
+ <i class="bi bi-journal-text me-1" aria-hidden="true"></i>Changelog
92
83
  </a>
93
84
  <a href="https://github.com/{{ site.remote_theme }}#readme" target="_blank" rel="noopener" class="btn btn-sm btn-outline-secondary">
94
- <i class="bi bi-book me-1"></i>Docs
85
+ <i class="bi bi-book me-1" aria-hidden="true"></i>Docs
95
86
  </a>
96
87
  {% endif %}
97
88
  <a href="https://jekyllrb.com/docs/" target="_blank" rel="noopener" class="btn btn-sm btn-outline-secondary">
98
- <i class="bi bi-gem me-1"></i>Jekyll
89
+ <i class="bi bi-gem me-1" aria-hidden="true"></i>Jekyll
99
90
  </a>
100
91
  </div>
101
92
  </div>
102
-
93
+
103
94
  </div>
104
95
  </div>
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-065 {% endcomment %}
1
2
  <!--
2
3
  Theme Preview — component gallery for live skin / mode testing.
3
4
  Pair with theme-controls-bar.html and theme-customizer.js.
@@ -1,3 +1,4 @@
1
+ {% comment %} Features: ZER0-044, ZER0-059 {% endcomment %}
1
2
  {%- comment -%}
2
3
  ===================================================================
3
4
  BACKLINKS — Pages that wiki-link or markdown-link to the current page
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-035 {% endcomment %}
1
2
  <!--
2
3
  giscus.html — GitHub Discussions comment system
3
4
  ===============================================
@@ -1,13 +1,14 @@
1
1
  <!--
2
2
  file: _includes/content/intro.html
3
- description: Enhanced intro section with share, edit, and Copilot agent session buttons
3
+ description: Enhanced intro section with share, edit, and improve/report buttons
4
4
  path: _includes/content/intro.html
5
5
  features:
6
6
  - Responsive share dropdown with multiple social platforms
7
7
  - Edit on GitHub quick link
8
- - Copilot Agent dropdown: select a prompt template to pre-fill a GitHub issue
9
- - Issue body includes prompt template + page context + environment details
10
- - Prompts sourced from _data/prompts.yml
8
+ - "Improve" button opens the page-feedback widget (capture type + description
9
+ + live page context + console logs GitHub issue). Progressive enhancement:
10
+ it is a real link to the issue form, upgraded by assets/js/page-feedback.js.
11
+ See _includes/components/page-feedback.html.
11
12
  - Responsive flex layout: full-width content block, frosted metadata footer with reading time and action buttons
12
13
  - Balanced padding and typography hierarchy via bd-intro-* classes
13
14
  - Metadata row: author, dates, category, difficulty, tags, view source
@@ -271,64 +272,21 @@
271
272
  </ul>
272
273
  </div>
273
274
 
274
- <!-- Copilot Agent Prompt Dropdown -->
275
- <div class="dropdown">
276
- <button class="btn btn-sm btn-success dropdown-toggle" type="button" id="copilotAgentDropdown" data-bs-toggle="dropdown" aria-expanded="false" title="Open a GitHub issue with a Copilot prompt for this page">
277
- <i class="bi bi-robot" aria-hidden="true"></i>
278
- <span class="d-none d-sm-inline ms-1">Copilot Agent</span>
279
- </button>
280
- <ul class="dropdown-menu dropdown-menu-end copilot-agent-menu" aria-labelledby="copilotAgentDropdown">
281
- {% assign current_group = "" %}
282
- {% for prompt in site.data.prompts %}
283
- {% if prompt.group and prompt.group != current_group %}
284
- {% unless forloop.first %}<li><hr class="dropdown-divider"></li>{% endunless %}
285
- <li><h6 class="dropdown-header">{{ prompt.group }}</h6></li>
286
- {% assign current_group = prompt.group %}
287
- {% endif %}
288
- {% capture issue_title %}[{{ prompt.label }}] {{ page.title }}{% endcapture %}
289
- {% capture issue_body %}{{ prompt.body }}
290
- ---
291
-
292
- ## 📄 Page Context
293
-
294
- | Field | Value |
295
- |---|---|
296
- | **Title** | {{ page.title }} |
297
- | **URL** | {{ site.url }}{{ page.url }} |
298
- | **File** | `{{ file_path }}` |
299
- | **Branch** | `{{ repo_branch }}` |
300
- | **Layout** | `{{ page.layout }}` |
301
- | **Collection** | `{{ page.collection }}` |
302
- | **Author** | {{ page.author | default: site.author.name }} |
303
- | **Date** | {{ page.date }} |
304
- | **Last Modified** | {{ page.lastmod }} |
305
- | **Tags** | {{ page.tags | join: ", " }} |
306
- | **Categories** | {{ page.categories | join: ", " }} |
307
-
308
- ## 🔧 Environment
309
-
310
- | Field | Value |
311
- |---|---|
312
- | **Repository** | `{{ site.repository }}` |
313
- | **Site URL** | {{ site.url }} |
314
- | **Jekyll Env** | `{{ jekyll.environment }}` |
315
- | **Theme** | `{{ site.theme | default: site.remote_theme }}` |
316
- | **Base URL** | `{{ site.baseurl }}` |
317
- {% endcapture %}
318
- <li>
319
- <a class="dropdown-item" href="https://github.com/{{ site.repository }}/issues/new?assignees=copilot&labels=ai-agent&title={{ issue_title | url_encode }}&body={{ issue_body | url_encode }}" target="_blank" rel="noopener noreferrer">
320
- <span class="copilot-agent-item">
321
- <i class="bi {{ prompt.icon }} copilot-agent-item__icon" aria-hidden="true"></i>
322
- <span class="copilot-agent-item__text">
323
- <span class="copilot-agent-item__label">{{ prompt.label }}</span>
324
- {% if prompt.description %}<small class="copilot-agent-item__desc">{{ prompt.description }}</small>{% endif %}
325
- </span>
326
- </span>
327
- </a>
328
- </li>
329
- {% endfor %}
330
- </ul>
331
- </div>
275
+ <!-- Improve / Report opens the page-feedback widget (assets/js/page-feedback.js).
276
+ Progressive enhancement: this is a real link to the issue form, so it
277
+ works without JavaScript; page-feedback.js intercepts [data-pf-open]
278
+ clicks and opens the richer capture modal (context + logs + AI) instead. -->
279
+ {% if site.page_feedback.enabled %}
280
+ <a class="btn btn-sm btn-success bd-intro-action-link"
281
+ href="https://github.com/{{ site.repository }}/issues/new?template=page_improvement.yml&labels=page-feedback"
282
+ data-pf-open
283
+ target="_blank"
284
+ rel="noopener noreferrer"
285
+ title="Report a problem or suggest an improvement for this page">
286
+ <i class="bi bi-megaphone" aria-hidden="true"></i>
287
+ <span class="d-none d-sm-inline ms-1">Improve</span>
288
+ </a>
289
+ {% endif %}
332
290
 
333
291
  <!-- Edit on GitHub Button -->
334
292
  <a href="https://github.com/{{ site.repository }}/blob/{{ repo_branch }}/{{ file_path }}"
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-054 {% endcomment %}
1
2
  <!--
2
3
  ===================================================================
3
4
  JSON-LD FAQ SCHEMA - FAQPage Structured Data
@@ -1,3 +1,4 @@
1
+ {% comment %} Features: ZER0-041, ZER0-054 {% endcomment %}
1
2
  <!--
2
3
  ===================================================================
3
4
  SEO SUPPLEMENT - Additional meta tags not covered by jekyll-seo-tag
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-042 {% endcomment %}
1
2
  <!--
2
3
  ===================================================================
3
4
  SITEMAP COMPONENT - Comprehensive Site Navigation Index
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-037 {% endcomment %}
1
2
  {% capture tocWorkspace %}
2
3
  {% comment %}
3
4
  Copyright (c) 2017 Vladimir "allejo" Jimenez
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-031 {% endcomment %}
1
2
  {%- comment -%}
2
3
  Component: color-mode-init
3
4
  Path: _includes/core/color-mode-init.html
@@ -0,0 +1,89 @@
1
+ {%- comment -%}
2
+ ===================================================================
3
+ CONSOLE CAPTURE — early, tiny console/error ring buffer
4
+ ===================================================================
5
+
6
+ File: console-capture.html
7
+ Path: _includes/core/console-capture.html
8
+ Purpose: Capture a capped, in-memory buffer of console output and
9
+ uncaught errors, installed HIGH in <head> — before the
10
+ theme's deferred scripts run — so the page-feedback widget
11
+ (assets/js/page-feedback.js) can attach the logs and JS
12
+ errors that led up to a report.
13
+
14
+ Renders: only when page_feedback.enabled AND page_feedback.capture_logs.
15
+
16
+ Privacy: in-memory only, never persisted, never sent anywhere until the
17
+ user opens the feedback modal, reviews the captured logs, and
18
+ chooses to submit. Any single entry is truncated; the buffer is
19
+ a fixed-size ring (page_feedback.log_limit).
20
+
21
+ Depends on: nothing — pure inline ES5, GitHub Pages-safe (no CDN).
22
+ ===================================================================
23
+ {%- endcomment -%}
24
+ {%- if site.page_feedback.enabled and site.page_feedback.capture_logs -%}
25
+ <script>
26
+ /* page-feedback: early console/error capture — see _includes/core/console-capture.html */
27
+ (function () {
28
+ "use strict";
29
+ if (window.__zer0Feedback && window.__zer0Feedback.installed) { return; }
30
+
31
+ var LIMIT = {{ site.page_feedback.log_limit | default: 40 | jsonify }};
32
+ var MAX_ENTRY = 600; // truncate any single entry so the buffer stays small
33
+ var start = (window.performance && performance.now) ? null : Date.now();
34
+
35
+ var buffer = [];
36
+ function now() { return start === null ? Math.round(performance.now()) : (Date.now() - start); }
37
+
38
+ function stringify(value) {
39
+ if (typeof value === "string") { return value; }
40
+ if (value instanceof Error) {
41
+ return (value.name || "Error") + ": " + (value.message || "") + (value.stack ? "\n" + value.stack : "");
42
+ }
43
+ try { return JSON.stringify(value); } catch (e) { return String(value); }
44
+ }
45
+
46
+ function push(level, args) {
47
+ try {
48
+ var parts = [];
49
+ for (var i = 0; i < args.length; i++) { parts.push(stringify(args[i])); }
50
+ var text = parts.join(" ");
51
+ if (text.length > MAX_ENTRY) { text = text.slice(0, MAX_ENTRY) + "…"; }
52
+ buffer.push({ t: now(), level: level, text: text });
53
+ while (buffer.length > LIMIT) { buffer.shift(); }
54
+ } catch (e) { /* logging must never break the page */ }
55
+ }
56
+
57
+ // Wrap console methods, preserving passthrough to the real console.
58
+ if (window.console) {
59
+ ["log", "info", "warn", "error", "debug"].forEach(function (level) {
60
+ var original = console[level] ? console[level].bind(console) : null;
61
+ console[level] = function () {
62
+ push(level, arguments);
63
+ if (original) { original.apply(null, arguments); }
64
+ };
65
+ });
66
+ }
67
+
68
+ // Uncaught errors and unhandled promise rejections.
69
+ window.addEventListener("error", function (e) {
70
+ if (!e) { return; }
71
+ var where = e.filename ? " (" + e.filename + ":" + (e.lineno || 0) + ":" + (e.colno || 0) + ")" : "";
72
+ push("error", [(e.message || "Script error") + where]);
73
+ });
74
+ window.addEventListener("unhandledrejection", function (e) {
75
+ var reason = e && e.reason;
76
+ var msg = reason && reason.message ? reason.message : stringify(reason);
77
+ push("error", ["Unhandled promise rejection: " + msg]);
78
+ });
79
+
80
+ window.__zer0Feedback = {
81
+ installed: true,
82
+ logs: buffer,
83
+ /** Snapshot the buffer (newest last) for the widget to read. */
84
+ snapshot: function () { return buffer.slice(); },
85
+ clear: function () { buffer.length = 0; }
86
+ };
87
+ })();
88
+ </script>
89
+ {%- endif -%}
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-046 {% endcomment %}
1
2
  <!--
2
3
  ===================================================================
3
4
  FOOTER - Comprehensive Site Footer with Branding & Navigation
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-001 {% endcomment %}
1
2
  <!--
2
3
  ===================================================================
3
4
  HEAD - HTML Document Head Section
@@ -33,6 +34,10 @@
33
34
  <!-- Only load in production builds (JEKYLL_ENV=production); the include also guards against dev hostnames -->
34
35
  {% if jekyll.environment == "production" %}{% include analytics/google-tag-manager-head.html %}{% endif %}
35
36
 
37
+ <!-- Page-feedback: early console/error capture — must run before the theme
38
+ scripts below so a report can include the logs that led up to it. -->
39
+ {% include core/console-capture.html %}
40
+
36
41
  <!-- ================================ -->
37
42
  <!-- JAVASCRIPT LIBRARIES -->
38
43
  <!-- ================================ -->
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-011 {% endcomment %}
1
2
  <!--
2
3
  ===================================================================
3
4
  HEADER - Main Navigation and Site Header
@@ -64,21 +65,10 @@
64
65
  <!-- MOBILE MENU CONTROLS -->
65
66
  <!-- ========================== -->
66
67
 
67
- <!-- Left Sidebar Toggle - Mobile view only (omit when page.sidebar: false matches default layout aside) -->
68
- {% assign sidebar_nav = nil %}
69
- {% if page.sidebar != false %}
70
- {% if page.sidebar and page.sidebar.nav %}
71
- {% assign sidebar_nav = page.sidebar.nav %}
72
- {% endif %}
73
- {% endif %}
74
- {% assign sidebar_has_content = false %}
75
- {% if sidebar_nav %}
76
- {% if sidebar_nav == "auto" or sidebar_nav == "categories" %}
77
- {% assign sidebar_has_content = true %}
78
- {% elsif site.data.navigation[sidebar_nav] %}
79
- {% assign sidebar_has_content = true %}
80
- {% endif %}
81
- {% endif %}
68
+ <!-- Left Sidebar Toggle - Mobile view only. Uses the shared
69
+ resolver (navigation/sidebar-config.html) so this gate can
70
+ never drift from the default layout's aside. -->
71
+ {% include navigation/sidebar-config.html %}
82
72
 
83
73
  {% if sidebar_has_content %}
84
74
  {%- comment -%}
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-050 {% endcomment %}
1
2
  <!--
2
3
  ===================================================================
3
4
  ADMIN NAV — Data-driven sidebar navigation for admin/settings pages
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-040 {% endcomment %}
1
2
  {%- comment -%}
2
3
  ===================================================================
3
4
  BREADCRUMBS — Accessible breadcrumb trail with Schema.org markup
@@ -2,15 +2,9 @@
2
2
  Local graph FAB trigger — rendered from footer.html so the fixed button
3
3
  stays above the footer band. Pair with navigation/local-graph.html (panel).
4
4
  {%- endcomment -%}
5
- {%- assign _effective_sidebar = page.sidebar -%}
6
- {%- if _effective_sidebar == nil -%}
7
- {%- if page.post_type == "featured" or page.post_type == "breaking" -%}
8
- {%- assign _effective_sidebar = false -%}
9
- {%- else -%}
10
- {%- assign _effective_sidebar = true -%}
11
- {%- endif -%}
12
- {%- endif -%}
13
- {%- if page.local_graph != false and _effective_sidebar != false -%}
5
+ {%- comment -%} Shared visibility resolver — sets sidebar_enabled {%- endcomment -%}
6
+ {%- include navigation/sidebar-config.html -%}
7
+ {%- if page.local_graph != false and sidebar_enabled -%}
14
8
  <div id="obsidianLocalGraphFab" class="obsidian-local-graph-fab d-print-none" data-obsidian-local-graph-toggle hidden>
15
9
  <button class="btn btn-primary rounded-circle shadow-lg p-0 obsidian-local-graph-toggle"
16
10
  type="button"
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-045 {% endcomment %}
1
2
  {%- comment -%}
2
3
  ===================================================================
3
4
  LOCAL GRAPH SIDEBAR — collapsible page-scoped graph
@@ -24,15 +25,9 @@
24
25
  Configurable depth: `local_graph_depth: 2` in page front matter (default 1).
25
26
  ===================================================================
26
27
  {%- endcomment -%}
27
- {%- assign _effective_sidebar = page.sidebar -%}
28
- {%- if _effective_sidebar == nil -%}
29
- {%- if page.post_type == "featured" or page.post_type == "breaking" -%}
30
- {%- assign _effective_sidebar = false -%}
31
- {%- else -%}
32
- {%- assign _effective_sidebar = true -%}
33
- {%- endif -%}
34
- {%- endif -%}
35
- {%- if page.local_graph != false and _effective_sidebar != false -%}
28
+ {%- comment -%} Shared visibility resolver — sets sidebar_enabled {%- endcomment -%}
29
+ {%- include navigation/sidebar-config.html -%}
30
+ {%- if page.local_graph != false and sidebar_enabled -%}
36
31
  {%- assign lg_depth = page.local_graph_depth | default: 1 -%}
37
32
  <aside class="offcanvas offcanvas-start obsidian-local-graph-panel"
38
33
  tabindex="-1"
@@ -1,3 +1,4 @@
1
+ {% comment %} Features: ZER0-049, ZER0-055 {% endcomment %}
1
2
  <!--
2
3
  file: navbar.html
3
4
  path: /includes/navbar.html
@@ -1,32 +1,74 @@
1
- <!-- sidebar-categories.html -->
2
- {%- assign categories = include.categories | default: site.categories -%}
1
+ {% comment %} Feature: ZER0-008 {% endcomment %}
2
+ {%- comment -%}
3
+ Component: sidebar-categories
4
+ Path: _includes/navigation/sidebar-categories.html
5
+ Purpose: "categories" / "tags" sidebar modes — collapsible taxonomy
6
+ groups with the posts filed under each term.
7
+ Params: taxonomy (string, optional) — "categories" (default) | "tags"
8
+ categories (map, optional) — explicit term→posts map to render
9
+ (back-compat; overrides the taxonomy lookup)
10
+ title (string, optional) — heading override
11
+ icon (string, optional) — Bootstrap Icons class override
12
+ Front matter (page.sidebar hash, all optional):
13
+ taxonomy — "categories" | "tags" (same as the param)
14
+ limit — max posts listed per term (default: all)
15
+ Depends on: site.categories / site.tags (posts only — Jekyll does not
16
+ index collection docs here), Bootstrap 5 collapse,
17
+ Bootstrap Icons, _sass/core/_sidebar-categories.scss
18
+ Notes: Groups containing the current page start expanded. Each term
19
+ shows a post-count badge.
20
+ {%- endcomment -%}
21
+ {%- assign _type = include.taxonomy | default: page.sidebar.taxonomy | default: "categories" -%}
22
+ {%- if _type == "tags" -%}
23
+ {%- assign _terms = site.tags -%}
24
+ {%- assign _heading = include.title | default: "Tags" -%}
25
+ {%- assign _heading_icon = include.icon | default: "bi-tags" -%}
26
+ {%- assign _id_prefix = "tag-" -%}
27
+ {%- else -%}
28
+ {%- assign _terms = site.categories -%}
29
+ {%- assign _heading = include.title | default: "Categories" -%}
30
+ {%- assign _heading_icon = include.icon | default: "bi-folder2-open" -%}
31
+ {%- assign _id_prefix = "cat-" -%}
32
+ {%- endif -%}
33
+ {%- assign _terms = include.categories | default: _terms -%}
34
+ {%- assign _terms = _terms | sort -%}
35
+ {%- assign _limit = page.sidebar.limit -%}
3
36
  <div class="sidebar-categories nav-tree nav-tree--categories w-100">
4
37
  <h2 class="sidebar-categories-heading h6 text-uppercase text-body-secondary fw-semibold mb-3 d-flex align-items-center gap-2">
5
- <i class="bi bi-folder2-open" aria-hidden="true"></i>
6
- <span>Categories</span>
38
+ <i class="bi {{ _heading_icon }}" aria-hidden="true"></i>
39
+ <span>{{ _heading }}</span>
7
40
  </h2>
8
41
 
9
42
  <ul class="list-unstyled nav-tree-root mb-0">
10
- {%- for category in categories -%}
11
- {%- assign category_id = category[0] | slugify | prepend: "cat-" -%}
43
+ {%- for term in _terms -%}
44
+ {%- assign term_id = term[0] | slugify | prepend: _id_prefix -%}
45
+ {%- assign term_posts = term[1] -%}
46
+ {%- assign _active_here = term_posts | where: "url", page.url | size -%}
47
+ {%- if _active_here > 0 -%}
48
+ {%- assign _expanded = true -%}
49
+ {%- else -%}
50
+ {%- assign _expanded = false -%}
51
+ {%- endif -%}
12
52
  <li class="nav-tree-item sidebar-categories-group" data-depth="0">
13
- <button class="btn btn-link nav-tree-toggle sidebar-categories-toggle w-100 d-flex align-items-center text-start collapsed"
53
+ <button class="btn btn-link nav-tree-toggle sidebar-categories-toggle w-100 d-flex align-items-center text-start{% unless _expanded %} collapsed{% endunless %}"
14
54
  type="button"
15
55
  data-bs-toggle="collapse"
16
- data-bs-target="#{{ category_id }}-list"
17
- aria-expanded="false"
18
- aria-controls="{{ category_id }}-list">
56
+ data-bs-target="#{{ term_id }}-list"
57
+ aria-expanded="{{ _expanded }}"
58
+ aria-controls="{{ term_id }}-list">
19
59
  <i class="bi bi-folder sidebar-categories-toggle__icon" aria-hidden="true"></i>
20
- <span class="sidebar-categories-toggle__label flex-grow-1">{{ category[0] }}</span>
60
+ <span class="sidebar-categories-toggle__label flex-grow-1">{{ term[0] }}</span>
61
+ <span class="badge text-bg-secondary bg-opacity-25 me-1">{{ term[1] | size }}</span>
21
62
  <i class="bi bi-chevron-down nav-tree-chevron sidebar-categories-toggle__chevron" aria-hidden="true"></i>
22
63
  </button>
23
64
 
24
- <div class="collapse" id="{{ category_id }}-list">
65
+ <div class="collapse{% if _expanded %} show{% endif %}" id="{{ term_id }}-list">
25
66
  <ul class="list-unstyled nav-tree-children sidebar-categories-posts mb-0">
26
- {%- for post in category[1] -%}
67
+ {%- for post in term_posts -%}
68
+ {%- if _limit and forloop.index0 >= _limit -%}{%- break -%}{%- endif -%}
27
69
  <li class="nav-tree-item" data-depth="1">
28
70
  <a href="{{ post.url | relative_url }}"
29
- class="nav-tree-link sidebar-categories-link d-flex align-items-start rounded text-decoration-none{% if page.url == post.url %} active{% endif %}">
71
+ class="nav-tree-link sidebar-categories-link d-flex align-items-start rounded text-decoration-none{% if page.url == post.url %} active{% endif %}"{% if page.url == post.url %} aria-current="page"{% endif %}>
30
72
  <i class="bi bi-file-earmark-text sidebar-categories-link__icon" aria-hidden="true"></i>
31
73
  <span class="sidebar-categories-link__text">{{ post.title }}</span>
32
74
  </a>