jekyll-theme-zer0 0.8.1 → 0.10.3

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +308 -4
  3. data/README.md +58 -25
  4. data/_data/README.md +4 -5
  5. data/_includes/README.md +1 -1
  6. data/_includes/components/mermaid.html +98 -9
  7. data/_includes/content/intro.html +13 -1
  8. data/_includes/core/header.html +1 -1
  9. data/_includes/stats/README.md +14 -2
  10. data/_layouts/README.md +3 -3
  11. data/_layouts/category.html +16 -6
  12. data/_layouts/collection.html +8 -3
  13. data/_layouts/journals.html +8 -3
  14. data/_sass/core/_theme.scss +4 -1
  15. data/_sass/custom.scss +20 -12
  16. data/assets/images/previews/10-ai-tools-that-will-transform-your-productivity-.png +0 -0
  17. data/assets/images/previews/business.png +0 -0
  18. data/assets/images/previews/css-grid-mastery-build-any-layout-you-can-imagine.png +0 -0
  19. data/assets/images/previews/development.png +0 -0
  20. data/assets/images/previews/github-setup-deployment.png +0 -0
  21. data/assets/images/previews/jekyll-setup.png +0 -0
  22. data/assets/images/previews/machine-setup.png +0 -0
  23. data/assets/images/previews/published-documentation-library.png +0 -0
  24. data/assets/images/previews/quantum-computing-explained-from-qubits-to-quantum.png +0 -0
  25. data/assets/images/previews/science.png +0 -0
  26. data/assets/images/previews/technology.png +0 -0
  27. data/assets/images/previews/the-complete-guide-to-startup-funding-in-2025.png +0 -0
  28. data/assets/images/previews/the-remote-work-revolution-how-global-teams-are-re.png +0 -0
  29. data/assets/images/previews/tutorial.png +0 -0
  30. data/assets/images/previews/world-news.png +0 -0
  31. data/assets/images/previews/zer0-mistakes-news-network-building-dynamic-news-s.png +0 -0
  32. data/assets/images/previews/zer0-mistakes-quick-start-guide.png +0 -0
  33. data/assets/js/auto-hide-nav.js +79 -16
  34. data/scripts/bin/build +115 -0
  35. data/scripts/bin/release +240 -0
  36. data/scripts/bin/test +203 -0
  37. data/scripts/features/generate-preview-images +846 -0
  38. data/scripts/features/install-preview-generator +531 -0
  39. data/scripts/features/preview_generator.py +646 -0
  40. data/scripts/generate-preview-images.sh +38 -93
  41. data/scripts/lib/README.md +35 -7
  42. data/scripts/lib/preview_generator.py +37 -22
  43. data/scripts/test/integration/auto-version +243 -0
  44. data/scripts/test/integration/mermaid +252 -0
  45. data/scripts/test/lib/run_tests.sh +151 -0
  46. data/scripts/test/lib/test_changelog.sh +90 -0
  47. data/scripts/test/lib/test_gem.sh +71 -0
  48. data/scripts/test/lib/test_git.sh +85 -0
  49. data/scripts/test/lib/test_validation.sh +75 -0
  50. data/scripts/test/lib/test_version.sh +101 -0
  51. data/scripts/test/theme/validate +120 -0
  52. data/scripts/test-mermaid.sh +51 -11
  53. data/scripts/utils/analyze-commits +300 -0
  54. data/scripts/utils/fix-markdown +251 -0
  55. data/scripts/utils/setup +137 -0
  56. data/scripts/version.sh +26 -0
  57. metadata +37 -8
  58. data/scripts/build.sh +0 -33
  59. data/scripts/build.sh.legacy +0 -174
  60. data/scripts/gem-publish.sh +0 -42
  61. data/scripts/gem-publish.sh.legacy +0 -700
  62. data/scripts/release.sh +0 -33
  63. data/scripts/release.sh.legacy +0 -342
@@ -79,13 +79,6 @@ else
79
79
  warn() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
80
80
  error() { echo -e "${RED}[ERROR]${NC} $1" >&2; exit 1; }
81
81
  debug() { [[ "${VERBOSE:-false}" == "true" ]] && echo -e "${PURPLE}[DEBUG]${NC} $1" >&2 || true; }
82
- print_header() {
83
- echo ""
84
- echo -e "${CYAN}════════════════════════════════════════════════════════════════${NC}"
85
- echo -e " ${GREEN}$1${NC}"
86
- echo -e "${CYAN}════════════════════════════════════════════════════════════════${NC}"
87
- echo ""
88
- }
89
82
  fi
