jekyll-theme-zer0 1.19.1 → 1.20.2
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 +395 -0
- data/README.md +27 -19
- data/_data/authors.yml +154 -5
- data/_data/backlog.yml +5 -5
- data/_data/content_statistics.yml +273 -297
- data/_data/features.yml +4 -25
- data/_data/navigation/README.md +24 -0
- data/_data/navigation/about.yml +2 -0
- data/_data/navigation/main.yml +2 -7
- data/_data/roadmap.yml +86 -12
- data/_includes/components/author-avatar-url.html +28 -0
- data/_includes/components/author-bio.html +86 -0
- data/_includes/components/author-card.html +184 -121
- data/_includes/components/author-eeat.html +10 -4
- data/_includes/components/info-section.html +1 -1
- data/_includes/components/mermaid.html +0 -3
- data/_includes/components/post-card.html +19 -9
- data/_includes/content/giscus.html +3 -2
- data/_includes/core/footer-fabs.html +28 -0
- data/_includes/core/footer.html +7 -17
- data/_includes/core/head.html +2 -2
- data/_includes/navigation/breadcrumbs.html +20 -2
- data/_includes/navigation/local-graph.html +18 -2
- data/_includes/obsidian/full-graph.html +4 -6
- data/_layouts/article.html +44 -74
- data/_layouts/author.html +274 -0
- data/_layouts/authors.html +55 -0
- data/_layouts/news.html +3 -3
- data/_layouts/note.html +21 -6
- data/_layouts/notebook.html +21 -6
- data/_layouts/root.html +31 -17
- data/_layouts/section.html +3 -3
- data/_plugins/author_pages_generator.rb +121 -0
- data/_sass/components/_author.scss +219 -0
- data/_sass/components/_content-tables.scss +16 -1
- data/_sass/components/_notes-index.scss +102 -0
- data/_sass/components/_search-modal.scss +40 -0
- data/_sass/components/_ui-enhancements.scss +570 -0
- data/_sass/core/_docs-code-examples.scss +463 -0
- data/_sass/core/_docs-layout.scss +0 -453
- data/_sass/core/_navbar.scss +253 -0
- data/_sass/core/_sidebar-extras.scss +79 -0
- data/_sass/core/_toc.scss +87 -0
- data/_sass/core/_variables.scss +7 -142
- data/_sass/custom.scss +24 -1122
- data/_sass/layouts/_global-chrome.scss +59 -0
- data/assets/css/main.scss +19 -2
- data/assets/js/author-profile.js +190 -0
- data/assets/js/modules/navigation/navbar.js +104 -0
- data/assets/js/obsidian-graph.js +2 -2
- data/assets/js/obsidian-local-graph.js +11 -5
- data/assets/vendor/cytoscape/cytoscape.min.js +32 -0
- data/scripts/README.md +39 -0
- data/scripts/bin/validate +11 -1
- data/scripts/dev/css-diff.sh +49 -0
- data/scripts/dev/shot.js +37 -0
- data/scripts/features/generate-preview-images +110 -6
- data/scripts/features/pixelate-preview-images +126 -0
- data/scripts/features/pixelate_images.py +662 -0
- data/scripts/github-setup.sh +0 -0
- data/scripts/lib/preview_generator.py +47 -3
- data/scripts/pixelate-preview-images.sh +12 -0
- data/scripts/test/integration/auto-version +10 -8
- data/scripts/test/lib/run_tests.sh +2 -0
- data/scripts/test/lib/test_content_review.sh +205 -0
- data/scripts/test/lib/test_pixelate_images.sh +108 -0
- metadata +25 -20
- data/_data/hub.yml +0 -68
- data/_data/hub_index.yml +0 -203
- data/_data/navigation/hub.yml +0 -110
- data/assets/vendor/font-awesome/css/all.min.css +0 -9
- data/assets/vendor/font-awesome/webfonts/fa-brands-400.ttf +0 -0
- data/assets/vendor/font-awesome/webfonts/fa-brands-400.woff2 +0 -0
- data/assets/vendor/font-awesome/webfonts/fa-regular-400.ttf +0 -0
- data/assets/vendor/font-awesome/webfonts/fa-regular-400.woff2 +0 -0
- data/assets/vendor/font-awesome/webfonts/fa-solid-900.ttf +0 -0
- data/assets/vendor/font-awesome/webfonts/fa-solid-900.woff2 +0 -0
- data/assets/vendor/font-awesome/webfonts/fa-v4compatibility.ttf +0 -0
- data/assets/vendor/font-awesome/webfonts/fa-v4compatibility.woff2 +0 -0
- data/assets/vendor/jquery/jquery-3.7.1.min.js +0 -2
- data/scripts/lib/hub.rb +0 -208
- data/scripts/provision-org-sites.rb +0 -252
- data/scripts/provision-org-sites.sh +0 -23
- data/scripts/sync-hub-metadata.rb +0 -184
- data/scripts/sync-hub-metadata.sh +0 -22
|
@@ -466,10 +466,42 @@ class PreviewGenerator:
|
|
|
466
466
|
|
|
467
467
|
# Rate limiter for API calls
|
|
468
468
|
self.rate_limiter = RateLimiter(requests_per_minute=rate_limit)
|
|
469
|
-
|
|
469
|
+
|
|
470
|
+
# Author data (for per-author art-style overrides). Loaded once and read
|
|
471
|
+
# (never mutated) per file, so it is safe to share across worker threads.
|
|
472
|
+
self.authors = self._load_authors()
|
|
473
|
+
|
|
470
474
|
# Ensure output directory exists
|
|
471
475
|
if not dry_run:
|
|
472
476
|
self.output_dir.mkdir(parents=True, exist_ok=True)
|
|
477
|
+
|
|
478
|
+
def _load_authors(self) -> Dict[str, Any]:
|
|
479
|
+
"""Load _data/authors.yml so posts can override the art style per author."""
|
|
480
|
+
authors_file = self.project_root / '_data' / 'authors.yml'
|
|
481
|
+
try:
|
|
482
|
+
data = yaml.safe_load(authors_file.read_text(encoding='utf-8'))
|
|
483
|
+
return data if isinstance(data, dict) else {}
|
|
484
|
+
except Exception:
|
|
485
|
+
return {}
|
|
486
|
+
|
|
487
|
+
def author_preview_overrides(self, author_key: Optional[str]) -> Dict[str, Any]:
|
|
488
|
+
"""Return the `preview:` override block for an author key (or {}).
|
|
489
|
+
|
|
490
|
+
A post whose `author:` references an entry in _data/authors.yml that
|
|
491
|
+
carries a `preview:` block gets that block's settings — they win over the
|
|
492
|
+
site-wide style for that post's banner (e.g. AI personas cassandra/vega).
|
|
493
|
+
"""
|
|
494
|
+
# Jekyll allows `author:` as a string, a list (multi-author), or a mapping
|
|
495
|
+
# (jekyll-seo-tag E-E-A-T). Only a hashable string can index authors.yml;
|
|
496
|
+
# anything else simply has no per-author override (and must not crash the
|
|
497
|
+
# run — keeping this path as resilient as the rest of the file).
|
|
498
|
+
if not isinstance(author_key, str) or not author_key:
|
|
499
|
+
return {}
|
|
500
|
+
author = self.authors.get(author_key)
|
|
501
|
+
if not isinstance(author, dict):
|
|
502
|
+
return {}
|
|
503
|
+
preview = author.get('preview')
|
|
504
|
+
return preview if isinstance(preview, dict) else {}
|
|
473
505
|
|
|
474
506
|
def debug(self, msg: str):
|
|
475
507
|
"""Print debug message if verbose mode is enabled."""
|
|
@@ -584,14 +616,26 @@ class PreviewGenerator:
|
|
|
584
616
|
clean_content = re.sub(r'\n+', ' ', clean_content)
|
|
585
617
|
prompt_parts.append(f"Key themes: {clean_content}")
|
|
586
618
|
|
|
619
|
+
# Per-author art-style override (e.g. AI personas) wins over the
|
|
620
|
+
# configured style for this post's banner. Computed per-call from the
|
|
621
|
+
# document's own front matter, so it is thread-safe under parallel workers.
|
|
622
|
+
effective_style = self.image_style
|
|
623
|
+
style_modifiers = ""
|
|
624
|
+
overrides = self.author_preview_overrides(content.front_matter.get('author'))
|
|
625
|
+
if overrides.get('style'):
|
|
626
|
+
effective_style = str(overrides['style']).strip()
|
|
627
|
+
self.debug(f"Author '{content.front_matter.get('author')}' style override applied")
|
|
628
|
+
if overrides.get('style_modifiers'):
|
|
629
|
+
style_modifiers = f" Additional style: {str(overrides['style_modifiers']).strip()}."
|
|
630
|
+
|
|
587
631
|
# Add style instructions
|
|
588
632
|
prompt_parts.extend([
|
|
589
|
-
f"Style: {
|
|
633
|
+
f"Style: {effective_style}.{style_modifiers}",
|
|
590
634
|
"The image should be suitable as a blog header/preview image.",
|
|
591
635
|
"Clean composition, professional look, visually appealing.",
|
|
592
636
|
"No text or letters in the image.",
|
|
593
637
|
])
|
|
594
|
-
|
|
638
|
+
|
|
595
639
|
return ' '.join(prompt_parts)
|
|
596
640
|
|
|
597
641
|
def generate_filename(self, title: str) -> str:
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# ============================================================================
|
|
4
|
+
# WRAPPER: This script forwards to scripts/features/pixelate-preview-images
|
|
5
|
+
#
|
|
6
|
+
# The canonical location is scripts/features/pixelate-preview-images. This
|
|
7
|
+
# wrapper exists for backward compatibility and discoverability alongside the
|
|
8
|
+
# other scripts/*.sh entry points.
|
|
9
|
+
# ============================================================================
|
|
10
|
+
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
exec "$SCRIPT_DIR/features/pixelate-preview-images" "$@"
|
|
@@ -21,12 +21,14 @@ log_warning() { warn "$@"; }
|
|
|
21
21
|
log_error() { error "$@"; }
|
|
22
22
|
log_test() { step "$@"; }
|
|
23
23
|
|
|
24
|
-
# Paths under test (
|
|
25
|
-
#
|
|
24
|
+
# Paths under test (commit analysis lives in scripts/utils/ with the
|
|
25
|
+
# analyze-commits.sh wrapper; releases go through scripts/bin/release). The CI
|
|
26
|
+
# release path is the release-please pipeline in .github/workflows/release.yml,
|
|
27
|
+
# which replaced the retired version-bump.yml.
|
|
26
28
|
ANALYZE_WRAPPER="$SCRIPTS_ROOT/analyze-commits.sh"
|
|
27
29
|
ANALYZE_IMPL="$SCRIPTS_ROOT/utils/analyze-commits"
|
|
28
30
|
RELEASE_BIN="$SCRIPTS_ROOT/bin/release"
|
|
29
|
-
WORKFLOW_FILE="$PROJECT_ROOT/.github/workflows/
|
|
31
|
+
WORKFLOW_FILE="$PROJECT_ROOT/.github/workflows/release.yml"
|
|
30
32
|
|
|
31
33
|
# Test counter
|
|
32
34
|
TESTS_RUN=0
|
|
@@ -114,14 +116,14 @@ test_release_command() {
|
|
|
114
116
|
test_workflow_syntax() {
|
|
115
117
|
log_info "Testing GitHub Actions workflow syntax..."
|
|
116
118
|
|
|
117
|
-
run_test "
|
|
119
|
+
run_test "release workflow exists" "test -f '$WORKFLOW_FILE'"
|
|
118
120
|
|
|
119
121
|
# Check if yamllint is available
|
|
120
122
|
if command -v yamllint >/dev/null 2>&1; then
|
|
121
|
-
run_test "
|
|
123
|
+
run_test "release workflow syntax" "yamllint -c '$PROJECT_ROOT/.github/config/.yamllint.yml' '$WORKFLOW_FILE'"
|
|
122
124
|
else
|
|
123
125
|
# Basic YAML syntax check with Ruby (always present for this repo)
|
|
124
|
-
run_test "
|
|
126
|
+
run_test "release workflow syntax" "ruby -ryaml -e 'YAML.safe_load_file(\"$WORKFLOW_FILE\", aliases: true)'"
|
|
125
127
|
fi
|
|
126
128
|
}
|
|
127
129
|
|
|
@@ -129,7 +131,7 @@ test_workflow_syntax() {
|
|
|
129
131
|
test_integration() {
|
|
130
132
|
log_info "Testing integration between components..."
|
|
131
133
|
|
|
132
|
-
#
|
|
134
|
+
# Validate that the commit-analysis wrapper returns a usable bump type
|
|
133
135
|
if git rev-list --count HEAD >/dev/null 2>&1; then
|
|
134
136
|
local commit_count=$(git rev-list --count HEAD)
|
|
135
137
|
if [[ $commit_count -gt 0 ]]; then
|
|
@@ -195,7 +197,7 @@ DESCRIPTION:
|
|
|
195
197
|
- Commit analysis functionality (scripts/analyze-commits.sh wrapper
|
|
196
198
|
and scripts/utils/analyze-commits implementation)
|
|
197
199
|
- Release command compatibility (scripts/bin/release)
|
|
198
|
-
- Workflow file syntax validation (.github/workflows/
|
|
200
|
+
- Workflow file syntax validation (.github/workflows/release.yml)
|
|
199
201
|
- Integration between components
|
|
200
202
|
- Conventional commit detection
|
|
201
203
|
|
|
@@ -129,6 +129,8 @@ main() {
|
|
|
129
129
|
source "$TEST_DIR/test_analyze_commits.sh"
|
|
130
130
|
source "$TEST_DIR/test_locale_independence.sh"
|
|
131
131
|
source "$TEST_DIR/test_migrate.sh"
|
|
132
|
+
source "$TEST_DIR/test_pixelate_images.sh"
|
|
133
|
+
source "$TEST_DIR/test_content_review.sh"
|
|
132
134
|
|
|
133
135
|
# Summary
|
|
134
136
|
echo -e "\n${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Unit tests for the scripts/content-review.rb scoring engine (issue #166).
|
|
4
|
+
#
|
|
5
|
+
# content-review.rb is the deterministic (no-API) tier of the AI content
|
|
6
|
+
# reviewer. It had zero direct unit coverage — it only ran as a side effect of
|
|
7
|
+
# the ai-content-review workflow on PRs touching pages/**. A scoring regression
|
|
8
|
+
# (code-fence false-positive) shipped in v1.18 and was fixed in v1.18.1 (PR
|
|
9
|
+
# #155), which is exactly the class of bug a unit test catches.
|
|
10
|
+
#
|
|
11
|
+
# These tests drive the real production config + schema
|
|
12
|
+
# (.github/config/content_review.yml, frontmatter_schema.yml) against synthetic
|
|
13
|
+
# Markdown fixtures, so they exercise the actual thresholds rather than a copy
|
|
14
|
+
# that can drift. Fixtures live under a temp `pages/_docs/**` tree so the
|
|
15
|
+
# production scope/path-pattern globs match without polluting the content dirs.
|
|
16
|
+
#
|
|
17
|
+
# Run under LC_ALL=C LANG=C for locale-independence parity with the T-015 guard.
|
|
18
|
+
|
|
19
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
|
|
20
|
+
CR_SCRIPT="$REPO_ROOT/scripts/content-review.rb"
|
|
21
|
+
CR_CONFIG="$REPO_ROOT/.github/config/content_review.yml"
|
|
22
|
+
CR_SCHEMA="$REPO_ROOT/.github/config/frontmatter_schema.yml"
|
|
23
|
+
|
|
24
|
+
echo "Testing content-review.rb scoring engine..."
|
|
25
|
+
|
|
26
|
+
# --- Fixture workspace -------------------------------------------------------
|
|
27
|
+
CR_WORK="$(mktemp -d)"
|
|
28
|
+
mkdir -p "$CR_WORK/pages/_docs/test"
|
|
29
|
+
|
|
30
|
+
# A well-formed docs page: complete front matter, in-range title/description,
|
|
31
|
+
# a keyword list, an H2, a language-tagged code fence, and an image with alt
|
|
32
|
+
# text. Should score high (no errors; at most cosmetic info items).
|
|
33
|
+
cat > "$CR_WORK/pages/_docs/test/good.md" <<'EOF'
|
|
34
|
+
---
|
|
35
|
+
title: Getting Started with the Zer0 Mistakes Theme
|
|
36
|
+
description: A friendly, complete walkthrough that takes you from an empty repository all the way to a deployed Zer0 Mistakes site in roughly ten short minutes.
|
|
37
|
+
lastmod: 2026-06-23T00:00:00.000Z
|
|
38
|
+
layout: default
|
|
39
|
+
categories:
|
|
40
|
+
- docs
|
|
41
|
+
tags:
|
|
42
|
+
- jekyll
|
|
43
|
+
- setup
|
|
44
|
+
keywords:
|
|
45
|
+
- jekyll
|
|
46
|
+
- theme
|
|
47
|
+
- setup
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Overview
|
|
51
|
+
|
|
52
|
+
This guide walks you through installing the theme, configuring your site, and
|
|
53
|
+
publishing it to GitHub Pages. Each step is short and copy-pasteable so you can
|
|
54
|
+
get a working site quickly without guessing at the details. We cover the local
|
|
55
|
+
development loop first, then the production deployment, and finally a few
|
|
56
|
+
verification checks so you know everything is wired up correctly before you
|
|
57
|
+
share the link with anyone else who might be reviewing your brand new site.
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
Run the bundled installer and start the development server with these commands:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
bundle install
|
|
65
|
+
bundle exec jekyll serve
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+

|
|
69
|
+
EOF
|
|
70
|
+
|
|
71
|
+
# Same page with the required `description` removed: one frontmatter error
|
|
72
|
+
# (required field) plus one SEO warning (no meta description). Must score
|
|
73
|
+
# strictly lower than the well-formed page.
|
|
74
|
+
cat > "$CR_WORK/pages/_docs/test/missing-desc.md" <<'EOF'
|
|
75
|
+
---
|
|
76
|
+
title: Getting Started with the Zer0 Mistakes Theme
|
|
77
|
+
lastmod: 2026-06-23T00:00:00.000Z
|
|
78
|
+
layout: default
|
|
79
|
+
categories:
|
|
80
|
+
- docs
|
|
81
|
+
tags:
|
|
82
|
+
- jekyll
|
|
83
|
+
- setup
|
|
84
|
+
keywords:
|
|
85
|
+
- jekyll
|
|
86
|
+
- theme
|
|
87
|
+
- setup
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Overview
|
|
91
|
+
|
|
92
|
+
This guide walks you through installing the theme, configuring your site, and
|
|
93
|
+
publishing it to GitHub Pages. Each step is short and copy-pasteable so you can
|
|
94
|
+
get a working site quickly without guessing at the details. We cover the local
|
|
95
|
+
development loop first, then the production deployment, and finally a few
|
|
96
|
+
verification checks so you know everything is wired up correctly before you
|
|
97
|
+
share the link with anyone else.
|
|
98
|
+
EOF
|
|
99
|
+
|
|
100
|
+
# A page whose only code fence is properly language-tagged. The closing bare
|
|
101
|
+
# ``` must NOT be flagged as a fence without a language (the v1.18.1 regression).
|
|
102
|
+
cat > "$CR_WORK/pages/_docs/test/codefence.md" <<'EOF'
|
|
103
|
+
---
|
|
104
|
+
title: Configuring the Zer0 Mistakes Theme Options
|
|
105
|
+
description: A focused reference covering the handful of configuration options you will set most often when tailoring the Zer0 Mistakes theme to your own project.
|
|
106
|
+
lastmod: 2026-06-23T00:00:00.000Z
|
|
107
|
+
layout: default
|
|
108
|
+
categories:
|
|
109
|
+
- docs
|
|
110
|
+
tags:
|
|
111
|
+
- jekyll
|
|
112
|
+
- config
|
|
113
|
+
keywords:
|
|
114
|
+
- jekyll
|
|
115
|
+
- config
|
|
116
|
+
- options
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Configuration
|
|
120
|
+
|
|
121
|
+
Set the options you need in your config file, then restart the server so the
|
|
122
|
+
changes take effect. The example below shows the most common starting point for
|
|
123
|
+
a new site and is safe to copy verbatim into your own configuration before you
|
|
124
|
+
begin customizing anything else about the project.
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
title: My Site
|
|
128
|
+
description: A site built with the theme
|
|
129
|
+
```
|
|
130
|
+
EOF
|
|
131
|
+
|
|
132
|
+
# A clearly failing page: missing description, layout, categories, and tags
|
|
133
|
+
# (four required-field errors) — well below the fail threshold.
|
|
134
|
+
cat > "$CR_WORK/pages/_docs/test/failing.md" <<'EOF'
|
|
135
|
+
---
|
|
136
|
+
title: Bad
|
|
137
|
+
lastmod: 2026-06-23T00:00:00.000Z
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
Too short.
|
|
141
|
+
EOF
|
|
142
|
+
|
|
143
|
+
# --- Helpers -----------------------------------------------------------------
|
|
144
|
+
# Run the reviewer against one fixture (path relative to CR_WORK), writing JSON.
|
|
145
|
+
# Echoes nothing; returns the script's exit code.
|
|
146
|
+
cr_run() { # $1 = relative md path, $2 = json out path, $3..= extra args
|
|
147
|
+
local rel="$1" out="$2"; shift 2
|
|
148
|
+
( cd "$CR_WORK" && LC_ALL=C LANG=C ruby "$CR_SCRIPT" \
|
|
149
|
+
--files "$rel" --config "$CR_CONFIG" --schema "$CR_SCHEMA" \
|
|
150
|
+
--json "$out" --quiet "$@" >/dev/null 2>&1 )
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
cr_score() { # $1 = json path -> prints first file's score
|
|
154
|
+
ruby -E UTF-8 -rjson -e 'puts JSON.parse(File.read(ARGV[0]))["files"][0]["score"]' "$1"
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
cr_collection() { # $1 = json path -> prints detected collection
|
|
158
|
+
ruby -E UTF-8 -rjson -e 'puts JSON.parse(File.read(ARGV[0]))["files"][0]["collection"]' "$1"
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
cr_has_message() { # $1 = json path, $2 = substring -> exit 0 if any issue contains it
|
|
162
|
+
ruby -E UTF-8 -rjson -e 'd=JSON.parse(File.read(ARGV[0]))["files"][0]; exit(d["issues"].any?{|i| i["message"].include?(ARGV[1])} ? 0 : 1)' "$1" "$2"
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# --- Run + assert ------------------------------------------------------------
|
|
166
|
+
cr_run "pages/_docs/test/good.md" "$CR_WORK/good.json"
|
|
167
|
+
cr_run "pages/_docs/test/missing-desc.md" "$CR_WORK/missing.json"
|
|
168
|
+
cr_run "pages/_docs/test/codefence.md" "$CR_WORK/codefence.json"
|
|
169
|
+
|
|
170
|
+
GOOD_SCORE="$(cr_score "$CR_WORK/good.json")"
|
|
171
|
+
MISSING_SCORE="$(cr_score "$CR_WORK/missing.json")"
|
|
172
|
+
|
|
173
|
+
assert_equals "docs" "$(cr_collection "$CR_WORK/good.json")" \
|
|
174
|
+
"fixture under pages/_docs/** is detected as the 'docs' collection"
|
|
175
|
+
|
|
176
|
+
assert_true "[ '${GOOD_SCORE:-0}' -ge 80 ]" \
|
|
177
|
+
"a well-formed docs page scores >= 80 (got ${GOOD_SCORE})"
|
|
178
|
+
|
|
179
|
+
assert_true "[ '${MISSING_SCORE:-100}' -lt '${GOOD_SCORE:-0}' ]" \
|
|
180
|
+
"removing the required description lowers the score (${MISSING_SCORE} < ${GOOD_SCORE})"
|
|
181
|
+
|
|
182
|
+
assert_true "cr_has_message '$CR_WORK/missing.json' 'description'" \
|
|
183
|
+
"the missing-description page reports a description issue"
|
|
184
|
+
|
|
185
|
+
assert_false "cr_has_message '$CR_WORK/codefence.json' 'Code fence without a language'" \
|
|
186
|
+
"a closing bare \`\`\` after a tagged fence is not flagged (v1.18.1 regression guard)"
|
|
187
|
+
|
|
188
|
+
# --strict exit behaviour: a failing page exits 0 in warn mode, non-zero strict.
|
|
189
|
+
cr_run "pages/_docs/test/failing.md" "$CR_WORK/failing.json"
|
|
190
|
+
FAILING_SCORE="$(cr_score "$CR_WORK/failing.json")"
|
|
191
|
+
assert_true "[ '${FAILING_SCORE:-100}' -lt 70 ]" \
|
|
192
|
+
"the failing fixture scores below the 70 fail threshold (got ${FAILING_SCORE})"
|
|
193
|
+
|
|
194
|
+
cr_run "pages/_docs/test/failing.md" "$CR_WORK/failing-warn.json"
|
|
195
|
+
assert_equals "0" "$?" \
|
|
196
|
+
"without --strict, a failing page still exits 0 (advisory mode)"
|
|
197
|
+
|
|
198
|
+
cr_run "pages/_docs/test/failing.md" "$CR_WORK/failing-strict.json" --strict
|
|
199
|
+
assert_equals "1" "$?" \
|
|
200
|
+
"with --strict, a failing page exits non-zero (1)"
|
|
201
|
+
|
|
202
|
+
# --- Cleanup -----------------------------------------------------------------
|
|
203
|
+
rm -rf "$CR_WORK"
|
|
204
|
+
|
|
205
|
+
echo -e "\n${GREEN}content-review.rb scoring tests complete${NC}"
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Unit tests for scripts/features/pixelate_images.py (preview-image pixelator).
|
|
4
|
+
#
|
|
5
|
+
# These drive the dependency-free Python engine as a subprocess and assert on
|
|
6
|
+
# its behaviour: the internal self-test, a real PNG round-trip that shrinks the
|
|
7
|
+
# file, dry-run leaving files untouched, and graceful handling of non-PNG input.
|
|
8
|
+
#
|
|
9
|
+
# Sourced by scripts/test/lib/run_tests.sh (uses its exported assert helpers),
|
|
10
|
+
# but also runnable directly for local iteration.
|
|
11
|
+
|
|
12
|
+
TEST_SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
REPO_ROOT="$(cd "$TEST_SELF_DIR/../../.." && pwd)"
|
|
14
|
+
ENGINE="$REPO_ROOT/scripts/features/pixelate_images.py"
|
|
15
|
+
|
|
16
|
+
# Fallback helpers so the file also runs standalone (when not sourced by the
|
|
17
|
+
# library runner that exports these).
|
|
18
|
+
if ! declare -F assert_true >/dev/null 2>&1; then
|
|
19
|
+
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
|
|
20
|
+
TESTS_RUN=0; TESTS_PASSED=0; TESTS_FAILED=0; declare -a FAILED_TESTS=()
|
|
21
|
+
assert_true() { ((TESTS_RUN++)); if eval "$1"; then ((TESTS_PASSED++)); echo -e "${GREEN}✓${NC} $2"; else ((TESTS_FAILED++)); echo -e "${RED}✗${NC} $2"; FAILED_TESTS+=("$2"); fi; }
|
|
22
|
+
assert_equals() { ((TESTS_RUN++)); if [[ "$1" == "$2" ]]; then ((TESTS_PASSED++)); echo -e "${GREEN}✓${NC} $3"; else ((TESTS_FAILED++)); echo -e "${RED}✗${NC} $3 (expected '$1' got '$2')"; FAILED_TESTS+=("$3"); fi; }
|
|
23
|
+
print_suite_header() { echo -e "\n${BLUE}=== $1 ===${NC}"; }
|
|
24
|
+
STANDALONE=true
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
set +e
|
|
28
|
+
|
|
29
|
+
print_suite_header "pixelate_images.py"
|
|
30
|
+
|
|
31
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
32
|
+
echo -e "${YELLOW}⚠ python3 not available — skipping pixelate_images tests${NC}"
|
|
33
|
+
else
|
|
34
|
+
# ---- Test: internal self-test ----------------------------------------
|
|
35
|
+
if python3 "$ENGINE" --selftest >/dev/null 2>&1; then
|
|
36
|
+
assert_true "true" "engine --selftest passes"
|
|
37
|
+
else
|
|
38
|
+
assert_true "false" "engine --selftest passes"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# ---- Set up a temp workspace with a synthetic gradient PNG -----------
|
|
42
|
+
PX_TMP="$(mktemp -d)"
|
|
43
|
+
trap '[[ -n "${PX_TMP:-}" ]] && rm -rf "$PX_TMP"' EXIT
|
|
44
|
+
|
|
45
|
+
# Build the input PNG independently of the engine's encoder so the
|
|
46
|
+
# round-trip genuinely exercises decode + re-encode.
|
|
47
|
+
python3 - "$PX_TMP/in.png" <<'PY'
|
|
48
|
+
import sys, struct, zlib
|
|
49
|
+
path = sys.argv[1]
|
|
50
|
+
w = h = 256
|
|
51
|
+
body = bytearray()
|
|
52
|
+
for y in range(h):
|
|
53
|
+
body.append(0) # filter: none
|
|
54
|
+
for x in range(w):
|
|
55
|
+
body += bytes(((x + y) % 256, (x * 2) % 256, (y * 2) % 256))
|
|
56
|
+
def chunk(t, d):
|
|
57
|
+
return struct.pack(">I", len(d)) + t + d + struct.pack(">I", zlib.crc32(t + d) & 0xffffffff)
|
|
58
|
+
png = (b"\x89PNG\r\n\x1a\n"
|
|
59
|
+
+ chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, 2, 0, 0, 0))
|
|
60
|
+
+ chunk(b"IDAT", zlib.compress(bytes(body), 9))
|
|
61
|
+
+ chunk(b"IEND", b""))
|
|
62
|
+
open(path, "wb").write(png)
|
|
63
|
+
PY
|
|
64
|
+
assert_true "[[ -s '$PX_TMP/in.png' ]]" "synthetic input PNG created"
|
|
65
|
+
ORIG_SIZE=$(wc -c < "$PX_TMP/in.png" | tr -d ' ')
|
|
66
|
+
|
|
67
|
+
# ---- Test: dry-run does not modify the input -------------------------
|
|
68
|
+
BEFORE=$(wc -c < "$PX_TMP/in.png" | tr -d ' ')
|
|
69
|
+
python3 "$ENGINE" --dry-run "$PX_TMP/in.png" >/dev/null 2>&1
|
|
70
|
+
AFTER=$(wc -c < "$PX_TMP/in.png" | tr -d ' ')
|
|
71
|
+
assert_equals "$BEFORE" "$AFTER" "dry-run leaves the file unchanged"
|
|
72
|
+
|
|
73
|
+
# ---- Test: real run writes a smaller, valid indexed PNG --------------
|
|
74
|
+
python3 "$ENGINE" --colors 64 --block 4 --output-dir "$PX_TMP/out" "$PX_TMP/in.png" >/dev/null 2>&1
|
|
75
|
+
OUT="$PX_TMP/out/in.png"
|
|
76
|
+
assert_true "[[ -s '$OUT' ]]" "optimized output written"
|
|
77
|
+
NEW_SIZE=$(wc -c < "$OUT" 2>/dev/null | tr -d ' ')
|
|
78
|
+
assert_true "[[ '${NEW_SIZE:-0}' -lt '$ORIG_SIZE' ]]" "output is smaller than original ($ORIG_SIZE -> ${NEW_SIZE:-0} bytes)"
|
|
79
|
+
|
|
80
|
+
# Output must be a valid PNG-8 (colour type 3) that decodes back.
|
|
81
|
+
if python3 - "$ENGINE" "$OUT" <<'PY'
|
|
82
|
+
import sys, importlib.util
|
|
83
|
+
spec = importlib.util.spec_from_file_location("pixelate_images", sys.argv[1])
|
|
84
|
+
P = importlib.util.module_from_spec(spec); spec.loader.exec_module(P)
|
|
85
|
+
img = P.decode_png(open(sys.argv[2], "rb").read())
|
|
86
|
+
assert img.width == 64 and img.height == 64, (img.width, img.height)
|
|
87
|
+
PY
|
|
88
|
+
then
|
|
89
|
+
assert_true "true" "output decodes back to expected 64x64 dimensions"
|
|
90
|
+
else
|
|
91
|
+
assert_true "false" "output decodes back to expected 64x64 dimensions"
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# ---- Test: non-PNG input is skipped gracefully (no crash) ------------
|
|
95
|
+
printf 'not a png at all' > "$PX_TMP/fake.png"
|
|
96
|
+
python3 "$ENGINE" --dry-run "$PX_TMP/fake.png" >/dev/null 2>&1
|
|
97
|
+
assert_equals "0" "$?" "non-PNG input is handled gracefully (exit 0)"
|
|
98
|
+
|
|
99
|
+
rm -rf "$PX_TMP"; PX_TMP=""
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# When run standalone, print a summary and exit with the right code.
|
|
103
|
+
if [[ "${STANDALONE:-false}" == "true" ]]; then
|
|
104
|
+
echo ""
|
|
105
|
+
echo -e "Total: $TESTS_RUN ${GREEN}Passed: $TESTS_PASSED${NC} ${RED}Failed: $TESTS_FAILED${NC}"
|
|
106
|
+
[[ $TESTS_FAILED -eq 0 ]]
|
|
107
|
+
exit $?
|
|
108
|
+
fi
|
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.
|
|
4
|
+
version: 1.20.2
|
|
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-06-
|
|
11
|
+
date: 2026-06-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jekyll
|
|
@@ -86,15 +86,12 @@ files:
|
|
|
86
86
|
- _data/generate_statistics.sh
|
|
87
87
|
- _data/github-actions-example.yml
|
|
88
88
|
- _data/glossary.yml
|
|
89
|
-
- _data/hub.yml
|
|
90
|
-
- _data/hub_index.yml
|
|
91
89
|
- _data/landing.yml
|
|
92
90
|
- _data/navigation/README.md
|
|
93
91
|
- _data/navigation/about.yml
|
|
94
92
|
- _data/navigation/admin.yml
|
|
95
93
|
- _data/navigation/docs.yml
|
|
96
94
|
- _data/navigation/home.yml
|
|
97
|
-
- _data/navigation/hub.yml
|
|
98
95
|
- _data/navigation/main.yml
|
|
99
96
|
- _data/navigation/posts.yml
|
|
100
97
|
- _data/navigation/quickstart.yml
|
|
@@ -117,6 +114,8 @@ files:
|
|
|
117
114
|
- _includes/components/admin-tabs.html
|
|
118
115
|
- _includes/components/ai-chat.html
|
|
119
116
|
- _includes/components/analytics-dashboard.html
|
|
117
|
+
- _includes/components/author-avatar-url.html
|
|
118
|
+
- _includes/components/author-bio.html
|
|
120
119
|
- _includes/components/author-card.html
|
|
121
120
|
- _includes/components/author-eeat.html
|
|
122
121
|
- _includes/components/background-customizer.html
|
|
@@ -170,6 +169,7 @@ files:
|
|
|
170
169
|
- _includes/content/toc.html
|
|
171
170
|
- _includes/content/transclude.html
|
|
172
171
|
- _includes/core/branding.html
|
|
172
|
+
- _includes/core/footer-fabs.html
|
|
173
173
|
- _includes/core/footer.html
|
|
174
174
|
- _includes/core/head.html
|
|
175
175
|
- _includes/core/header.html
|
|
@@ -205,6 +205,8 @@ files:
|
|
|
205
205
|
- _layouts/README.md
|
|
206
206
|
- _layouts/admin.html
|
|
207
207
|
- _layouts/article.html
|
|
208
|
+
- _layouts/author.html
|
|
209
|
+
- _layouts/authors.html
|
|
208
210
|
- _layouts/collection.html
|
|
209
211
|
- _layouts/default.html
|
|
210
212
|
- _layouts/home.html
|
|
@@ -222,32 +224,41 @@ files:
|
|
|
222
224
|
- _layouts/tag.html
|
|
223
225
|
- _layouts/welcome.html
|
|
224
226
|
- _plugins/admin_page_urls.rb
|
|
227
|
+
- _plugins/author_pages_generator.rb
|
|
225
228
|
- _plugins/content_statistics_generator.rb
|
|
226
229
|
- _plugins/obsidian_links.rb
|
|
227
230
|
- _plugins/preview_image_generator.rb
|
|
228
231
|
- _plugins/sanitize_config_filter.rb
|
|
229
232
|
- _plugins/search_and_sitemap_generator.rb
|
|
230
233
|
- _plugins/theme_version.rb
|
|
234
|
+
- _sass/components/_author.scss
|
|
231
235
|
- _sass/components/_back-to-top.scss
|
|
232
236
|
- _sass/components/_callout.scss
|
|
233
237
|
- _sass/components/_content-tables.scss
|
|
234
238
|
- _sass/components/_cookie-banner.scss
|
|
235
239
|
- _sass/components/_footer.scss
|
|
240
|
+
- _sass/components/_notes-index.scss
|
|
236
241
|
- _sass/components/_notes.scss
|
|
237
242
|
- _sass/components/_post-navigation.scss
|
|
243
|
+
- _sass/components/_search-modal.scss
|
|
238
244
|
- _sass/components/_skeleton.scss
|
|
239
245
|
- _sass/components/_theme-preview.scss
|
|
246
|
+
- _sass/components/_ui-enhancements.scss
|
|
247
|
+
- _sass/core/_docs-code-examples.scss
|
|
240
248
|
- _sass/core/_docs-layout.scss
|
|
241
249
|
- _sass/core/_nav-tree.scss
|
|
242
250
|
- _sass/core/_navbar.scss
|
|
243
251
|
- _sass/core/_obsidian.scss
|
|
244
252
|
- _sass/core/_offcanvas-panels.scss
|
|
245
253
|
- _sass/core/_sidebar-categories.scss
|
|
254
|
+
- _sass/core/_sidebar-extras.scss
|
|
246
255
|
- _sass/core/_syntax.scss
|
|
247
256
|
- _sass/core/_theme.scss
|
|
257
|
+
- _sass/core/_toc.scss
|
|
248
258
|
- _sass/core/_variables.scss
|
|
249
259
|
- _sass/core/code-copy.scss
|
|
250
260
|
- _sass/custom.scss
|
|
261
|
+
- _sass/layouts/_global-chrome.scss
|
|
251
262
|
- _sass/layouts/_landing.scss
|
|
252
263
|
- _sass/layouts/_navbar-extras.scss
|
|
253
264
|
- _sass/layouts/_section.scss
|
|
@@ -278,6 +289,7 @@ files:
|
|
|
278
289
|
- assets/data/notebooks/weather_data.csv
|
|
279
290
|
- assets/data/wiki-index.json
|
|
280
291
|
- assets/js/ai-chat.js
|
|
292
|
+
- assets/js/author-profile.js
|
|
281
293
|
- assets/js/auto-hide-nav.js
|
|
282
294
|
- assets/js/back-to-top.js
|
|
283
295
|
- assets/js/background-customizer.js
|
|
@@ -327,18 +339,9 @@ files:
|
|
|
327
339
|
- assets/vendor/bootstrap-icons/font/fonts/bootstrap-icons.woff2
|
|
328
340
|
- assets/vendor/bootstrap/css/bootstrap.min.css
|
|
329
341
|
- assets/vendor/bootstrap/js/bootstrap.bundle.min.js
|
|
330
|
-
- assets/vendor/
|
|
331
|
-
- assets/vendor/font-awesome/webfonts/fa-brands-400.ttf
|
|
332
|
-
- assets/vendor/font-awesome/webfonts/fa-brands-400.woff2
|
|
333
|
-
- assets/vendor/font-awesome/webfonts/fa-regular-400.ttf
|
|
334
|
-
- assets/vendor/font-awesome/webfonts/fa-regular-400.woff2
|
|
335
|
-
- assets/vendor/font-awesome/webfonts/fa-solid-900.ttf
|
|
336
|
-
- assets/vendor/font-awesome/webfonts/fa-solid-900.woff2
|
|
337
|
-
- assets/vendor/font-awesome/webfonts/fa-v4compatibility.ttf
|
|
338
|
-
- assets/vendor/font-awesome/webfonts/fa-v4compatibility.woff2
|
|
342
|
+
- assets/vendor/cytoscape/cytoscape.min.js
|
|
339
343
|
- assets/vendor/github-calendar/github-calendar-responsive.css
|
|
340
344
|
- assets/vendor/github-calendar/github-calendar.min.js
|
|
341
|
-
- assets/vendor/jquery/jquery-3.7.1.min.js
|
|
342
345
|
- assets/vendor/mathjax/es5/adaptors/liteDOM.js
|
|
343
346
|
- assets/vendor/mathjax/es5/core.js
|
|
344
347
|
- assets/vendor/mathjax/es5/output/chtml/fonts/woff-v2/MathJax_AMS-Regular.woff
|
|
@@ -380,6 +383,8 @@ files:
|
|
|
380
383
|
- scripts/build
|
|
381
384
|
- scripts/content-review.rb
|
|
382
385
|
- scripts/convert-notebooks.sh
|
|
386
|
+
- scripts/dev/css-diff.sh
|
|
387
|
+
- scripts/dev/shot.js
|
|
383
388
|
- scripts/docker-publish
|
|
384
389
|
- scripts/docs/check-freshness.sh
|
|
385
390
|
- scripts/docs/check-links.sh
|
|
@@ -388,6 +393,8 @@ files:
|
|
|
388
393
|
- scripts/example-usage.sh
|
|
389
394
|
- scripts/features/generate-preview-images
|
|
390
395
|
- scripts/features/install-preview-generator
|
|
396
|
+
- scripts/features/pixelate-preview-images
|
|
397
|
+
- scripts/features/pixelate_images.py
|
|
391
398
|
- scripts/features/validate_preview_urls.py
|
|
392
399
|
- scripts/fix-markdown-format.sh
|
|
393
400
|
- scripts/fork-cleanup.sh
|
|
@@ -446,7 +453,6 @@ files:
|
|
|
446
453
|
- scripts/lib/frontmatter.sh
|
|
447
454
|
- scripts/lib/gem.sh
|
|
448
455
|
- scripts/lib/git.sh
|
|
449
|
-
- scripts/lib/hub.rb
|
|
450
456
|
- scripts/lib/install/README.md
|
|
451
457
|
- scripts/lib/install/agents.sh
|
|
452
458
|
- scripts/lib/install/ai/diagnose.sh
|
|
@@ -476,18 +482,15 @@ files:
|
|
|
476
482
|
- scripts/lint-pages
|
|
477
483
|
- scripts/migrate-nav-modes.sh
|
|
478
484
|
- scripts/migrate.sh
|
|
485
|
+
- scripts/pixelate-preview-images.sh
|
|
479
486
|
- scripts/platform/setup-linux.sh
|
|
480
487
|
- scripts/platform/setup-macos.sh
|
|
481
488
|
- scripts/platform/setup-wsl.sh
|
|
482
489
|
- scripts/post-template-setup.sh
|
|
483
|
-
- scripts/provision-org-sites.rb
|
|
484
|
-
- scripts/provision-org-sites.sh
|
|
485
490
|
- scripts/release
|
|
486
491
|
- scripts/setup.sh
|
|
487
492
|
- scripts/sync-backlog.rb
|
|
488
493
|
- scripts/sync-backlog.sh
|
|
489
|
-
- scripts/sync-hub-metadata.rb
|
|
490
|
-
- scripts/sync-hub-metadata.sh
|
|
491
494
|
- scripts/test-auto-version.sh
|
|
492
495
|
- scripts/test-mermaid.sh
|
|
493
496
|
- scripts/test-notebook-conversion.sh
|
|
@@ -497,10 +500,12 @@ files:
|
|
|
497
500
|
- scripts/test/lib/run_tests.sh
|
|
498
501
|
- scripts/test/lib/test_analyze_commits.sh
|
|
499
502
|
- scripts/test/lib/test_changelog.sh
|
|
503
|
+
- scripts/test/lib/test_content_review.sh
|
|
500
504
|
- scripts/test/lib/test_gem.sh
|
|
501
505
|
- scripts/test/lib/test_git.sh
|
|
502
506
|
- scripts/test/lib/test_locale_independence.sh
|
|
503
507
|
- scripts/test/lib/test_migrate.sh
|
|
508
|
+
- scripts/test/lib/test_pixelate_images.sh
|
|
504
509
|
- scripts/test/lib/test_validation.sh
|
|
505
510
|
- scripts/test/lib/test_version.sh
|
|
506
511
|
- scripts/test/theme/validate
|