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.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +558 -5
  3. data/README.md +10 -27
  4. data/_data/authors.yml +4 -3
  5. data/_data/backlog.yml +28 -0
  6. data/_data/consumers.yml +147 -0
  7. data/_data/features.yml +142 -23
  8. data/_data/i18n/fr.yml +98 -0
  9. data/_data/i18n/languages.yml +36 -0
  10. data/_data/i18n/manifest.yml +1502 -0
  11. data/_data/navigation/docs.yml +2 -0
  12. data/_data/navigation/main.yml +6 -0
  13. data/_data/series.yml +19 -0
  14. data/_data/theme-manifest.yml +348 -268
  15. data/_data/ui-text.yml +36 -246
  16. data/_includes/README.md +25 -2
  17. data/_includes/analytics/google-tag-manager-body.html +11 -2
  18. data/_includes/analytics/google-tag-manager-head.html +16 -6
  19. data/_includes/analytics/posthog.html +33 -4
  20. data/_includes/components/abc-letter.html +43 -0
  21. data/_includes/components/author-avatar-url.html +4 -2
  22. data/_includes/components/book-card.html +42 -0
  23. data/_includes/components/book-nav.html +80 -0
  24. data/_includes/components/book-plate.html +31 -0
  25. data/_includes/components/book-toc.html +47 -0
  26. data/_includes/components/bookshelf.html +68 -0
  27. data/_includes/components/card-grid.html +60 -0
  28. data/_includes/components/data-card.html +95 -0
  29. data/_includes/components/env-switcher.html +3 -1
  30. data/_includes/components/halfmoon.html +5 -1
  31. data/_includes/components/language-toggle.html +81 -0
  32. data/_includes/components/page-feedback.html +65 -2
  33. data/_includes/components/search-modal.html +2 -2
  34. data/_includes/components/shortcuts-modal.html +1 -1
  35. data/_includes/components/theme-controls-bar.html +10 -2
  36. data/_includes/components/theme-customizer.html +8 -2
  37. data/_includes/components/translation-notice.html +27 -0
  38. data/_includes/content/intro.html +16 -15
  39. data/_includes/content/seo.html +9 -3
  40. data/_includes/core/color-mode-init.html +13 -4
  41. data/_includes/core/favicon.html +46 -0
  42. data/_includes/core/footer.html +9 -4
  43. data/_includes/core/head.html +18 -0
  44. data/_includes/core/header.html +9 -4
  45. data/_includes/core/hreflang.html +33 -0
  46. data/_includes/core/i18n.html +36 -0
  47. data/_includes/custom/body-end.html +18 -0
  48. data/_includes/custom/body-start.html +17 -0
  49. data/_includes/custom/footer.html +18 -0
  50. data/_includes/custom/head.html +18 -0
  51. data/_includes/navigation/breadcrumbs.html +1 -1
  52. data/_includes/navigation/local-graph.html +28 -2
  53. data/_includes/navigation/nav-tree.html +3 -3
  54. data/_includes/navigation/navbar.html +5 -4
  55. data/_includes/navigation/sidebar-config.html +21 -0
  56. data/_includes/navigation/sidebar-nav.html +4 -0
  57. data/_includes/navigation/sidebar-pagetree.html +150 -0
  58. data/_includes/navigation/sidebar-right.html +3 -2
  59. data/_includes/navigation/unified-drawer.html +9 -3
  60. data/_includes/obsidian/full-graph.html +165 -136
  61. data/_layouts/404.html +260 -0
  62. data/_layouts/README.md +2 -0
  63. data/_layouts/article.html +7 -1
  64. data/_layouts/book-abc.html +106 -0
  65. data/_layouts/book-story.html +91 -0
  66. data/_layouts/book.html +113 -0
  67. data/_layouts/collection.html +17 -5
  68. data/_layouts/default.html +5 -3
  69. data/_layouts/home.html +5 -2
  70. data/_layouts/landing.html +9 -0
  71. data/_layouts/news.html +159 -33
  72. data/_layouts/root.html +29 -9
  73. data/_layouts/section.html +61 -17
  74. data/_sass/components/_book.scss +423 -0
  75. data/_sass/core/_navbar.scss +13 -0
  76. data/_sass/core/_obsidian.scss +295 -7
  77. data/_sass/layouts/_navbar-extras.scss +6 -1
  78. data/_sass/theme/_backgrounds.scss +21 -8
  79. data/assets/css/main.scss +1 -0
  80. data/assets/js/auto-hide-nav.js +5 -1
  81. data/assets/js/halfmoon.js +26 -0
  82. data/assets/js/obsidian-graph.js +707 -265
  83. data/assets/js/obsidian-local-graph.js +161 -54
  84. data/assets/js/search-modal.js +4 -1
  85. data/scripts/README.md +20 -26
  86. data/scripts/bin/audit-consumer +1 -1
  87. data/scripts/bin/manifest +31 -5
  88. data/scripts/bin/sync-plugins +0 -1
  89. data/scripts/bin/validate +5 -1
  90. data/scripts/dev/rasterize-svg.js +65 -0
  91. data/scripts/features/generate-preview-images +49 -1390
  92. data/scripts/features/install-preview-generator +55 -33
  93. data/scripts/install/README.md +55 -25
  94. data/scripts/install/ai/client.sh +302 -93
  95. data/scripts/install/ai/prompts/spec.schema.json +1 -1
  96. data/scripts/install/ai/prompts/wizard.system.md +8 -17
  97. data/scripts/install/ai/wizard.sh +10 -5
  98. data/scripts/install/apply.sh +7 -3
  99. data/scripts/install/cli.sh +54 -4
  100. data/scripts/install/config.sh +167 -0
  101. data/scripts/install/doctor.sh +38 -0
  102. data/scripts/install/plan.sh +10 -0
  103. data/scripts/install/spec.sh +15 -7
  104. data/scripts/install/template.sh +4 -0
  105. data/scripts/lib/README.md +1 -5
  106. data/scripts/lib/install/deploy/README.md +3 -9
  107. data/scripts/lib/preview_generator.py +2261 -1341
  108. data/scripts/propagate.rb +277 -0
  109. data/scripts/translate.rb +1204 -0
  110. metadata +34 -3
  111. data/_plugins/preview_image_generator.rb +0 -351
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-theme-zer0
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.26.0
4
+ version: 1.28.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amr Abdel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-07 00:00:00.000000000 Z
11
+ date: 2026-08-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -81,6 +81,7 @@ files:
81
81
  - _data/ai.yml