90
83
 
91
84
  # =============================================================================
@@ -577,35 +570,27 @@ update_front_matter() {
577
570
  # Create backup
578
571
  cp "$file" "$file.bak"
579
572
 
580
- # Always use sed for reliability (yq can fail on complex YAML)
581
573
  # Check if preview field exists
582
574
  if grep -q "^preview:" "$file"; then
583
- # Update existing preview field using sed
584
- if [[ "$(uname)" == "Darwin" ]]; then
585
- # macOS sed requires empty string for -i
586
- sed -i '' "s|^preview:.*|preview: $preview_path|" "$file"
575
+ # Update existing preview field
576
+ if [[ "$YAML_PARSER" == "yq" ]]; then
577
+ # Use yq for in-place update
578
+ yq -i ".preview = \"$preview_path\"" "$file"
587
579
  else
588
- sed -i "s|^preview:.*|preview: $preview_path|" "$file"
580
+ # Use sed for simple replacement
581
+ sed -i.tmp "s|^preview:.*|preview: $preview_path|" "$file"
582
+ rm -f "$file.tmp"
589
583
  fi
590
584
  else
591
585
  # Add preview field after description or title
592
586
  if grep -q "^description:" "$file"; then
593
- if [[ "$(uname)" == "Darwin" ]]; then
594
- sed -i '' "/^description:/a\\
595
- preview: $preview_path" "$file"
596
- else
597
- sed -i "/^description:/a\\
587
+ sed -i.tmp "/^description:/a\\
598
588
  preview: $preview_path" "$file"
599
- fi
600
589
  else
601
- if [[ "$(uname)" == "Darwin" ]]; then
602
- sed -i '' "/^title:/a\\
603
- preview: $preview_path" "$file"
604
- else
605
- sed -i "/^title:/a\\
590
+ sed -i.tmp "/^title:/a\\
606
591
  preview: $preview_path" "$file"
607
- fi
608
592
  fi
593
+ rm -f "$file.tmp"
609
594
  fi
610
595
 
611
596
  # Remove backup on success
@@ -740,54 +725,6 @@ main() {
740
725
  echo " List Only: $LIST_ONLY"
741
726
  echo ""
742
727
 
743
- # Get configured collections from _config.yml
744
- get_configured_collections() {
745
- local collections=()
746
- local in_preview_images=false
747
- local in_collections=false
748
-
749
- while IFS= read -r line; do
750
- if [[ "$line" =~ ^preview_images: ]]; then
751
- in_preview_images=true
752
- continue
753
- fi
754
- if [[ "$in_preview_images" == true && "$line" =~ ^[[:space:]]+collections: ]]; then
755
- in_collections=true
756
- continue
757
- fi
758
- if [[ "$in_collections" == true && "$line" =~ ^[[:space:]]+- ]]; then
759
- local collection="${line#*- }"
760
- collection="${collection%%#*}"
761
- collection="${collection%"${collection##*[![:space:]]}"}"
762
- collections+=("$collection")
763
- elif [[ "$in_collections" == true && ! "$line" =~ ^[[:space:]]+- && ! "$line" =~ ^[[:space:]]*$ ]]; then
764
- break
765
- fi
766
- if [[ "$in_preview_images" == true && "$line" =~ ^[a-zA-Z_]+: && ! "$line" =~ ^[[:space:]] ]]; then
767
- break
768
- fi
769
- done < "$CONFIG_FILE"
770
-
771
- if [[ ${#collections[@]} -eq 0 ]]; then
772
- collections=("posts" "quickstart" "docs")
773
- fi
774
-
775
- echo "${collections[@]}"
776
- }
777
-
778
- # Process a collection by name
779
- process_collection_by_name() {
780
- local name="$1"
781
- local path="$PROJECT_ROOT/pages/_${name}"
782
-
783
- if [[ -d "$path" ]]; then
784
- step "Processing ${name} collection..."
785
- process_collection "$path"
786
- else
787
- warn "Collection directory not found: $path"
788
- fi
789
- }
790
-
791
728
  # Process files
792
729
  if [[ -n "$SPECIFIC_FILE" ]]; then
793
730
  # Process single file
@@ -797,27 +734,35 @@ main() {
797
734
  process_file "$PROJECT_ROOT/$SPECIFIC_FILE"
798
735
  elif [[ -n "$COLLECTION" ]]; then
799
736
  # Process specific collection
800
- if [[ "$COLLECTION" == "all" ]]; then
801
- step "Processing all configured collections..."
802
- for col in $(get_configured_collections); do
803
- process_collection_by_name "$col"
804
- done
805
- else
806
- # Check if collection directory exists
807
- local collection_path="$PROJECT_ROOT/pages/_${COLLECTION}"
808
- if [[ -d "$collection_path" ]]; then
809
- process_collection_by_name "$COLLECTION"
810
- else
811
- local available=$(get_configured_collections | tr ' ' ', ')
812
- error "Unknown collection: $COLLECTION. Available: $available, all"
813
- fi
814
- fi
737
+ case "$COLLECTION" in
738
+ posts)
739
+ step "Processing posts collection..."
740
+ process_collection "$PROJECT_ROOT/pages/_posts"
741
+ ;;
742
+ quickstart)
743
+ step "Processing quickstart collection..."
744
+ process_collection "$PROJECT_ROOT/pages/_quickstart"
745
+ ;;
746
+ docs)
747
+ step "Processing docs collection..."
748
+ process_collection "$PROJECT_ROOT/pages/_docs"
749
+ ;;
750
+ all)
751
+ step "Processing all collections..."
752
+ process_collection "$PROJECT_ROOT/pages/_posts"
753
+ process_collection "$PROJECT_ROOT/pages/_quickstart"
754
+ process_collection "$PROJECT_ROOT/pages/_docs"
755
+ ;;
756
+ *)
757
+ error "Unknown collection: $COLLECTION. Use: posts, quickstart, docs, or all"
758
+ ;;
759
+ esac
815
760
  else
816
- # Process all configured collections by default
817
- step "Processing all configured collections..."
818
- for col in $(get_configured_collections); do
819
- process_collection_by_name "$col"
820
- done
761
+ # Process all content by default
762
+ step "Processing all content collections..."
763
+ process_collection "$PROJECT_ROOT/pages/_posts"
764
+ process_collection "$PROJECT_ROOT/pages/_quickstart"
765
+ process_collection "$PROJECT_ROOT/pages/_docs"
821
766
  fi
822
767
 
823
768
  # Print summary
@@ -9,9 +9,11 @@ This directory contains focused, single-responsibility libraries that power the
9
9
  ## Libraries
10
10
 
11
11
  ### 📦 `common.sh` - Shared Utilities
12
+
12
13
  Core utilities used by all other libraries.
13
14
 
14
15
  **Functions:**
16
+
15
17
  - `log()`, `info()`, `warn()`, `error()` - Colored logging
16
18
  - `confirm()` - User confirmation prompts
17
19
  - `dry_run_exec()` - Dry run wrapper for commands
@@ -19,15 +21,18 @@ Core utilities used by all other libraries.
19
21
  - `get_repo_root()` - Find repository root directory
20
22
 
21
23
  **Usage:**
24
+
22
25
  ```bash
23
26
  source "$(dirname "$0")/lib/common.sh"
24
27
  log "Starting process..."
25
28
  ```
26
29
 
27
30
  ### 🔍 `validation.sh` - Environment Validation
31
+
28
32
  Validates environment, dependencies, and prerequisites.
29
33
 
30
34
  **Functions:**
35
+
31
36
  - `validate_git_repo()` - Verify git repository
32
37
  - `validate_clean_working_dir()` - Check for uncommitted changes
33
38
  - `validate_required_files()` - Check required files exist
@@ -36,15 +41,18 @@ Validates environment, dependencies, and prerequisites.
36
41
  - `validate_environment()` - Comprehensive validation
37
42
 
38
43
  **Usage:**
44
+
39
45
  ```bash
40
46
  source "$(dirname "$0")/lib/validation.sh"
41
47
  validate_environment false false # skip_publish=false, require_gh=false
42
48
  ```
43
49
 
44
50
  ### 📝 `version.sh` - Version Management
51
+
45
52
  Read, calculate, and update semantic versions.
46
53
 
47
54
  **Functions:**
55
+
48
56
  - `get_current_version()` - Read version from version.rb
49
57
  - `calculate_new_version()` - Calculate new version (major/minor/patch)
50
58
  - `update_version_files()` - Update all version files
@@ -52,6 +60,7 @@ Read, calculate, and update semantic versions.
52
60
  - `version_less_than()` - Compare two versions
53
61
 
54
62
  **Usage:**
63
+
55
64
  ```bash
56
65
  source "$(dirname "$0")/lib/version.sh"
57
66
 
@@ -61,15 +70,18 @@ update_version_files "$new"
61
70
  ```
62
71
 
63
72
  ### 📋 `changelog.sh` - Changelog Generation
73
+
64
74
  Generate changelogs from conventional commits.
65
75
 
66
76
  **Functions:**
77
+
67
78
  - `generate_changelog()` - Generate changelog for version
68
79
  - `categorize_commit()` - Categorize commit by type
69
80
  - `clean_commit_message()` - Clean conventional commit prefixes
70
81
  - `extract_release_notes()` - Extract notes for specific version
71
82
 
72
83
  **Commit Categories:**
84
+
73
85
  - `feat:` → Added
74
86
  - `fix:` → Fixed
75
87
  - `BREAKING:` → Breaking Changes
@@ -78,6 +90,7 @@ Generate changelogs from conventional commits.
78
90
  - `security:` → Security
79
91
 
80
92
  **Usage:**
93
+
81
94
  ```bash
82
95
  source "$(dirname "$0")/lib/changelog.sh"
83
96
 
@@ -85,9 +98,11 @@ generate_changelog "1.2.0" "v1.1.0" "HEAD"
85
98
  ```
86
99
 
87
100
  ### 🔄 `git.sh` - Git Operations
101
+
88
102
  Git commits, tags, and repository operations.
89
103
 
90
104
  **Functions:**
105
+
91
106
  - `get_last_version_tag()` - Find last version tag
92
107
  - `commit_and_tag()` - Create release commit and tag
93
108
  - `push_changes()` - Push to remote with tags
@@ -95,6 +110,7 @@ Git commits, tags, and repository operations.
95
110
  - `get_repo_info()` - Extract owner/repo from URL
96
111
 
97
112
  **Usage:**
113
+
98
114
  ```bash
99
115
  source "$(dirname "$0")/lib/git.sh"
100
116
 
@@ -103,9 +119,11 @@ push_changes "origin" "main"
103
119
  ```
104
120
 
105
121
  ### 💎 `gem.sh` - Gem Operations
122
+
106
123
  Build, test, publish, and release gems.
107
124
 
108
125
  **Functions:**
126
+
109
127
  - `build_gem()` - Build the gem package
110
128
  - `publish_gem()` - Publish to RubyGems
111
129
  - `create_github_release()` - Create GitHub release
@@ -113,6 +131,7 @@ Build, test, publish, and release gems.
113
131
  - `gem_version_exists()` - Check if version exists on RubyGems
114
132
 
115
133
  **Usage:**
134
+
116
135
  ```bash
117
136
  source "$(dirname "$0")/lib/gem.sh"
118
137
 
@@ -127,11 +146,13 @@ create_github_release "1.2.0"
127
146
  Each library has comprehensive unit tests in `test/`.
128
147
 
129
148
  ### Run All Tests
149
+
130
150
  ```bash
131
151
  ./scripts/lib/test/run_tests.sh
132
152
  ```
133
153
 
134
154
  ### Run Individual Tests
155
+
135
156
  ```bash
136
157
  ./scripts/lib/test/test_version.sh
137
158
  ./scripts/lib/test/test_changelog.sh
@@ -139,6 +160,7 @@ Each library has comprehensive unit tests in `test/`.
139
160
  ```
140
161
 
141
162
  ### Test Coverage
163
+
142
164
  - ✅ Version calculations and validation
143
165
  - ✅ Changelog generation and categorization
144
166
  - ✅ Git operations and tag management
@@ -178,30 +200,30 @@ source "$LIB_DIR/gem.sh"
178
200
  # Main workflow
179
201
  main() {
180
202
  print_header "Custom Release"
181
-
203
+
182
204
  # Validate
183
205
  validate_environment
184
-
206
+
185
207
  # Version
186
208
  local current=$(get_current_version)
187
209
  local new=$(calculate_new_version "$current" "patch")
188
210
  update_version_files "$new"
189
-
211
+
190
212
  # Changelog
191
213
  generate_changelog "$new"
192
-
214
+
193
215
  # Build & Test
194
216
  build_gem "$new"
195
217
  run_tests
196
-
218
+
197
219
  # Commit & Tag
198
220
  commit_and_tag "$new"
199
-
221
+
200
222
  # Publish
201
223
  publish_gem "$new"
202
224
  create_github_release "$new"
203
225
  push_changes
204
-
226
+
205
227
  success "Release $new complete!"
206
228
  }
207
229
 
@@ -211,18 +233,23 @@ main "$@"
211
233
  ## Architecture Benefits
212
234
 
213
235
  ### ✅ Modularity
236
+
214
237
  Each library has ONE responsibility - easy to understand and modify.
215
238
 
216
239
  ### ✅ Testability
240
+
217
241
  Small, focused functions can be unit tested independently.
218
242
 
219
243
  ### ✅ Reusability
244
+
220
245
  Libraries can be used in different scripts or GitHub Actions.
221
246
 
222
247
  ### ✅ Maintainability
248
+
223
249
  Changes isolated to specific files - less ripple effect.
224
250
 
225
251
  ### ✅ Clarity
252
+
226
253
  Functions have clear names and single purposes.
227
254
 
228
255
  ## Migrating Old Scripts
@@ -234,6 +261,7 @@ Old monolithic scripts (`gem-publish.sh`, `release.sh`, `build.sh`) can now be:
234
261
  3. **Removed** once adoption is confirmed
235
262
 
236
263
  Example deprecation wrapper:
264
+
237
265
  ```bash
238
266
  #!/bin/bash
239
267
  echo "⚠️ WARNING: This script is deprecated"
@@ -234,13 +234,13 @@ class PreviewGenerator:
234
234
  return safe_name[:50] # Limit length
235
235
 
236
236
  def generate_image_openai(self, prompt: str, output_path: Path) -> GenerationResult:
237
- """Generate image using OpenAI DALL-E."""
238
- if not HAS_OPENAI:
237
+ """Generate image using OpenAI DALL-E via HTTP API (no SDK required)."""
238
+ if not HAS_REQUESTS:
239
239
  return GenerationResult(
240
240
  success=False,
241
241
  image_path=None,
242
242
  preview_url=None,
243
- error="openai package not installed. Run: pip install openai",
243
+ error="requests package not installed. Run: pip install requests",
244
244
  prompt_used=prompt,
245
245
  )
246
246
 
@@ -255,8 +255,6 @@ class PreviewGenerator:
255
255
  )
256
256
 
257
257
  try:
258
- client = OpenAI(api_key=api_key)
259
-
260
258
  self.debug(f"Generating with prompt: {prompt[:200]}...")
261
259
 
262
260
  # Parse size
@@ -267,27 +265,29 @@ class PreviewGenerator:
267
265
  }
