jekyll-theme-zer0 0.10.0 → 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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +107 -0
  3. data/_includes/components/mermaid.html +98 -9
  4. data/_includes/content/intro.html +13 -1
  5. data/_includes/core/header.html +1 -1
  6. data/_layouts/category.html +16 -6
  7. data/_layouts/collection.html +8 -3
  8. data/_layouts/journals.html +8 -3
  9. data/_sass/custom.scss +20 -12
  10. data/assets/images/previews/10-ai-tools-that-will-transform-your-productivity-.png +0 -0
  11. data/assets/images/previews/business.png +0 -0
  12. data/assets/images/previews/css-grid-mastery-build-any-layout-you-can-imagine.png +0 -0
  13. data/assets/images/previews/development.png +0 -0
  14. data/assets/images/previews/github-setup-deployment.png +0 -0
  15. data/assets/images/previews/jekyll-setup.png +0 -0
  16. data/assets/images/previews/machine-setup.png +0 -0
  17. data/assets/images/previews/published-documentation-library.png +0 -0
  18. data/assets/images/previews/quantum-computing-explained-from-qubits-to-quantum.png +0 -0
  19. data/assets/images/previews/science.png +0 -0
  20. data/assets/images/previews/technology.png +0 -0
  21. data/assets/images/previews/the-complete-guide-to-startup-funding-in-2025.png +0 -0
  22. data/assets/images/previews/the-remote-work-revolution-how-global-teams-are-re.png +0 -0
  23. data/assets/images/previews/tutorial.png +0 -0
  24. data/assets/images/previews/world-news.png +0 -0
  25. data/assets/images/previews/zer0-mistakes-news-network-building-dynamic-news-s.png +0 -0
  26. data/assets/images/previews/zer0-mistakes-quick-start-guide.png +0 -0
  27. data/assets/js/auto-hide-nav.js +79 -16
  28. data/scripts/lib/preview_generator.py +37 -22
  29. data/scripts/test-mermaid.sh +51 -11
  30. metadata +18 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 11f1a090a15824b7b1fc5754e858839d93318efad779deb8d63f5b5641a08f02
4
- data.tar.gz: 476de246227f2f492eb142f7d4f9f0b1e20b9dc97350069bb6b3fff3ef295f8b
3
+ metadata.gz: d85d69f52f828fb686300bac5c2312c0c8bfbd1eed9059fab3c071073efb003b
4
+ data.tar.gz: dd68d098ec262a62b7ab3c50163830467fdaa36fe3cbba9c00941e6893f334bd
5
5
  SHA512:
6
- metadata.gz: 8a2aba3a52815a120646b1b094a973dd01e4c28555f37de5562a9a262d4ad6dda7944753c9f235862ef60b8ce99ad94c62b98bdabf821b983243812d07b27480
7
- data.tar.gz: e54a3074c2d26c178211144219c9e934258edca74f498e073e37edc7e68d4a84d18181295d7467ac6de5647d6bc0d52e41605838171028f96b86dbbdd04caa1f
6
+ metadata.gz: c352ba15e82dfdeb1945caddbc35fff0dc7c8d26431d1d73f5ba66fb51055fa0ba046bdcde3f4c4e48d5ddcafc8bcdca50ce43b39a656e85ba95af656a274b21
7
+ data.tar.gz: a52ea996e45089efcb33286a86555e6f340d93273e45552455e2b5331564fe0ce3cbc781246372d1fac567f5a974cb4cf6b5613caa9a798eec7d1a69587459a4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,112 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.3] - 2025-11-29
4
+
5
+ ### Added
6
+
7
+ - **New: AI-Generated Preview Images** - 17 new preview images for posts and collections
8
+ - Business, Development, Science, Technology, Tutorial, World category index pages
9
+ - Individual post previews: startup funding, quantum computing, AI tools, CSS grid, remote work
10
+ - Quickstart guide previews: GitHub setup, Jekyll setup, machine setup
11
+ - Documentation and blog index previews
12
+
13
+ ### Changed
14
+
15
+ - **Improved: Preview Image Path Handling** (`_layouts/journals.html`, `_layouts/category.html`, `_layouts/collection.html`)
16
+ - Layouts now support both absolute paths (`/assets/...`) and relative paths
17
+ - Conditional logic detects path type and constructs URL correctly
18
+ - Eliminates double-slash issues in image URLs
19
+
20
+ - **Improved: Intro Section Preview Image Logic** (`_includes/content/intro.html`)
21
+ - Smart path detection for preview images (absolute vs relative)
22
+ - Handles URLs with `://` schemes, paths starting with `/`, and relative filenames
23
+ - Cleaner Liquid template logic with proper variable assignment
24
+
25
+ - **Improved: Docker Development Setup** (`docker-compose.yml`, `docker/Dockerfile`)
26
+ - Command now runs `bundle install` before Jekyll serve (fixes volume mount overwrites)
27
+ - Dockerfile copies gemspec and lib/ for proper dependency resolution
28
+ - More reliable container startup with dependency installation
29
+
30
+ - **Improved: Preview Image Generator** (`scripts/lib/preview_generator.py`)
31
+ - Switched from OpenAI SDK to direct HTTP API calls
32
+ - Eliminates SDK dependency - only requires `requests` package
33
+ - Better error handling with HTTP status code parsing
34
+ - Added request timeouts (120s for generation, 60s for download)
35
+
36
+ ### Fixed
37
+
38
+ - **Fixed: Asset Paths in Config** (`_config.yml`)
39
+ - Corrected `teaser` and `info_banner` paths to use `/assets/images/` prefix
40
+ - Images now load correctly across all pages
41
+
42
+ - **Fixed: Preview Image Double-Slash URLs**
43
+ - Removed extra `/` between `public_folder` and `site.teaser` in fallback images
44
+ - All layouts now generate clean, valid image URLs
45
+
46
+ ## [0.10.2] - 2025-11-28
47
+
48
+ ### Added
49
+
50
+ - **Enhanced: Navbar Auto-Hide on Scroll** (`assets/js/auto-hide-nav.js`)
51
+ - Navbar hides when scrolling down past 100px threshold
52
+ - Navbar reappears immediately when scrolling up
53
+ - Automatic body padding to prevent content jump under fixed navbar
54
+ - Performance-optimized with `requestAnimationFrame` throttling
55
+ - Respects `prefers-reduced-motion` accessibility setting
56
+
57
+ ### Changed
58
+
59
+ - **Improved: Header Positioning** (`_includes/core/header.html`)
60
+ - Changed from `z-1` to Bootstrap's `fixed-top` class
61
+ - Provides proper z-index (1030) and fixed positioning
62
+
63
+ - **Refactored: Navbar CSS** (`_sass/custom.scss`)
64
+ - Replaced broken `.hide-navbar` and `.fixed-navbar` classes
65
+ - New `.navbar-hidden` class with `translateY(-100%)` transform
66
+ - Added `!important` to override Bootstrap's `fixed-top` positioning
67
+ - Added explicit background color for opaque navbar
68
+ - Added `prefers-reduced-motion` media query for accessibility
69
+
70
+ ### Fixed
71
+
72
+ - **Fixed: Navbar Blocking Content on Scroll Up**
73
+ - Content no longer obscured when scrolling back to top
74
+ - Body padding dynamically calculated based on navbar height
75
+
76
+ ## [0.10.1] - 2025-11-28
77
+
78
+ ### Added
79
+
80
+ - **Enhanced: Mermaid v2.1 - GitHub Pages Compatible** (`_includes/components/mermaid.html`)
81
+ - Client-side conversion of native markdown ` ```mermaid ` code blocks to rendered diagrams
82
+ - Full GitHub Pages compatibility without custom plugins (all processing client-side)
83
+ - CSS to hide code blocks during conversion (prevents flash of unstyled content)
84
+ - Print styles and improved responsive design
85
+ - Documented dual syntax support (native markdown and HTML div)
86
+
87
+ ### Changed
88
+
89
+ - **Improved: Mermaid Configuration** (`_config.yml`)
90
+ - Added clear comments explaining GitHub Pages compatibility
91
+ - Documented that `jekyll-mermaid` plugin is optional
92
+ - Updated usage instructions for both syntax options
93
+
94
+ - **Improved: Mermaid Documentation**
95
+ - `docs/jekyll/mermaid.md`: Added native markdown syntax as recommended option
96
+ - `docs/jekyll/mermaid-native-markdown.md`: Fixed documentation about front matter requirements
97
+ - Added GitHub Pages compatibility badges to documentation
98
+
99
+ ### Fixed
100
+
101
+ - **Fixed: Native Markdown Mermaid Syntax Not Rendering**
102
+ - ` ```mermaid ` code blocks now properly convert to diagrams via JavaScript
103
+ - Works with GitHub Pages remote_theme deployment
104
+
105
+ - **Fixed: Mermaid Test Script** (`scripts/test-mermaid.sh`)
106
+ - Corrected file path references from `pages/_docs/jekyll/` to `docs/jekyll/`
107
+ - Fixed SIGPIPE issues with `curl | grep` pipelines causing false test failures
108
+ - All 21 tests now pass successfully
109
+
3
110
  ## [0.10.0] - 2025-11-28