82
82
  - _data/authors.yml
83
83
  - _data/backlog.yml
84
+ - _data/consumers.yml
84
85
  - _data/content_statistics.yml
85
86
  - _data/features.yml
86
87
  - _data/feedback_types.yml
@@ -88,6 +89,9 @@ files:
88
89
  - _data/generate_statistics.sh
89
90
  - _data/github-actions-example.yml
90
91
  - _data/glossary.yml
92
+ - _data/i18n/fr.yml
93
+ - _data/i18n/languages.yml
94
+ - _data/i18n/manifest.yml
91
95
  - _data/landing.yml
92
96
  - _data/navigation/README.md
93
97
  - _data/navigation/about.yml
@@ -102,6 +106,7 @@ files:
102
106
  - _data/roadmap.yml
103
107
  - _data/roadmap_plan.yml
104
108
  - _data/routing.yml
109
+ - _data/series.yml
105
110
  - _data/statistics_config.yml
106
111
  - _data/theme-manifest.yml
107
112
  - _data/theme_backgrounds.yml
@@ -114,6 +119,7 @@ files:
114
119
  - _includes/analytics/google-tag-manager-head.html
115
120
  - _includes/analytics/posthog.html
116
121
  - _includes/components/README.md
122
+ - _includes/components/abc-letter.html
117
123
  - _includes/components/admin-links.html
118
124
  - _includes/components/admin-tabs.html
