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,20 +1,35 @@
1
+ {% comment %} Feature: ZER0-046 {% endcomment %}
1
2
  <!--
2
3
  ===================================================================
3
4
  INFO SECTION - Settings Offcanvas with Tabbed Interface
4
5
  ===================================================================
5
-
6
+
6
7
  File: info-section.html
7
8
  Path: _includes/components/info-section.html
8
- Purpose: Unified settings offcanvas panel with tabs for Settings, Environment, Developer
9
-
9
+ Purpose: Unified settings offcanvas panel with three tabs
10
+ Appearance (color mode, skin, backgrounds, primary color),
11
+ Site (environment, theme & build info, admin links) and
12
+ Developer (page location, metadata, source shortcuts).
13
+
10
14
  Dependencies:
11
15
  - Bootstrap 5 Offcanvas, Tabs
16
+ - _includes/components/env-detect.html (sets is_production/build_env)
17
+ - _includes/components/halfmoon.html (color-mode segmented control)
18
+ - _includes/components/background-settings.html (skin + backgrounds)
12
19
  - _includes/components/env-switcher.html
13
20
  - _includes/components/theme-info.html
21
+ - _includes/components/admin-links.html (cached admin quick links)
14
22
  - _includes/components/dev-shortcuts.html
15
- - _includes/components/halfmoon.html
16
23
  - _includes/navigation/breadcrumbs.html
17
-
24
+ - assets/js/modules/theme/appearance.js mounts the primary-color
25
+ picker into #zer0AppearanceSlot when site.appearance_panel is on
26
+
27
+ Notes:
28
+ - #info-section and the three pane ids are a public surface: the
29
+ header, navbar, footer, unified drawer and Playwright suites all
30
+ target them. Rename with care.
31
+ - Appearance preferences persist in localStorage only (no cookies).
32
+
18
33
  References:
19
34
  - https://getbootstrap.com/docs/5.3/components/offcanvas/
20
35
  - https://getbootstrap.com/docs/5.3/components/navs-tabs/
@@ -22,38 +37,29 @@
22
37
  -->
23
38
 
24
39
  {% include components/env-detect.html %}
25
- {%- comment -%}
26
- Admin page URL lookup string is computed once per build by the
27
- _plugins/admin_page_urls.rb :pre_render hook and exposed as
28
- site.data.admin_page_urls. This avoids re-running
29
- `site.html_pages | where_exp | map | join` on every page render
30
- (150+ renders → 1 plugin call per build).
31
- {%- endcomment -%}
32
- {%- assign _admin_page_urls = site.data.admin_page_urls | default: "" -%}
40
+
33
41
  <!-- Settings Offcanvas -->
34
42
  <div class="offcanvas offcanvas-end" tabindex="-1" id="info-section" aria-labelledby="infoSectionLabel">
35
-
43
+
36
44
  <!-- Offcanvas Header -->
37
45
  <div class="offcanvas-header border-bottom pb-0 d-block">
38
46
  <div class="d-flex align-items-center justify-content-between mb-2">
39
47
  <h5 class="offcanvas-title" id="infoSectionLabel">
40
- <i class="bi bi-gear-wide-connected me-2"></i>Settings
48
+ <i class="bi bi-gear-wide-connected me-2" aria-hidden="true"></i>Settings
41
49
  </h5>
42
50
  <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
43
51
  </div>
44
-
52
+
45
53
  <!-- Tab Navigation -->
46
- <ul class="nav nav-tabs" id="infoTabs" role="tablist">
54
+ <ul class="nav nav-tabs nav-fill" id="infoTabs" role="tablist">
47
55
  <li class="nav-item" role="presentation">
48
- <button class="nav-link active" id="settings-tab" data-bs-toggle="tab" data-bs-target="#settings-pane" type="button" role="tab" aria-controls="settings-pane" aria-selected="true">
49
- <i class="bi bi-sliders me-1"></i>
50
- <span class="d-none d-sm-inline">Settings</span>
56
+ <button class="nav-link active" id="appearance-tab" data-bs-toggle="tab" data-bs-target="#appearance-pane" type="button" role="tab" aria-controls="appearance-pane" aria-selected="true">
57
+ <i class="bi bi-palette me-1" aria-hidden="true"></i>Appearance
51
58
  </button>