4
111
 
5
112
  ### Added
@@ -1,21 +1,39 @@
1
1
  <!--
2
2
  ===================================================================
3
- MERMAID DIAGRAM INTEGRATION
3
+ MERMAID DIAGRAM INTEGRATION (v2.1 - GitHub Pages Compatible)
4
4
  ===================================================================
5
5
 
6
6
  File: mermaid.html
7
7
  Path: _includes/components/mermaid.html
8
8
  Purpose: Load and initialize Mermaid.js for diagram rendering
9
9
 
10
- Usage:
10
+ Usage (Two syntaxes supported):
11
+
12
+ 1. Native Markdown Code Blocks (Recommended):
13
+ ```mermaid
14
+ graph TD
15
+ A[Start] -> B[End]
16
+ ```
17
+
18
+ 2. HTML Div Syntax:
19
+ <div class="mermaid">
20
+ graph TD
21
+ A[Start] -- > B[End]
22
+ </div>
23
+
24
+ Requirements:
11
25
  - Set 'mermaid: true' in page front matter
12
- - Use <div class="mermaid">...</div> syntax in content
13
26
  - Supports all Mermaid diagram types (flowcharts, sequence, gantt, etc.)
14
27
 
15
28
  Configuration:
16
- - Latest stable version via CDN
29
+ - Mermaid v10 via CDN (latest stable)
17
30
  - Forest theme for dark mode compatibility
18
- - Initialized on document ready
31
+ - Client-side code block conversion for GitHub Pages compatibility
32
+
33
+ GitHub Pages Compatibility:
34
+ - No custom plugins required (jekyll-mermaid is optional)
35
+ - All processing done client-side via JavaScript
36
+ - Works with remote_theme on GitHub Pages
19
37
 
20
38
  Documentation:
21
39
  - https://mermaid.js.org/
@@ -29,9 +47,51 @@
29
47
  <!-- Initialize Mermaid with custom configuration -->
30
48
  <script>