119
125
  - _includes/components/ai-chat.html
@@ -124,13 +130,20 @@ files:
124
130
  - _includes/components/author-eeat.html
125
131
  - _includes/components/background-customizer.html
126
132
  - _includes/components/background-settings.html
133
+ - _includes/components/book-card.html
134
+ - _includes/components/book-nav.html
135
+ - _includes/components/book-plate.html
136
+ - _includes/components/book-toc.html
137
+ - _includes/components/bookshelf.html
127
138
  - _includes/components/callout.html
139
+ - _includes/components/card-grid.html
128
140
  - _includes/components/collection-manager.html
129
141
  - _includes/components/component-showcase.html
130
142
  - _includes/components/config-editor.html
131
143
  - _includes/components/config-viewer.html
132
144
  - _includes/components/cookie-consent.html
133
145
  - _includes/components/cta-button.html
146
+ - _includes/components/data-card.html
134
147
  - _includes/components/dev-shortcuts.html
135
148
  - _includes/components/env-dashboard.html
136
149
  - _includes/components/env-detect.html
@@ -139,6 +152,7 @@ files:
139
152
  - _includes/components/halfmoon.html
140
153
  - _includes/components/info-section.html
141
154
  - _includes/components/js-cdn.html
155
+ - _includes/components/language-toggle.html
142
156
  - _includes/components/mermaid.html
143
157
  - _includes/components/nanobar.html
144
158
  - _includes/components/nav-editor.html
@@ -163,6 +177,7 @@ files:
163
177
  - _includes/components/theme-customizer.html
164
178
  - _includes/components/theme-info.html
165
179
  - _includes/components/theme-preview-gallery.html
180
+ - _includes/components/translation-notice.html
166
181
  - _includes/components/zer0-env-var.html
167
182
  - _includes/content/backlinks.html
168
183
  - _includes/content/giscus.html
@@ -176,11 +191,18 @@ files:
176
191
  - _includes/core/branding.html
177
192
  - _includes/core/color-mode-init.html
178
193
  - _includes/core/console-capture.html
194
+ - _includes/core/favicon.html
179
195
  - _includes/core/footer-fabs.html
180
196
  - _includes/core/footer.html
181
197
  - _includes/core/head.html
182
198
  - _includes/core/header.html
199
+ - _includes/core/hreflang.html
200
+ - _includes/core/i18n.html
183
201
  - _includes/core/tokens-inline.html
202
+ - _includes/custom/body-end.html
203
+ - _includes/custom/body-start.html
204
+ - _includes/custom/footer.html
205
+ - _includes/custom/head.html
184
206
  - _includes/docs/bootstrap-docs.html
185
207
  - _includes/landing/landing-install-cards.html
186
208
  - _includes/landing/landing-quick-links.html
@@ -198,6 +220,7 @@ files:
198
220
  - _includes/navigation/sidebar-folders.html
199
221
  - _includes/navigation/sidebar-left.html
200
222
  - _includes/navigation/sidebar-nav.html
223
+ - _includes/navigation/sidebar-pagetree.html
201
224
  - _includes/navigation/sidebar-right.html
202
225
  - _includes/navigation/toc-fab.html
203
226
  - _includes/navigation/unified-drawer.html
@@ -211,11 +234,15 @@ files:
211
234
  - _includes/stats/stats-no-data.html
212
235
  - _includes/stats/stats-overview.html
213
236
  - _includes/stats/stats-tags.html
237
+ - _layouts/404.html
214
238
  - _layouts/README.md
215
239
  - _layouts/admin.html
216
240
  - _layouts/article.html
217
241
  - _layouts/author.html
218
242
  - _layouts/authors.html
243
+ - _layouts/book-abc.html
244
+ - _layouts/book-story.html
245
+ - _layouts/book.html
219
246
  - _layouts/collection.html
220
247
  - _layouts/default.html
221
248
  - _layouts/home.html
@@ -235,12 +262,12 @@ files:
235
262
  - _plugins/author_pages_generator.rb