52
59
  </li>
53
60
  <li class="nav-item" role="presentation">
54
- <button class="nav-link" id="environment-tab" data-bs-toggle="tab" data-bs-target="#environment-pane" type="button" role="tab" aria-controls="environment-pane" aria-selected="false">
55
- <i class="bi bi-hdd-network me-1"></i>
56
- <span class="d-none d-sm-inline">Environment</span>
61
+ <button class="nav-link" id="site-tab" data-bs-toggle="tab" data-bs-target="#site-pane" type="button" role="tab" aria-controls="site-pane" aria-selected="false">
62
+ <i class="bi bi-globe2 me-1" aria-hidden="true"></i>Site
57
63
  {% if is_production %}
58
64
  <span class="badge bg-success ms-1 d-none d-md-inline-block">Prod</span>
59
65
  {% else %}
@@ -63,139 +69,87 @@
63
69
  </li>
64
70
  <li class="nav-item" role="presentation">
65
71
  <button class="nav-link" id="developer-tab" data-bs-toggle="tab" data-bs-target="#developer-pane" type="button" role="tab" aria-controls="developer-pane" aria-selected="false">
66
- <i class="bi bi-code-slash me-1"></i>
67
- <span class="d-none d-sm-inline">Developer</span>
68
- </button>
69
- </li>
70
- <li class="nav-item" role="presentation">
71
- <button class="nav-link" id="background-tab" data-bs-toggle="tab" data-bs-target="#background-pane" type="button" role="tab" aria-controls="background-pane" aria-selected="false">
72
- <i class="bi bi-palette2 me-1"></i>
73
- <span class="d-none d-sm-inline">Background</span>
72
+ <i class="bi bi-code-slash me-1" aria-hidden="true"></i>Developer
74
73
  </button>
75
74
  </li>
76
75
  </ul>
77
76
  </div>
78
-
77
+
79
78
  <!-- Offcanvas Body with Tab Content -->
80
79
  <div class="offcanvas-body">
81
80
  <div class="tab-content" id="infoTabContent">
82
-
83
- <!-- Settings Tab -->
84
- <div class="tab-pane fade show active" id="settings-pane" role="tabpanel" aria-labelledby="settings-tab" tabindex="0">
85
-
86
- <!-- Search -->
81
+
82
+ <!-- Appearance Tab — everything that changes how the site looks -->
83
+ <div class="tab-pane fade show active" id="appearance-pane" role="tabpanel" aria-labelledby="appearance-tab" tabindex="0">
84
+
85
+ <!-- Color Mode -->
87
86
  <div class="mb-4">
88
87
  <h6 class="text-body-secondary small text-uppercase fw-semibold mb-2">
89
- <i class="bi bi-search me-1"></i>Search
88
+ <i class="bi bi-moon-stars me-1" aria-hidden="true"></i>Color Mode
90
89
  </h6>
91
- {% include components/searchbar.html %}
90
+ {% include components/halfmoon.html class="w-100" %}
92
91
  </div>
93
-
94
- <!-- Theme Toggle -->
92
+
93
+ <!-- Theme skin + SVG backgrounds (skin buttons, toggle, opacity) -->
94
+ {% include_cached components/background-settings.html %}
95
+
96
+ <!-- Primary color picker mounts here (appearance.js, optional) -->
97
+ <div id="zer0AppearanceSlot"></div>
98
+
99
+ <p class="text-body-secondary small mb-0 mt-4">
100
+ <i class="bi bi-device-hdd me-1" aria-hidden="true"></i>
101
+ Appearance preferences are saved in this browser only.
102
+ </p>
103
+ </div>
104
+
105
+ <!-- Site Tab — where the site runs and what it is built from -->
106
+ <div class="tab-pane fade" id="site-pane" role="tabpanel" aria-labelledby="site-tab" tabindex="0">
107
+
108
+ <!-- Environment -->
95
109
  <div class="mb-4">
96
110
  <h6 class="text-body-secondary small text-uppercase fw-semibold mb-2">