268
266
  size = size_map.get(self.image_size, "1024x1024")
269
267
 
270
- response = client.images.generate(
271
- model="dall-e-3",
272
- prompt=prompt,
273
- size=size,
274
- quality="standard",
275
- n=1,
268
+ # Use HTTP API directly instead of SDK
269
+ response = requests.post(
270
+ "https://api.openai.com/v1/images/generations",
271
+ headers={
272
+ "Authorization": f"Bearer {api_key}",
273
+ "Content-Type": "application/json",
274
+ },
275
+ json={
276
+ "model": "dall-e-3",
277
+ "prompt": prompt,
278
+ "size": size,
279
+ "quality": "standard",
280
+ "n": 1,
281
+ },
282
+ timeout=120, # 2 minute timeout for image generation
276
283
  )
284
+ response.raise_for_status()
277
285
 
278
- image_url = response.data[0].url
286
+ data = response.json()
287
+ image_url = data['data'][0]['url']
279
288
 
280
289
  # Download image
281
- if not HAS_REQUESTS:
282
- return GenerationResult(
283
- success=False,
284
- image_path=None,
285
- preview_url=image_url,
286
- error="requests package not installed. Run: pip install requests",
287
- prompt_used=prompt,
288
- )
289
-
290
- img_response = requests.get(image_url)
290
+ img_response = requests.get(image_url, timeout=60)
291
291
  img_response.raise_for_status()
292
292
 
293
293
  output_path.write_bytes(img_response.content)
@@ -300,6 +300,21 @@ class PreviewGenerator:
300
300
  prompt_used=prompt,
301
301
  )
302
302
 
303
+ except requests.exceptions.HTTPError as e:
304
+ error_msg = str(e)
305
+ try:
306
+ error_data = e.response.json()
307
+ if 'error' in error_data:
308
+ error_msg = error_data['error'].get('message', str(e))
309
+ except:
310
+ pass
311
+ return GenerationResult(
312
+ success=False,
313
+ image_path=None,
314
+ preview_url=None,
315
+ error=error_msg,
316
+ prompt_used=prompt,
317
+ )
303
318
  except Exception as e:
304
319
  return GenerationResult(
305
320
  success=False,