236
263
  - _plugins/content_statistics_generator.rb
237
264
  - _plugins/obsidian_links.rb
238
- - _plugins/preview_image_generator.rb
239
265
  - _plugins/sanitize_config_filter.rb
240
266
  - _plugins/search_and_sitemap_generator.rb
241
267
  - _plugins/theme_version.rb
242
268
  - _sass/components/_author.scss
243
269
  - _sass/components/_back-to-top.scss
270
+ - _sass/components/_book.scss
244
271
  - _sass/components/_callout.scss
245
272
  - _sass/components/_content-tables.scss
246
273
  - _sass/components/_cookie-banner.scss
@@ -397,6 +424,7 @@ files:
397
424
  - scripts/content-review.rb
398
425
  - scripts/convert-notebooks.sh
399
426
  - scripts/dev/css-diff.sh
427
+ - scripts/dev/rasterize-svg.js
400
428
  - scripts/dev/shot.js
401
429
  - scripts/docker-publish
402
430
  - scripts/docs/check-freshness.sh
@@ -429,6 +457,7 @@ files:
429
457
  - scripts/install/ai/wizard.sh
430
458
  - scripts/install/apply.sh
431
459
  - scripts/install/cli.sh
460
+ - scripts/install/config.sh
432
461
  - scripts/install/diff.sh
433
462
  - scripts/install/doctor.sh
434
463
  - scripts/install/fs.sh
@@ -504,6 +533,7 @@ files:
504
533
  - scripts/platform/setup-macos.sh
505
534
  - scripts/platform/setup-wsl.sh
506
535
  - scripts/post-template-setup.sh
536
+ - scripts/propagate.rb
507
537
  - scripts/release
508
538
  - scripts/requirements.txt
509
539
  - scripts/setup.sh
@@ -530,6 +560,7 @@ files:
530
560
  - scripts/test/lib/test_validation.sh
531
561
  - scripts/test/lib/test_version.sh
532
562
  - scripts/test/theme/validate
563
+ - scripts/translate.rb
533
564
  - scripts/update-preview-paths.sh
534
565
  - scripts/utils/analyze-commits
535
566
  - scripts/utils/fix-markdown
