jekyll-theme-zer0 1.25.0 → 1.27.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +201 -0
  3. data/README.md +11 -28
  4. data/_data/authors.yml +4 -3
  5. data/_data/backlog.yml +54 -1
  6. data/_data/features.yml +101 -61
  7. data/_data/feedback_types.yml +148 -0
  8. data/_data/i18n/languages.yml +36 -0
  9. data/_data/navigation/README.md +1 -1
  10. data/_data/theme-manifest.yml +0 -2
  11. data/_data/ui-text.yml +36 -246
  12. data/_includes/README.md +4 -0
  13. data/_includes/components/README.md +1 -0
  14. data/_includes/components/author-avatar-url.html +4 -2
  15. data/_includes/components/env-switcher.html +3 -1
  16. data/_includes/components/language-toggle.html +81 -0
  17. data/_includes/components/page-feedback.html +252 -0
  18. data/_includes/components/search-modal.html +2 -2
  19. data/_includes/components/shortcuts-modal.html +1 -1
  20. data/_includes/components/translation-notice.html +27 -0
  21. data/_includes/content/intro.html +35 -76
  22. data/_includes/core/console-capture.html +89 -0
  23. data/_includes/core/footer.html +9 -4
  24. data/_includes/core/head.html +13 -0
  25. data/_includes/core/header.html +9 -4
  26. data/_includes/core/hreflang.html +33 -0
  27. data/_includes/core/i18n.html +36 -0
  28. data/_includes/navigation/breadcrumbs.html +1 -1
  29. data/_includes/navigation/navbar.html +5 -4
  30. data/_includes/navigation/sidebar-right.html +3 -2
  31. data/_includes/navigation/unified-drawer.html +1 -1
  32. data/_layouts/article.html +7 -1
  33. data/_layouts/default.html +5 -3
  34. data/_layouts/news.html +4 -2
  35. data/_layouts/root.html +17 -8
  36. data/_layouts/section.html +4 -2
  37. data/_sass/components/_page-feedback.scss +276 -0
  38. data/_sass/core/_docs-layout.scss +2 -78
  39. data/_sass/core/_obsidian.scss +9 -1
  40. data/_sass/layouts/_navbar-extras.scss +6 -1
  41. data/_sass/tokens/_layers.scss +4 -0
  42. data/assets/css/main.scss +1 -0
  43. data/assets/js/obsidian-graph.js +5 -1
  44. data/assets/js/page-feedback.js +660 -0
  45. data/scripts/README.md +20 -26
  46. data/scripts/bin/audit-consumer +1 -1
  47. data/scripts/bin/manifest +0 -1
  48. data/scripts/bin/sync-plugins +0 -1
  49. data/scripts/dev/rasterize-svg.js +65 -0
  50. data/scripts/features/generate-preview-images +49 -1390
  51. data/scripts/features/install-preview-generator +55 -33
  52. data/scripts/install/README.md +9 -20
  53. data/scripts/install/ai/prompts/wizard.system.md +8 -17
  54. data/scripts/lib/README.md +1 -5
  55. data/scripts/lib/install/deploy/README.md +3 -9
  56. data/scripts/lib/preview_generator.py +2261 -1341
  57. data/scripts/translate.rb +1114 -0
  58. metadata +14 -4
  59. data/_data/prompts.yml +0 -302
  60. data/_plugins/preview_image_generator.rb +0 -351
data/scripts/README.md CHANGED
@@ -18,7 +18,7 @@ scripts/
18
18
  │ ├── git.sh # Git operations
19
19
  │ ├── changelog.sh # Changelog generation
20
20
  │ ├── gem.sh # Gem build/publish
21
- │ └── preview_generator.py # Python preview image generator
21
+ │ └── preview_generator.py # Preview-image engine (Claude orchestrates; ZER0-004)
22
22
  ├── features/ # Feature-specific scripts
23
23
  │ ├── generate-preview-images # AI preview image generator
24
24
  │ ├── pixelate-preview-images # Shrink preview banners (pixelate + PNG-8)
@@ -65,10 +65,7 @@ Build the gem without the full release workflow.
65
65
  ```
66
66
 
67
67
  #### `bin/validate`
68
- Run preflight validation before refactors, pull requests, and releases. The
69
- quick path validates repository files, version consistency, YAML parsing, active
70
- configuration contracts, config-file classification, and navigation data before
71
- the Docker/local build stages run.
68
+ Run preflight validation before refactors, pull requests, and releases. The quick path validates repository files, version consistency, YAML parsing, active configuration contracts, config-file classification, and navigation data before the Docker/local build stages run.
72
69
 
73
70
  ```bash
74
71
  ./scripts/bin/validate [options]
@@ -115,22 +112,23 @@ Options:
115
112
  --dry-run Preview without changes
116
113
  --collection TYPE Generate for specific collection (posts, docs, etc.)
117
114
  -f, --file PATH Process specific file