31
49
  document.addEventListener('DOMContentLoaded', function() {
50
+ // ============================================================
51
+ // Step 1: Convert native markdown code blocks to mermaid divs
52
+ // This enables ```mermaid syntax to work with GitHub Pages
53
+ // ============================================================
54
+
55
+ // Find all code blocks with language-mermaid class (from ```mermaid)
56
+ var codeBlocks = document.querySelectorAll('pre > code.language-mermaid, code.language-mermaid');
57
+
58
+ codeBlocks.forEach(function(codeBlock) {
59
+ // Get the mermaid content (decode HTML entities)
60
+ var content = codeBlock.textContent || codeBlock.innerText;
61
+
62
+ // Create a new mermaid div
63
+ var mermaidDiv = document.createElement('div');
64
+ mermaidDiv.className = 'mermaid';
65
+ mermaidDiv.textContent = content;
66
+
67
+ // Replace the pre/code block with the mermaid div
68
+ var preElement = codeBlock.closest('pre');
69
+ if (preElement && preElement.parentNode) {
70
+ preElement.parentNode.replaceChild(mermaidDiv, preElement);
71
+ } else if (codeBlock.parentNode) {
72
+ codeBlock.parentNode.replaceChild(mermaidDiv, codeBlock);
73
+ }
74
+ });
75
+
76
+ // Also handle pre elements that might have data-language="mermaid"
77
+ var preBlocks = document.querySelectorAll('pre[data-language="mermaid"]');
78
+ preBlocks.forEach(function(preBlock) {
79
+ var content = preBlock.textContent || preBlock.innerText;
80
+ var mermaidDiv = document.createElement('div');
81
+ mermaidDiv.className = 'mermaid';
82
+ mermaidDiv.textContent = content;
83
+ if (preBlock.parentNode) {
84
+ preBlock.parentNode.replaceChild(mermaidDiv, preBlock);
85
+ }
86
+ });
87
+
88
+ // ============================================================
89
+ // Step 2: Initialize Mermaid with custom configuration
90
+ // ============================================================
91
+
32
92
  mermaid.initialize({
33
- startOnLoad: true,
34
- theme: 'forest', // Options: default, forest, dark, neutral, base
93
+ startOnLoad: false, // We'll manually run after conversion
94
+ theme: 'forest', // Options: default, forest, dark, neutral, base
35
95
  themeVariables: {
36
96
  primaryColor: '#007bff',
37
97
  primaryTextColor: '#fff',
@@ -69,9 +129,20 @@
69
129
  fontSize: 11,
70
130
  numberSectionStyles: 4,
71
131
  axisFormat: '%Y-%m-%d'
72
- }
132
+ },
133
+ securityLevel: 'loose' // Required for some advanced features
134
+ });
135
+
136
+ // ============================================================
137
+ // Step 3: Render all mermaid diagrams
138
+ // ============================================================
139
+
140
+ // Run mermaid to render all .mermaid divs
141
+ mermaid.run().then(function() {
142
+ console.log('Mermaid.js: All diagrams rendered successfully');
143
+ }).catch(function(error) {
144
+ console.warn('Mermaid.js: Some diagrams failed to render', error);
73
145
  });
74
- console.log('Mermaid.js initialized successfully');
75
146
  });
76
147
  </script>
77
148
 
@@ -80,10 +151,12 @@
80
151
 
81
152
  <!-- Custom CSS for Mermaid diagrams -->
82
153
  <style>
154
+ /* Mermaid container styling */
83
155
  .mermaid {
84
156
  text-align: center;
85
157
  margin: 2rem auto;
86
158
  background-color: transparent;
159
+ overflow-x: auto;
87
160
  }
88
161
 
89
162
  /* Ensure diagrams are responsive */
@@ -92,10 +165,26 @@
92
165
  height: auto;
93
166
  }
94
167
 
168
+ /* Hide code blocks before conversion (prevent flash of unstyled content) */
169
+ pre > code.language-mermaid,
170
+ code.language-mermaid {
171
+ display: block;
172
+ opacity: 0;
173
+ transition: opacity 0.2s;
174
+ }
175
+
95
176
  /* Dark mode compatibility */
96
177
  @media (prefers-color-scheme: dark) {
97
178
  .mermaid {
98
179
  filter: brightness(0.9);
99
180
  }
100
181
  }
182
+
183
+ /* Print styles */
184
+ @media print {
185
+ .mermaid svg {
186
+ max-width: 100% !important;
187
+ page-break-inside: avoid;
188
+ }
189
+ }
101
190
  </style>
@@ -15,8 +15,20 @@
15
15
 
16
16
  <!-- Intro Section -->
17
17
 
18
+ {% comment %}
19
+ Determine the preview image path. If page.preview starts with '/' or 'http',
20
+ use it directly. Otherwise, prepend the public folder path.
21
+ {% endcomment %}
22
+ {% assign preview_image = page.preview | default: site.info_banner %}
23
+ {% assign preview_first_char = preview_image | slice: 0 %}
24
+ {% if preview_image contains '://' or preview_first_char == '/' %}
25
+ {% assign preview_path = preview_image %}
26
+ {% else %}
27
+ {% assign preview_path = '/' | append: site.public_folder | append: '/' | append: preview_image %}
28
+ {% endif %}
29
+
18
30
  <div class="bd-intro pt-5 ps-lg-2 position-relative" style="
19
- background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('{{site.baseurl}}/{{ site.public_folder }}/{{ page.preview | default: site.info_banner }}') no-repeat center center / cover;
31
+ background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('{{ site.baseurl }}{{ preview_path }}') no-repeat center center / cover;
20
32
  color: #fff;">
