jekyll-theme-zer0 0.10.4 → 0.15.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 +459 -0
- data/README.md +24 -8
- data/_data/navigation/about.yml +39 -11
- data/_data/navigation/docs.yml +53 -23
- data/_data/navigation/home.yml +27 -9
- data/_data/navigation/main.yml +27 -8
- data/_data/navigation/posts.yml +22 -6
- data/_data/navigation/quickstart.yml +8 -3
- data/_includes/README.md +2 -0
- data/_includes/components/js-cdn.html +4 -1
- data/_includes/components/post-card.html +2 -11
- data/_includes/components/preview-image.html +32 -0
- data/_includes/content/intro.html +5 -6
- data/_includes/core/footer.html +5 -3
- data/_includes/core/header.html +14 -0
- data/_includes/navigation/sidebar-categories.html +20 -9
- data/_includes/navigation/sidebar-folders.html +8 -7
- data/_includes/navigation/sidebar-right.html +16 -10
- data/_layouts/blog.html +15 -45
- data/_layouts/category.html +4 -24
- data/_layouts/collection.html +2 -12
- data/_layouts/default.html +1 -1
- data/_layouts/journals.html +2 -12
- data/_layouts/notebook.html +296 -0
- data/_sass/core/_docs.scss +1 -1
- data/_sass/custom.scss +55 -18
- data/_sass/notebooks.scss +458 -0
- data/assets/images/notebooks/test-notebook_files/test-notebook_4_0.png +0 -0
- data/assets/js/sidebar.js +511 -0
- data/scripts/README.md +128 -105
- data/scripts/analyze-commits.sh +9 -311
- data/scripts/bin/build +22 -22
- data/scripts/build +7 -111
- data/scripts/convert-notebooks.sh +415 -0
- data/scripts/features/validate_preview_urls.py +500 -0
- data/scripts/fix-markdown-format.sh +8 -262
- data/scripts/generate-preview-images.sh +7 -787
- data/scripts/install-preview-generator.sh +8 -528
- data/scripts/lib/README.md +5 -5
- data/scripts/lib/gem.sh +19 -7
- data/scripts/release +7 -236
- data/scripts/setup.sh +9 -153
- data/scripts/test-auto-version.sh +7 -256
- data/scripts/test-mermaid.sh +7 -287
- data/scripts/test.sh +9 -154
- metadata +9 -10
- data/scripts/features/preview_generator.py +0 -646
- data/scripts/lib/test/run_tests.sh +0 -140
- data/scripts/lib/test/test_changelog.sh +0 -87
- data/scripts/lib/test/test_gem.sh +0 -68
- data/scripts/lib/test/test_git.sh +0 -82
- data/scripts/lib/test/test_validation.sh +0 -72
- data/scripts/lib/test/test_version.sh +0 -96
- data/scripts/version.sh +0 -178
data/scripts/build
CHANGED
|
@@ -1,115 +1,11 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
3
|
+
# ============================================================================
|
|
4
|
+
# WRAPPER: This script forwards to scripts/bin/build
|
|
5
|
+
#
|
|
6
|
+
# The canonical location is scripts/bin/build. This wrapper exists for
|
|
7
|
+
# backward compatibility with existing workflows and VS Code tasks.
|
|
8
|
+
# ============================================================================
|
|
7
9
|
|
|
8
|
-
set -euo pipefail
|
|
9
|
-
|
|
10
|
-
# Get script and library directories
|
|
11
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# Source required libraries
|
|
15
|
-
source "$LIB_DIR/common.sh"
|
|
16
|
-
source "$LIB_DIR/validation.sh"
|
|
17
|
-
source "$LIB_DIR/version.sh"
|
|
18
|
-
source "$LIB_DIR/gem.sh"
|
|
19
|
-
|
|
20
|
-
# Parse arguments
|
|
21
|
-
while [[ $# -gt 0 ]]; do
|
|
22
|
-
case $1 in
|
|
23
|
-
--dry-run)
|
|
24
|
-
export DRY_RUN=true
|
|
25
|
-
shift
|
|
26
|
-
;;
|
|
27
|
-
--verbose)
|
|
28
|
-
export VERBOSE=true
|
|
29
|
-
shift
|
|
30
|
-
;;
|
|
31
|
-
--help|-h)
|
|
32
|
-
show_usage
|
|
33
|
-
exit 0
|
|
34
|
-
;;
|
|
35
|
-
*)
|
|
36
|
-
error "Unknown option: $1 (use --help for usage)"
|
|
37
|
-
;;
|
|
38
|
-
esac
|
|
39
|
-
done
|
|
40
|
-
|
|
41
|
-
# Show usage
|
|
42
|
-
show_usage() {
|
|
43
|
-
cat << EOF
|
|
44
|
-
🔨 Simplified Build Command for zer0-mistakes
|
|
45
|
-
|
|
46
|
-
USAGE:
|
|
47
|
-
./scripts/build [OPTIONS]
|
|
48
|
-
|
|
49
|
-
DESCRIPTION:
|
|
50
|
-
Builds the Jekyll theme gem without running the full release workflow.
|
|
51
|
-
Useful for testing gem packaging or preparing for manual publication.
|
|
52
|
-
|
|
53
|
-
OPTIONS:
|
|
54
|
-
--dry-run Preview build without creating files
|
|
55
|
-
--verbose Show detailed debug output
|
|
56
|
-
--help, -h Show this help message
|
|
57
|
-
|
|
58
|
-
EXAMPLES:
|
|
59
|
-
./scripts/build # Build gem with current version
|
|
60
|
-
./scripts/build --dry-run # Preview what would be built
|
|
61
|
-
./scripts/build --verbose # Build with debug output
|
|
62
|
-
|
|
63
|
-
ENVIRONMENT VARIABLES:
|
|
64
|
-
DRY_RUN=true Enable dry run mode
|
|
65
|
-
VERBOSE=true Enable debug output
|
|
66
|
-
|
|
67
|
-
OUTPUT:
|
|
68
|
-
Creates: jekyll-theme-zer0-VERSION.gem in current directory
|
|
69
|
-
|
|
70
|
-
For full release workflow, use: ./scripts/release
|
|
71
|
-
EOF
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
# Main build workflow
|
|
75
|
-
main() {
|
|
76
|
-
print_header "🔨 Gem Build"
|
|
77
|
-
|
|
78
|
-
# Step 1: Basic validation (no RubyGems auth needed for build)
|
|
79
|
-
step "Validating environment..."
|
|
80
|
-
validate_git_repo
|
|
81
|
-
validate_required_files
|
|
82
|
-
validate_gemspec
|
|
83
|
-
success "Environment validated"
|
|
84
|
-
echo ""
|
|
85
|
-
|
|
86
|
-
# Step 2: Get current version
|
|
87
|
-
step "Reading version..."
|
|
88
|
-
local version
|
|
89
|
-
version=$(get_current_version)
|
|
90
|
-
info "Current version: $version"
|
|
91
|
-
info "Gem file: jekyll-theme-zer0-${version}.gem"
|
|
92
|
-
echo ""
|
|
93
|
-
|
|
94
|
-
# Step 3: Build gem
|
|
95
|
-
build_gem "$version"
|
|
96
|
-
echo ""
|
|
97
|
-
|
|
98
|
-
# Success summary
|
|
99
|
-
if [[ "$DRY_RUN" != "true" ]]; then
|
|
100
|
-
print_summary "Build Complete" \
|
|
101
|
-
"✅ Built: jekyll-theme-zer0-${version}.gem" \
|
|
102
|
-
"" \
|
|
103
|
-
"Next steps:" \
|
|
104
|
-
" • Test: gem install jekyll-theme-zer0-${version}.gem" \
|
|
105
|
-
" • Publish: gem push jekyll-theme-zer0-${version}.gem" \
|
|
106
|
-
" • Or use: ./scripts/release for full workflow"
|
|
107
|
-
else
|
|
108
|
-
info "Dry run complete - no files created"
|
|
109
|
-
fi
|
|
110
|
-
|
|
111
|
-
success "Build completed successfully!"
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
# Run main function
|
|
115
|
-
main "$@"
|
|
11
|
+
exec "$SCRIPT_DIR/bin/build" "$@"
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Script Name: convert-notebooks.sh
|
|
4
|
+
# Description: Converts Jupyter notebooks (.ipynb) to Jekyll-compatible Markdown
|
|
5
|
+
# Adds proper front matter, extracts images, and prepares for Jekyll build
|
|
6
|
+
#
|
|
7
|
+
# Usage: ./scripts/convert-notebooks.sh [options]
|
|
8
|
+
#
|
|
9
|
+
# Options:
|
|
10
|
+
# -h, --help Show this help message
|
|
11
|
+
# -d, --dry-run Preview what would be converted (no actual changes)
|
|
12
|
+
# -v, --verbose Enable verbose output
|
|
13
|
+
# -f, --file FILE Convert a specific notebook file only
|
|
14
|
+
# --output-dir DIR Output directory for converted files (default: pages/_notebooks)
|
|
15
|
+
# --image-dir DIR Output directory for extracted images (default: assets/images/notebooks)
|
|
16
|
+
# --force Reconvert notebooks even if .md exists
|
|
17
|
+
# --clean Remove converted .md files before converting
|
|
18
|
+
# --list Only list notebooks to be converted
|
|
19
|
+
#
|
|
20
|
+
# Dependencies:
|
|
21
|
+
# - bash 4.0+
|
|
22
|
+
# - python3 with jupyter nbconvert installed
|
|
23
|
+
# - yq or python (for YAML parsing)
|
|
24
|
+
#
|
|
25
|
+
# Environment Variables:
|
|
26
|
+
# NOTEBOOKS_DIR Source directory for .ipynb files (default: pages/_notebooks)
|
|
27
|
+
# OUTPUT_DIR Output directory for .md files (default: pages/_notebooks)
|
|
28
|
+
# IMAGE_DIR Output directory for images (default: assets/images/notebooks)
|
|
29
|
+
#
|
|
30
|
+
# Examples:
|
|
31
|
+
# ./scripts/convert-notebooks.sh --dry-run
|
|
32
|
+
# ./scripts/convert-notebooks.sh --file pages/_notebooks/my-notebook.ipynb
|
|
33
|
+
# ./scripts/convert-notebooks.sh --verbose --force
|
|
34
|
+
# ./scripts/convert-notebooks.sh --clean
|
|
35
|
+
#
|
|
36
|
+
|
|
37
|
+
set -euo pipefail
|
|
38
|
+
|
|
39
|
+
# Get script directory and source common utilities
|
|
40
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
41
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
42
|
+
|
|
43
|
+
# Load environment variables from .env file if it exists
|
|
44
|
+
if [[ -f "$PROJECT_ROOT/.env" ]]; then
|
|
45
|
+
while IFS='=' read -r key value; do
|
|
46
|
+
[[ -z "$key" || "$key" =~ ^# ]] && continue
|
|
47
|
+
value="${value%\"}"
|
|
48
|
+
value="${value#\"}"
|
|
49
|
+
value="${value%\'}"
|
|
50
|
+
value="${value#\'}"
|
|
51
|
+
export "$key=$value"
|
|
52
|
+
done < "$PROJECT_ROOT/.env"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Source common library if available
|
|
56
|
+
if [[ -f "$SCRIPT_DIR/lib/common.sh" ]]; then
|
|
57
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
58
|
+
else
|
|
59
|
+
# Fallback logging functions
|
|
60
|
+
RED='\033[0;31m'
|
|
61
|
+
GREEN='\033[0;32m'
|
|
62
|
+
YELLOW='\033[1;33m'
|
|
63
|
+
BLUE='\033[0;34m'
|
|
64
|
+
CYAN='\033[0;36m'
|
|
65
|
+
NC='\033[0m'
|
|
66
|
+
|
|
67
|
+
log() { echo -e "${GREEN}[LOG]${NC} $1"; }
|
|
68
|
+
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
|
69
|
+
step() { echo -e "${CYAN}[STEP]${NC} $1"; }
|
|
70
|
+
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
|
71
|
+
warn() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
|
72
|
+
error() { echo -e "${RED}[ERROR]${NC} $1" >&2; exit 1; }
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# Default configuration
|
|
76
|
+
NOTEBOOKS_DIR="${NOTEBOOKS_DIR:-pages/_notebooks}"
|
|
77
|
+
OUTPUT_DIR="${OUTPUT_DIR:-pages/_notebooks}"
|
|
78
|
+
IMAGE_DIR="${IMAGE_DIR:-assets/images/notebooks}"
|
|
79
|
+
DRY_RUN=false
|
|
80
|
+
VERBOSE=false
|
|
81
|
+
FORCE=false
|
|
82
|
+
CLEAN=false
|
|
83
|
+
LIST_ONLY=false
|
|
84
|
+
SPECIFIC_FILE=""
|
|
85
|
+
|
|
86
|
+
# Parse command line arguments
|
|
87
|
+
show_help() {
|
|
88
|
+
head -n 40 "$0" | grep "^#" | sed 's/^# //; s/^#//'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
while [[ $# -gt 0 ]]; do
|
|
92
|
+
case $1 in
|
|
93
|
+
-h|--help)
|
|
94
|
+
show_help
|
|
95
|
+
exit 0
|
|
96
|
+
;;
|
|
97
|
+
-d|--dry-run)
|
|
98
|
+
DRY_RUN=true
|
|
99
|
+
shift
|
|
100
|
+
;;
|
|
101
|
+
-v|--verbose)
|
|
102
|
+
VERBOSE=true
|
|
103
|
+
shift
|
|
104
|
+
;;
|
|
105
|
+
-f|--file)
|
|
106
|
+
SPECIFIC_FILE="$2"
|
|
107
|
+
shift 2
|
|
108
|
+
;;
|
|
109
|
+
--output-dir)
|
|
110
|
+
OUTPUT_DIR="$2"
|
|
111
|
+
shift 2
|
|
112
|
+
;;
|
|
113
|
+
--image-dir)
|
|
114
|
+
IMAGE_DIR="$2"
|
|
115
|
+
shift 2
|
|
116
|
+
;;
|
|
117
|
+
--force)
|
|
118
|
+
FORCE=true
|
|
119
|
+
shift
|
|
120
|
+
;;
|
|
121
|
+
--clean)
|
|
122
|
+
CLEAN=true
|
|
123
|
+
shift
|
|
124
|
+
;;
|
|
125
|
+
--list)
|
|
126
|
+
LIST_ONLY=true
|
|
127
|
+
shift
|
|
128
|
+
;;
|
|
129
|
+
*)
|
|
130
|
+
error "Unknown option: $1"
|
|
131
|
+
;;
|
|
132
|
+
esac
|
|
133
|
+
done
|
|
134
|
+
|
|
135
|
+
# Validate dependencies
|
|
136
|
+
check_dependencies() {
|
|
137
|
+
step "Checking dependencies..."
|
|
138
|
+
|
|
139
|
+
if ! command -v python3 &> /dev/null; then
|
|
140
|
+
error "python3 is not installed"
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
if ! python3 -c "import nbconvert" 2>/dev/null; then
|
|
144
|
+
error "jupyter nbconvert is not installed. Install with: pip3 install jupyter nbconvert"
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
success "All dependencies satisfied"
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# Extract front matter from notebook metadata or filename
|
|
151
|
+
extract_front_matter() {
|
|
152
|
+
local notebook_file="$1"
|
|
153
|
+
local basename
|
|
154
|
+
basename=$(basename "$notebook_file" .ipynb)
|
|
155
|
+
|
|
156
|
+
# Try to extract metadata from notebook JSON
|
|
157
|
+
local title description date
|
|
158
|
+
|
|
159
|
+
# Use Python to parse notebook and extract metadata
|
|
160
|
+
# Output as JSON to avoid delimiter issues with multiline content
|
|
161
|
+
local metadata_json
|
|
162
|
+
metadata_json=$(python3 -c "
|
|
163
|
+
import json
|
|
164
|
+
from datetime import datetime
|
|
165
|
+
|
|
166
|
+
with open('${notebook_file}', 'r') as f:
|
|
167
|
+
nb = json.load(f)
|
|
168
|
+
|
|
169
|
+
metadata = nb.get('metadata', {})
|
|
170
|
+
|
|
171
|
+
# Try to get title from notebook metadata
|
|
172
|
+
title = metadata.get('title', '')
|
|
173
|
+
if not title:
|
|
174
|
+
# Try to extract from first markdown cell
|
|
175
|
+
for cell in nb.get('cells', []):
|
|
176
|
+
if cell.get('cell_type') == 'markdown':
|
|
177
|
+
source = ''.join(cell.get('source', []))
|
|
178
|
+
if source.startswith('# '):
|
|
179
|
+
title = source.split('\n')[0].replace('# ', '').strip()
|
|
180
|
+
break
|
|
181
|
+
|
|
182
|
+
if not title:
|
|
183
|
+
title = '${basename}'.replace('-', ' ').replace('_', ' ').title()
|
|
184
|
+
|
|
185
|
+
# Extract description (first non-heading markdown content)
|
|
186
|
+
description = metadata.get('description', '')
|
|
187
|
+
if not description:
|
|
188
|
+
for cell in nb.get('cells', []):
|
|
189
|
+
if cell.get('cell_type') == 'markdown':
|
|
190
|
+
source = ''.join(cell.get('source', []))
|
|
191
|
+
# Skip heading lines and empty lines
|
|
192
|
+
for line in source.split('\n'):
|
|
193
|
+
if line.strip() and not line.startswith('#'):
|
|
194
|
+
description = line.strip()[:200]
|
|
195
|
+
break
|
|
196
|
+
if description:
|
|
197
|
+
break
|
|
198
|
+
|
|
199
|
+
if not description:
|
|
200
|
+
description = 'Jupyter notebook demonstration'
|
|
201
|
+
|
|
202
|
+
# Format date in ISO 8601 format
|
|
203
|
+
date_str = metadata.get('date', datetime.now().strftime('%Y-%m-%dT%H:%M:%S.000Z'))
|
|
204
|
+
|
|
205
|
+
# Generate permalink from basename
|
|
206
|
+
permalink = '/notebooks/${basename}/'
|
|
207
|
+
|
|
208
|
+
# Output as JSON to avoid parsing issues
|
|
209
|
+
result = {
|
|
210
|
+
'title': title,
|
|
211
|
+
'description': description,
|
|
212
|
+
'date': date_str,
|
|
213
|
+
'permalink': permalink
|
|
214
|
+
}
|
|
215
|
+
print(json.dumps(result))
|
|
216
|
+
" 2>/dev/null)
|
|
217
|
+
|
|
218
|
+
# Parse JSON output
|
|
219
|
+
local title description date permalink
|
|
220
|
+
if [[ -n "$metadata_json" ]]; then
|
|
221
|
+
title=$(echo "$metadata_json" | python3 -c "import sys, json; print(json.load(sys.stdin)['title'])")
|
|
222
|
+
description=$(echo "$metadata_json" | python3 -c "import sys, json; print(json.load(sys.stdin)['description'])")
|
|
223
|
+
date=$(echo "$metadata_json" | python3 -c "import sys, json; print(json.load(sys.stdin)['date'])")
|
|
224
|
+
permalink=$(echo "$metadata_json" | python3 -c "import sys, json; print(json.load(sys.stdin)['permalink'])")
|
|
225
|
+
else
|
|
226
|
+
# Fallback if Python parsing fails
|
|
227
|
+
title="${basename//-/ }"
|
|
228
|
+
description="Jupyter notebook"
|
|
229
|
+
date=$(date -u +%Y-%m-%dT%H:%M:%S.000Z)
|
|
230
|
+
permalink="/notebooks/${basename}/"
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
# Generate front matter
|
|
234
|
+
cat <<EOF
|
|
235
|
+
---
|
|
236
|
+
title: "${title}"
|
|
237
|
+
description: "${description}"
|
|
238
|
+
layout: notebook
|
|
239
|
+
collection: notebooks
|
|
240
|
+
date: ${date}
|
|
241
|
+
categories: [Notebooks]
|
|
242
|
+
tags: [jupyter, python]
|
|
243
|
+
comments: true
|
|
244
|
+
jupyter_metadata: true
|
|
245
|
+
lastmod: $(date -u +%Y-%m-%dT%H:%M:%S.000Z)
|
|
246
|
+
permalink: ${permalink}
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
EOF
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
# Convert a single notebook
|
|
253
|
+
convert_notebook() {
|
|
254
|
+
local notebook_file="$1"
|
|
255
|
+
local basename
|
|
256
|
+
basename=$(basename "$notebook_file" .ipynb)
|
|
257
|
+
local output_file="$OUTPUT_DIR/${basename}.md"
|
|
258
|
+
|
|
259
|
+
# Check if conversion is needed
|
|
260
|
+
if [[ -f "$output_file" && "$FORCE" != true ]]; then
|
|
261
|
+
if [[ "$VERBOSE" == true ]]; then
|
|
262
|
+
info "Skipping $notebook_file (output exists, use --force to reconvert)"
|
|
263
|
+
fi
|
|
264
|
+
return 0
|
|
265
|
+
fi
|
|
266
|
+
|
|
267
|
+
step "Converting: $notebook_file"
|
|
268
|
+
|
|
269
|
+
if [[ "$DRY_RUN" == true ]]; then
|
|
270
|
+
info "Would convert: $notebook_file -> $output_file"
|
|
271
|
+
return 0
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
# Create output directories
|
|
275
|
+
mkdir -p "$OUTPUT_DIR"
|
|
276
|
+
mkdir -p "$IMAGE_DIR"
|
|
277
|
+
|
|
278
|
+
# Create temp directory for conversion
|
|
279
|
+
local temp_dir
|
|
280
|
+
temp_dir=$(mktemp -d "${TMPDIR:-/tmp}/notebook-XXXXXXXXXX")
|
|
281
|
+
local temp_md="${temp_dir}/${basename}.md"
|
|
282
|
+
|
|
283
|
+
if [[ "$VERBOSE" == true ]]; then
|
|
284
|
+
info "Running nbconvert..."
|
|
285
|
+
fi
|
|
286
|
+
|
|
287
|
+
# Use nbconvert to convert to markdown with image extraction
|
|
288
|
+
python3 -m nbconvert \
|
|
289
|
+
--to markdown \
|
|
290
|
+
--output-dir="$temp_dir" \
|
|
291
|
+
--output="${basename}" \
|
|
292
|
+
"$notebook_file" 2>&1 | \
|
|
293
|
+
if [[ "$VERBOSE" == true ]]; then cat; else cat > /dev/null; fi
|
|
294
|
+
|
|
295
|
+
# Move extracted images to assets directory
|
|
296
|
+
local notebook_images="${temp_dir}/${basename}_files"
|
|
297
|
+
if [[ -d "$notebook_images" ]]; then
|
|
298
|
+
mkdir -p "$IMAGE_DIR/${basename}_files"
|
|
299
|
+
cp -r "$notebook_images"/* "$IMAGE_DIR/${basename}_files/"
|
|
300
|
+
if [[ "$VERBOSE" == true ]]; then
|
|
301
|
+
info "Copied images to $IMAGE_DIR/${basename}_files/"
|
|
302
|
+
fi
|
|
303
|
+
fi
|
|
304
|
+
|
|
305
|
+
# Extract front matter
|
|
306
|
+
local front_matter
|
|
307
|
+
front_matter=$(extract_front_matter "$notebook_file")
|
|
308
|
+
|
|
309
|
+
# Combine front matter with converted content
|
|
310
|
+
{
|
|
311
|
+
echo "$front_matter"
|
|
312
|
+
cat "$temp_md"
|
|
313
|
+
} > "$output_file"
|
|
314
|
+
|
|
315
|
+
# Fix image paths in the markdown to use Jekyll's asset path
|
|
316
|
+
sed -i.bak "s|${basename}_files/|/assets/images/notebooks/${basename}_files/|g" "$output_file"
|
|
317
|
+
rm -f "${output_file}.bak"
|
|
318
|
+
|
|
319
|
+
# Clean up temp directory
|
|
320
|
+
rm -rf "$temp_dir"
|
|
321
|
+
|
|
322
|
+
success "Converted: $output_file"
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
# Clean converted files
|
|
326
|
+
clean_converted() {
|
|
327
|
+
step "Cleaning converted markdown files..."
|
|
328
|
+
|
|
329
|
+
local count=0
|
|
330
|
+
while IFS= read -r -d '' md_file; do
|
|
331
|
+
local basename
|
|
332
|
+
basename=$(basename "$md_file" .md)
|
|
333
|
+
|
|
334
|
+
# Only remove .md files if corresponding .ipynb exists
|
|
335
|
+
if [[ -f "$NOTEBOOKS_DIR/${basename}.ipynb" ]]; then
|
|
336
|
+
if [[ "$DRY_RUN" == true ]]; then
|
|
337
|
+
info "Would remove: $md_file"
|
|
338
|
+
else
|
|
339
|
+
rm -f "$md_file"
|
|
340
|
+
info "Removed: $md_file"
|
|
341
|
+
fi
|
|
342
|
+
((count++))
|
|
343
|
+
fi
|
|
344
|
+
done < <(find "$OUTPUT_DIR" -name "*.md" -print0 2>/dev/null)
|
|
345
|
+
|
|
346
|
+
if [[ $count -eq 0 ]]; then
|
|
347
|
+
info "No converted files to clean"
|
|
348
|
+
else
|
|
349
|
+
success "Cleaned $count converted file(s)"
|
|
350
|
+
fi
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
# List notebooks to be converted
|
|
354
|
+
list_notebooks() {
|
|
355
|
+
step "Listing notebooks to convert..."
|
|
356
|
+
|
|
357
|
+
local count=0
|
|
358
|
+
|
|
359
|
+
if [[ -n "$SPECIFIC_FILE" ]]; then
|
|
360
|
+
if [[ -f "$SPECIFIC_FILE" ]]; then
|
|
361
|
+
echo "$SPECIFIC_FILE"
|
|
362
|
+
count=1
|
|
363
|
+
fi
|
|
364
|
+
else
|
|
365
|
+
while IFS= read -r -d '' notebook_file; do
|
|
366
|
+
echo "$notebook_file"
|
|
367
|
+
((count++))
|
|
368
|
+
done < <(find "$NOTEBOOKS_DIR" -name "*.ipynb" -print0 2>/dev/null)
|
|
369
|
+
fi
|
|
370
|
+
|
|
371
|
+
info "Found $count notebook(s)"
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
# Main conversion process
|
|
375
|
+
main() {
|
|
376
|
+
info "Jupyter Notebook Converter for Jekyll"
|
|
377
|
+
info "======================================"
|
|
378
|
+
|
|
379
|
+
if [[ "$CLEAN" == true ]]; then
|
|
380
|
+
clean_converted
|
|
381
|
+
exit 0
|
|
382
|
+
fi
|
|
383
|
+
|
|
384
|
+
if [[ "$LIST_ONLY" == true ]]; then
|
|
385
|
+
list_notebooks
|
|
386
|
+
exit 0
|
|
387
|
+
fi
|
|
388
|
+
|
|
389
|
+
check_dependencies
|
|
390
|
+
|
|
391
|
+
# Convert specific file or all notebooks
|
|
392
|
+
if [[ -n "$SPECIFIC_FILE" ]]; then
|
|
393
|
+
if [[ ! -f "$SPECIFIC_FILE" ]]; then
|
|
394
|
+
error "File not found: $SPECIFIC_FILE"
|
|
395
|
+
fi
|
|
396
|
+
convert_notebook "$SPECIFIC_FILE"
|
|
397
|
+
else
|
|
398
|
+
step "Scanning for notebooks in: $NOTEBOOKS_DIR"
|
|
399
|
+
|
|
400
|
+
local count=0
|
|
401
|
+
while IFS= read -r -d '' notebook_file; do
|
|
402
|
+
convert_notebook "$notebook_file"
|
|
403
|
+
((count++))
|
|
404
|
+
done < <(find "$NOTEBOOKS_DIR" -name "*.ipynb" -print0 2>/dev/null)
|
|
405
|
+
|
|
406
|
+
if [[ $count -eq 0 ]]; then
|
|
407
|
+
warn "No notebooks found in $NOTEBOOKS_DIR"
|
|
408
|
+
else
|
|
409
|
+
success "Converted $count notebook(s)"
|
|
410
|
+
fi
|
|
411
|
+
fi
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
# Run main function
|
|
415
|
+
main
|