97
- <i class="bi bi-moon-stars me-1"></i>Appearance
98
- </h6>
99
- <div class="d-flex align-items-center justify-content-between p-3 bg-body-tertiary rounded">
100
- <span>Theme Mode</span>
101
- {% include components/halfmoon.html %}
102
- </div>
103
- </div>
104
-
105
- <!-- Theme Info (Collapsible) -->
106
- <div class="mb-3">
107
- <h6 class="text-body-secondary small text-uppercase fw-semibold mb-2">
108
- <i class="bi bi-info-circle me-1"></i>About
111
+ <i class="bi bi-hdd-network me-1" aria-hidden="true"></i>Environment
109
112
  </h6>
110
- <button class="btn btn-outline-secondary btn-sm w-100 d-flex justify-content-between align-items-center" type="button" data-bs-toggle="collapse" data-bs-target="#themeInfoCollapse" aria-expanded="false" aria-controls="themeInfoCollapse">
111
- <span>Theme & Build Info</span>
112
- <i class="bi bi-chevron-down"></i>
113
- </button>
114
- <div class="collapse mt-2" id="themeInfoCollapse">
115
- {% include components/theme-info.html %}
116
- </div>
113
+ {% include components/env-switcher.html %}
117
114
  </div>
118
115
 
119
- <!-- Admin Quick Links — only render links to pages that actually exist
120
- in the build (prevents 404s on bare-minimum / remote-theme sites
121
- that haven't created the admin pages). -->
122
- {% assign _cfg_page = false %}
123
- {% assign _theme_page = false %}
124
- {% assign _nav_page = false %}
125
- {% if _admin_page_urls contains "|/about/config/|" %}{% assign _cfg_page = true %}{% endif %}
126
- {% if _admin_page_urls contains "|/about/settings/theme/|" %}{% assign _theme_page = true %}{% endif %}
127
- {% if _admin_page_urls contains "|/about/settings/navigation/|" %}{% assign _nav_page = true %}{% endif %}
128
- {% if _cfg_page or _theme_page or _nav_page %}
129
- <div class="mb-3">
116
+ <!-- Theme & Build -->
117
+ <div class="mb-4">
130
118
  <h6 class="text-body-secondary small text-uppercase fw-semibold mb-2">
131
- <i class="bi bi-speedometer2 me-1"></i>Admin Dashboard
119
+ <i class="bi bi-info-circle me-1" aria-hidden="true"></i>Theme &amp; Build
132
120
  </h6>
133
- <div class="list-group list-group-flush small">
134
- {% if _cfg_page %}
135
- <a href="{{ '/about/config/' | relative_url }}" class="list-group-item list-group-item-action d-flex align-items-center px-0">
136
- <i class="bi bi-gear me-2 text-body-secondary"></i>Configuration
137
- </a>
138
- {% endif %}
139
- {% if _theme_page %}
140
- <a href="{{ '/about/settings/theme/' | relative_url }}" class="list-group-item list-group-item-action d-flex align-items-center px-0">
141
- <i class="bi bi-palette me-2 text-body-secondary"></i>Theme Customizer
142
- </a>
143
- {% endif %}
144
- {% if _nav_page %}
145
- <a href="{{ '/about/settings/navigation/' | relative_url }}" class="list-group-item list-group-item-action d-flex align-items-center px-0">
146
- <i class="bi bi-signpost-2 me-2 text-body-secondary"></i>Navigation Editor
147
- </a>
148
- {% endif %}
149
- </div>
121
+ {% include components/theme-info.html %}
150
122
  </div>
151
- {% endif %}
152
- </div>
153
-
154
- <!-- Environment Tab -->
155
- <div class="tab-pane fade" id="environment-pane" role="tabpanel" aria-labelledby="environment-tab" tabindex="0">
156
- {% include components/env-switcher.html %}
157
-
158
- <!-- Admin Link — only when target page exists -->
159
- {% assign _env_page = false %}
160
- {% if _admin_page_urls contains "|/about/settings/environment/|" %}{% assign _env_page = true %}{% endif %}
161
- {% if _env_page %}
162
- <div class="mt-4 pt-3 border-top">
163
- <a href="{{ '/about/settings/environment/' | relative_url }}" class="btn btn-outline-secondary btn-sm w-100">
164
- <i class="bi bi-box-arrow-up-right me-1"></i>Full Environment &amp; Build Info
165
- </a>
166
- </div>
167
- {% endif %}
123
+
124
+ <!-- Admin quick links — cached include; renders only links to pages
125
+ that exist in the build (see components/admin-links.html) -->
126
+ {% include_cached components/admin-links.html %}
168
127
  </div>
169
-
170
- <!-- Developer Tab -->
128
+
129
+ <!-- Developer Tab — tools scoped to the current page -->
171
130
  <div class="tab-pane fade" id="developer-pane" role="tabpanel" aria-labelledby="developer-tab" tabindex="0">
172
-
173
- <!-- Page Location -->
131
+
132
+ <!-- Page Location — breadcrumbs render nothing on the homepage or
133
+ when site.breadcrumbs is off, so the section mirrors that gate -->
134
+ {% if page.url != "/" and site.breadcrumbs %}
174
135
  <div class="mb-4">
175
136
  <h6 class="text-body-secondary small text-uppercase fw-semibold mb-2">
176
- <i class="bi bi-signpost-split me-1"></i>Page Location
137
+ <i class="bi bi-signpost-split me-1" aria-hidden="true"></i>Page Location
177
138
  </h6>
178
139
  {% include navigation/breadcrumbs.html %}
179
140
  </div>
180
-
181
- <!-- Source Code Shortcuts -->
182
- <div class="mb-4">
183
- <h6 class="text-body-secondary small text-uppercase fw-semibold mb-2">
184
- <i class="bi bi-code-square me-1"></i>Source Code
185
- </h6>
186
- {% include components/dev-shortcuts.html %}
187
- </div>
188
-
141
+ {% endif %}
142
+
189
143
  <!-- Page Metadata -->
190
- <div class="mb-3">
144
+ <div class="mb-4">
191
145
  <h6 class="text-body-secondary small text-uppercase fw-semibold mb-2">
192
- <i class="bi bi-file-earmark-code me-1"></i>Page Info
146
+ <i class="bi bi-file-earmark-code me-1" aria-hidden="true"></i>Page Info
193
147
  </h6>
194
148
  <div class="table-responsive">
195
149
  <table class="table table-sm table-hover mb-0 small">
196
150
  <tbody>
197
151
  <tr>
198
- <td class="text-body-secondary" style="width: 30%;">Layout</td>
152
+ <td class="text-body-secondary w-25">Layout</td>
199
153
  <td><code>{{ page.layout | default: "none" }}</code></td>
200
154
  </tr>
201
155
  <tr>
@@ -220,14 +174,57 @@
220
174
  </table>
221
175
  </div>
222
176
  </div>
223
- </div>
224
-
225
- <!-- Background Tab -->
226
- <div class="tab-pane fade" id="background-pane" role="tabpanel" aria-labelledby="background-tab" tabindex="0">
227
- {% include_cached components/background-settings.html %}
177
+
178
+ <!-- Source Code Shortcuts -->
179
+ <div class="mb-3">
180
+ <h6 class="text-body-secondary small text-uppercase fw-semibold mb-2">
181
+ <i class="bi bi-code-square me-1" aria-hidden="true"></i>Source Code
182
+ </h6>
183
+ {% include components/dev-shortcuts.html %}
184
+ </div>
228
185
  </div>
229
186
 
230
187
  </div>
231
188
  </div>
232
-
233
- </div>
189
+
190
+ </div>
191
+
192
+ <!-- Copy-to-clipboard for the panel's URL fields (event delegation; no
193
+ inline handlers). Buttons declare data-zer0-copy="<text>" or
194
+ data-zer0-copy-target="#input-id". -->
195
+ <script>
196
+ document.addEventListener("click", function (e) {
197
+ var btn = e.target.closest("[data-zer0-copy], [data-zer0-copy-target]");
198
+ if (!btn) return;
199
+ var text = btn.getAttribute("data-zer0-copy") || "";
200
+ var targetSel = btn.getAttribute("data-zer0-copy-target");
201
+ if (!text && targetSel) {
202
+ // Guard the selector lookup — a missing/invalid value must not throw
203
+ // inside a document-level click listener.
204
+ try {
205
+ var target = document.querySelector(targetSel);
206
+ text = target && "value" in target ? target.value : "";
207
+ } catch (err) {
208
+ text = "";
209
+ }
210
+ }
211
+ if (!text || !navigator.clipboard) return;
212
+ navigator.clipboard.writeText(text).then(function () {
213
+ zer0CopyFeedback(btn, true);
214
+ }).catch(function () {
215
+ zer0CopyFeedback(btn, false);
216
+ });
217
+ });
218
+
219
+ function zer0CopyFeedback(btn, success) {
220
+ var original = btn.innerHTML;
221
+ btn.innerHTML = success ? '<i class="bi bi-check2"></i>' : '<i class="bi bi-x"></i>';
222
+ btn.classList.add(success ? "btn-success" : "btn-danger");
223
+ btn.classList.remove("btn-outline-secondary");
224
+ setTimeout(function () {
225
+ btn.innerHTML = original;
226
+ btn.classList.remove("btn-success", "btn-danger");
227
+ btn.classList.add("btn-outline-secondary");
228
+ }, 1500);
229
+ }
230
+ </script>
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-013 {% endcomment %}
1
2
  <!--