118
- --provider PROVIDER Use specific AI provider (openai, stability, xai)
115
+ --provider PROVIDER Renderer (openai, xai, stability, gemini, local)
116
+ --prompt-engine ENG claude analyzes the article (default) | template
117
+ --review ENG claude reviews the render (default) | none
119
118
  --assets-prefix Custom assets path prefix (default: /assets)
120
119
  --no-auto-prefix Disable automatic path prefixing
121
120
 
122
- AI Providers:
123
- openai - OpenAI DALL-E (requires OPENAI_API_KEY)
124
- stability - Stability AI (requires STABILITY_API_KEY)
121
+ Renderers (Claude orchestrates all of them — analysis + review via
122
+ CLAUDE_CODE_OAUTH_TOKEN / ANTHROPIC_API_KEY / logged-in `claude` CLI):
123
+ openai - OpenAI gpt-image-2 / DALL-E, default (requires OPENAI_API_KEY)
125
124
  xai - xAI Grok image generation (requires XAI_API_KEY)
125
+ stability - Stability AI (requires STABILITY_API_KEY)
126
+ gemini - Google Gemini (requires GEMINI_API_KEY)
127
+ local - Deterministic template SVG/PNG (no API key)
126
128
  ```
127
129
 
128
130
  #### `pixelate-preview-images`
129
- Pixelate + palette-quantize the preview banners so they are dramatically smaller
130
- files while retaining the retro pixel-art look. Pure Python stdlib (no
131
- ImageMagick / Pillow / pngquant needed) — it downsamples the image and reduces
132
- it to an indexed PNG-8 palette. Typical savings on the AI-generated banners are
133
- ~90% (e.g. a 2.7 MB banner becomes ~230 KB).
131
+ Pixelate + palette-quantize the preview banners so they are dramatically smaller files while retaining the retro pixel-art look. Pure Python stdlib (no ImageMagick / Pillow / pngquant needed) — it downsamples the image and reduces it to an indexed PNG-8 palette. Typical savings on the AI-generated banners are ~90% (e.g. a 2.7 MB banner becomes ~230 KB).
134
132
 
135
133
  ```bash
136
134
  # Preview the savings for every banner (no changes), 4 workers:
@@ -157,10 +155,7 @@ Options (forwarded to scripts/features/pixelate_images.py):
157
155
  -j, --jobs N Parallel worker processes (default: 1)
158
156
  ```
159
157
 
160
- With no path argument it processes `preview_images.output_dir` from
161
- `_config.yml` (default `assets/images/previews`). Non-PNG and 16-bit/interlaced
162
- inputs are skipped gracefully. The engine has a built-in `--selftest`, exercised
163
- by `scripts/test/lib/test_pixelate_images.sh`.
158
+ With no path argument it processes `preview_images.output_dir` from `_config.yml` (default `assets/images/previews`). Non-PNG and 16-bit/interlaced inputs are skipped gracefully. The engine has a built-in `--selftest`, exercised by `scripts/test/lib/test_pixelate_images.sh`.
164
159
 
165
160
  #### `install-preview-generator`
166
161
  Install the preview image generator feature.
@@ -254,7 +249,7 @@ These are sourced by other scripts, not executed directly:
254
249
  - `git.sh` - Git operations (tags, commits, branches)
255
250
  - `changelog.sh` - Changelog generation from commits
256
251
  - `gem.sh` - Gem build/publish operations
257
- - `preview_generator.py` - Python preview image generator
252
+ - `preview_generator.py` - Preview-image engine (Claude analyze/review + openai/xai/stability/gemini/local renderers)
258
253
 
259
254
  ### Test Suites (scripts/test/)
260
255
 
@@ -272,8 +267,7 @@ These are sourced by other scripts, not executed directly:
272
267
 
273
268
  ## Backward Compatibility
274
269
 
275
- Legacy script paths (e.g., `./scripts/build.sh`) are maintained as thin wrappers
276
- that forward to the canonical locations in `bin/`, `utils/`, `features/`, or `test/`.
270
+ Legacy script paths (e.g., `./scripts/build.sh`) are maintained as thin wrappers that forward to the canonical locations in `bin/`, `utils/`, `features/`, or `test/`.
277
271
 
278
272
  ## Development Workflow
279
273
 
@@ -538,15 +532,15 @@ The script is designed to integrate with AI agents for automated content managem
538
532
  3. **Front Matter Updates**: Automatically updates the markdown file with the new preview path
539
533
  4. **Idempotent**: Won't regenerate images that already exist (unless `--force`)
540
534
 
541
- ### Python Alternative
535
+ ### Engine
542
536
 
543
- A Python version is available at `scripts/lib/preview_generator.py` with additional features:
537
+ All logic lives in the single-file Python engine `scripts/lib/preview_generator.py` (the shell entry points are thin wrappers around it):
544
538
 
545
539
  ```bash
546
- # Install dependencies
547
- pip install openai pyyaml requests
540
+ # Install the one dependency
541
+ pip3 install pyyaml
548
542
 
549
- # Run Python version
543
+ # Run the engine directly (same flags as the wrapper)
550
544
  python3 scripts/lib/preview_generator.py --collection posts --dry-run