21
33
 
22
34
  <br>
@@ -28,7 +28,7 @@
28
28
  ===================================================================
29
29
  -->
30
30
 
31
- <header id="navbar" class="container-fluid text-center z-1" role="navigation">
31
+ <header id="navbar" class="container-fluid text-center fixed-top" role="navigation">
32
32
 
33
33
  <!-- ================================ -->
34
34
  <!-- TOP NAVIGATION BAR -->
@@ -112,10 +112,15 @@ layout: default
112
112
  {% endif %}
113
113
  <a href="{{ fpost.url | relative_url }}" class="text-decoration-none">
114
114
  {% if fpost.preview %}
115
- <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ fpost.preview }}"
116
- class="card-img-top" alt="{{ fpost.title }}" loading="lazy">
115
+ {% if fpost.preview contains '/assets' %}
116
+ <img src="{{ site.baseurl }}{{ fpost.preview }}"
117
+ class="card-img-top" alt="{{ fpost.title }}" loading="lazy">
118
+ {% else %}
119
+ <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ fpost.preview }}"
120
+ class="card-img-top" alt="{{ fpost.title }}" loading="lazy">
121
+ {% endif %}
117
122
  {% else %}
118
- <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ site.teaser }}"
123
+ <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ site.teaser }}"
119
124
  class="card-img-top" alt="Preview" loading="lazy">
120
125
  {% endif %}
121
126
  </a>
@@ -167,10 +172,15 @@ layout: default
167
172
  {% endif %}
168
173
  <a href="{{ cpost.url | relative_url }}" class="text-decoration-none">
169
174
  {% if cpost.preview %}
170
- <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ cpost.preview }}"
171
- class="card-img-top" alt="{{ cpost.title }}" loading="lazy">
175
+ {% if cpost.preview contains '/assets' %}
176
+ <img src="{{ site.baseurl }}{{ cpost.preview }}"
177
+ class="card-img-top" alt="{{ cpost.title }}" loading="lazy">
178
+ {% else %}
179
+ <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ cpost.preview }}"
180
+ class="card-img-top" alt="{{ cpost.title }}" loading="lazy">
181
+ {% endif %}
172
182
  {% else %}
173
- <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ site.teaser }}"
183
+ <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ site.teaser }}"
174
184
  class="card-img-top" alt="Preview" loading="lazy">
175
185
  {% endif %}
176
186
  </a>
@@ -84,10 +84,15 @@ layout: default
84
84
  <!-- ====================== -->
85
85
  <!-- Display preview image with fallback to site teaser -->
86
86
  {% if post.preview %}
87
- <img src="{{site.baseurl}}/{{ site.public_folder }}/{{ post.preview}}"
88
- class="card-img-top" alt="Preview image for {{ post.title }}">
87
+ {% if post.preview contains '/assets' %}
88
+ <img src="{{ site.baseurl }}{{ post.preview }}"
89
+ class="card-img-top" alt="Preview image for {{ post.title }}">
90
+ {% else %}
91
+ <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ post.preview }}"
92
+ class="card-img-top" alt="Preview image for {{ post.title }}">
93
+ {% endif %}
89
94
  {% else %}
90
- <img src="{{site.baseurl}}/{{ site.public_folder }}/{{ site.teaser }}"
95
+ <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ site.teaser }}"
91
96
  class="card-img-top" alt="Default preview image">
92
97
  {% endif %}
93
98
 
@@ -189,10 +189,15 @@ layout: default
189
189
  <div class="card h-100 border-0 shadow-sm">
190
190
  <a href="{{ rpost.url | relative_url }}" class="text-decoration-none">
191
191
  {% if rpost.preview %}
192
- <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ rpost.preview }}"
193
- class="card-img-top" alt="{{ rpost.title }}" loading="lazy">
192
+ {% if rpost.preview contains '/assets' %}
193
+ <img src="{{ site.baseurl }}{{ rpost.preview }}"
194
+ class="card-img-top" alt="{{ rpost.title }}" loading="lazy">
195
+ {% else %}
196
+ <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ rpost.preview }}"
197
+ class="card-img-top" alt="{{ rpost.title }}" loading="lazy">
198
+ {% endif %}
194
199
  {% else %}
