jekyll-theme-zer0 1.28.0 → 1.30.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 (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1143 -13
  3. data/_data/README.md +2 -0
  4. data/_data/ai.yml +5 -3
  5. data/_data/ai_pricing.yml +36 -0
  6. data/_data/backlog.yml +507 -2
  7. data/_data/consumers.yml +157 -9
  8. data/_data/features.yml +303 -20
  9. data/_data/feedback_types.yml +17 -12
  10. data/_data/i18n/fr.yml +12 -7
  11. data/_data/i18n/manifest.yml +39 -12
  12. data/_data/ingredient_densities.yml +122 -0
  13. data/_data/landing.yml +5 -2
  14. data/_data/navigation/main.yml +16 -0
  15. data/_data/navigation/quickstart.yml +4 -0
  16. data/_data/recipe_courses.yml +64 -0
  17. data/_data/site_builder.yml +874 -0
  18. data/_data/theme-manifest.yml +160 -124
  19. data/_data/ui-text.yml +26 -0
  20. data/_includes/README.md +26 -2
  21. data/_includes/analytics/posthog.html +2 -2
  22. data/_includes/components/admin-links.html +2 -2
  23. data/_includes/components/admin-tabs.html +2 -2
  24. data/_includes/components/ai-chat.html +14 -11
  25. data/_includes/components/analytics-dashboard.html +8 -8
  26. data/_includes/components/author-bio.html +1 -1
  27. data/_includes/components/author-card.html +10 -2
  28. data/_includes/components/author-eeat.html +4 -4
  29. data/_includes/components/background-customizer.html +10 -10
  30. data/_includes/components/background-image.html +114 -0
  31. data/_includes/components/background-settings.html +28 -15
  32. data/_includes/components/collection-manager.html +5 -5
  33. data/_includes/components/component-showcase.html +13 -13
  34. data/_includes/components/config-editor.html +12 -12
  35. data/_includes/components/config-viewer.html +8 -8
  36. data/_includes/components/cookie-consent.html +15 -15
  37. data/_includes/components/cta-button.html +7 -2
  38. data/_includes/components/dev-shortcuts.html +7 -7
  39. data/_includes/components/env-dashboard.html +8 -8
  40. data/_includes/components/env-switcher.html +9 -9
  41. data/_includes/components/feature-card.html +2 -2
  42. data/_includes/components/halfmoon.html +2 -2
  43. data/_includes/components/info-section.html +42 -37
  44. data/_includes/components/js-cdn.html +15 -15
  45. data/_includes/components/language-toggle.html +168 -21
  46. data/_includes/components/mermaid.html +72 -435
  47. data/_includes/components/nanobar.html +5 -5
  48. data/_includes/components/nav-editor.html +2 -2
  49. data/_includes/components/nav-export.html +2 -2
  50. data/_includes/components/nav-overview.html +2 -2
  51. data/_includes/components/page-feedback.html +45 -30
  52. data/_includes/components/page-views-init.html +55 -0
  53. data/_includes/components/page-views.html +33 -0
  54. data/_includes/components/post-card.html +22 -22
  55. data/_includes/components/post-type-badge.html +2 -2
  56. data/_includes/components/powered-by.html +2 -2
  57. data/_includes/components/preview-image.html +6 -0
  58. data/_includes/components/quick-index.html +2 -2
  59. data/_includes/components/recipe-card.html +67 -0
  60. data/_includes/components/recipe-duration.html +50 -0
  61. data/_includes/components/recipe-grams.html +58 -0
  62. data/_includes/components/recipe-index.html +96 -0
  63. data/_includes/components/recipe-ingredients.html +90 -0
  64. data/_includes/components/recipe-meta.html +96 -0
  65. data/_includes/components/recipe-nutrition.html +57 -0
  66. data/_includes/components/recipe-qty.html +73 -0
  67. data/_includes/components/recipe-ratio.html +151 -0
  68. data/_includes/components/recipe-scaler.html +73 -0
  69. data/_includes/components/recipe-steps.html +86 -0
  70. data/_includes/components/recipe-temp.html +45 -0
  71. data/_includes/components/search-modal.html +29 -4
  72. data/_includes/components/searchbar.html +2 -2
  73. data/_includes/components/shortcuts-modal.html +3 -0
  74. data/_includes/components/svg-background.html +2 -2
  75. data/_includes/components/theme-customizer.html +2 -2
  76. data/_includes/components/theme-info.html +14 -7
  77. data/_includes/components/theme-preview-gallery.html +22 -22
  78. data/_includes/content/giscus.html +2 -2
  79. data/_includes/content/intro.html +8 -8
  80. data/_includes/content/jsonld-faq.html +2 -2
  81. data/_includes/content/jsonld-software.html +24 -5
  82. data/_includes/content/seo.html +4 -4
  83. data/_includes/content/sitemap.html +27 -27
  84. data/_includes/content/toc.html +183 -183
  85. data/_includes/core/branding.html +6 -6
  86. data/_includes/core/console-capture.html +32 -74
  87. data/_includes/core/favicon.html +49 -7
  88. data/_includes/core/footer-fabs.html +17 -3
  89. data/_includes/core/footer.html +49 -34
  90. data/_includes/core/head.html +110 -86
  91. data/_includes/core/header.html +76 -54
  92. data/_includes/docs/bootstrap-docs.html +8 -8
  93. data/_includes/landing/landing-install-cards.html +2 -2
  94. data/_includes/landing/landing-quick-links.html +1 -1
  95. data/_includes/navigation/admin-nav.html +2 -2
  96. data/_includes/navigation/nav-tree.html +8 -8
  97. data/_includes/navigation/navbar.html +12 -12
  98. data/_includes/navigation/section-sidebar.html +109 -27
  99. data/_includes/navigation/sidebar-config.html +36 -2
  100. data/_includes/navigation/sidebar-left.html +17 -16
  101. data/_includes/navigation/sidebar-right.html +8 -7
  102. data/_includes/obsidian/full-graph.html +2 -2
  103. data/_includes/setup/claude-session.html +72 -0
  104. data/_includes/setup/prereq-checklist.html +90 -0
  105. data/_includes/setup/wizard.html +924 -222
  106. data/_includes/stats/stats-categories.html +8 -8
  107. data/_includes/stats/stats-header.html +14 -14
  108. data/_includes/stats/stats-metrics.html +14 -14
  109. data/_includes/stats/stats-no-data.html +12 -12
  110. data/_includes/stats/stats-overview.html +6 -6
  111. data/_includes/stats/stats-tags.html +8 -8
  112. data/_layouts/404.html +38 -24
  113. data/_layouts/README.md +2 -0
  114. data/_layouts/admin.html +24 -24
  115. data/_layouts/article.html +43 -33
  116. data/_layouts/author.html +20 -20
  117. data/_layouts/authors.html +2 -2
  118. data/_layouts/book-abc.html +12 -12
  119. data/_layouts/book-story.html +15 -15
  120. data/_layouts/book.html +12 -12
  121. data/_layouts/collection.html +33 -33
  122. data/_layouts/cookbook.html +88 -0
  123. data/_layouts/default.html +31 -28
  124. data/_layouts/home.html +23 -23
  125. data/_layouts/index.html +10 -10
  126. data/_layouts/landing.html +17 -17
  127. data/_layouts/news.html +44 -44
  128. data/_layouts/note.html +38 -38
  129. data/_layouts/notebook.html +34 -34
  130. data/_layouts/recipe.html +274 -0
  131. data/_layouts/root.html +92 -55
  132. data/_layouts/section.html +62 -33
  133. data/_layouts/setup.html +3 -3
  134. data/_layouts/sitemap-collection.html +49 -49
  135. data/_layouts/stats.html +40 -40
  136. data/_layouts/tag.html +12 -12
  137. data/_layouts/welcome.html +21 -21
  138. data/_sass/components/_callout.scss +1 -1
  139. data/_sass/components/_footer.scss +37 -1
  140. data/_sass/components/_mermaid.scss +375 -0
  141. data/_sass/components/_page-views.scss +36 -0
  142. data/_sass/components/_recipe.scss +506 -0
  143. data/_sass/components/_setup-wizard.scss +764 -0
  144. data/_sass/components/_ui-enhancements.scss +6 -6
  145. data/_sass/core/_navbar.scss +261 -46
  146. data/_sass/layouts/_landing.scss +2 -2
  147. data/_sass/layouts/_navbar-extras.scss +14 -4
  148. data/_sass/tokens/_color.scss +6 -0
  149. data/_sass/tokens/_index.scss +2 -0
  150. data/_sass/tokens/_radius.scss +21 -0
  151. data/_sass/tokens/_typography.scss +4 -0
  152. data/_sass/utilities/_focus.scss +14 -0
  153. data/assets/css/main.scss +4 -0
  154. data/assets/js/ai-chat.js +47 -5
  155. data/assets/js/fleet-feedback-capture.js +124 -0
  156. data/assets/js/fleet-feedback.js +853 -0
  157. data/assets/js/mermaid-diagrams.js +1267 -0
  158. data/assets/js/modules/navigation/config.js +9 -6
  159. data/assets/js/modules/navigation/navbar.js +55 -0
  160. data/assets/js/modules/navigation/scroll-spy.js +315 -80
  161. data/assets/js/modules/theme/appearance.js +8 -2
  162. data/assets/js/obsidian-wiki-links.js +8 -3
  163. data/assets/js/page-feedback.js +125 -192
  164. data/assets/js/page-views.js +372 -0
  165. data/assets/js/recipe-scaler.js +501 -0
  166. data/assets/js/search-modal.js +36 -0
  167. data/assets/js/setup-wizard.js +2279 -226
  168. data/assets/js/site-builder.js +1834 -0
  169. data/assets/js/ui-enhancements.js +11 -3
  170. data/scripts/README.md +44 -0
  171. data/scripts/ai/README.md +38 -0
  172. data/scripts/ai/api_call.rb +124 -0
  173. data/scripts/ai/usage.rb +314 -0
  174. data/scripts/ai/usage_report.rb +225 -0
  175. data/scripts/bin/audit-consumer +39 -7
  176. data/scripts/bin/giscus-discussions +213 -14
  177. data/scripts/bin/manifest +35 -12
  178. data/scripts/ci/agent_review_result.py +164 -0
  179. data/scripts/ci/test_agent_review_result.py +172 -0
  180. data/scripts/ci/test_visual_evidence_autogen.py +341 -0
  181. data/scripts/ci/visual_evidence_autogen.py +1060 -0
  182. data/scripts/content-review.rb +20 -1
  183. data/scripts/design-system-check.rb +170 -0
  184. data/scripts/lib/audit.sh +42 -2
  185. data/scripts/lint-liquid-raw.rb +137 -0
  186. data/scripts/test/integration/mermaid +22 -8
  187. data/scripts/test/lib/run_tests.sh +3 -1
  188. data/scripts/test/lib/test_agent_review_result.sh +27 -0
  189. data/scripts/test/lib/test_visual_evidence_autogen.sh +24 -0
  190. data/scripts/translate.rb +94 -16
  191. metadata +48 -2
data/_layouts/note.html CHANGED
@@ -5,7 +5,7 @@ layout: default
5
5
  {% assign page_absolute_url = page.url | absolute_url %}
6
6
  {% assign linkedin_share_title = page.title | default: site.title | strip_html | strip_newlines | strip %}
7
7
  {% assign linkedin_share_description = page.description | default: page.excerpt | default: site.description | strip_html | strip_newlines | strip %}
8
- <!--
8
+ {% comment %}
9
9
  ===================================================================
10
10
  NOTE LAYOUT - Quick Notes and Reference Display
11
11
  ===================================================================
@@ -36,21 +36,21 @@ layout: default
36
36
  - Bootstrap 5 components
37
37
  - Giscus comments (optional)
38
38
  ===================================================================
39
- -->
39
+ {% endcomment %}
40
40
 
41
41
  <article id="main" class="note-article h-entry" role="main" itemscope itemtype="https://schema.org/Article">
42
42
 
43
- <!-- ================================ -->
44
- <!-- NOTE HEADER -->
45
- <!-- ================================ -->
43
+ {% comment %} ================================ {% endcomment %}
44
+ {% comment %} NOTE HEADER {% endcomment %}
45
+ {% comment %} ================================ {% endcomment %}
46
46
  <header class="note-header mb-4">
47
47
  <h1 id="page-title" class="note-title p-name" itemprop="headline">
48
48
  {{ page.title }}
49
49
  </h1>
50
50
 
51
- <!-- Note Metadata -->
51
+ {% comment %} Note Metadata {% endcomment %}
52
52
  <div class="note-meta text-muted mb-3">
53
- <!-- Author -->
53
+ {% comment %} Author {% endcomment %}
54
54
  {% if page.author %}
55
55
  <span class="note-author" itemprop="author" itemscope itemtype="https://schema.org/Person">
56
56
  {% include components/author-card.html author=page.author style="inline" name_itemprop="name" %}
@@ -58,7 +58,7 @@ layout: default
58
58
  <span class="mx-2">•</span>
59
59
  {% endif %}
60
60
 
61
- <!-- Publication Date -->
61
+ {% comment %} Publication Date {% endcomment %}
62
62
  {% if page.date %}
63
63
  <time class="note-date dt-published" datetime="{{ page.date | date_to_xmlschema }}" itemprop="datePublished">
64
64
  <i class="bi bi-calendar me-1"></i>
@@ -67,7 +67,7 @@ layout: default
67
67
  <span class="mx-2">•</span>
68
68
  {% endif %}
69
69
 
70
- <!-- Reading Time -->
70
+ {% comment %} Reading Time {% endcomment %}
71
71
  {% assign words = content | number_of_words %}
72
72
  {% assign minutes = words | divided_by: 200 %}
73
73
  {% if minutes < 1 %}{% assign minutes = 1 %}{% endif %}
@@ -76,7 +76,7 @@ layout: default
76
76
  {{ minutes }} min read
77
77
  </span>
78
78
 
79
- <!-- Last Modified -->
79
+ {% comment %} Last Modified {% endcomment %}
80
80
  {% if page.lastmod %}
81
81
  <span class="mx-2">•</span>
82
82
  <span class="note-updated" itemprop="dateModified" content="{{ page.lastmod | date_to_xmlschema }}">
@@ -85,7 +85,7 @@ layout: default
85
85
  </span>
86
86
  {% endif %}
87
87
 
88
- <!-- Difficulty Badge -->
88
+ {% comment %} Difficulty Badge {% endcomment %}
89
89
  {% if page.difficulty %}
90
90
  <span class="mx-2">•</span>
91
91
  <span class="note-difficulty">
@@ -103,7 +103,7 @@ layout: default
103
103
  {% endif %}
104
104
  </div>
105
105
 
106
- <!-- Tags -->
106
+ {% comment %} Tags {% endcomment %}
107
107
  {% comment %} Link tag badges only when a tags index exists (configurable via
108
108
  `tags_page`); otherwise render plain badges so remote-theme consumers
109
109
  without a /tags/ page don't get 404 links. See issue #204. {% endcomment %}
@@ -123,7 +123,7 @@ layout: default
123
123
  </div>
124
124
  {% endif %}
125
125
 
126
- <!-- Categories -->
126
+ {% comment %} Categories {% endcomment %}
127
127
  {% if page.categories and page.categories.size > 0 %}
128
128
  <div class="note-categories mb-3">
129
129
  <i class="bi bi-folder me-1"></i>
@@ -136,7 +136,7 @@ layout: default
136
136
  </div>
137
137
  {% endif %}
138
138
 
139
- <!-- Description -->
139
+ {% comment %} Description {% endcomment %}
140
140
  {% if page.description %}
141
141
  <div class="note-description lead mb-4" itemprop="description">
142
142
  {{ page.description }}
@@ -144,26 +144,26 @@ layout: default
144
144
  {% endif %}
145
145
  </header>
146
146
 
147
- <!-- ================================ -->
148
- <!-- NOTE CONTENT -->
149
- <!-- ================================ -->
147
+ {% comment %} ================================ {% endcomment %}
148
+ {% comment %} NOTE CONTENT {% endcomment %}
149
+ {% comment %} ================================ {% endcomment %}
150
150
  <div class="note-content e-content" itemprop="articleBody">
151
151
  {{ content }}
152
152
  </div>
153
153
 
154
- <!-- ================================ -->
155
- <!-- AUTHOR BIO SECTION -->
156
- <!-- ================================ -->
154
+ {% comment %} ================================ {% endcomment %}
155
+ {% comment %} AUTHOR BIO SECTION {% endcomment %}
156
+ {% comment %} ================================ {% endcomment %}
157
157
  {% if page.author and page.author_profile != false %}
158
158
  {% include components/author-bio.html author=page.author %}
159
159
  {% endif %}
160
160
 
161
- <!-- ================================ -->
162
- <!-- NOTE FOOTER -->
163
- <!-- ================================ -->
161
+ {% comment %} ================================ {% endcomment %}
162
+ {% comment %} NOTE FOOTER {% endcomment %}
163
+ {% comment %} ================================ {% endcomment %}
164
164
  <footer class="note-footer mt-5 pt-4 border-top">
165
165
 
166
- <!-- Share Buttons -->
166
+ {% comment %} Share Buttons {% endcomment %}
167
167
  {% if page.share != false %}
168
168
  <div class="share-buttons mb-4">
169
169
  <h5 class="mb-3">Share this note</h5>
@@ -190,7 +190,7 @@ layout: default
190
190
  </div>
191
191
  {% endif %}
192
192
 
193
- <!-- Related Notes -->
193
+ {% comment %} Related Notes {% endcomment %}
194
194
  {% if site.notes.size > 1 %}
195
195
  <div class="related-notes mt-5">
196
196
  <h5 class="mb-3">Related Notes</h5>
@@ -238,28 +238,28 @@ layout: default
238
238
  {% endif %}
239
239
  </footer>
240
240
 
241
- <!-- ================================ -->
242
- <!-- COMMENTS SECTION -->
243
- <!-- ================================ -->
241
+ {% comment %} ================================ {% endcomment %}
242
+ {% comment %} COMMENTS SECTION {% endcomment %}
243
+ {% comment %} ================================ {% endcomment %}
244
244
  {% if page.comments != false and site.giscus.enabled %}
245
245
  <div class="note-comments mt-5 pt-4 border-top">
246
246
  <h5 class="mb-4">Comments</h5>
247
247
  {% include content/giscus.html %}
248
248
  </div>
249
249
  {% endif %}
250
- <!-- ================================ -->
251
- <!-- BACKLINKS — "Linked mentions" -->
252
- <!-- ================================ -->
253
- <!-- Obsidian-style panel of pages that wiki-link or markdown-link to this note. -->
254
- <!-- Disable per-page with `backlinks: false` in front matter. -->
250
+ {% comment %} ================================ {% endcomment %}
251
+ {% comment %} BACKLINKS — "Linked mentions" {% endcomment %}
252
+ {% comment %} ================================ {% endcomment %}
253
+ {% comment %} Obsidian-style panel of pages that wiki-link or markdown-link to this note. {% endcomment %}
254
+ {% comment %} Disable per-page with `backlinks: false` in front matter. {% endcomment %}
255
255
  {% include content/backlinks.html %}
256
256
 
257
257
  </article>
258
258
 
259
- <!-- ================================ -->
260
- <!-- NAVIGATION -->
261
- <!-- ================================ -->
262
- <!-- Previous/Next Note Navigation -->
259
+ {% comment %} ================================ {% endcomment %}
260
+ {% comment %} NAVIGATION {% endcomment %}
261
+ {% comment %} ================================ {% endcomment %}
262
+ {% comment %} Previous/Next Note Navigation {% endcomment %}
263
263
  {% assign notes = site.notes | sort: 'date' | reverse %}
264
264
  {% assign current_index = nil %}
265
265
  {% for note in notes %}
@@ -304,7 +304,7 @@ layout: default
304
304
  </nav>
305
305
  {% endif %}
306
306
 
307
- <!-- Schema.org metadata -->
307
+ {% comment %} Schema.org metadata {% endcomment %}
308
308
  <script type="application/ld+json">
309
309
  {
310
310
  "@context": "https://schema.org",
@@ -2,7 +2,7 @@
2
2
  # Features: ZER0-012, ZER0-019, ZER0-035
3
3
  layout: default
4
4
  ---
5
- <!--
5
+ {% comment %}
6
6
  ===================================================================
7
7
  NOTEBOOK LAYOUT - Jupyter Notebook Display
8
8
  ===================================================================
@@ -32,21 +32,21 @@ layout: default
32
32
  - Rouge syntax highlighter (Jekyll default)
33
33
  - Bootstrap 5 components
34
34
  ===================================================================
35
- -->
35
+ {% endcomment %}
36
36
 
37
37
  <article id="main" class="notebook-article h-entry" role="main" itemscope itemtype="https://schema.org/TechArticle">
38
38
 
39
- <!-- ================================ -->
40
- <!-- NOTEBOOK HEADER -->
41
- <!-- ================================ -->
39
+ {% comment %} ================================ {% endcomment %}
40
+ {% comment %} NOTEBOOK HEADER {% endcomment %}
41
+ {% comment %} ================================ {% endcomment %}
42
42
  <header class="notebook-header mb-4">
43
43
  <h1 id="page-title" class="notebook-title p-name" itemprop="headline">
44
44
  {{ page.title }}
45
45
  </h1>
46
46
 
47
- <!-- Notebook Metadata -->
47
+ {% comment %} Notebook Metadata {% endcomment %}
48
48
  <div class="notebook-meta text-muted mb-3">
49
- <!-- Author -->
49
+ {% comment %} Author {% endcomment %}
50
50
  {% if page.author %}
51
51
  <span class="notebook-author" itemprop="author" itemscope itemtype="https://schema.org/Person">
52
52
  {% include components/author-card.html author=page.author style="inline" name_itemprop="name" %}
@@ -54,7 +54,7 @@ layout: default
54
54
  <span class="mx-2">•</span>
55
55
  {% endif %}
56
56
 
57
- <!-- Publication Date -->
57
+ {% comment %} Publication Date {% endcomment %}
58
58
  {% if page.date %}
59
59
  <time class="notebook-date dt-published" datetime="{{ page.date | date_to_xmlschema }}" itemprop="datePublished">
60
60
  <i class="bi bi-calendar me-1"></i>
@@ -63,7 +63,7 @@ layout: default
63
63
  <span class="mx-2">•</span>
64
64
  {% endif %}
65
65
 
66
- <!-- Reading Time -->
66
+ {% comment %} Reading Time {% endcomment %}
67
67
  {% assign words = content | number_of_words %}
68
68
  {% assign minutes = words | divided_by: 200 %}
69
69
  {% if minutes < 1 %}{% assign minutes = 1 %}{% endif %}
@@ -72,7 +72,7 @@ layout: default
72
72
  {{ minutes }} min read
73
73
  </span>
74
74
 
75
- <!-- Last Modified -->
75
+ {% comment %} Last Modified {% endcomment %}
76
76
  {% if page.lastmod %}
77
77
  <span class="mx-2">•</span>
78
78
  <span class="notebook-updated" itemprop="dateModified" content="{{ page.lastmod | date_to_xmlschema }}">
@@ -81,7 +81,7 @@ layout: default
81
81
  </span>
82
82
  {% endif %}
83
83
 
84
- <!-- Kernel Info -->
84
+ {% comment %} Kernel Info {% endcomment %}
85
85
  {% if page.jupyter_metadata %}
86
86
  <span class="mx-2">•</span>
87
87
  <span class="notebook-kernel">
@@ -91,7 +91,7 @@ layout: default
91
91
  {% endif %}
92
92
  </div>
93
93
 
94
- <!-- Tags -->
94
+ {% comment %} Tags {% endcomment %}
95
95
  {% comment %} Link tag badges only when a tags index exists (configurable via
96
96
  `tags_page`); otherwise render plain badges so remote-theme consumers
97
97
  without a /tags/ page don't get 404 links. See issue #204. {% endcomment %}
@@ -111,7 +111,7 @@ layout: default
111
111
  </div>
112
112
  {% endif %}
113
113
 
114
- <!-- Categories -->
114
+ {% comment %} Categories {% endcomment %}
115
115
  {% if page.categories and page.categories.size > 0 %}
116
116
  <div class="notebook-categories mb-3">
117
117
  <i class="bi bi-folder me-1"></i>
@@ -124,7 +124,7 @@ layout: default
124
124
  </div>
125
125
  {% endif %}
126
126
 
127
- <!-- Description -->
127
+ {% comment %} Description {% endcomment %}
128
128
  {% if page.description %}
129
129
  <div class="notebook-description lead mb-4" itemprop="description">
130
130
  {{ page.description }}
@@ -132,26 +132,26 @@ layout: default
132
132
  {% endif %}
133
133
  </header>
134
134
 
135
- <!-- ================================ -->
136
- <!-- NOTEBOOK CONTENT -->
137
- <!-- ================================ -->
135
+ {% comment %} ================================ {% endcomment %}
136
+ {% comment %} NOTEBOOK CONTENT {% endcomment %}
137
+ {% comment %} ================================ {% endcomment %}
138
138
  <div class="notebook-content e-content" itemprop="articleBody">
139
139
  {{ content }}
140
140
  </div>
141
141
 
142
- <!-- ================================ -->
143
- <!-- AUTHOR BIO SECTION -->
144
- <!-- ================================ -->
142
+ {% comment %} ================================ {% endcomment %}
143
+ {% comment %} AUTHOR BIO SECTION {% endcomment %}
144
+ {% comment %} ================================ {% endcomment %}
145
145
  {% if page.author and page.author_profile != false %}
146
146
  {% include components/author-bio.html author=page.author %}
147
147
  {% endif %}
148
148
 
149
- <!-- ================================ -->
150
- <!-- NOTEBOOK FOOTER -->
151
- <!-- ================================ -->
149
+ {% comment %} ================================ {% endcomment %}
150
+ {% comment %} NOTEBOOK FOOTER {% endcomment %}
151
+ {% comment %} ================================ {% endcomment %}
152
152
  <footer class="notebook-footer mt-5 pt-4 border-top">
153
153
 
154
- <!-- Share Buttons -->
154
+ {% comment %} Share Buttons {% endcomment %}
155
155
  {% if page.share != false %}
156
156
  <div class="share-buttons mb-4">
157
157
  <h5 class="mb-3">Share this notebook</h5>
@@ -172,7 +172,7 @@ layout: default
172
172
  </div>
173
173
  {% endif %}
174
174
 
175
- <!-- Download Original Notebook -->
175
+ {% comment %} Download Original Notebook {% endcomment %}
176
176
  <div class="download-notebook mb-4">
177
177
  <a href="https://raw.githubusercontent.com/{{ site.repository }}/{{ site.branch | default: 'main' }}/pages/_notebooks/{{ page.slug }}.ipynb"
178
178
  class="btn btn-outline-secondary btn-sm" download>
@@ -180,7 +180,7 @@ layout: default
180
180
  </a>
181
181
  </div>
182
182
 
183
- <!-- Related Notebooks -->
183
+ {% comment %} Related Notebooks {% endcomment %}
184
184
  {% if site.notebooks.size > 1 %}
185
185
  <div class="related-notebooks mt-5">
186
186
  <h5 class="mb-3">Related Notebooks</h5>
@@ -221,9 +221,9 @@ layout: default
221
221
  {% endif %}
222
222
  </footer>
223
223
 
224
- <!-- ================================ -->
225
- <!-- COMMENTS SECTION -->
226
- <!-- ================================ -->
224
+ {% comment %} ================================ {% endcomment %}
225
+ {% comment %} COMMENTS SECTION {% endcomment %}
226
+ {% comment %} ================================ {% endcomment %}
227
227
  {% if page.comments != false and site.giscus.enabled %}
228
228
  <div class="notebook-comments mt-5 pt-4 border-top">
229
229
  <h5 class="mb-4">Comments</h5>
@@ -233,10 +233,10 @@ layout: default
233
233
 
234
234
  </article>
235
235
 
236
- <!-- ================================ -->
237
- <!-- NAVIGATION -->
238
- <!-- ================================ -->
239
- <!-- Previous/Next Notebook Navigation -->
236
+ {% comment %} ================================ {% endcomment %}
237
+ {% comment %} NAVIGATION {% endcomment %}
238
+ {% comment %} ================================ {% endcomment %}
239
+ {% comment %} Previous/Next Notebook Navigation {% endcomment %}
240
240
  {% assign notebooks = site.notebooks | sort: 'date' | reverse %}
241
241
  {% assign current_index = nil %}
242
242
  {% for notebook in notebooks %}
@@ -281,7 +281,7 @@ layout: default
281
281
  </nav>
282
282
  {% endif %}
283
283
 
284
- <!-- Schema.org metadata -->
284
+ {% comment %} Schema.org metadata {% endcomment %}
285
285
  <script type="application/ld+json">
286
286
  {
287
287
  "@context": "https://schema.org",
@@ -0,0 +1,274 @@
1
+ ---
2
+ # Feature: ZER0-084
3
+ layout: default
4
+ ---
5
+ {%- comment -%}
6
+ Layout: recipe.html
7
+ Extends: default.html
8
+ Purpose: One recipe — headnote, fact bar, serving scaler, ingredients,
9
+ equipment, method, ratio table, notes, and nutrition, all driven by
10
+ structured front matter rather than hand-written markdown.
11
+ Front matter required: layout, title
12
+ Front matter optional (the whole cookbook schema):
13
+ description, image/preview, course, cookbook, cuisine, difficulty,
14
+ yield: {amount, unit, singular}, times: {prep, cook, rest, rest_label,
15
+ total}, oven: {temp_f, temp_c, mode}, ratio, ratio_basis, equipment,
16
+ ingredients, steps, notes, nutrition, source, source_url, scaler (false to
17
+ hide the controls), units (false to hide the unit switch)
18
+ Notes: Every amount is rendered as authored, so the page is complete and
19
+ correct without JavaScript; assets/js/recipe-scaler.js only rewrites
20
+ the numbers once a reader asks it to. Machine-readable data is emitted
21
+ once, as JSON-LD — the itemprops inside the components are inert
22
+ annotations unless a consumer adds their own itemscope.
23
+ {%- endcomment -%}
24
+
25
+ {%- assign _rl_id = page.slug | default: "recipe" -%}
26
+ {%- assign _rl_course_meta = site.data.recipe_courses[page.course] -%}
27
+ {%- assign _rl_cookbook = site.recipes | where: "layout", "cookbook" | where: "cookbook", page.cookbook | first -%}
28
+ {%- assign _rl_image = page.image | default: page.preview -%}
29
+ {%- assign _rl_show_scaler = true -%}
30
+ {%- if page.scaler == false -%}{%- assign _rl_show_scaler = false -%}{%- endif -%}
31
+
32
+ <article class="recipe" data-recipe data-recipe-id="{{ _rl_id }}">
33
+
34
+ {% comment %} ================================ {% endcomment %}
35
+ {% comment %} HEADER {% endcomment %}
36
+ {% comment %} ================================ {% endcomment %}
37
+ <header class="recipe-header">
38
+ {%- if _rl_cookbook or _rl_course_meta -%}
39
+ <p class="recipe-eyebrow">
40
+ {%- if _rl_cookbook -%}
41
+ <a href="{{ _rl_cookbook.url | relative_url }}"><i class="bi bi-journal-bookmark me-1" aria-hidden="true"></i>{{ _rl_cookbook.title }}</a>
42
+ {%- endif -%}
43
+ {%- if _rl_cookbook and _rl_course_meta %} <span class="mx-1" aria-hidden="true">•</span> {% endif -%}
44
+ {%- if _rl_course_meta -%}{{ _rl_course_meta.title | escape }}{%- endif -%}
45
+ </p>
46
+ {%- endif -%}
47
+
48
+ <h1 id="page-title" class="recipe-title">{{ page.title }}</h1>
49
+
50
+ {%- if page.description -%}
51
+ <p class="lead recipe-description">{{ page.description }}</p>
52
+ {%- endif -%}
53
+
54
+ {%- if page.source or page.author -%}
55
+ <p class="recipe-byline text-body-secondary small">
56
+ {%- if page.author %}By {{ page.author }}{% endif -%}
57
+ {%- if page.author and page.source %} <span class="mx-1" aria-hidden="true">•</span> {% endif -%}
58
+ {%- if page.source -%}
59
+ Adapted from
60
+ {%- if page.source_url %} <a href="{{ page.source_url }}" rel="noopener">{{ page.source | escape }}</a>
61
+ {%- else %} {{ page.source | escape }}{% endif -%}
62
+ {%- endif -%}
63
+ </p>
64
+ {%- endif -%}
65
+
66
+ {%- if _rl_image -%}
67
+ <figure class="recipe-hero-figure">
68
+ {%- include components/preview-image.html src=_rl_image alt=page.title class="recipe-hero-image" loading="eager" -%}
69
+ {%- if page.image_caption -%}
70
+ <figcaption class="small text-body-secondary mt-2">{{ page.image_caption | escape }}</figcaption>
71
+ {%- endif -%}
72
+ </figure>
73
+ {%- endif -%}
74
+ </header>
75
+
76
+ {% comment %} ================================ {% endcomment %}
77
+ {% comment %} HEADNOTE (the markdown body) {% endcomment %}
78
+ {% comment %} ================================ {% endcomment %}
79
+ {%- assign _rl_body = content | strip -%}
80
+ {%- if _rl_body != "" -%}
81
+ <div class="recipe-headnote">
82
+ {{ content }}
83
+ </div>
84
+ {%- endif -%}
85
+
86
+ {% comment %} ================================ {% endcomment %}
87
+ {% comment %} FACT BAR + SCALER {% endcomment %}
88
+ {% comment %} ================================ {% endcomment %}
89
+ {%- include components/recipe-meta.html -%}
90
+
91
+ {%- if _rl_show_scaler -%}
92
+ {%- include components/recipe-scaler.html recipe_id=_rl_id units=page.units -%}
93
+ <noscript>
94
+ <p class="recipe-noscript small text-body-secondary">
95
+ <i class="bi bi-info-circle me-1" aria-hidden="true"></i>Serving scaling and unit conversion need JavaScript.
96
+ Every amount below is shown exactly as the recipe was written.
97
+ </p>
98
+ </noscript>
99
+ {%- endif -%}
100
+
101
+ {% comment %} ================================ {% endcomment %}
102
+ {% comment %} INGREDIENTS + METHOD {% endcomment %}
103
+ {% comment %} ================================ {% endcomment %}
104
+ <div class="row g-4 g-lg-5 recipe-body">
105
+ <div class="col-lg-5">
106
+ {%- include components/recipe-ingredients.html id_prefix=_rl_id -%}
107
+
108
+ {%- if page.equipment and page.equipment.size > 0 -%}
109
+ <section class="recipe-equipment">
110
+ <h2 class="recipe-section-heading">
111
+ <i class="bi bi-tools me-2" aria-hidden="true"></i>Equipment
112
+ </h2>
113
+ <ul class="recipe-equipment-list list-unstyled">
114
+ {%- for _rl_e in page.equipment -%}
115
+ <li><i class="bi bi-dot" aria-hidden="true"></i>{{ _rl_e | escape }}</li>
116
+ {%- endfor -%}
117
+ </ul>
118
+ </section>
119
+ {%- endif -%}
120
+ </div>
121
+
122
+ <div class="col-lg-7">
123
+ {%- include components/recipe-steps.html -%}
124
+ </div>
125
+ </div>
126
+
127
+ {% comment %} ================================ {% endcomment %}
128
+ {% comment %} RATIO / FORMULA {% endcomment %}
129
+ {% comment %} ================================ {% endcomment %}
130
+ {%- include components/recipe-ratio.html -%}
131
+
132
+ {% comment %} ================================ {% endcomment %}
133
+ {% comment %} NOTES {% endcomment %}
134
+ {% comment %} ================================ {% endcomment %}
135
+ {%- if page.notes and page.notes.size > 0 -%}
136
+ <section class="recipe-notes">
137
+ <h2 class="recipe-section-heading">
138
+ <i class="bi bi-lightbulb me-2" aria-hidden="true"></i>Cook's notes
139
+ </h2>
140
+ <ul class="recipe-notes-list">
141
+ {%- for _rl_note in page.notes -%}
142
+ <li>{{ _rl_note | markdownify | remove: "<p>" | remove: "</p>" }}</li>
143
+ {%- endfor -%}
144
+ </ul>
145
+ </section>
146
+ {%- endif -%}
147
+
148
+ {% comment %} ================================ {% endcomment %}
149
+ {% comment %} NUTRITION {% endcomment %}
150
+ {% comment %} ================================ {% endcomment %}
151
+ {%- include components/recipe-nutrition.html -%}
152
+
153
+ {% comment %} ================================ {% endcomment %}
154
+ {% comment %} COMMENTS {% endcomment %}
155
+ {% comment %} ================================ {% endcomment %}
156
+ {%- if page.comments and site.giscus.enabled -%}
157
+ <section class="post-comments mt-5 pt-4 border-top" id="comments">
158
+ <h2 class="h4 mb-4"><i class="bi bi-chat-dots me-2" aria-hidden="true"></i>Comments</h2>
159
+ {%- include content/giscus.html -%}
160
+ </section>
161
+ {%- endif -%}
162
+ </article>
163
+
164
+ {%- comment -%}
165
+ ================================================================
166
+ STRUCTURED DATA — schema.org/Recipe as JSON-LD.
167
+ Emitted once, here, so search engines see exactly one Recipe entity.
168
+ ================================================================
169
+ {%- endcomment -%}
170
+ {%- assign _rl_prep = page.times.prep | default: 0 -%}
171
+ {%- assign _rl_cook = page.times.cook | default: 0 -%}
172
+ {%- assign _rl_rest = page.times.rest | default: 0 -%}
173
+ {%- if page.times.total -%}
174
+ {%- assign _rl_total = page.times.total -%}
175
+ {%- else -%}
176
+ {%- assign _rl_total = _rl_prep | plus: _rl_cook | plus: _rl_rest -%}
177
+ {%- endif -%}
178
+ {%- capture _rl_prep_iso -%}{%- include components/recipe-duration.html minutes=_rl_prep format="iso" -%}{%- endcapture -%}
179
+ {%- capture _rl_cook_iso -%}{%- include components/recipe-duration.html minutes=_rl_cook format="iso" -%}{%- endcapture -%}
180
+ {%- capture _rl_total_iso -%}{%- include components/recipe-duration.html minutes=_rl_total format="iso" -%}{%- endcapture -%}
181
+ {%- assign _rl_flat = "" | split: "" -%}
182
+ {%- for _rl_entry in page.ingredients -%}
183
+ {%- if _rl_entry.items -%}
184
+ {%- for _rl_i in _rl_entry.items -%}{%- assign _rl_flat = _rl_flat | push: _rl_i -%}{%- endfor -%}
185
+ {%- else -%}
186
+ {%- assign _rl_flat = _rl_flat | push: _rl_entry -%}
187
+ {%- endif -%}
188
+ {%- endfor -%}
189
+ {%- assign _rl_flat_steps = "" | split: "" -%}
190
+ {%- for _rl_entry in page.steps -%}
191
+ {%- if _rl_entry.items -%}
192
+ {%- for _rl_s in _rl_entry.items -%}{%- assign _rl_flat_steps = _rl_flat_steps | push: _rl_s -%}{%- endfor -%}
193
+ {%- else -%}
194
+ {%- assign _rl_flat_steps = _rl_flat_steps | push: _rl_entry -%}
195
+ {%- endif -%}
196
+ {%- endfor -%}
197
+ <script type="application/ld+json">
198
+ {
199
+ "@context": "https://schema.org",
200
+ "@type": "Recipe",
201
+ "name": {{ page.title | jsonify }},
202
+ "url": {{ page.url | absolute_url | jsonify }}
203
+ {%- if page.description %},
204
+ "description": {{ page.description | strip_html | strip | jsonify }}
205
+ {%- endif -%}
206
+ {%- if _rl_image %},
207
+ "image": {{ _rl_image | absolute_url | jsonify }}
208
+ {%- endif -%}
209
+ {%- if page.author %},
210
+ "author": { "@type": "Person", "name": {{ page.author | jsonify }} }
211
+ {%- endif -%}
212
+ {%- if page.date %},
213
+ "datePublished": {{ page.date | date_to_xmlschema | jsonify }}
214
+ {%- endif -%}
215
+ {%- assign _rl_prep_iso = _rl_prep_iso | strip -%}
216
+ {%- if _rl_prep_iso != "" %},
217
+ "prepTime": {{ _rl_prep_iso | jsonify }}
218
+ {%- endif -%}
219
+ {%- assign _rl_cook_iso = _rl_cook_iso | strip -%}
220
+ {%- if _rl_cook_iso != "" %},
221
+ "cookTime": {{ _rl_cook_iso | jsonify }}
222
+ {%- endif -%}
223
+ {%- assign _rl_total_iso = _rl_total_iso | strip -%}
224
+ {%- if _rl_total_iso != "" %},
225
+ "totalTime": {{ _rl_total_iso | jsonify }}
226
+ {%- endif -%}
227
+ {%- if page.yield.amount %},
228
+ {%- capture _rl_yield_text %}{{ page.yield.amount }} {{ page.yield.unit | default: "servings" }}{% endcapture %}
229
+ "recipeYield": {{ _rl_yield_text | strip | jsonify }}
230
+ {%- endif -%}
231
+ {%- if _rl_course_meta %},
232
+ "recipeCategory": {{ _rl_course_meta.title | jsonify }}
233
+ {%- endif -%}
234
+ {%- if page.cuisine %},
235
+ "recipeCuisine": {{ page.cuisine | jsonify }}
236
+ {%- endif -%}
237
+ {%- if page.tags and page.tags.size > 0 %},
238
+ "keywords": {{ page.tags | join: ", " | jsonify }}
239
+ {%- endif -%}
240
+ {%- if _rl_flat.size > 0 %},
241
+ "recipeIngredient": [
242
+ {%- assign _rl_first = true -%}
243
+ {%- for _rl_i in _rl_flat -%}
244
+ {%- if _rl_i.item -%}
245
+ {%- capture _rl_line %}{% if _rl_i.qty %}{{ _rl_i.qty }} {% endif %}{% if _rl_i.unit %}{{ _rl_i.unit }} {% endif %}{{ _rl_i.item }}{% if _rl_i.prep %}, {{ _rl_i.prep }}{% endif %}{% endcapture -%}
246
+ {%- unless _rl_first %},{% endunless %}{%- assign _rl_first = false -%}
247
+ {{ _rl_line | strip | jsonify }}
248
+ {%- endif -%}
249
+ {%- endfor -%}
250
+ ]
251
+ {%- endif -%}
252
+ {%- if _rl_flat_steps.size > 0 %},
253
+ "recipeInstructions": [
254
+ {%- assign _rl_first = true -%}
255
+ {%- for _rl_s in _rl_flat_steps -%}
256
+ {%- if _rl_s.text -%}{%- assign _rl_stext = _rl_s.text -%}{%- else -%}{%- assign _rl_stext = _rl_s -%}{%- endif -%}
257
+ {%- assign _rl_stext = _rl_stext | strip_html | strip -%}
258
+ {%- if _rl_stext != "" -%}
259
+ {%- unless _rl_first %},{% endunless %}{%- assign _rl_first = false -%}
260
+ { "@type": "HowToStep"{% if _rl_s.title %}, "name": {{ _rl_s.title | jsonify }}{% endif %}, "text": {{ _rl_stext | jsonify }} }
261
+ {%- endif -%}
262
+ {%- endfor -%}
263
+ ]
264
+ {%- endif -%}
265
+ {%- if page.nutrition.calories %},
266
+ {%- capture _rl_serving_size %}1 {{ page.yield.singular | default: "serving" }}{% endcapture %}
267
+ "nutrition": {
268
+ "@type": "NutritionInformation",
269
+ "servingSize": {{ _rl_serving_size | strip | jsonify }},
270
+ "calories": {{ page.nutrition.calories | append: " calories" | jsonify }}
271
+ }
272
+ {%- endif %}
273
+ }
274
+ </script>