551
545
  ```
552
546
 
@@ -190,7 +190,7 @@ if [[ ${#THEMABLE_DIRS[@]} -eq 0 ]]; then
190
190
  THEMABLE_DIRS=("_layouts" "_includes" "_sass" "assets/css" "assets/js")
191
191
  fi
192
192
  if [[ ${#REQUIRED_PLUGIN_FILES[@]} -eq 0 && ${#OPTIONAL_PLUGIN_FILES[@]} -eq 0 ]]; then
193
- REQUIRED_PLUGIN_FILES=("_plugins/preview_image_generator.rb" "_plugins/obsidian_links.rb")
193
+ REQUIRED_PLUGIN_FILES=("_plugins/obsidian_links.rb")
194
194
  OPTIONAL_PLUGIN_FILES=("_plugins/admin_page_urls.rb" "_plugins/content_statistics_generator.rb" "_plugins/theme_version.rb")
195
195
  fi
196
196
 
data/scripts/bin/manifest CHANGED
@@ -67,7 +67,6 @@ THEME_DATA_PATHS=(
67
67
  # Plugins that remote_theme consumers MUST vendor locally (GitHub Pages won't
68
68
  # load plugins from a remote theme).
69
69
  REQUIRED_PLUGIN_PATHS=(
70
- "_plugins/preview_image_generator.rb"
71
70
  "_plugins/obsidian_links.rb"
72
71
  )
73
72
 
@@ -172,7 +172,6 @@ fi
172
172
  # Default hardcoded fallback if manifest has no plugin entries at all
173
173
  if [[ ${#REQUIRED_PLUGINS[@]} -eq 0 && ${#OPTIONAL_PLUGINS[@]} -eq 0 ]]; then
174
174
  REQUIRED_PLUGINS=(
175
- "_plugins/preview_image_generator.rb"
176
175
  "_plugins/obsidian_links.rb"
177
176
  )
178
177
  OPTIONAL_PLUGINS=(
@@ -0,0 +1,65 @@
1
+ // Feature: ZER0-004
2
+ // SVG → PNG rasterizer for the preview-image engine (scripts/lib/preview_generator.py).
3
+ // Last resort in the rasterizer chain (rsvg-convert → inkscape → magick → THIS):
4
+ // uses the repo's Playwright devDependency (the same Chromium that powers the
5
+ // visual test tiers) so contributors need no native image tooling installed.
6
+ //
7
+ // Usage:
8
+ // node scripts/dev/rasterize-svg.js <in.svg> <out.png> [width] [height]
9
+ //
10
+ // Exit codes:
11
+ // 0 PNG written
12
+ // 1 rasterization failed (Chromium error, unreadable SVG, …)
13
+ // 2 usage error
14
+ // 3 @playwright/test not resolvable (caller should fall through / keep .svg)
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ const [svgPath, pngPath, widthArg, heightArg] = process.argv.slice(2);
19
+ if (!svgPath || !pngPath) {
20
+ console.error('Usage: node scripts/dev/rasterize-svg.js <in.svg> <out.png> [width] [height]');
21
+ process.exit(2);
22
+ }
23
+ const width = parseInt(widthArg, 10) || 1536;
24
+ const height = parseInt(heightArg, 10) || 1024;
25
+
26
+ let chromium;
27
+ try {
28
+ ({ chromium } = require('@playwright/test'));
29
+ } catch (e) {
30
+ console.error('rasterize-svg: @playwright/test not resolvable from ' + process.cwd());
31
+ process.exit(3);
32
+ }
33
+
34
+ (async () => {
35
+ const svg = fs.readFileSync(path.resolve(svgPath), 'utf8');
36
+ const browser = await chromium.launch();
37
+ try {
38
+ const ctx = await browser.newContext({
39
+ viewport: { width, height },
40
+ deviceScaleFactor: 1,
41
+ });
42
+ // Defense-in-depth: banner SVG must be self-contained, so block ALL
43
+ // network fetches — an external reference that slipped past the engine's
44
+ // sanitizer becomes a no-op instead of an SSRF/tracking request.
45
+ await ctx.route('**/*', (route) => route.abort());
46
+ const page = await ctx.newPage();
47
+ // Inline the SVG in a zero-margin shim and stretch it to the viewport so the
48
+ // screenshot is exactly width×height regardless of the SVG's own attributes.
49
+ await page.setContent(
50
+ `<!DOCTYPE html><html><head><style>
51
+ html,body{margin:0;padding:0;width:${width}px;height:${height}px;overflow:hidden;background:#000}
52
+ svg{display:block;width:${width}px;height:${height}px}
53
+ </style></head><body>${svg}</body></html>`,
54
+ { waitUntil: 'load', timeout: 30000 }
55
+ );
56
+ await page.screenshot({ path: path.resolve(pngPath), fullPage: false });
57
+ await ctx.close();
58
+ console.log('wrote', pngPath);
59
+ } finally {
60
+ await browser.close();
61
+ }
62
+ })().catch((err) => {
63
+ console.error('rasterize-svg: ' + (err && err.message ? err.message : err));
64
+ process.exit(1);
65
+ });