195
- <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ site.teaser }}"
200
+ <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ site.teaser }}"
196
201
  class="card-img-top" alt="Preview" loading="lazy">
197
202
  {% endif %}
198
203
  </a>
data/_sass/custom.scss CHANGED
@@ -177,23 +177,31 @@ img {
177
177
  height: auto;
178
178
  }
179
179
 
180
+ // ============================================
181
+ // NAVBAR - Fixed positioning with auto-hide
182
+ // ============================================
183
+ // Uses Bootstrap's fixed-top class for positioning (z-index: 1030)
184
+ // JavaScript adds/removes .navbar-hidden class on scroll
185
+
180
186
  #navbar {
181
- // position: fixed;
182
- top: 0;
183
- width: 100%;
184
- transition: transform 0.3s;
187
+ // Transition for smooth hide/show animation
188
+ transition: transform 0.3s ease-in-out !important;
189
+
190
+ // Ensure navbar background is opaque
191
+ background-color: var(--bs-body-bg);
185
192
  }
186
193
 
187
- .fixed-navbar {
188
- position: fixed;
189
- top: 0;
190
- width: 100%;
191
- transition: transform 0.3s;
194
+ // Hidden state - slides up out of view
195
+ // Using higher specificity to override Bootstrap's fixed-top
196
+ #navbar.navbar-hidden {
197
+ transform: translateY(-100%) !important;
192
198
  }
193
199
 
194
- .hide-navbar {
195
- transform: translateY(-100%);
196
- top: 0px;
200
+ // Respect user's motion preferences
201
+ @media (prefers-reduced-motion: reduce) {
202
+ #navbar {
203
+ transition: none !important;
204
+ }
197
205
  }
198
206
 
199
207
  $enable-cssgrid: true;