2
3
  ===================================================================
3
4
  MERMAID DIAGRAM INTEGRATION (v2.1 - GitHub Pages Compatible)
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-066 {% endcomment %}
1
2
  <!--
2
3
  ===================================================================
3
4
  NANOBAR - Config-Driven Page Loading Progress Bar
@@ -0,0 +1,252 @@
1
+ {%- comment -%}
2
+ ===================================================================
3
+ PAGE FEEDBACK — "Improve this page" capture widget
4
+ ===================================================================
5
+
6
+ Component: page-feedback
7
+ Path: _includes/components/page-feedback.html
8
+ Purpose: Turn what a reader is looking at into a well-formed GitHub
9
+ issue — request type + description + live page context +
10
+ captured console/error logs — filed with labels that exist.
11
+ Replaces the build-time Copilot dropdown that used to live in
12
+ content/intro.html and baked _data/prompts.yml into <a href>s.
13
+
14
+ Params: none — configured via site.page_feedback (+ site.data.feedback_types)
15
+ Depends on:
16
+ - assets/js/page-feedback.js (behaviour)
17
+ - _sass/components/_page-feedback.scss (styling)
18
+ - _includes/core/console-capture.html (the log buffer, installed in <head>)
19
+ - _data/feedback_types.yml (request-type taxonomy)
20
+ - FAB tokens (_sass/tokens/_spacing.scss, _layers.scss)
21
+
22
+ Layers:
23
+ - Base (always): opens a pre-filled github.com/issues/new form. No token,
24
+ works on any fork / GitHub Pages.
25
+ - AI enrichment (optional): when the chat proxy is deployed
26
+ (ai_chat.proxy_ready), an "Analyze with AI" step sends the capture to
27
+ Claude — via Claude Code OAuth, server-side — to clarify, vet,
28
+ prioritize, and recommend before the issue is filed.
29
+
30
+ Triggers: the global FAB below, plus any element with [data-pf-open]
31
+ elsewhere on the page (e.g. the button in content/intro.html).
32
+ Add data-pf-type="<id>" to preselect a request type.
33
+
34
+ a11y: role=dialog + aria-modal, labelled title, focus trap, Escape to
35
+ close, backdrop click to close, aria-live status region.
36
+ ===================================================================
37
+ {%- endcomment -%}
38
+
39
+ {%- unless site.page_feedback.enabled -%}{%- comment -%} widget disabled {%- endcomment -%}
40
+ {%- else -%}
41
+
42
+ {%- comment -%}
43
+ Booleans aren't evaluated inside assign tags (Liquid stores a truthy string),
44
+ so compute render flags with if-tags. The AI layer needs the chat proxy
45
+ deployed — reuse ai_chat.proxy_ready rather than a second readiness flag.
46
+ {%- endcomment -%}
47
+ {%- assign pf_ai_available = false -%}
48
+ {%- if site.page_feedback.ai.enabled and site.ai_chat.proxy_ready -%}
49
+ {%- assign pf_ai_available = true -%}
50
+ {%- endif -%}
51
+ {%- assign pf_show_fab = false -%}
52
+ {%- if site.page_feedback.fab -%}{%- assign pf_show_fab = true -%}{%- endif -%}
53
+
54
+ {%- comment -%} Repo + file coordinates for the issue body (mirrors content/intro.html). {%- endcomment -%}
55
+ {%- assign pf_repo = site.repository | join: "" -%}
56
+ {%- assign pf_branch = site.branch | default: "main" -%}
57
+ {%- assign pf_source_url = "" -%}
58
+ {%- if pf_repo != "" and page.path -%}
59
+ {%- capture pf_source_url -%}https://github.com/{{ pf_repo }}/blob/{{ pf_branch }}/{{ page.path }}{%- endcapture -%}
60
+ {%- endif -%}
61
+ {%- assign pf_theme = site.theme | default: site.remote_theme | default: "" -%}
62
+
63
+ {%- if pf_show_fab -%}
64
+ <!-- Global feedback FAB (page-invariant; opens the modal) -->
65
+ <button type="button"
66
+ id="pageFeedbackFab"
67
+ class="pf-fab btn shadow-lg position-fixed"
68
+ data-pf-open
69
+ aria-haspopup="dialog"
70
+ aria-controls="pageFeedbackModal"
71
+ title="{{ site.page_feedback.fab_label | default: 'Improve this page' | escape }}"
72
+ aria-label="{{ site.page_feedback.fab_label | default: 'Improve this page' | escape }}">
73
+ <i class="bi {{ site.page_feedback.fab_icon | default: 'bi-megaphone' }}" aria-hidden="true"></i>
74
+ </button>
75
+ {%- endif -%}
76
+
77
+ <!-- Feedback modal (self-contained overlay; controlled by page-feedback.js).
78
+ `position-fixed` is required, not cosmetic: it opts this <body> child out of
79
+ the .zer0-bg-body flattening rule (_sass/theme/_backgrounds.scss) that would
80
+ otherwise force position:relative; z-index:1 and let the cookie banner steal
81
+ the dialog's clicks. -->
82
+ <div class="pf-overlay position-fixed" id="pageFeedbackModal" role="dialog" aria-modal="true"
83
+ aria-labelledby="pfTitle" aria-describedby="pfIntro" hidden>
84
+ <div class="pf-dialog shadow-lg" role="document">
85
+
86
+ <header class="pf-header">
87
+ <div class="pf-header-text">
88
+ <h2 id="pfTitle" class="pf-title h5 mb-1">
89
+ <i class="bi bi-megaphone me-2" aria-hidden="true"></i>Share feedback
90
+ </h2>
91
+ <p id="pfIntro" class="pf-intro text-muted mb-0">
92
+ Report a problem or suggest an improvement — it becomes a GitHub issue
93
+ with the page details attached.
94
+ </p>
95
+ </div>
96
+ <button type="button" class="pf-close btn btn-sm" id="pfClose" aria-label="Close feedback dialog">
97
+ <i class="bi bi-x-lg" aria-hidden="true"></i>
98
+ </button>
99
+ </header>
100
+
101
+ <form id="pfForm" class="pf-body" novalidate>
102
+
103
+ <!-- Step 1 — request type -->
104
+ <fieldset class="pf-field pf-types">
105
+ <legend class="pf-legend">What kind of feedback?</legend>
106
+ {%- assign pf_group = "" -%}
107
+ {%- for t in site.data.feedback_types -%}
108
+ {%- if t.group != pf_group -%}
109
+ {%- unless forloop.first -%}</div>{%- endunless -%}
110
+ <p class="pf-types-group">{{ t.group }}</p>
111
+ <div class="pf-types-grid" role="radiogroup" aria-label="{{ t.group | escape }}">
112
+ {%- assign pf_group = t.group -%}
113
+ {%- endif -%}
114
+ <button type="button"
115
+ class="pf-type"
116
+ role="radio"
117
+ aria-checked="false"
118
+ data-pf-type-id="{{ t.id }}"
119
+ tabindex="{% if forloop.first %}0{% else %}-1{% endif %}">
120
+ <i class="bi {{ t.icon | default: 'bi-chat-dots' }} pf-type-icon" aria-hidden="true"></i>
121
+ <span class="pf-type-text">
122
+ <span class="pf-type-label">{{ t.label }}</span>
123
+ {%- if t.description -%}<span class="pf-type-desc">{{ t.description }}</span>{%- endif -%}
124
+ </span>
125
+ </button>
126
+ {%- if forloop.last -%}</div>{%- endif -%}
127
+ {%- endfor -%}
128
+ </fieldset>
129
+
130
+ <!-- Step 2 — description -->
131
+ <div class="pf-field">
132
+ <label for="pfDescription" class="pf-label">Describe it</label>
133
+ <textarea id="pfDescription" class="form-control" rows="4"
134
+ placeholder="Pick a type above, then tell us what you have in mind."
135
+ aria-describedby="pfDescHelp" required></textarea>
136
+ <p id="pfDescHelp" class="pf-help text-muted">
137
+ Be specific — what you expected, what happened, or what would be better.
138
+ </p>
139
+ </div>
140
+
141
+ <!-- Step 3 — what gets attached -->
142
+ <details class="pf-field pf-context" id="pfContextWrap">
143
+ <summary class="pf-summary">
144
+ <i class="bi bi-paperclip me-1" aria-hidden="true"></i>What gets attached
145
+ <span class="pf-context-count" id="pfContextCount"></span>
146
+ </summary>
147
+ <ul class="pf-context-list" id="pfContextList" aria-live="polite">
148
+ <!-- page title / url / path filled in by JS -->
149
+ </ul>
150
+
151
+ {%- if site.page_feedback.capture_logs -%}
152
+ <div class="pf-logs" id="pfLogsWrap" hidden>
153
+ <label class="pf-check">
154
+ <input type="checkbox" id="pfIncludeLogs" checked>
155
+ Include <span id="pfLogsCount">0</span> captured console/error line(s)
156
+ </label>
157
+ <pre class="pf-logs-preview" id="pfLogsPreview" tabindex="0" aria-label="Captured console logs"></pre>
158
+ <p class="pf-help text-muted mb-0">
159
+ <i class="bi bi-shield-check me-1" aria-hidden="true"></i>
160
+ These come from your browser only and are never sent until you submit.
161
+ Review them and untick the box to leave them out.
162
+ </p>
163
+ </div>
164
+ {%- endif -%}
165
+ </details>
166
+
167
+ {%- if pf_ai_available -%}
168
+ <!-- Step 4 — optional AI review (proxy + Claude Code OAuth) -->
169
+ <div class="pf-field pf-ai" id="pfAiWrap">
170
+ <div class="pf-ai-head">
171
+ <button type="button" class="btn btn-sm btn-outline-primary" id="pfAnalyze">
172
+ <i class="bi bi-robot me-1" aria-hidden="true"></i>Analyze with AI
173
+ </button>
174
+ <span class="pf-ai-hint text-muted">Optional — clarifies, prioritizes, and suggests labels.</span>
175
+ </div>
176
+ <div class="pf-ai-result" id="pfAiResult" aria-live="polite" hidden></div>
177
+ </div>
178
+ {%- endif -%}
179
+
180
+ <p class="pf-status" id="pfStatus" role="status" aria-live="polite"></p>
181
+
182
+ </form>
183
+
184
+ <footer class="pf-footer">
185
+ <button type="button" class="btn btn-link btn-sm pf-copy" id="pfCopy">
186
+ <i class="bi bi-clipboard me-1" aria-hidden="true"></i>Copy details
187
+ </button>
188
+ <div class="pf-footer-primary">
189
+ <button type="button" class="btn btn-secondary btn-sm" id="pfCancel">Cancel</button>
190
+ <button type="submit" form="pfForm" class="btn btn-primary btn-sm" id="pfSubmit" disabled>
191
+ <i class="bi bi-github me-1" aria-hidden="true"></i>
192
+ <span class="pf-submit-label">Open GitHub issue</span>
193
+ </button>
194
+ </div>
195
+ </footer>
196
+
197
+ </div>
198
+ </div>
199
+
200
+ <!-- Widget configuration consumed by assets/js/page-feedback.js -->
201
+ <script id="pageFeedbackConfig" type="application/json">
202
+ {
203
+ "enabled": true,
204
+ "mode": {{ site.page_feedback.mode | default: "url" | jsonify }},
205
+ "endpoint": {{ site.page_feedback.endpoint | default: "/api/feedback" | jsonify }},
206
+ "issueEndpoint": {{ site.page_feedback.issue_endpoint | default: "/api/github/issue" | jsonify }},
207
+ "repository": {{ pf_repo | jsonify }},
208
+ "baseBranch": {{ pf_branch | jsonify }},
209
+ "defaultLabels": {% if site.page_feedback.default_labels %}{{ site.page_feedback.default_labels | jsonify }}{% else %}[]{% endif %},
210
+ "assignee": {{ site.page_feedback.assignee | default: "" | jsonify }},
211
+ "captureLogs": {{ site.page_feedback.capture_logs | default: true | jsonify }},
212
+ "fabLabel": {{ site.page_feedback.fab_label | default: "Improve this page" | jsonify }},
213
+ "ai": {
214
+ "available": {{ pf_ai_available | jsonify }},
215
+ "auto": {{ site.page_feedback.ai.auto | default: false | jsonify }},
216
+ "model": {{ site.page_feedback.ai.model | default: "claude-opus-4-8" | jsonify }},
217
+ "maxTokens": {{ site.page_feedback.ai.max_tokens | default: 1024 }}
218
+ }
219
+ }
220
+ </script>
221
+
222
+ <!-- Request-type taxonomy (from _data/feedback_types.yml) -->
223
+ <script id="pageFeedbackTypes" type="application/json">
224
+ {{ site.data.feedback_types | jsonify }}
225
+ </script>
226
+
227
+ <!-- Live page context (build-time front matter; runtime detail added by JS) -->
228
+ <script id="pageFeedbackContext" type="application/json">
229
+ {
230
+ "title": {{ page.title | default: site.title | jsonify }},
231
+ "url": {{ page.url | absolute_url | jsonify }},
232
+ "path": {{ page.path | default: "" | jsonify }},
233
+ "sourceUrl": {{ pf_source_url | jsonify }},
234
+ "layout": {{ page.layout | default: "" | jsonify }},
235
+ "collection": {{ page.collection | default: "" | jsonify }},
236
+ "categories": {{ page.categories | default: "" | jsonify }},
237
+ "tags": {{ page.tags | default: "" | jsonify }},
238
+ "author": {{ page.author | default: site.author.name | default: "" | jsonify }},
239
+ "date": {{ page.date | date: "%Y-%m-%d" | default: "" | jsonify }},
240
+ "lastmod": {{ page.last_modified_at | default: page.lastmod | date: "%Y-%m-%d" | default: "" | jsonify }},
241
+ "branch": {{ pf_branch | jsonify }},
242
+ "repository": {{ pf_repo | jsonify }},
243
+ "siteUrl": {{ site.url | default: "" | jsonify }},
244
+ "baseurl": {{ site.baseurl | default: "" | jsonify }},
245
+ "theme": {{ pf_theme | jsonify }},
246
+ "jekyllEnv": {{ jekyll.environment | jsonify }}
247
+ }
248
+ </script>
249
+
250
+ <script defer src="{{ '/assets/js/page-feedback.js' | relative_url }}"></script>
251
+
252
+ {%- endunless -%}
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-064 {% endcomment %}
1
2
  <!--
2
3
  ===================================================================
3
4
  POST TYPE BADGE COMPONENT - Visual indicator for article types
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-004 {% endcomment %}
1
2
  {% comment %}
2
3
  PREVIEW IMAGE - Simple, Consistent Image Rendering
3
4
 
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-032 {% endcomment %}
1
2
  <!--
2
3
  ===================================================================
3
4
  SEARCH MODAL - Site-wide Search Popup
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-032 {% endcomment %}
1
2
 
2
3
  <!--
3
4
  title: Searchbar and hits
@@ -1,3 +1,4 @@
1
+ {% comment %} Feature: ZER0-009 {% endcomment %}
1
2
  {%- comment -%}
2
3
  ===================================================================
3
4
  SHORTCUTS-MODAL — Keyboard-shortcut reference