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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +201 -0
- data/README.md +11 -28
- data/_data/authors.yml +4 -3
- data/_data/backlog.yml +54 -1
- data/_data/features.yml +101 -61
- data/_data/feedback_types.yml +148 -0
- data/_data/i18n/languages.yml +36 -0
- data/_data/navigation/README.md +1 -1
- data/_data/theme-manifest.yml +0 -2
- data/_data/ui-text.yml +36 -246
- data/_includes/README.md +4 -0
- data/_includes/components/README.md +1 -0
- data/_includes/components/author-avatar-url.html +4 -2
- data/_includes/components/env-switcher.html +3 -1
- data/_includes/components/language-toggle.html +81 -0
- data/_includes/components/page-feedback.html +252 -0
- data/_includes/components/search-modal.html +2 -2
- data/_includes/components/shortcuts-modal.html +1 -1
- data/_includes/components/translation-notice.html +27 -0
- data/_includes/content/intro.html +35 -76
- data/_includes/core/console-capture.html +89 -0
- data/_includes/core/footer.html +9 -4
- data/_includes/core/head.html +13 -0
- data/_includes/core/header.html +9 -4
- data/_includes/core/hreflang.html +33 -0
- data/_includes/core/i18n.html +36 -0
- data/_includes/navigation/breadcrumbs.html +1 -1
- data/_includes/navigation/navbar.html +5 -4
- data/_includes/navigation/sidebar-right.html +3 -2
- data/_includes/navigation/unified-drawer.html +1 -1
- data/_layouts/article.html +7 -1
- data/_layouts/default.html +5 -3
- data/_layouts/news.html +4 -2
- data/_layouts/root.html +17 -8
- data/_layouts/section.html +4 -2
- data/_sass/components/_page-feedback.scss +276 -0
- data/_sass/core/_docs-layout.scss +2 -78
- data/_sass/core/_obsidian.scss +9 -1
- data/_sass/layouts/_navbar-extras.scss +6 -1
- data/_sass/tokens/_layers.scss +4 -0
- data/assets/css/main.scss +1 -0
- data/assets/js/obsidian-graph.js +5 -1
- data/assets/js/page-feedback.js +660 -0
- data/scripts/README.md +20 -26
- data/scripts/bin/audit-consumer +1 -1
- data/scripts/bin/manifest +0 -1
- data/scripts/bin/sync-plugins +0 -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 +9 -20
- data/scripts/install/ai/prompts/wizard.system.md +8 -17
- 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/translate.rb +1114 -0
- metadata +14 -4
- data/_data/prompts.yml +0 -302
- 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 #
|
|
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
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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` -
|
|
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
|
-
###
|
|
535
|
+
### Engine
|
|
542
536
|
|
|
543
|
-
|
|
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
|
|
547
|
-
|
|
540
|
+
# Install the one dependency
|
|
541
|
+
pip3 install pyyaml
|
|
548
542
|
|
|
549
|
-
# Run
|
|
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
|
|
data/scripts/bin/audit-consumer
CHANGED
|
@@ -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/
|
|
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
data/scripts/bin/sync-plugins
CHANGED
|
@@ -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
|
+
});
|