@@ -1,351 +0,0 @@
1
- # Feature: ZER0-004
2
- # frozen_string_literal: true
3
-
4
- #
5
- # File: preview_image_generator.rb
6
- # Path: _plugins/preview_image_generator.rb
7
- # Purpose: Jekyll plugin for AI-powered preview image generation
8
- #
9
- # This plugin provides Jekyll integration for the preview image generator,
10
- # including Liquid tags, filters, and hooks for automatic image generation.
11
- #
12
- # Usage in templates:
13
- # {% preview_image_status %} - Shows missing preview count
14
- # {{ page | preview_image_path }} - Returns the preview image path
15
- # {{ page | has_preview_image }} - Returns true/false
16
- #
17
- # Configuration in _config.yml:
18
- # preview_images:
19
- # enabled: true
20
- # provider: openai
21
- # auto_generate: false # Set to true to generate during build (slow!)
22
- # style: "retro pixel art, 8-bit video game aesthetic"
23
- #
24
-
25
- module Jekyll
26
- module PreviewImageGenerator
27
- # Configuration defaults
28
- DEFAULTS = {
29
- 'enabled' => true,
30
- 'provider' => 'openai',
31
- 'model' => 'dall-e-3',
32
- 'size' => '1792x1024',
33
- 'quality' => 'standard',
34
- 'style' => 'retro pixel art, 8-bit video game aesthetic, vibrant colors, nostalgic, clean pixel graphics',
35
- 'style_modifiers' => 'pixelated, retro gaming style, CRT screen glow effect, limited color palette',
36
- 'output_dir' => 'assets/images/previews',
37
- 'assets_prefix' => '/assets',
38
- 'auto_prefix' => true,
39
- 'auto_generate' => false,
40
- 'collections' => ['posts', 'docs', 'quickstart']
41
- }.freeze
42
-
43
- # Get configuration with defaults
44
- def self.config(site)
45
- site_config = site.config['preview_images'] || {}
46
- DEFAULTS.merge(site_config)
47
- end
48
-
49
- # Check if a document has a preview image defined
50
- def self.has_preview?(doc)
51
- cached = cached_preview_entry(doc)
52
- return cached['has_preview'] unless cached.nil?
53
-
54
- preview = doc.data['preview']
55
- return false if preview.nil? || preview.to_s.strip.empty?
56
-
57
- # Reject non-path values (text descriptions used as preview)
58
- return false unless preview.match?(/\.(png|jpe?g|gif|svg|webp)$/i) || preview.start_with?('http')
59
-
60
- # Check if the preview file actually exists
61
- site = doc.site
62
- config = self.config(site)
63
-
64
- # External URL — assume it exists
65
- return true if preview.start_with?('http')
66
-
67
- # Normalize the preview path using assets_prefix
68
- normalized_preview = normalize_preview_path(preview, config)
69
-
70
- # Build candidate file paths to check
71
- candidates = []
72
- if normalized_preview.start_with?('/')
73
- candidates << File.join(site.source, normalized_preview.sub(/^\//, ''))
74
- candidates << File.join(site.source, 'assets', normalized_preview.sub(/^\//, ''))
75
- else
76
- candidates << File.join(site.source, 'assets', normalized_preview)
77
- candidates << File.join(site.source, normalized_preview)
78
- candidates << File.join(site.source, config['output_dir'], normalized_preview)
79
- end
80
-
81
- candidates.any? { |path| File.exist?(path) }
82
- end
83
-
84
- # Normalize a preview path by adding assets_prefix if needed
85
- def self.normalize_preview_path(preview, config)
86
- return preview if preview.nil? || preview.to_s.strip.empty?
87
- return preview if preview.start_with?('http')
88
-
89
- assets_prefix = config['assets_prefix'] || '/assets'
90
- auto_prefix = config['auto_prefix'] != false # Default to true
91
-
92
- # If auto_prefix is enabled and path doesn't already contain assets_prefix
93
- if auto_prefix && !preview.include?(assets_prefix)
94
- "#{assets_prefix}#{preview}"
95
- else
96
- preview
97
- end
98
- end
99
-
100
- # Get the preview image path for a document
101
- def self.preview_path(doc)
102
- cached = cached_preview_entry(doc)
103
- return cached['path'] unless cached.nil?
104
-
105
- preview = doc.data['preview']
106
- return nil if preview.nil? || preview.to_s.strip.empty?
107
-
108
- # Reject non-path values
109
- return nil unless preview.match?(/\.(png|jpe?g|gif|svg|webp)$/i) || preview.start_with?('http')
110
-
111
- site = doc.site
112
- config = self.config(site)
113
-
114
- # If it's an external URL, return as-is
115
- return preview if preview.start_with?('http')
116
-
117
- # Normalize path with assets_prefix
118
- normalize_preview_path(preview, config)
119
- end
120
-
121
- # Get list of documents missing preview images
122
- def self.missing_previews(site)
123
- build_index(site)['missing']
124
- end
125
-
126
- def self.build_index(site)
127
- cached = site.data['preview_image_index']
128
- return cached if cached
129
-
130
- config = self.config(site)
131
- index = {}
132
- missing = []
133
-
134
- preview_documents(site, config).each do |doc, collection_name|
135
- key = preview_cache_key(doc)
136
- next if index.key?(key)
137
-
138
- preview = doc.data['preview']
139
- normalized_path = nil
140
- has_preview = false
141
-
142
- if preview && !preview.to_s.strip.empty? && (preview.match?(/\.(png|jpe?g|gif|svg|webp)$/i) || preview.start_with?('http'))
143
- normalized_path = preview.start_with?('http') ? preview : normalize_preview_path(preview, config)
144
- has_preview = preview.start_with?('http') || preview_file_exists?(site, normalized_path, config)
145
- end
146
-
147
- index[key] = {
148
- 'has_preview' => has_preview,
149
- 'path' => normalized_path,
150
- 'collection' => collection_name
151
- }
152
-
153
- next if has_preview
154
-
155
- missing << {
156
- 'path' => doc.relative_path,
157
- 'title' => doc.data['title'] || File.basename(doc.relative_path),
158
- 'collection' => collection_name
159
- }
160
- end
161
-
162
- site.data['preview_image_index'] = {
163
- 'documents' => index,
164
- 'missing' => missing
165
- }
166
- end
167
-
168
- def self.cached_preview_entry(doc)
169
- return nil unless doc.respond_to?(:site) && doc.site && doc.site.data['preview_image_index']
170
-
171
- doc.site.data['preview_image_index']['documents'][preview_cache_key(doc)]
172
- end
173
-
174
- def self.preview_documents(site, config)
175
- docs = []
176
- Array(config['collections']).each do |collection_name|
177
- collection = site.collections[collection_name]
178
- next unless collection
179
-
180
- collection.docs.each { |doc| docs << [doc, collection_name] }
181
- end
182
-
183
- site.posts.docs.each { |doc| docs << [doc, 'posts'] } if site.respond_to?(:posts) && site.posts
184
- docs
185
- end
186
-
187
- def self.preview_cache_key(doc)
188
- doc.respond_to?(:relative_path) ? doc.relative_path : doc.path
189
- end
190
-
191
- def self.preview_file_exists?(site, normalized_preview, config)
192
- candidates = []
193
- if normalized_preview.start_with?('/')
194
- candidates << File.join(site.source, normalized_preview.sub(/^\//, ''))
195
- candidates << File.join(site.source, 'assets', normalized_preview.sub(/^\//, ''))
196
- else
197
- candidates << File.join(site.source, 'assets', normalized_preview)
198
- candidates << File.join(site.source, normalized_preview)
199
- candidates << File.join(site.source, config['output_dir'], normalized_preview)
200
- end
201
-
202
- candidates.any? { |path| File.exist?(path) }
203
- end
204
-
205
- # Generate a preview image filename from document
206
- def self.generate_filename(doc)
207
- # Use the document's slug or generate from title
208
- slug = doc.data['slug'] || doc.basename_without_ext
209
-
210
- # Sanitize the slug for use as filename
211
- sanitized = slug.to_s.downcase
212
- .gsub(/[^a-z0-9\-_]/, '-')
213
- .gsub(/-+/, '-')
214
- .gsub(/^-|-$/, '')
215
-
216
- "#{sanitized}-preview.png"
217
- end
218
- end
219
-
220
- # ==========================================================================
221
- # Liquid Filters
222
- # ==========================================================================
223
-
224
- module PreviewImageFilters
225
- # Check if a page/document has a preview image
226
- # Usage: {{ page | has_preview_image }}
227
- def has_preview_image(doc)
228
- return false unless doc.is_a?(Hash) || doc.respond_to?(:data)
229
-
230
- # Handle both Hash (from assign) and Document objects
231
- if doc.is_a?(Hash)
232
- preview = doc['preview']
233
- return false if preview.nil? || preview.to_s.strip.empty?
234
- # Only consider values that look like image paths or URLs
235
- preview.to_s.match?(/\.(png|jpe?g|gif|svg|webp)$/i) || preview.to_s.start_with?('http')
236
- else
237
- PreviewImageGenerator.has_preview?(doc)
238
- end
239
- end
240
-
241
- # Get the preview image path
242
- # Usage: {{ page | preview_image_path }}
243
- def preview_image_path(doc)
244
- return nil unless doc.is_a?(Hash) || doc.respond_to?(:data)
245
-
246
- if doc.is_a?(Hash)
247
- preview = doc['preview']
248
- return nil if preview.nil? || preview.to_s.strip.empty?
249
- return nil unless preview.to_s.match?(/\.(png|jpe?g|gif|svg|webp)$/i) || preview.to_s.start_with?('http')
250
-
251
- return preview if preview.start_with?('/') || preview.start_with?('http')
252
-
253
- # Get config from context if available
254
- site_config = @context.registers[:site].config['preview_images'] || {}
255
- output_dir = site_config['output_dir'] || 'assets/images/previews'
256
- "#{output_dir}/#{preview}"
257
- else
258
- PreviewImageGenerator.preview_path(doc)
259
- end
260
- end
261
-
262
- # Get suggested filename for a preview image
263
- # Usage: {{ page | preview_filename }}
264
- def preview_filename(doc)
265
- return nil unless doc.respond_to?(:data) || doc.respond_to?(:basename_without_ext)
266
- PreviewImageGenerator.generate_filename(doc)
267
- end
268
- end
269
-
270
- # ==========================================================================
271
- # Liquid Tags
272
- # ==========================================================================
273
-
274
- # Tag to display preview image status/count
275
- # Usage: {% preview_image_status %}
276
- class PreviewImageStatusTag < Liquid::Tag
277
- def render(context)
278
- site = context.registers[:site]
279
- missing = PreviewImageGenerator.missing_previews(site)
280
-
281
- if missing.empty?
282
- "<span class=\"badge bg-success\">All preview images present</span>"
283
- else
284
- "<span class=\"badge bg-warning text-dark\">#{missing.length} missing preview images</span>"
285
- end
286
- end
287
- end
288
-
289
- # Tag to list missing preview images
290
- # Usage: {% preview_images_missing %}
291
- class PreviewImagesMissingTag < Liquid::Tag
292
- def render(context)
293
- site = context.registers[:site]
294
- missing = PreviewImageGenerator.missing_previews(site)
295
-
296
- return "<p>All documents have preview images!</p>" if missing.empty?
297
-
298
- html = "<ul class=\"list-group\">\n"
299
- missing.each do |item|
300
- html += " <li class=\"list-group-item d-flex justify-content-between align-items-center\">\n"
301
- html += " <span>#{item['title']}</span>\n"
302
- html += " <span class=\"badge bg-secondary\">#{item['collection']}</span>\n"
303
- html += " </li>\n"
304
- end
305
- html += "</ul>"
306
- html
307
- end
308
- end
309
-
310
- # ==========================================================================
311
- # Generator Hook (optional auto-generation during build)
312
- # ==========================================================================
313
-
314
- class PreviewImageGeneratorHook < Generator
315
- safe true
316
- priority :low
317
-
318
- def generate(site)
319
- config = PreviewImageGenerator.config(site)
320
-
321
- return unless config['enabled']
322
-
323
- # Store missing previews in site data for access in templates
324
- preview_index = PreviewImageGenerator.build_index(site)
325
- site.data['preview_images_missing'] = preview_index['missing']
326
- site.data['preview_images_config'] = config
327
-
328
- # Log status
329
- missing_count = site.data['preview_images_missing'].length
330
- if missing_count > 0
331
- Jekyll.logger.info "Preview Images:", "#{missing_count} documents missing preview images"
332
- Jekyll.logger.info "Preview Images:", "Run 'scripts/generate-preview-images.sh' to generate them"
333
- end
334
-
335
- # Auto-generate is disabled by default (it's slow and requires API calls)
336
- # Users should run the shell script manually or via CI/CD
337
- if config['auto_generate']
338
- Jekyll.logger.warn "Preview Images:", "Auto-generation is enabled but not implemented in plugin"
339
- Jekyll.logger.warn "Preview Images:", "Use 'scripts/generate-preview-images.sh' instead"
340
- end
341
- end
342
- end
343
-
344
- # ==========================================================================
345
- # Register Liquid components
346
- # ==========================================================================
347
-
348
- Liquid::Template.register_filter(PreviewImageFilters)
349
- Liquid::Template.register_tag('preview_image_status', PreviewImageStatusTag)
350
- Liquid::Template.register_tag('preview_images_missing', PreviewImagesMissingTag)
351
- end