jekyll-theme-zer0 1.26.0 → 1.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +558 -5
- data/README.md +10 -27
- data/_data/authors.yml +4 -3
- data/_data/backlog.yml +28 -0
- data/_data/consumers.yml +147 -0
- data/_data/features.yml +142 -23
- data/_data/i18n/fr.yml +98 -0
- data/_data/i18n/languages.yml +36 -0
- data/_data/i18n/manifest.yml +1502 -0
- data/_data/navigation/docs.yml +2 -0
- data/_data/navigation/main.yml +6 -0
- data/_data/series.yml +19 -0
- data/_data/theme-manifest.yml +348 -268
- data/_data/ui-text.yml +36 -246
- data/_includes/README.md +25 -2
- data/_includes/analytics/google-tag-manager-body.html +11 -2
- data/_includes/analytics/google-tag-manager-head.html +16 -6
- data/_includes/analytics/posthog.html +33 -4
- data/_includes/components/abc-letter.html +43 -0
- data/_includes/components/author-avatar-url.html +4 -2
- data/_includes/components/book-card.html +42 -0
- data/_includes/components/book-nav.html +80 -0
- data/_includes/components/book-plate.html +31 -0
- data/_includes/components/book-toc.html +47 -0
- data/_includes/components/bookshelf.html +68 -0
- data/_includes/components/card-grid.html +60 -0
- data/_includes/components/data-card.html +95 -0
- data/_includes/components/env-switcher.html +3 -1
- data/_includes/components/halfmoon.html +5 -1
- data/_includes/components/language-toggle.html +81 -0
- data/_includes/components/page-feedback.html +65 -2
- data/_includes/components/search-modal.html +2 -2
- data/_includes/components/shortcuts-modal.html +1 -1
- data/_includes/components/theme-controls-bar.html +10 -2
- data/_includes/components/theme-customizer.html +8 -2
- data/_includes/components/translation-notice.html +27 -0
- data/_includes/content/intro.html +16 -15
- data/_includes/content/seo.html +9 -3
- data/_includes/core/color-mode-init.html +13 -4
- data/_includes/core/favicon.html +46 -0
- data/_includes/core/footer.html +9 -4
- data/_includes/core/head.html +18 -0
- data/_includes/core/header.html +9 -4
- data/_includes/core/hreflang.html +33 -0
- data/_includes/core/i18n.html +36 -0
- data/_includes/custom/body-end.html +18 -0
- data/_includes/custom/body-start.html +17 -0
- data/_includes/custom/footer.html +18 -0
- data/_includes/custom/head.html +18 -0
- data/_includes/navigation/breadcrumbs.html +1 -1
- data/_includes/navigation/local-graph.html +28 -2
- data/_includes/navigation/nav-tree.html +3 -3
- data/_includes/navigation/navbar.html +5 -4
- data/_includes/navigation/sidebar-config.html +21 -0
- data/_includes/navigation/sidebar-nav.html +4 -0
- data/_includes/navigation/sidebar-pagetree.html +150 -0
- data/_includes/navigation/sidebar-right.html +3 -2
- data/_includes/navigation/unified-drawer.html +9 -3
- data/_includes/obsidian/full-graph.html +165 -136
- data/_layouts/404.html +260 -0
- data/_layouts/README.md +2 -0
- data/_layouts/article.html +7 -1
- data/_layouts/book-abc.html +106 -0
- data/_layouts/book-story.html +91 -0
- data/_layouts/book.html +113 -0
- data/_layouts/collection.html +17 -5
- data/_layouts/default.html +5 -3
- data/_layouts/home.html +5 -2
- data/_layouts/landing.html +9 -0
- data/_layouts/news.html +159 -33
- data/_layouts/root.html +29 -9
- data/_layouts/section.html +61 -17
- data/_sass/components/_book.scss +423 -0
- data/_sass/core/_navbar.scss +13 -0
- data/_sass/core/_obsidian.scss +295 -7
- data/_sass/layouts/_navbar-extras.scss +6 -1
- data/_sass/theme/_backgrounds.scss +21 -8
- data/assets/css/main.scss +1 -0
- data/assets/js/auto-hide-nav.js +5 -1
- data/assets/js/halfmoon.js +26 -0
- data/assets/js/obsidian-graph.js +707 -265
- data/assets/js/obsidian-local-graph.js +161 -54
- data/assets/js/search-modal.js +4 -1
- data/scripts/README.md +20 -26
- data/scripts/bin/audit-consumer +1 -1
- data/scripts/bin/manifest +31 -5
- data/scripts/bin/sync-plugins +0 -1
- data/scripts/bin/validate +5 -1
- data/scripts/dev/rasterize-svg.js +65 -0
- data/scripts/features/generate-preview-images +49 -1390
- data/scripts/features/install-preview-generator +55 -33
- data/scripts/install/README.md +55 -25
- data/scripts/install/ai/client.sh +302 -93
- data/scripts/install/ai/prompts/spec.schema.json +1 -1
- data/scripts/install/ai/prompts/wizard.system.md +8 -17
- data/scripts/install/ai/wizard.sh +10 -5
- data/scripts/install/apply.sh +7 -3
- data/scripts/install/cli.sh +54 -4
- data/scripts/install/config.sh +167 -0
- data/scripts/install/doctor.sh +38 -0
- data/scripts/install/plan.sh +10 -0
- data/scripts/install/spec.sh +15 -7
- data/scripts/install/template.sh +4 -0
- data/scripts/lib/README.md +1 -5
- data/scripts/lib/install/deploy/README.md +3 -9
- data/scripts/lib/preview_generator.py +2261 -1341
- data/scripts/propagate.rb +277 -0
- data/scripts/translate.rb +1204 -0
- metadata +34 -3
- data/_plugins/preview_image_generator.rb +0 -351
data/_data/ui-text.yml
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
# User interface text and labels
|
|
2
|
-
# Used by:
|
|
3
|
-
#
|
|
1
|
+
# User interface text and labels — ENGLISH SOURCE OF TRUTH
|
|
2
|
+
# Used by: _includes/core/i18n.html (and through it, layouts/includes)
|
|
3
|
+
#
|
|
4
|
+
# Multilingual model (ZER0-078): this file holds ONLY the English strings.
|
|
5
|
+
# Alternate languages are NOT hand-maintained here anymore — they are
|
|
6
|
+
# machine-generated into _data/i18n/<lang>.yml by scripts/translate.rb
|
|
7
|
+
# (Claude Code OAuth) via .github/workflows/translate.yml. To change a
|
|
8
|
+
# translated label, edit the English string here and let the translate
|
|
9
|
+
# workflow regenerate the alternates. Languages are configured under
|
|
10
|
+
# `translation.languages` in _config.yml.
|
|
11
|
+
#
|
|
12
|
+
# (The former hand-written es/fr/de/ar blocks were removed in favor of the
|
|
13
|
+
# generated pipeline — see docs/systems/multilingual-translation.md.)
|
|
4
14
|
|
|
5
15
|
# English (default)
|
|
6
16
|
# -----------------
|
|
@@ -8,6 +18,7 @@ en: &DEFAULT_EN
|
|
|
8
18
|
skip_links : "Skip links"
|
|
9
19
|
skip_primary_nav : "Skip to primary navigation"
|
|
10
20
|
skip_content : "Skip to content"
|
|
21
|
+
skip_main_content : "Skip to main content"
|
|
11
22
|
skip_footer : "Skip to footer"
|
|
12
23
|
page : "Page"
|
|
13
24
|
pagination_previous : "Previous"
|
|
@@ -79,7 +90,28 @@ en: &DEFAULT_EN
|
|
|
79
90
|
shortcuts_toggle_sidebar : "Toggle sidebar"
|
|
80
91
|
shortcuts_toggle_toc : "Toggle table of contents"
|
|
81
92
|
shortcuts_show_help : "Show this help"
|
|
82
|
-
|
|
93
|
+
# Header / navbar chrome (ZER0-078)
|
|
94
|
+
home_label : "Home"
|
|
95
|
+
main_nav_label : "Main Navigation"
|
|
96
|
+
search_button_label : "Search"
|
|
97
|
+
settings_button_label : "Settings"
|
|
98
|
+
toc_title : "Table of Contents"
|
|
99
|
+
# Article intro / metadata chrome (ZER0-078)
|
|
100
|
+
published_label : "Published"
|
|
101
|
+
updated_label : "Updated"
|
|
102
|
+
reading_time_label : "Estimated reading time:"
|
|
103
|
+
reading_time_minute : "minute"
|
|
104
|
+
share_label : "Share"
|
|
105
|
+
copy_link_label : "Copy Link"
|
|
106
|
+
improve_label : "Improve"
|
|
107
|
+
edit_on_github : "Edit on GitHub"
|
|
108
|
+
# Language toggle & machine translation (ZER0-078)
|
|
109
|
+
lang_toggle_label : "Language"
|
|
110
|
+
lang_toggle_aria : "Change language"
|
|
111
|
+
lang_not_available : "Not yet translated"
|
|
112
|
+
machine_translation_notice : "This page was translated automatically from the English original."
|
|
113
|
+
machine_translation_view_source : "View the original"
|
|
114
|
+
|
|
83
115
|
en-US:
|
|
84
116
|
<<: *DEFAULT_EN
|
|
85
117
|
en-CA:
|
|
@@ -88,245 +120,3 @@ en-GB:
|
|
|
88
120
|
<<: *DEFAULT_EN
|
|
89
121
|
en-AU:
|
|
90
122
|
<<: *DEFAULT_EN
|
|
91
|
-
|
|
92
|
-
# Spanish
|
|
93
|
-
# -------
|
|
94
|
-
es: &DEFAULT_ES
|
|
95
|
-
skip_links : "Saltar enlaces"
|
|
96
|
-
skip_primary_nav : "Saltar a navegación principal"
|
|
97
|
-
skip_content : "Saltar a contenido"
|
|
98
|
-
skip_footer : "Saltar a pie"
|
|
99
|
-
page : "Página"
|
|
100
|
-
pagination_previous : "Anterior"
|
|
101
|
-
pagination_next : "Siguiente"
|
|
102
|
-
breadcrumb_home_label : "Inicio"
|
|
103
|
-
breadcrumb_separator : "/"
|
|
104
|
-
menu_label : "Alternar menú"
|
|
105
|
-
search_label : "Alternar búsqueda"
|
|
106
|
-
toc_label : "En esta página"
|
|
107
|
-
ext_link_label : "Enlace directo"
|
|
108
|
-
less_than : "menos de"
|
|
109
|
-
minute_read : "minuto(s) de lectura"
|
|
110
|
-
share_on_label : "Compartir en"
|
|
111
|
-
meta_label :
|
|
112
|
-
tags_label : "Etiquetas:"
|
|
113
|
-
categories_label : "Categorías:"
|
|
114
|
-
date_label : "Actualizado:"
|
|
115
|
-
author_label : "Autor:"
|
|
116
|
-
comments_label : "Deja un comentario"
|
|
117
|
-
comments_title : "Comentarios"
|
|
118
|
-
more_label : "Ver más"
|
|
119
|
-
related_label : "Puede que también te interese"
|
|
120
|
-
follow_label : "Seguir:"
|
|
121
|
-
feed_label : "Feed"
|
|
122
|
-
powered_by : "Funciona con"
|
|
123
|
-
website_label : "Sitio web"
|
|
124
|
-
email_label : "Correo electrónico"
|
|
125
|
-
recent_posts : "Entradas recientes"
|
|
126
|
-
undefined_wpm : "El parámetro words_per_minute (palabras por minuto) no está definido en _config.yml"
|
|
127
|
-
comment_form_info : "Tu dirección de correo electrónico no se publicará. Los campos obligatorios están marcados"
|
|
128
|
-
comment_form_comment_label : "Comentario"
|
|
129
|
-
comment_form_md_info : "Puedes utilizar Markdown"
|
|
130
|
-
comment_form_name_label : "Nombre"
|
|
131
|
-
comment_form_email_label : "Dirección de correo electrónico"
|
|
132
|
-
comment_form_website_label : "Sitio web (opcional)"
|
|
133
|
-
comment_btn_submit : "Enviar comentario"
|
|
134
|
-
comment_btn_submitted : "Enviado"
|
|
135
|
-
comment_success_msg : "¡Gracias por tu comentario! Se mostrará en el sitio una vez que haya sido aprobado."
|
|
136
|
-
comment_error_msg : "Lo sentimos, ha habido un error con tu envío. Por favor, asegúrate de que todos los campos obligatorios se han completado e inténtalo de nuevo."
|
|
137
|
-
loading_label : "Cargando..."
|
|
138
|
-
search_label_text : "Introduce tu término de búsqueda..."
|
|
139
|
-
search_placeholder_text : "Introduce tu término de búsqueda..."
|
|
140
|
-
search_no_results : "Sin resultados"
|
|
141
|
-
results_found : "Resultado(s) encontrado(s)"
|
|
142
|
-
back_to_top : "Volver arriba"
|
|
143
|
-
# Statistics labels
|
|
144
|
-
stats_total_posts : "Total de Publicaciones"
|
|
145
|
-
stats_total_pages : "Total de Páginas"
|
|
146
|
-
stats_categories : "Categorías"
|
|
147
|
-
stats_tags : "Etiquetas"
|
|
148
|
-
stats_last_updated : "Última Actualización"
|
|
149
|
-
stats_overview : "Resumen"
|
|
150
|
-
stats_content_breakdown : "Desglose de Contenido"
|
|
151
|
-
# Theme-specific labels
|
|
152
|
-
theme_toggle : "Cambiar tema"
|
|
153
|
-
dark_mode : "Modo oscuro"
|
|
154
|
-
light_mode : "Modo claro"
|
|
155
|
-
view_source : "Ver código fuente"
|
|
156
|
-
edit_page : "Editar esta página"
|
|
157
|
-
report_issue : "Reportar un problema"
|
|
158
|
-
|
|
159
|
-
es-ES:
|
|
160
|
-
<<: *DEFAULT_ES
|
|
161
|
-
es-MX:
|
|
162
|
-
<<: *DEFAULT_ES
|
|
163
|
-
|
|
164
|
-
# French
|
|
165
|
-
# ------
|
|
166
|
-
fr: &DEFAULT_FR
|
|
167
|
-
skip_links : "Liens de saut"
|
|
168
|
-
skip_primary_nav : "Aller à la navigation principale"
|
|
169
|
-
skip_content : "Aller au contenu"
|
|
170
|
-
skip_footer : "Aller au pied de page"
|
|
171
|
-
page : "Page"
|
|
172
|
-
pagination_previous : "Précédent"
|
|
173
|
-
pagination_next : "Suivant"
|
|
174
|
-
breadcrumb_home_label : "Accueil"
|
|
175
|
-
breadcrumb_separator : "/"
|
|
176
|
-
menu_label : "Basculer le menu"
|
|
177
|
-
search_label : "Basculer la recherche"
|
|
178
|
-
toc_label : "Sur cette page"
|
|
179
|
-
ext_link_label : "Lien direct"
|
|
180
|
-
less_than : "moins de"
|
|
181
|
-
minute_read : "minute(s) de lecture"
|
|
182
|
-
share_on_label : "Partager sur"
|
|
183
|
-
meta_label :
|
|
184
|
-
tags_label : "Tags :"
|
|
185
|
-
categories_label : "Catégories :"
|
|
186
|
-
date_label : "Mis à jour :"
|
|
187
|
-
author_label : "Auteur :"
|
|
188
|
-
comments_label : "Laisser un commentaire"
|
|
189
|
-
comments_title : "Commentaires"
|
|
190
|
-
more_label : "En savoir plus"
|
|
191
|
-
related_label : "Vous pourriez aussi aimer"
|
|
192
|
-
follow_label : "Suivre :"
|
|
193
|
-
feed_label : "Flux"
|
|
194
|
-
powered_by : "Propulsé par"
|
|
195
|
-
website_label : "Site web"
|
|
196
|
-
email_label : "Email"
|
|
197
|
-
recent_posts : "Articles récents"
|
|
198
|
-
back_to_top : "Retour en haut"
|
|
199
|
-
# Statistics labels
|
|
200
|
-
stats_total_posts : "Total des Publications"
|
|
201
|
-
stats_total_pages : "Total des Pages"
|
|
202
|
-
stats_categories : "Catégories"
|
|
203
|
-
stats_tags : "Tags"
|
|
204
|
-
stats_last_updated : "Dernière Mise à Jour"
|
|
205
|
-
stats_overview : "Aperçu"
|
|
206
|
-
stats_content_breakdown : "Répartition du Contenu"
|
|
207
|
-
# Theme-specific labels
|
|
208
|
-
theme_toggle : "Changer de thème"
|
|
209
|
-
dark_mode : "Mode sombre"
|
|
210
|
-
light_mode : "Mode clair"
|
|
211
|
-
view_source : "Voir la source"
|
|
212
|
-
edit_page : "Modifier cette page"
|
|
213
|
-
report_issue : "Signaler un problème"
|
|
214
|
-
|
|
215
|
-
fr-FR:
|
|
216
|
-
<<: *DEFAULT_FR
|
|
217
|
-
fr-CA:
|
|
218
|
-
<<: *DEFAULT_FR
|
|
219
|
-
|
|
220
|
-
# German
|
|
221
|
-
# ------
|
|
222
|
-
de: &DEFAULT_DE
|
|
223
|
-
skip_links : "Links überspringen"
|
|
224
|
-
skip_primary_nav : "Zur Hauptnavigation springen"
|
|
225
|
-
skip_content : "Zum Inhalt springen"
|
|
226
|
-
skip_footer : "Zur Fußzeile springen"
|
|
227
|
-
page : "Seite"
|
|
228
|
-
pagination_previous : "Zurück"
|
|
229
|
-
pagination_next : "Weiter"
|
|
230
|
-
breadcrumb_home_label : "Startseite"
|
|
231
|
-
breadcrumb_separator : "/"
|
|
232
|
-
menu_label : "Menü umschalten"
|
|
233
|
-
search_label : "Suche umschalten"
|
|
234
|
-
toc_label : "Auf dieser Seite"
|
|
235
|
-
ext_link_label : "Direktlink"
|
|
236
|
-
less_than : "weniger als"
|
|
237
|
-
minute_read : "Minute(n) Lesezeit"
|
|
238
|
-
share_on_label : "Teilen auf"
|
|
239
|
-
meta_label :
|
|
240
|
-
tags_label : "Tags:"
|
|
241
|
-
categories_label : "Kategorien:"
|
|
242
|
-
date_label : "Aktualisiert:"
|
|
243
|
-
author_label : "Autor:"
|
|
244
|
-
comments_label : "Einen Kommentar hinterlassen"
|
|
245
|
-
comments_title : "Kommentare"
|
|
246
|
-
more_label : "Mehr erfahren"
|
|
247
|
-
related_label : "Das könnte Sie auch interessieren"
|
|
248
|
-
follow_label : "Folgen:"
|
|
249
|
-
feed_label : "Feed"
|
|
250
|
-
powered_by : "Betrieben mit"
|
|
251
|
-
website_label : "Webseite"
|
|
252
|
-
email_label : "E-Mail"
|
|
253
|
-
recent_posts : "Neueste Beiträge"
|
|
254
|
-
back_to_top : "Nach oben"
|
|
255
|
-
# Statistics labels
|
|
256
|
-
stats_total_posts : "Gesamte Beiträge"
|
|
257
|
-
stats_total_pages : "Gesamte Seiten"
|
|
258
|
-
stats_categories : "Kategorien"
|
|
259
|
-
stats_tags : "Tags"
|
|
260
|
-
stats_last_updated : "Zuletzt Aktualisiert"
|
|
261
|
-
stats_overview : "Übersicht"
|
|
262
|
-
stats_content_breakdown : "Inhaltsaufschlüsselung"
|
|
263
|
-
# Theme-specific labels
|
|
264
|
-
theme_toggle : "Thema wechseln"
|
|
265
|
-
dark_mode : "Dunkler Modus"
|
|
266
|
-
light_mode : "Heller Modus"
|
|
267
|
-
view_source : "Quelle ansehen"
|
|
268
|
-
edit_page : "Diese Seite bearbeiten"
|
|
269
|
-
report_issue : "Ein Problem melden"
|
|
270
|
-
|
|
271
|
-
de-DE:
|
|
272
|
-
<<: *DEFAULT_DE
|
|
273
|
-
de-AT:
|
|
274
|
-
<<: *DEFAULT_DE
|
|
275
|
-
de-CH:
|
|
276
|
-
<<: *DEFAULT_DE
|
|
277
|
-
|
|
278
|
-
# Arabic
|
|
279
|
-
# ------
|
|
280
|
-
ar: &DEFAULT_AR
|
|
281
|
-
skip_links : "تخطي الروابط"
|
|
282
|
-
skip_primary_nav : "انتقل إلى التنقل الرئيسي"
|
|
283
|
-
skip_content : "انتقل إلى المحتوى"
|
|
284
|
-
skip_footer : "انتقل إلى التذييل"
|
|
285
|
-
page : "صفحة"
|
|
286
|
-
pagination_previous : "السابق"
|
|
287
|
-
pagination_next : "التالي"
|
|
288
|
-
breadcrumb_home_label : "الرئيسية"
|
|
289
|
-
breadcrumb_separator : "/"
|
|
290
|
-
menu_label : "تبديل القائمة"
|
|
291
|
-
search_label : "تبديل البحث"
|
|
292
|
-
toc_label : "في هذه الصفحة"
|
|
293
|
-
ext_link_label : "رابط مباشر"
|
|
294
|
-
less_than : "أقل من"
|
|
295
|
-
minute_read : "دقيقة للقراءة"
|
|
296
|
-
share_on_label : "مشاركة على"
|
|
297
|
-
meta_label :
|
|
298
|
-
tags_label : "الوسوم:"
|
|
299
|
-
categories_label : "الأقسام:"
|
|
300
|
-
date_label : "تاريخ التحديث:"
|
|
301
|
-
author_label : "الكاتب:"
|
|
302
|
-
comments_label : "اترك تعليقاً"
|
|
303
|
-
comments_title : "التعليقات"
|
|
304
|
-
more_label : "اقرأ المزيد"
|
|
305
|
-
related_label : "قد يعجبك أيضاً"
|
|
306
|
-
follow_label : "تابعنا:"
|
|
307
|
-
feed_label : "الخلاصة"
|
|
308
|
-
powered_by : "يعمل بواسطة"
|
|
309
|
-
website_label : "الموقع"
|
|
310
|
-
email_label : "البريد الإلكتروني"
|
|
311
|
-
recent_posts : "المنشورات الأخيرة"
|
|
312
|
-
back_to_top : "العودة للأعلى"
|
|
313
|
-
# Statistics labels
|
|
314
|
-
stats_total_posts : "إجمالي المنشورات"
|
|
315
|
-
stats_total_pages : "إجمالي الصفحات"
|
|
316
|
-
stats_categories : "الأقسام"
|
|
317
|
-
stats_tags : "الوسوم"
|
|
318
|
-
stats_last_updated : "آخر تحديث"
|
|
319
|
-
stats_overview : "نظرة عامة"
|
|
320
|
-
stats_content_breakdown : "تفصيل المحتوى"
|
|
321
|
-
# Theme-specific labels
|
|
322
|
-
theme_toggle : "تبديل المظهر"
|
|
323
|
-
dark_mode : "الوضع الداكن"
|
|
324
|
-
light_mode : "الوضع الفاتح"
|
|
325
|
-
view_source : "عرض المصدر"
|
|
326
|
-
edit_page : "تحرير هذه الصفحة"
|
|
327
|
-
report_issue : "الإبلاغ عن مشكلة"
|
|
328
|
-
|
|
329
|
-
ar-SA:
|
|
330
|
-
<<: *DEFAULT_AR
|
|
331
|
-
ar-EG:
|
|
332
|
-
<<: *DEFAULT_AR
|
data/_includes/README.md
CHANGED
|
@@ -9,9 +9,12 @@ This directory has been reorganized for better maintainability and clarity. File
|
|
|
9
9
|
Essential layout components that form the foundation of the site:
|
|
10
10
|
|
|
11
11
|
- `head.html` - HTML document head with meta tags, scripts, and styles
|
|
12
|
+
- `favicon.html` - Favicon / browser-identity tags (icon links, apple-touch, manifest, theme-color) driven by the optional `favicon:` config block
|
|
12
13
|
- `header.html` - Main site header with navigation
|
|
13
14
|
- `footer.html` - Site footer (if exists)
|
|
14
15
|
- `branding.html` - Site branding and title display
|
|
16
|
+
- `i18n.html` - Resolves the per-page-language UI-string set into the `ui` variable (generated translations fall back to English)
|
|
17
|
+
- `hreflang.html` - `<link rel="alternate" hreflang>` tags for pages with machine-generated translations
|
|
15
18
|
|
|
16
19
|
### `navigation/`
|
|
17
20
|
|
|
@@ -32,14 +35,16 @@ All navigation-related components:
|
|
|
32
35
|
Analytics and tracking integrations:
|
|
33
36
|
|
|
34
37
|
- `google-analytics.html` - Google Analytics tracking
|
|
35
|
-
- `google-tag-manager-head.html` - GTM head section
|
|
36
|
-
- `google-tag-manager-body.html` - GTM body section
|
|
38
|
+
- `google-tag-manager-head.html` - GTM head section (emits nothing unless `google_tag_manager:` is set in `_config.yml`)
|
|
39
|
+
- `google-tag-manager-body.html` - GTM body section (same config gate)
|
|
37
40
|
|
|
38
41
|
### `components/`
|
|
39
42
|
|
|
40
43
|
Reusable UI components and widgets:
|
|
41
44
|
|
|
42
45
|
- `searchbar.html` - Deprecated search stub (superseded by `search-modal.html`)
|
|
46
|
+
- `language-toggle.html` - Navbar dropdown switching between the English original and its machine-generated translations
|
|
47
|
+
- `translation-notice.html` - Disclosure banner on machine-translated pages linking back to the English original
|
|
43
48
|
- `powered-by.html` - "Powered by" credits display
|
|
44
49
|
- `quick-index.html` - Quick page index
|
|
45
50
|
- `dev-shortcuts.html` - Developer shortcuts
|
|
@@ -51,6 +56,13 @@ Reusable UI components and widgets:
|
|
|
51
56
|
- `js-cdn.html` - CDN JavaScript libraries
|
|
52
57
|
- `preview-image.html` - Consistent preview image rendering with lazy loading
|
|
53
58
|
- `post-card.html` - Reusable post card component for listings
|
|
59
|
+
- `data-card.html` - One generic card rendered from a plain data hash (title/url/icon/badge/meta/buttons schema in its header)
|
|
60
|
+
- `card-grid.html` - Responsive grid of `data-card`s from any data array — hub dashboards, fleet registries, service grids
|
|
61
|
+
- `bookshelf.html` - Grid of every book in the `books` collection (home-page library)
|
|
62
|
+
- `book-card.html` - One book's portrait cover card (bookshelf tile)
|
|
63
|
+
- `book-toc.html` - Ordered story/chapter list for one book
|
|
64
|
+
- `book-nav.html` - Prev/next-story + contents navigation inside a book
|
|
65
|
+
- `book-plate.html` - Full-width illustration figure with optional caption for story pages
|
|
54
66
|
|
|
55
67
|
### `content/`
|
|
56
68
|
|
|
@@ -75,6 +87,17 @@ Documentation and reference materials:
|
|
|
75
87
|
|
|
76
88
|
- `bootstrap-docs.html` - Bootstrap documentation (moved from style.html)
|
|
77
89
|
|
|
90
|
+
### `custom/` — consumer extension hooks
|
|
91
|
+
|
|
92
|
+
Empty stubs the theme includes at fixed points in the page chrome. A consumer site creates a file at the same path to inject markup there — **no fork of `root.html`, `head.html`, or `footer.html` needed**, so theme updates keep flowing without merge drift:
|
|
93
|
+
|
|
94
|
+
- `custom/head.html` - end of `<head>` (JSON-LD, font preloads, verification tags, extra meta)
|
|
95
|
+
- `custom/body-start.html` - immediately after `<body>` (e.g. GTM `<noscript>`)
|
|
96
|
+
- `custom/footer.html` - after the theme footer, before the cookie-consent banner
|
|
97
|
+
- `custom/body-end.html` - last thing before `</body>` (site scripts, third-party widgets)
|
|
98
|
+
|
|
99
|
+
The stubs ship empty (comment-only) and render nothing until shadowed.
|
|
100
|
+
|
|
78
101
|
## Usage
|
|
79
102
|
|
|
80
103
|
When including files in layouts or other templates, use the full path:
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
{% comment %} Feature: ZER0-039 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: google-tag-manager-body
|
|
4
|
+
Path: _includes/analytics/google-tag-manager-body.html
|
|
5
|
+
Purpose: Google Tag Manager noscript fallback (body section). Config-driven:
|
|
6
|
+
emits nothing unless the site sets a container ID.
|
|
7
|
+
Params: none — reads `google_tag_manager: "<container-id>"` from _config.yml
|
|
8
|
+
Depends on: google-tag-manager-head.html (loader)
|
|
9
|
+
{%- endcomment -%}
|
|
10
|
+
{%- if site.google_tag_manager -%}
|
|
2
11
|
<!-- Google Tag Manager (noscript) -->
|
|
3
12
|
<noscript>
|
|
4
|
-
<iframe src="https://www.googletagmanager.com/ns.html?id=
|
|
13
|
+
<iframe src="https://www.googletagmanager.com/ns.html?id={{ site.google_tag_manager }}"
|
|
5
14
|
height="0" width="0" style="display:none;visibility:hidden">
|
|
6
15
|
</iframe>
|
|
7
16
|
</noscript>
|
|
8
17
|
<!-- End Google Tag Manager (noscript) -->
|
|
9
|
-
|
|
18
|
+
{%- endif -%}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
{% comment %} Feature: ZER0-039 {% endcomment %}
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: google-tag-manager-head
|
|
4
|
+
Path: _includes/analytics/google-tag-manager-head.html
|
|
5
|
+
Purpose: Google Tag Manager loader (head section). Config-driven: emits
|
|
6
|
+
nothing unless the site sets a container ID, so consumer sites
|
|
7
|
+
never inherit this theme's own GTM container.
|
|
8
|
+
Params: none — reads `google_tag_manager: "<container-id>"` from _config.yml
|
|
9
|
+
Depends on: google-tag-manager-body.html (optional noscript counterpart)
|
|
10
|
+
Notes: Previously the container ID was hardcoded, silently enrolling
|
|
11
|
+
every consumer site's production build into the theme owner's
|
|
12
|
+
GTM container. Set your own ID (or leave unset for no GTM).
|
|
13
|
+
{%- endcomment -%}
|
|
14
|
+
{%- if site.google_tag_manager -%}
|
|
6
15
|
<script>
|
|
7
16
|
// Skip GTM on local/dev hostnames even if a prod build is served locally (Docker/CI preview).
|
|
8
17
|
(function () {
|
|
@@ -13,7 +22,8 @@
|
|
|
13
22
|
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
|
14
23
|
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
|
15
24
|
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
|
16
|
-
})(window,document,'script','dataLayer','
|
|
25
|
+
})(window,document,'script','dataLayer','{{ site.google_tag_manager }}');
|
|
17
26
|
})();
|
|
18
27
|
</script>
|
|
19
|
-
<!-- End Google Tag Manager -->
|
|
28
|
+
<!-- End Google Tag Manager -->
|
|
29
|
+
{%- endif -%}
|
|
@@ -64,6 +64,9 @@ Privacy: Respects Do Not Track and provides opt-out mechanisms
|
|
|
64
64
|
capturePageleave: {{ site.posthog.capture_pageleave | default: true }},
|
|
65
65
|
disableCookie: {{ site.posthog.disable_cookie | default: false }},
|
|
66
66
|
respectDnt: {{ site.posthog.respect_dnt | default: true }},
|
|
67
|
+
respectGpc: {{ site.posthog.privacy.respect_gpc | default: true }},
|
|
68
|
+
requireConsent: {{ site.posthog.privacy.require_consent | default: false }},
|
|
69
|
+
geoipDisable: {{ site.posthog.privacy.geoip_disable | default: false }},
|
|
67
70
|
crossSubdomainCookie: {{ site.posthog.cross_subdomain_cookie | default: false }},
|
|
68
71
|
secureCookie: {{ site.posthog.secure_cookie | default: true }},
|
|
69
72
|
persistence: '{{ site.posthog.persistence | default: "localStorage+cookie" }}',
|
|
@@ -74,9 +77,28 @@ Privacy: Respects Do Not Track and provides opt-out mechanisms
|
|
|
74
77
|
}
|
|
75
78
|
};
|
|
76
79
|
|
|
77
|
-
{% comment %}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
{% comment %} Consent gate (privacy.require_consent): until the visitor
|
|
81
|
+
accepts the "analytics" category in the theme's cookie-consent banner
|
|
82
|
+
(localStorage "zer0-cookie-consent"), PostHog starts opted OUT with
|
|
83
|
+
memory-only persistence — no cookies, nothing stored. The banner's
|
|
84
|
+
applyConsent() flips opt-in/opt-out live once a choice is made. {% endcomment %}
|
|
85
|
+
window.__phHasAnalyticsConsent = true;
|
|
86
|
+
if (window.posthogConfig.requireConsent) {
|
|
87
|
+
window.__phHasAnalyticsConsent = false;
|
|
88
|
+
try {
|
|
89
|
+
var __phStored = window.localStorage.getItem('zer0-cookie-consent');
|
|
90
|
+
if (__phStored) {
|
|
91
|
+
var __phConsent = JSON.parse(__phStored);
|
|
92
|
+
window.__phHasAnalyticsConsent = __phConsent && __phConsent.analytics === true;
|
|
93
|
+
}
|
|
94
|
+
} catch (e) { /* localStorage unavailable — stay opted out */ }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
{% comment %} Honor browser privacy signals before loading: Do Not Track
|
|
98
|
+
and Global Privacy Control (GPC is a legal signal under CCPA/CPRA). {% endcomment %}
|
|
99
|
+
if ((window.posthogConfig.respectDnt && navigator.doNotTrack === '1') ||
|
|
100
|
+
(window.posthogConfig.respectGpc && window.navigator.globalPrivacyControl === true)) {
|
|
101
|
+
console.log('PostHog: respecting browser privacy signal (DNT/GPC)');
|
|
80
102
|
} else {
|
|
81
103
|
{% comment %} PostHog Loading Script {% endcomment %}
|
|
82
104
|
!function(t,e){var o,n,p,r;e.__SV||(window.posthog && window.posthog.__loaded)||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init Rr Mr fi Or Ar ci Tr Cr capture Mi calculateEventProperties Lr register register_once register_for_session unregister unregister_for_session Hr getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSurveysLoaded onSessionId getSurveys getActiveMatchingSurveys renderSurvey displaySurvey canRenderSurvey canRenderSurveyAsync identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty Ur jr createPersonProfile zr kr Br opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing get_explicit_consent_status is_capturing clear_opt_in_out_capturing Dr debug M Nr getPageViewId captureTraceFeedback captureTraceMetric $r".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
|
|
@@ -91,7 +113,8 @@ Privacy: Respects Do Not Track and provides opt-out mechanisms
|
|
|
91
113
|
disable_cookie: window.posthogConfig.disableCookie,
|
|
92
114
|
cross_subdomain_cookie: window.posthogConfig.crossSubdomainCookie,
|
|
93
115
|
secure_cookie: window.posthogConfig.secureCookie,
|
|
94
|
-
persistence: window.posthogConfig.persistence,
|
|
116
|
+
persistence: window.__phHasAnalyticsConsent ? window.posthogConfig.persistence : 'memory',
|
|
117
|
+
opt_out_capturing_by_default: !window.__phHasAnalyticsConsent,
|
|
95
118
|
session_recording: window.posthogConfig.sessionRecording,
|
|
96
119
|
{% if site.posthog.privacy.ip_anonymization %}
|
|
97
120
|
ip: false,
|
|
@@ -100,6 +123,12 @@ Privacy: Respects Do Not Track and provides opt-out mechanisms
|
|
|
100
123
|
{% comment %} PostHog loaded successfully {% endcomment %}
|
|
101
124
|
console.log('PostHog analytics loaded successfully');
|
|
102
125
|
|
|
126
|
+
{% comment %} privacy.geoip_disable: stop PostHog from resolving
|
|
127
|
+
IP to location on every event {% endcomment %}
|
|
128
|
+
if (window.posthogConfig.geoipDisable) {
|
|
129
|
+
posthog.register({ '$geoip_disable': true });
|
|
130
|
+
}
|
|
131
|
+
|
|
103
132
|
{% comment %} Track Jekyll-specific page properties {% endcomment %}
|
|
104
133
|
posthog.register({
|
|
105
134
|
'site_title': '{{ site.title }}',
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{% comment %} Feature: ZER0-081 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: abc-letter
|
|
4
|
+
Path: _includes/components/abc-letter.html
|
|
5
|
+
Purpose: One card of an ABC board book — the illustration (or a friendly
|
|
6
|
+
"coming soon" placeholder), a big letter badge, the word, and the
|
|
7
|
+
read-aloud tagline. Rendered once per `alphabet:` entry by
|
|
8
|
+
_layouts/book-abc.html.
|
|
9
|
+
Params: entry (hash, required) — an alphabet entry with keys:
|
|
10
|
+
letter, word, tagline, image, alt, status ("planned"|"rendered")
|
|
11
|
+
Depends on: components/preview-image.html,
|
|
12
|
+
_sass/components/_book.scss (.abc-letter, .abc-letter-*)
|
|
13
|
+
Notes: The big letter is HTML typography, never baked into the art (so
|
|
14
|
+
raster models never garble it). A `status: planned` entry (no
|
|
15
|
+
rendered plate yet) shows a coloured placeholder tile instead of
|
|
16
|
+
a broken image, so the book reads end-to-end before any art runs.
|
|
17
|
+
{%- endcomment -%}
|
|
18
|
+
{%- if include.entry == nil -%}
|
|
19
|
+
{%- comment -%} ERROR: abc-letter requires `entry` {%- endcomment -%}
|
|
20
|
+
{%- else -%}
|
|
21
|
+
{%- assign _e = include.entry -%}
|
|
22
|
+
{%- assign _letter = _e.letter -%}
|
|
23
|
+
{%- comment -%} A plate counts as rendered only when it has an image AND is not
|
|
24
|
+
still "planned". Branch inline: Liquid's assign does not reliably apply the
|
|
25
|
+
"not-equals" comparison, so the condition lives in the if-tag below. {%- endcomment -%}
|
|
26
|
+
<article class="abc-letter" id="letter-{{ _letter | downcase }}">
|
|
27
|
+
<div class="abc-letter-plate">
|
|
28
|
+
{%- if _e.image and _e.status != "planned" -%}
|
|
29
|
+
{%- include components/preview-image.html src=_e.image alt=_e.alt class="abc-letter-img" loading="lazy" -%}
|
|
30
|
+
{%- else -%}
|
|
31
|
+
<div class="abc-letter-placeholder" role="img" aria-label="{{ _e.alt | default: _e.word | escape }} — illustration coming soon">
|
|
32
|
+
<span class="abc-letter-glyph" aria-hidden="true">{{ _letter }}</span>
|
|
33
|
+
<span class="abc-letter-planned">illustration coming soon</span>
|
|
34
|
+
</div>
|
|
35
|
+
{%- endif -%}
|
|
36
|
+
<span class="abc-letter-badge" aria-hidden="true">{{ _letter }}</span>
|
|
37
|
+
</div>
|
|
38
|
+
<p class="abc-letter-word">{{ _e.word }}</p>
|
|
39
|
+
{%- if _e.tagline -%}
|
|
40
|
+
<p class="abc-letter-tagline">{{ _e.tagline }}</p>
|
|
41
|
+
{%- endif -%}
|
|
42
|
+
</article>
|
|
43
|
+
{%- endif -%}
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
reference an external avatar, e.g. a GitHub one:
|
|
11
11
|
avatar: "https://avatars.githubusercontent.com/u/10567847?v=4"
|
|
12
12
|
2. `avatar` is a relative path (e.g. /images/authors/me.png) — prefixed with
|
|
13
|
-
the site's
|
|
13
|
+
the site's public_folder and passed through relative_url, so baseurl is
|
|
14
|
+
applied and the slashes never double into a protocol-relative //… URL
|
|
15
|
+
(even when public_folder and/or baseurl are empty or unset).
|
|
14
16
|
3. No `avatar`, but a `github` handle is set — falls back to that account's
|
|
15
17
|
GitHub avatar: https://github.com/<handle>.png
|
|
16
18
|
4. Otherwise — empty output (the caller renders a fallback icon).
|
|
@@ -22,7 +24,7 @@
|
|
|
22
24
|
{%- endcomment -%}
|
|
23
25
|
{%- assign _avatar = include.data.avatar -%}
|
|
24
26
|
{%- if _avatar and _avatar != "" -%}
|
|
25
|
-
{%- if _avatar contains "://" -%}{{ _avatar }}{%- else -%}{
|
|
27
|
+
{%- if _avatar contains "://" -%}{{ _avatar }}{%- else -%}{%- capture _avatar_path -%}/{{ site.public_folder }}{{ _avatar }}{%- endcapture -%}{{ _avatar_path | replace: "//", "/" | relative_url }}{%- endif -%}
|
|
26
28
|
{%- elsif include.data.github and include.data.github != "" -%}
|
|
27
29
|
https://github.com/{{ include.data.github }}.png
|
|
28
30
|
{%- endif -%}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{% comment %} Feature: ZER0-080 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: book-card
|
|
4
|
+
Path: _includes/components/book-card.html
|
|
5
|
+
Purpose: Bookshelf card for one book — portrait cover, title, subtitle,
|
|
6
|
+
chapter count, and audience badge, linking to the book page.
|
|
7
|
+
Params: book (document, required) — a `books` collection doc with
|
|
8
|
+
`layout: book` (the book's landing page)
|
|
9
|
+
Depends on: components/preview-image.html,
|
|
10
|
+
_sass/components/_book.scss (.book-card, .book-cover-frame)
|
|
11
|
+
Notes: Chapter count is derived live from the collection so the card
|
|
12
|
+
never goes stale. Whole card is one link for a big tap target.
|
|
13
|
+
{%- endcomment -%}
|
|
14
|
+
{%- if include.book == nil -%}
|
|
15
|
+
{%- comment -%} ERROR: book-card requires `book` {%- endcomment -%}
|
|
16
|
+
{%- else -%}
|
|
17
|
+
{%- assign _book = include.book -%}
|
|
18
|
+
{%- assign _slug = _book.book | default: _book.slug -%}
|
|
19
|
+
{%- assign _is_abc = _book.layout == "book-abc" -%}
|
|
20
|
+
{%- assign _chapters = site.books | where: "book", _slug | where_exp: "doc", "doc.chapter" -%}
|
|
21
|
+
{%- assign _cover = _book.cover_image | default: _book.preview | default: site.teaser -%}
|
|
22
|
+
<article class="book-card h-100">
|
|
23
|
+
<a href="{{ _book.url | relative_url }}" class="d-block text-decoration-none text-body h-100">
|
|
24
|
+
<div class="book-cover-frame mb-3">
|
|
25
|
+
{%- include components/preview-image.html src=_cover alt=_book.title class="" -%}
|
|
26
|
+
</div>
|
|
27
|
+
<h3 class="h5 mb-1">{{ _book.title }}</h3>
|
|
28
|
+
{%- if _book.subtitle -%}
|
|
29
|
+
<p class="text-body-secondary small mb-2">{{ _book.subtitle }}</p>
|
|
30
|
+
{%- endif -%}
|
|
31
|
+
<p class="small text-body-secondary mb-0">
|
|
32
|
+
{%- if _is_abc -%}
|
|
33
|
+
{%- assign _letters = _book.alphabet | size -%}
|
|
34
|
+
<i class="bi bi-type me-1" aria-hidden="true"></i>{{ _letters }} {% if _letters == 1 %}letter{% else %}letters{% endif %}
|
|
35
|
+
{%- else -%}
|
|
36
|
+
<i class="bi bi-book me-1" aria-hidden="true"></i>{{ _chapters.size }} {% if _chapters.size == 1 %}story{% else %}stories{% endif %}
|
|
37
|
+
{%- endif -%}
|
|
38
|
+
{%- if _book.audience %} <span class="mx-1" aria-hidden="true">•</span> {{ _book.audience }}{% endif -%}
|
|
39
|
+
</p>
|
|
40
|
+
</a>
|
|
41
|
+
</article>
|
|
42
|
+
{%- endif -%}
|