Binary file
Binary file
Binary file
@@ -1,18 +1,81 @@
1
- // Hides the navbar when scrolling down half a page and shows it when scrolling up
2
- window.onload = function() {
3
- let lastScrollTop = 0;
4
- const navbar = document.getElementById('navbar');
5
-
6
- window.addEventListener('scroll', function() {
7
- let scrollTop = window.pageYOffset || document.documentElement.scrollTop;
8
- let halfPageHeight = window.innerHeight / 2;
9
-
10
- if (scrollTop > lastScrollTop && scrollTop > halfPageHeight) {
11
- navbar.classList.add('hide-navbar');
12
- navbar.classList.add('fixed-navbar');
13
- } else {
14
- navbar.classList.remove('hide-navbar');
1
+ /**
2
+ * Auto-hide navbar on scroll
3
+ *
4
+ * Behavior:
5
+ * - Navbar is fixed at top and visible by default
6
+ * - Hides when scrolling DOWN past a threshold (100px)
7
+ * - Reappears when scrolling UP
8
+ * - Respects prefers-reduced-motion accessibility setting
9
+ * - Adds body padding to prevent content jump
10
+ */
11
+ (function() {
12
+ 'use strict';
13
+
14
+ // Configuration
15
+ const SCROLL_THRESHOLD = 100; // Pixels before hide/show triggers
16
+ const SCROLL_DELTA = 5; // Minimum scroll distance to trigger change
17
+
18
+ document.addEventListener('DOMContentLoaded', function() {
19
+ const navbar = document.getElementById('navbar');
20
+ if (!navbar) return;
21
+
22
+ let lastScrollTop = 0;
23
+ let ticking = false;
24
+
25
+ // Check for reduced motion preference
26
+ const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
27
+
28
+ // Calculate and set body padding to prevent content jump
29
+ function updateBodyPadding() {
30
+ const navbarHeight = navbar.offsetHeight;
31
+ document.body.style.paddingTop = navbarHeight + 'px';
32
+ }
33
+
34
+ // Initial padding setup
35
+ updateBodyPadding();
36
+
37
+ // Update padding on window resize
38
+ window.addEventListener('resize', updateBodyPadding, { passive: true });
39
+
40
+ // Scroll handler
41
+ function handleScroll() {
42
+ const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
43
+ const scrollDelta = scrollTop - lastScrollTop;
44
+
45
+ // Only trigger if scroll delta exceeds minimum threshold
46
+ if (Math.abs(scrollDelta) < SCROLL_DELTA) {
47
+ ticking = false;
48
+ return;
49
+ }
50
+
51
+ if (scrollDelta > 0 && scrollTop > SCROLL_THRESHOLD) {
52
+ // Scrolling DOWN past threshold - hide navbar
53
+ navbar.classList.add('navbar-hidden');
54
+ } else if (scrollDelta < 0) {
55
+ // Scrolling UP - show navbar
56
+ navbar.classList.remove('navbar-hidden');
57
+ }
58
+
59
+ // Always show navbar when at top of page
60
+ if (scrollTop <= 0) {
61
+ navbar.classList.remove('navbar-hidden');
62
+ }
63
+
64
+ lastScrollTop = Math.max(0, scrollTop);
65
+ ticking = false;
66
+ }
67
+
68
+ // Optimized scroll listener using requestAnimationFrame
69
+ window.addEventListener('scroll', function() {
70
+ if (!ticking) {
71
+ window.requestAnimationFrame(handleScroll);
72
+ ticking = true;
73
+ }
74
+ }, { passive: true });
75
+
76
+ // Disable animations if user prefers reduced motion
77
+ if (prefersReducedMotion) {
78
+ navbar.style.transition = 'none';
15
79
  }
16
- lastScrollTop = scrollTop;
17
80
  });
18
- }
81
+ })();
@@ -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,
@@ -13,7 +13,8 @@
13
13
  # --docker Test Docker container
14
14
  ###############################################################################
15
15
 
16
- set -euo pipefail
16
+ # Note: Removed -e flag to avoid SIGPIPE issues with pipelines (curl | grep)
17
+ set -uo pipefail
17
18
 
18
19
  # Colors for output
19
20
  RED='\033[0;31m'
@@ -132,24 +133,63 @@ test_url() {
132
133
  test_mermaid_script() {
133
134
  local url="$1"
134
135
  local description="$2"
136
+ local response
137
+ local count
135
138
 
136
- run_test "$description" "curl -s '$url' | grep -q 'mermaid.min.js'"
139
+ TOTAL_TESTS=$((TOTAL_TESTS + 1))
140
+ response=$(curl -s "$url" 2>/dev/null) || true
141
+ count=$(echo "$response" | grep -c 'mermaid.min.js' || true)
142
+ if [ "$count" -gt 0 ]; then
143
+ TESTS_PASSED=$((TESTS_PASSED + 1))
144
+ log_success "$description"
145
+ return 0
146
+ else
147
+ TESTS_FAILED=$((TESTS_FAILED + 1))
148
+ log_error "$description"
149
+ return 1
150
+ fi
137
151
  }
138
152
 
139
153
  # Test Mermaid initialization
140
154
  test_mermaid_init() {
141
155
  local url="$1"
142
156
  local description="$2"
157
+ local response
158
+ local count
143
159
 
144
- run_test "$description" "curl -s '$url' | grep -q 'mermaid.initialize'"
160
+ TOTAL_TESTS=$((TOTAL_TESTS + 1))
161
+ response=$(curl -s "$url" 2>/dev/null) || true
162
+ count=$(echo "$response" | grep -c 'mermaid.initialize' || true)
163
+ if [ "$count" -gt 0 ]; then
164
+ TESTS_PASSED=$((TESTS_PASSED + 1))
165
+ log_success "$description"
166
+ return 0
167
+ else
168
+ TESTS_FAILED=$((TESTS_FAILED + 1))
169
+ log_error "$description"
170
+ return 1
171
+ fi
145
172
  }
146
173
 
147
174
  # Test diagram rendering
148
175
  test_diagram_rendering() {
149
176
  local url="$1"
150
177
  local description="$2"
178
+ local response
179
+ local count
151
180
 
152
- run_test "$description" "curl -s '$url' | grep -q 'class=\"mermaid\"'"
181
+ TOTAL_TESTS=$((TOTAL_TESTS + 1))
182
+ response=$(curl -s "$url" 2>/dev/null) || true
183
+ count=$(echo "$response" | grep -c 'class="mermaid"' || true)
184
+ if [ "$count" -gt 0 ]; then
185
+ TESTS_PASSED=$((TESTS_PASSED + 1))
186
+ log_success "$description"
187
+ return 0
188
+ else
189
+ TESTS_FAILED=$((TESTS_FAILED + 1))
190
+ log_error "$description"
191
+ return 1
192
+ fi
153
193
  }
154
194
 
155
195
  # Main test execution
@@ -165,9 +205,9 @@ main() {
165
205
  log_info "Testing core files..."
166
206
 
167
207
  test_file_exists "_includes/components/mermaid.html" "Mermaid include file exists"
168
- test_file_exists "pages/_docs/jekyll/mermaid.md" "Main documentation exists"
169
- test_file_exists "pages/_docs/jekyll/mermaid-test-suite.md" "Test suite exists"
170
- test_file_exists "pages/_docs/jekyll/jekyll-diagram-with-mermaid.md" "Tutorial exists"
208
+ test_file_exists "docs/jekyll/mermaid.md" "Main documentation exists"
209
+ test_file_exists "docs/jekyll/mermaid-test-suite.md" "Test suite exists"
210
+ test_file_exists "docs/jekyll/jekyll-diagram-with-mermaid.md" "Tutorial exists"
171
211
 
172
212
  # Configuration tests
173
213
  log_info "Testing configuration..."
@@ -188,10 +228,10 @@ main() {
188
228
  # Documentation tests
189
229
  log_info "Testing documentation..."
190
230
 
191
- test_file_content "pages/_docs/jekyll/mermaid.md" "mermaid: true" "Main docs have front matter"
192
- test_file_content "pages/_docs/jekyll/mermaid-test-suite.md" "mermaid: true" "Test suite has front matter"
193
- test_file_content "pages/_docs/jekyll/mermaid.md" "graph TD" "Main docs have examples"
194
- test_file_content "pages/_docs/jekyll/mermaid-test-suite.md" "graph TD" "Test suite has examples"
231
+ test_file_content "docs/jekyll/mermaid.md" "mermaid: true" "Main docs have front matter"
232
+ test_file_content "docs/jekyll/mermaid-test-suite.md" "mermaid: true" "Test suite has front matter"
233
+ test_file_content "docs/jekyll/mermaid.md" "graph TD" "Main docs have examples"
234
+ test_file_content "docs/jekyll/mermaid-test-suite.md" "graph TD" "Test suite has examples"
195
235
 
196
236
  # Server tests (if not quick mode)
197
237
  if [ "$QUICK" = false ]; then
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-theme-zer0
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.10.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amr Abdel
@@ -161,7 +161,24 @@ files:
161
161
  - assets/images/gravatar-small.png
162
162
  - assets/images/gravatar.png
163
163
  - assets/images/info-banner-mountain-wizard.png
164
+ - assets/images/previews/10-ai-tools-that-will-transform-your-productivity-.png
165
+ - assets/images/previews/business.png
166
+ - assets/images/previews/css-grid-mastery-build-any-layout-you-can-imagine.png
167
+ - assets/images/previews/development.png
164
168
  - assets/images/previews/git-workflow-best-practices-for-modern-teams.png
169
+ - assets/images/previews/github-setup-deployment.png
170
+ - assets/images/previews/jekyll-setup.png
171
+ - assets/images/previews/machine-setup.png
172
+ - assets/images/previews/published-documentation-library.png
173
+ - assets/images/previews/quantum-computing-explained-from-qubits-to-quantum.png
174
+ - assets/images/previews/science.png
175
+ - assets/images/previews/technology.png
176
+ - assets/images/previews/the-complete-guide-to-startup-funding-in-2025.png
177
+ - assets/images/previews/the-remote-work-revolution-how-global-teams-are-re.png
178
+ - assets/images/previews/tutorial.png
179
+ - assets/images/previews/world-news.png
180
+ - assets/images/previews/zer0-mistakes-news-network-building-dynamic-news-s.png
181
+ - assets/images/previews/zer0-mistakes-quick-start-guide.png
165
182
  - assets/images/wizard-on-journey.png
166
183
  - assets/images/zer0-checkpoint-1.png
167
184
  - assets/images/zer0-checkpoint-2.png