jekyll-theme-zer0 0.15.0 → 0.16.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.
data/scripts/README.md CHANGED
@@ -91,7 +91,14 @@ Options:
91
91
  --dry-run Preview without changes
92
92
  --collection TYPE Generate for specific collection (posts, docs, etc.)
93
93
  -f, --file PATH Process specific file
94
- --provider PROVIDER Use specific AI provider (openai)
94
+ --provider PROVIDER Use specific AI provider (openai, stability, xai)
95
+ --assets-prefix Custom assets path prefix (default: /assets)
96
+ --no-auto-prefix Disable automatic path prefixing
97
+
98
+ AI Providers:
99
+ openai - OpenAI DALL-E (requires OPENAI_API_KEY)
100
+ stability - Stability AI (requires STABILITY_API_KEY)
101
+ xai - xAI Grok image generation (requires XAI_API_KEY)
95
102
  ```
96
103
 
97
104
  #### `install-preview-generator`
@@ -209,11 +216,14 @@ The project includes GitHub Actions workflows for automation:
209
216
  ## Requirements
210
217
 
211
218
  ### System Dependencies
219
+ - **Bash**: >= 3.2 (macOS default supported - no Homebrew Bash required!)
212
220
  - **Ruby**: >= 2.7.0
213
221
  - **Bundler**: For dependency management
214
222
  - **jq**: For JSON processing
215
223
  - **Git**: For version control
216
224
 
225
+ **Note on Bash Compatibility**: All scripts are compatible with Bash 3.2+ (the default version on macOS). You do NOT need to install Homebrew Bash. The release automation, changelog generation, and all CI/CD workflows work seamlessly with the system-provided Bash on macOS and standard Bash installations on Linux.
226
+
217
227
  ### RubyGems Publishing Setup
218
228
  To publish gems, you need:
219
229
 
@@ -2,14 +2,7 @@
2
2
 
3
3
  # Changelog generation library for zer0-mistakes release scripts
4
4
  # Provides automatic changelog generation from git commit history
5
-
6
- # Check Bash version (need 4+ for associative arrays)
7
- if [[ "${BASH_VERSINFO[0]}" -lt 4 ]]; then
8
- echo "[ERROR] This script requires Bash 4.0 or higher (current: ${BASH_VERSION})" >&2
9
- echo "[INFO] On macOS, install via: brew install bash" >&2
10
- echo "[INFO] Then update scripts to use: #!/usr/local/bin/bash" >&2
11
- exit 1
12
- fi
5
+ # Compatible with Bash 3.2+ (macOS default) and Bash 4+
13
6
 
14
7
  # Source common utilities
15
8
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -119,18 +112,19 @@ generate_changelog() {
119
112
  return 0
120
113
  fi
121
114
 
122
- # Parse and categorize commits
123
- declare -A categories
124
- categories=(
125
- ["breaking"]=""
126
- ["added"]=""
127
- ["changed"]=""
128
- ["deprecated"]=""
129
- ["removed"]=""
130
- ["fixed"]=""
131
- ["security"]=""
132
- ["other"]=""
133
- )
115
+ # Parse and categorize commits using parallel indexed arrays (Bash 3.2 compatible)
116
+ # Category names in order
117
+ local category_names=("breaking" "added" "changed" "deprecated" "removed" "fixed" "security" "other")
118
+
119
+ # Initialize category content arrays
120
+ local cat_breaking=""
121
+ local cat_added=""
122
+ local cat_changed=""
123
+ local cat_deprecated=""
124
+ local cat_removed=""
125
+ local cat_fixed=""
126
+ local cat_security=""
127
+ local cat_other=""
134
128
 
135
129
  local commit_count=0
136
130
  while IFS='|' read -r hash subject author date; do
@@ -176,10 +170,41 @@ generate_changelog() {
176
170
  local clean_msg
177
171
  clean_msg=$(clean_commit_message "$subject")
178
172
 
179
- if [[ -n "${categories[$category]}" ]]; then
180
- categories[$category]+=$'\n'
181
- fi
182
- categories[$category]+="- $clean_msg"
173
+ # Append to appropriate category variable
174
+ case "$category" in
175
+ "breaking")
176
+ [[ -n "$cat_breaking" ]] && cat_breaking+=$'\n'
177
+ cat_breaking+="- $clean_msg"
178
+ ;;
179
+ "added")
180
+ [[ -n "$cat_added" ]] && cat_added+=$'\n'
181
+ cat_added+="- $clean_msg"
182
+ ;;
183
+ "changed")
184
+ [[ -n "$cat_changed" ]] && cat_changed+=$'\n'
185
+ cat_changed+="- $clean_msg"
186
+ ;;
187
+ "deprecated")
188
+ [[ -n "$cat_deprecated" ]] && cat_deprecated+=$'\n'
189
+ cat_deprecated+="- $clean_msg"
190
+ ;;
191
+ "removed")
192
+ [[ -n "$cat_removed" ]] && cat_removed+=$'\n'
193
+ cat_removed+="- $clean_msg"
194
+ ;;
195
+ "fixed")
196
+ [[ -n "$cat_fixed" ]] && cat_fixed+=$'\n'
197
+ cat_fixed+="- $clean_msg"
198
+ ;;
199
+ "security")
200
+ [[ -n "$cat_security" ]] && cat_security+=$'\n'
201
+ cat_security+="- $clean_msg"
202
+ ;;
203
+ "other")
204
+ [[ -n "$cat_other" ]] && cat_other+=$'\n'
205
+ cat_other+="- $clean_msg"
206
+ ;;
207
+ esac
183
208
 
184
209
  done <<< "$commits_raw"
185
210
 
@@ -192,39 +217,46 @@ generate_changelog() {
192
217
 
193
218
  changelog_entry+="## [$new_version] - $date"$'\n\n'
194
219
 
195
- # Add sections in order
196
- for category in "breaking" "added" "changed" "deprecated" "removed" "fixed" "security" "other"; do
197
- if [[ -n "${categories[$category]}" ]]; then
198
- case "$category" in
199
- "breaking")
200
- changelog_entry+="### ⚠️ BREAKING CHANGES"$'\n'
201
- ;;
202
- "added")
203
- changelog_entry+="### Added"$'\n'
204
- ;;
205
- "changed")
206
- changelog_entry+="### Changed"$'\n'
207
- ;;
208
- "deprecated")
209
- changelog_entry+="### Deprecated"$'\n'
210
- ;;
211
- "removed")
212
- changelog_entry+="### Removed"$'\n'
213
- ;;
214
- "fixed")
215
- changelog_entry+="### Fixed"$'\n'
216
- ;;
217
- "security")
218
- changelog_entry+="### Security"$'\n'
219
- ;;
220
- "other")
221
- changelog_entry+="### Other"$'\n'
222
- ;;
223
- esac
224
-
225
- changelog_entry+="${categories[$category]}"$'\n\n'
226
- fi
227
- done
220
+ # Add sections in order (using individual variables instead of associative array)
221
+ if [[ -n "$cat_breaking" ]]; then
222
+ changelog_entry+="### ⚠️ BREAKING CHANGES"$'\n'
223
+ changelog_entry+="$cat_breaking"$'\n\n'
224
+ fi
225
+
226
+ if [[ -n "$cat_added" ]]; then
227
+ changelog_entry+="### Added"$'\n'
228
+ changelog_entry+="$cat_added"$'\n\n'
229
+ fi
230
+
231
+ if [[ -n "$cat_changed" ]]; then
232
+ changelog_entry+="### Changed"$'\n'
233
+ changelog_entry+="$cat_changed"$'\n\n'
234
+ fi
235
+
236
+ if [[ -n "$cat_deprecated" ]]; then
237
+ changelog_entry+="### Deprecated"$'\n'
238
+ changelog_entry+="$cat_deprecated"$'\n\n'
239
+ fi
240
+
241
+ if [[ -n "$cat_removed" ]]; then
242
+ changelog_entry+="### Removed"$'\n'
243
+ changelog_entry+="$cat_removed"$'\n\n'
244
+ fi
245
+
246
+ if [[ -n "$cat_fixed" ]]; then
247
+ changelog_entry+="### Fixed"$'\n'
248
+ changelog_entry+="$cat_fixed"$'\n\n'
249
+ fi
250
+
251
+ if [[ -n "$cat_security" ]]; then
252
+ changelog_entry+="### Security"$'\n'
253
+ changelog_entry+="$cat_security"$'\n\n'
254
+ fi
255
+
256
+ if [[ -n "$cat_other" ]]; then
257
+ changelog_entry+="### Other"$'\n'
258
+ changelog_entry+="$cat_other"$'\n\n'
259
+ fi
228
260
 
229
261
  # Preview changelog
230
262
  info "Changelog preview:"
@@ -101,6 +101,8 @@ class PreviewGenerator:
101
101
  output_dir: str = "assets/images/previews",
102
102
  image_style: str = "digital art, professional blog illustration",
103
103
  image_size: str = "1024x1024",
104
+ assets_prefix: str = "/assets",
105
+ auto_prefix: bool = True,
104
106
  dry_run: bool = False,
105
107
  verbose: bool = False,
106
108
  force: bool = False,
@@ -110,6 +112,8 @@ class PreviewGenerator:
110
112
  self.output_dir = project_root / output_dir
111
113
  self.image_style = image_style
112
114
  self.image_size = image_size
115
+ self.assets_prefix = assets_prefix
116
+ self.auto_prefix = auto_prefix
113
117
  self.dry_run = dry_run
114
118
  self.verbose = verbose
115
119
  self.force = force
@@ -129,6 +133,27 @@ class PreviewGenerator:
129
133
  if self.verbose:
130
134
  log(msg, "debug")
131
135
 
136
+ def normalize_preview_path(self, preview_path: Optional[str]) -> Optional[str]:
137
+ """Normalize a preview path by adding assets_prefix if needed.
138
+
139
+ This allows users to omit the /assets/ prefix in frontmatter:
140
+ - /images/previews/my-image.png -> /assets/images/previews/my-image.png
141
+ - /assets/images/previews/my-image.png -> unchanged
142
+ - https://example.com/image.png -> unchanged (external URL)
143
+ """
144
+ if not preview_path:
145
+ return preview_path
146
+
147
+ # External URLs pass through unchanged
148
+ if preview_path.startswith('http://') or preview_path.startswith('https://'):
149
+ return preview_path
150
+
151
+ # If auto_prefix is enabled and path doesn't contain assets_prefix
152
+ if self.auto_prefix and self.assets_prefix not in preview_path:
153
+ return f"{self.assets_prefix}{preview_path}"
154
+
155
+ return preview_path
156
+
132
157
  def parse_front_matter(self, file_path: Path) -> Optional[ContentFile]:
133
158
  """Parse front matter and content from a markdown file."""
134
159
  try:
@@ -178,19 +203,19 @@ class PreviewGenerator:
178
203
  if not preview_path:
179
204
  return False
180
205
 
206
+ # Normalize the path first (adds assets_prefix if needed)
207
+ normalized_path = self.normalize_preview_path(preview_path)
208
+ if not normalized_path:
209
+ return False
210
+
181
211
  # Handle absolute and relative paths
182
- clean_path = preview_path.lstrip('/')
212
+ clean_path = normalized_path.lstrip('/')
183
213
 
184
214
  # Check direct path
185
215
  full_path = self.project_root / clean_path
186
216
  if full_path.exists():
187
217
  return True
188
218
 
189
- # Check in assets directory
190
- assets_path = self.project_root / 'assets' / clean_path
191
- if assets_path.exists():
192
- return True
193
-
194
219
  return False
195
220
 
196
221
  def generate_prompt(self, content: ContentFile) -> str:
@@ -395,12 +420,131 @@ class PreviewGenerator:
395
420
  prompt_used=prompt,
396
421
  )
397
422
 
423
+ def generate_image_xai(self, prompt: str, output_path: Path) -> GenerationResult:
424
+ """Generate image using xAI Grok API."""
425
+ if not HAS_REQUESTS:
426
+ return GenerationResult(
427
+ success=False,
428
+ image_path=None,
429
+ preview_url=None,
430
+ error="requests package not installed. Run: pip install requests",
431
+ prompt_used=prompt,
432
+ )
433
+
434
+ api_key = os.environ.get('XAI_API_KEY')
435
+ if not api_key:
436
+ return GenerationResult(
437
+ success=False,
438
+ image_path=None,
439
+ preview_url=None,
440
+ error="XAI_API_KEY environment variable not set",
441
+ prompt_used=prompt,
442
+ )
443
+
444
+ try:
445
+ # xAI has a max prompt length of 1024 characters
446
+ truncated_prompt = prompt[:1000] if len(prompt) > 1000 else prompt
447
+ self.debug(f"Generating with xAI Grok, prompt: {truncated_prompt[:200]}...")
448
+
449
+ # xAI uses OpenAI-compatible API format
450
+ response = requests.post(
451
+ "https://api.x.ai/v1/images/generations",
452
+ headers={
453
+ "Authorization": f"Bearer {api_key}",
454
+ "Content-Type": "application/json",
455
+ },
456
+ json={
457
+ "model": "grok-2-image",
458
+ "prompt": truncated_prompt,
459
+ "n": 1,
460
+ },
461
+ timeout=120, # 2 minute timeout for image generation
462
+ )
463
+ response.raise_for_status()
464
+
465
+ data = response.json()
466
+
467
+ # xAI returns base64-encoded images
468
+ if 'data' not in data or not data['data']:
469
+ return GenerationResult(
470
+ success=False,
471
+ image_path=None,
472
+ preview_url=None,
473
+ error="No image data in response",
474
+ prompt_used=prompt,
475
+ )
476
+
477
+ image_data = data['data'][0]
478
+
479
+ # Check if it's a URL or base64
480
+ if 'url' in image_data:
481
+ # Download from URL
482
+ img_response = requests.get(image_data['url'], timeout=60)
483
+ img_response.raise_for_status()
484
+ output_path.write_bytes(img_response.content)
485
+ elif 'b64_json' in image_data:
486
+ # Decode base64
487
+ import base64
488
+ image_bytes = base64.b64decode(image_data['b64_json'])
489
+ output_path.write_bytes(image_bytes)
490
+ else:
491
+ return GenerationResult(
492
+ success=False,
493
+ image_path=None,
494
+ preview_url=None,
495
+ error="Unexpected response format from xAI",
496
+ prompt_used=prompt,
497
+ )
498
+
499
+ return GenerationResult(
500
+ success=True,
501
+ image_path=str(output_path),
502
+ preview_url=str(output_path.relative_to(self.project_root)),
503
+ error=None,
504
+ prompt_used=prompt,
505
+ )
506
+
507
+ except requests.exceptions.HTTPError as e:
508
+ error_msg = str(e)
509
+ try:
510
+ error_data = e.response.json()
511
+ self.debug(f"xAI error response: {error_data}")
512
+ if 'error' in error_data:
513
+ error_msg = error_data['error'].get('message', str(error_data['error']))
514
+ elif 'detail' in error_data:
515
+ error_msg = str(error_data['detail'])
516
+ else:
517
+ error_msg = str(error_data)
518
+ except:
519
+ # Try to get raw text
520
+ try:
521
+ error_msg = e.response.text[:500]
522
+ except:
523
+ pass
524
+ return GenerationResult(
525
+ success=False,
526
+ image_path=None,
527
+ preview_url=None,
528
+ error=f"xAI API error: {error_msg}",
529
+ prompt_used=prompt,
530
+ )
531
+ except Exception as e:
532
+ return GenerationResult(
533
+ success=False,
534
+ image_path=None,
535
+ preview_url=None,
536
+ error=str(e),
537
+ prompt_used=prompt,
538
+ )
539
+
398
540
  def generate_image(self, prompt: str, output_path: Path) -> GenerationResult:
399
541
  """Generate image using configured provider."""
400
542
  if self.provider == "openai":
401
543
  return self.generate_image_openai(prompt, output_path)
402
544
  elif self.provider == "stability":
403
545
  return self.generate_image_stability(prompt, output_path)
546
+ elif self.provider == "xai":
547
+ return self.generate_image_xai(prompt, output_path)
404
548
  else:
405
549
  return GenerationResult(
406
550
  success=False,
@@ -559,9 +703,9 @@ def main():
559
703
  )
560
704
  parser.add_argument(
561
705
  '-p', '--provider',
562
- choices=['openai', 'stability'],
706
+ choices=['openai', 'stability', 'xai'],
563
707
  default='openai',
564
- help="AI provider for image generation"
708
+ help="AI provider for image generation (openai, stability, xai)"
565
709
  )
566
710
  parser.add_argument(
567
711
  '-d', '--dry-run',
@@ -593,6 +737,16 @@ def main():
593
737
  default='digital art, professional blog illustration, clean design',
594
738
  help="Image style prompt"
595
739
  )
740
+ parser.add_argument(
741
+ '--assets-prefix',
742
+ default='/assets',
743
+ help="Prefix to prepend to relative preview paths (default: /assets)"
744
+ )
745
+ parser.add_argument(
746
+ '--no-auto-prefix',
747
+ action='store_true',
748
+ help="Disable automatic assets prefix prepending"
749
+ )
596
750
 
597
751
  args = parser.parse_args()
598
752
 
@@ -606,6 +760,8 @@ def main():
606
760
  provider=args.provider,
607
761
  output_dir=args.output_dir,
608
762
  image_style=args.style,
763
+ assets_prefix=args.assets_prefix,
764
+ auto_prefix=not args.no_auto_prefix,
609
765
  dry_run=args.dry_run,
610
766
  verbose=args.verbose,
611
767
  force=args.force,
@@ -3,8 +3,7 @@
3
3
  # Test runner for library unit tests
4
4
  # Usage: ./scripts/test/lib/run_tests.sh
5
5
  #
6
- # Note: Requires Bash 4.0+ (for associative arrays in changelog.sh)
7
- # On macOS: brew install bash
6
+ # Compatible with Bash 3.2+ (macOS default) and Bash 4+
8
7
 
9
8
  # Note: We intentionally don't use set -e here because test assertions
10
9
  # may return non-zero and we want to continue running tests
@@ -0,0 +1,145 @@
1
+ #!/bin/bash
2
+ #
3
+ # update-preview-paths.sh
4
+ #
5
+ # Updates all preview paths in markdown frontmatter to remove the /assets/ prefix.
6
+ # This aligns with the new auto_prefix feature that automatically prepends /assets/.
7
+ #
8
+ # Usage:
9
+ # ./scripts/update-preview-paths.sh # Dry run (preview changes)
10
+ # ./scripts/update-preview-paths.sh --apply # Apply changes
11
+ #
12
+ # Example transformation:
13
+ # preview: /assets/images/previews/my-image.png
14
+ # becomes:
15
+ # preview: /images/previews/my-image.png
16
+ #
17
+
18
+ set -euo pipefail
19
+
20
+ # Colors for output
21
+ RED='\033[0;31m'
22
+ GREEN='\033[0;32m'
23
+ YELLOW='\033[1;33m'
24
+ BLUE='\033[0;34m'
25
+ CYAN='\033[0;36m'
26
+ NC='\033[0m' # No Color
27
+
28
+ # Script directory
29
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
30
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
31
+
32
+ # Counters
33
+ TOTAL_FILES=0
34
+ MODIFIED_FILES=0
35
+ SKIPPED_FILES=0
36
+
37
+ # Mode
38
+ DRY_RUN=true
39
+
40
+ # Parse arguments
41
+ for arg in "$@"; do
42
+ case $arg in
43
+ --apply)
44
+ DRY_RUN=false
45
+ shift
46
+ ;;
47
+ --help|-h)
48
+ echo "Usage: $0 [--apply]"
49
+ echo ""
50
+ echo "Updates preview paths in markdown frontmatter to remove /assets/ prefix."
51
+ echo ""
52
+ echo "Options:"
53
+ echo " --apply Apply changes (default is dry run)"
54
+ echo " --help Show this help message"
55
+ exit 0
56
+ ;;
57
+ esac
58
+ done
59
+
60
+ log_info() {
61
+ echo -e "${BLUE}[INFO]${NC} $1"
62
+ }
63
+
64
+ log_success() {
65
+ echo -e "${GREEN}[SUCCESS]${NC} $1"
66
+ }
67
+
68
+ log_warning() {
69
+ echo -e "${YELLOW}[WARNING]${NC} $1"
70
+ }
71
+
72
+ log_change() {
73
+ echo -e "${CYAN}[CHANGE]${NC} $1"
74
+ }
75
+
76
+ echo ""
77
+ echo -e "${BLUE}========================================${NC}"
78
+ echo -e "${BLUE}🔄 Preview Path Updater${NC}"
79
+ echo -e "${BLUE}========================================${NC}"
80
+ echo ""
81
+
82
+ if [ "$DRY_RUN" = true ]; then
83
+ log_warning "DRY RUN MODE - No files will be modified"
84
+ log_info "Run with --apply to make changes"
85
+ else
86
+ log_warning "APPLY MODE - Files will be modified"
87
+ fi
88
+ echo ""
89
+
90
+ # Find all markdown files in pages directory
91
+ find "$PROJECT_ROOT/pages" -name "*.md" -type f | while read -r file; do
92
+ TOTAL_FILES=$((TOTAL_FILES + 1))
93
+
94
+ # Check if file has a preview field with /assets/ prefix
95
+ if grep -q "^preview: /assets/" "$file" 2>/dev/null; then
96
+ # Extract current preview path
97
+ current_path=$(grep "^preview:" "$file" | head -1 | sed 's/^preview: //')
98
+
99
+ # Remove /assets/ prefix
100
+ new_path=$(echo "$current_path" | sed 's|^/assets/|/|')
101
+
102
+ log_change "$file"
103
+ echo " Old: $current_path"
104
+ echo " New: $new_path"
105
+
106
+ if [ "$DRY_RUN" = false ]; then
107
+ # Use sed to replace the preview line
108
+ if [[ "$OSTYPE" == "darwin"* ]]; then
109
+ # macOS sed requires empty string for -i
110
+ sed -i '' "s|^preview: /assets/|preview: /|" "$file"
111
+ else
112
+ # Linux sed
113
+ sed -i "s|^preview: /assets/|preview: /|" "$file"
114
+ fi
115
+ log_success "Updated: $file"
116
+ fi
117
+
118
+ MODIFIED_FILES=$((MODIFIED_FILES + 1))
119
+ else
120
+ SKIPPED_FILES=$((SKIPPED_FILES + 1))
121
+ fi
122
+ done
123
+
124
+ echo ""
125
+ echo -e "${CYAN}========================================${NC}"
126
+ echo -e "${CYAN}📊 Summary${NC}"
127
+ echo -e "${CYAN}========================================${NC}"
128
+
129
+ # Re-count since subshell doesn't persist variables
130
+ MODIFIED_COUNT=$(find "$PROJECT_ROOT/pages" -name "*.md" -type f -exec grep -l "^preview: /assets/" {} \; 2>/dev/null | wc -l | tr -d ' ')
131
+ TOTAL_COUNT=$(find "$PROJECT_ROOT/pages" -name "*.md" -type f | wc -l | tr -d ' ')
132
+
133
+ if [ "$DRY_RUN" = true ]; then
134
+ echo " Files to update: $MODIFIED_COUNT"
135
+ echo " Total markdown files: $TOTAL_COUNT"
136
+ echo ""
137
+ log_info "Run with --apply to make these changes"
138
+ else
139
+ # After applying, count should be 0
140
+ REMAINING=$(find "$PROJECT_ROOT/pages" -name "*.md" -type f -exec grep -l "^preview: /assets/" {} \; 2>/dev/null | wc -l | tr -d ' ')
141
+ echo " Files updated: $MODIFIED_COUNT"
142
+ echo " Files remaining: $REMAINING"
143
+ echo " Total markdown files: $TOTAL_COUNT"
144
+ fi
145
+ echo ""
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: 0.15.0
4
+ version: 0.16.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: 2025-12-12 00:00:00.000000000 Z
11
+ date: 2025-12-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -81,12 +81,19 @@ files:
81
81
  - _data/authors.yml
82
82
  - _data/content_statistics.yml
83
83
  - _data/generate_statistics.rb
84
+ - _data/generate_statistics.sh
85
+ - _data/github-actions-example.yml
84
86
  - _data/navigation/about.yml
85
87
  - _data/navigation/docs.yml
86
88
  - _data/navigation/home.yml
87
89
  - _data/navigation/main.yml
88
90
  - _data/navigation/posts.yml
89
91
  - _data/navigation/quickstart.yml
92
+ - _data/posts_organization.yml
93
+ - _data/prerequisites.yml
94
+ - _data/statistics_config.yml
95
+ - _data/ui-text.yml
96
+ - _data/update_statistics.sh
90
97
  - _includes/README.md
91
98
  - _includes/analytics/google-analytics.html
92
99
  - _includes/analytics/google-tag-manager-body.html
@@ -176,6 +183,7 @@ files:
176
183
  - assets/images/previews/published-documentation-library.png
177
184
  - assets/images/previews/quantum-computing-explained-from-qubits-to-quantum.png
178
185
  - assets/images/previews/science.png
186
+ - assets/images/previews/site-personalization-configuration.png
179
187
  - assets/images/previews/technology.png
180
188
  - assets/images/previews/the-complete-guide-to-startup-funding-in-2025.png
181
189
  - assets/images/previews/the-remote-work-revolution-how-global-teams-are-re.png
@@ -237,6 +245,7 @@ files:
237
245
  - scripts/test/lib/test_validation.sh
238
246
  - scripts/test/lib/test_version.sh
239
247
  - scripts/test/theme/validate
248
+ - scripts/update-preview-paths.sh
240
249
  - scripts/utils/analyze-commits
241
250
  - scripts/utils/fix-markdown
242
251
  - scripts/utils/setup