datalog-theme 0.7.0 → 0.9.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.
Files changed (203) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +188 -0
  3. data/CITATION.cff +2 -2
  4. data/README.md +25 -17
  5. data/_data/cdn-integrity.yml +0 -30
  6. data/_data/i18n/en.yml +297 -0
  7. data/_data/i18n/es.yml +297 -0
  8. data/_data/i18n/pt.yml +297 -0
  9. data/_data/js_manifest.json +16 -0
  10. data/_includes/analytics/dashboard.html +3 -1
  11. data/_includes/components/api-function.html +20 -1
  12. data/_includes/components/author-bio.html +21 -11
  13. data/_includes/components/author-list.html +32 -0
  14. data/_includes/components/citation-tools.html +33 -25
  15. data/_includes/components/comments-thread.html +90 -0
  16. data/_includes/components/contact-form.html +120 -0
  17. data/_includes/components/correction-report.html +82 -0
  18. data/_includes/components/enhanced-code-block.html +1 -1
  19. data/_includes/components/enhanced-toc.html +6 -7
  20. data/_includes/components/license-link.html +13 -0
  21. data/_includes/components/license-notice.html +28 -0
  22. data/_includes/components/moderation-inbox.html +139 -0
  23. data/_includes/components/reactions.html +49 -0
  24. data/_includes/components/reading-list.html +31 -0
  25. data/_includes/components/reading-mode-toggle.html +65 -0
  26. data/_includes/components/reading-state-bookmark.html +27 -0
  27. data/_includes/components/reading-state-panel.html +71 -0
  28. data/_includes/components/reproducibility.html +61 -0
  29. data/_includes/components/responsive-image.html +3 -3
  30. data/_includes/components/revision-history.html +39 -0
  31. data/_includes/components/revision-notice.html +35 -0
  32. data/_includes/components/series-nav.html +64 -0
  33. data/_includes/components/subscribe-form.html +88 -0
  34. data/_includes/components/subscription-manage.html +68 -0
  35. data/_includes/components/webmentions.html +48 -0
  36. data/_includes/csp-meta.html +135 -11
  37. data/_includes/footer.html +22 -20
  38. data/_includes/head.html +111 -63
  39. data/_includes/header/navigation.html +12 -15
  40. data/_includes/header.html +28 -19
  41. data/_includes/layouts/default/article.html +9 -7
  42. data/_includes/meta/dynamic-services-config.html +14 -0
  43. data/_includes/meta/math-config.html +18 -10
  44. data/_includes/meta/person-json.html +27 -0
  45. data/_includes/meta/publisher.html +45 -0
  46. data/_includes/meta/schema.html +67 -30
  47. data/_includes/meta/scholarly.html +121 -0
  48. data/_includes/meta/scripts-loader.html +18 -32
  49. data/_includes/meta/webmention-discovery.html +14 -0
  50. data/_includes/post/related-posts.html +4 -7
  51. data/_includes/scripts.html +57 -0
  52. data/_includes/search/index-data.json +9 -34
  53. data/_layouts/dataset.html +5 -3
  54. data/_layouts/default.html +16 -8
  55. data/_layouts/notebook.html +1 -0
  56. data/_layouts/package.html +4 -2
  57. data/_layouts/page.html +15 -0
  58. data/_layouts/portfolio.html +1 -0
  59. data/_layouts/post.html +102 -23
  60. data/_layouts/project.html +4 -3
  61. data/_layouts/research.html +20 -8
  62. data/_plugins/analytics_dashboard.rb +9 -3
  63. data/_plugins/authors.rb +133 -0
  64. data/_plugins/config_validator.rb +231 -23
  65. data/_plugins/critical_css_check.rb +42 -0
  66. data/_plugins/csp_generator.rb +18 -28
  67. data/_plugins/datalog_bibliography.rb +9 -7
  68. data/_plugins/datalog_comments.rb +8 -5
  69. data/_plugins/datalog_slides.rb +9 -8
  70. data/_plugins/i18n.rb +13 -12
  71. data/_plugins/image_optimizer.rb +241 -178
  72. data/_plugins/licenses.rb +135 -0
  73. data/_plugins/math_preprocessor.rb +59 -7
  74. data/_plugins/notebook_converter.rb +23 -4
  75. data/_plugins/plugin_loader.rb +3 -1
  76. data/_plugins/publications_generator.rb +8 -2
  77. data/_plugins/references.rb +238 -0
  78. data/_plugins/reproducibility.rb +150 -0
  79. data/_plugins/revisions.rb +101 -0
  80. data/_plugins/rouge_highlight_filter.rb +42 -0
  81. data/_plugins/scholarly.rb +50 -0
  82. data/_plugins/search_code_blocks.rb +30 -0
  83. data/_plugins/search_normalizer.rb +15 -53
  84. data/_plugins/search_pages.rb +3 -4
  85. data/_plugins/series.rb +104 -0
  86. data/_plugins/statements.rb +87 -0
  87. data/_sass/_academic-dashboard.scss +262 -0
  88. data/_sass/_base.scss +20 -1
  89. data/_sass/_comments-thread.scss +159 -0
  90. data/_sass/_components.scss +64 -1153
  91. data/_sass/_features.scss +17 -0
  92. data/_sass/_layout.scss +385 -1
  93. data/_sass/_mathematical.scss +27 -0
  94. data/_sass/_moderation.scss +222 -0
  95. data/_sass/_notebooks.scss +322 -0
  96. data/_sass/_open-science-badges.scss +56 -0
  97. data/_sass/{_phase1-enhancements.scss → _post-components.scss} +5 -3
  98. data/_sass/_print.scss +291 -0
  99. data/_sass/_reactions.scss +89 -0
  100. data/_sass/_reading-state.scss +290 -0
  101. data/_sass/_search-page.scss +530 -0
  102. data/_sass/_search.scss +46 -0
  103. data/_sass/_service-forms.scss +204 -0
  104. data/_sass/_subscriptions.scss +140 -0
  105. data/_sass/_syntax-highlighting.scss +212 -97
  106. data/_sass/_theme.scss +40 -19
  107. data/_sass/_typography.scss +130 -0
  108. data/_sass/_utilities.scss +5 -0
  109. data/_sass/_variables.scss +6 -0
  110. data/_sass/_webmentions.scss +125 -0
  111. data/assets/css/main.scss +14 -0
  112. data/assets/js/dist/academic.js +1 -1
  113. data/assets/js/dist/analytics-dashboard.js +1 -1
  114. data/assets/js/dist/chunks/chunk-2DYDWUFX.js +1 -0
  115. data/assets/js/dist/chunks/chunk-PATLC23F.js +1 -0
  116. data/assets/js/dist/chunks/chunk-V7734B2G.js +1 -0
  117. data/assets/js/dist/comments.js +2 -0
  118. data/assets/js/dist/contact.js +1 -0
  119. data/assets/js/dist/core.js +1 -1
  120. data/assets/js/dist/corrections.js +1 -0
  121. data/assets/js/dist/loader.js +1 -1
  122. data/assets/js/dist/math.js +1 -1
  123. data/assets/js/dist/moderation.js +1 -0
  124. data/assets/js/dist/notebook.js +1 -1
  125. data/assets/js/dist/reactions.js +1 -0
  126. data/assets/js/dist/reading-state.js +1 -0
  127. data/assets/js/dist/search.js +1 -1
  128. data/assets/js/dist/sources.json +52 -0
  129. data/assets/js/dist/subscriptions.js +1 -0
  130. data/assets/js/dist/visualizations.js +11 -2
  131. data/assets/js/dist/webmentions.js +1 -0
  132. data/assets/js/loader.js +37 -1
  133. data/datalog-theme.gemspec +35 -23
  134. data/lib/datalog/cli.rb +43 -15
  135. data/lib/datalog/critical_css.rb +168 -0
  136. data/lib/datalog/plugin_system/dependency_resolver.rb +0 -2
  137. data/lib/datalog/plugins/comments.rb +33 -3
  138. data/lib/datalog/theme/installed_files.rb +113 -0
  139. data/lib/datalog/theme/package.rb +57 -0
  140. data/lib/datalog/theme/repository_checkout.rb +94 -0
  141. data/lib/datalog/theme/version.rb +5 -1
  142. data/lib/datalog/warning_filter.rb +5 -11
  143. data/lib/datalog-theme.rb +6 -0
  144. metadata +96 -147
  145. data/_data/academic.yml +0 -217
  146. data/_data/config/author.yml +0 -121
  147. data/_data/datasets.yml +0 -28
  148. data/_data/js_meta.json +0 -371
  149. data/_data/navigation.yml +0 -145
  150. data/_data/projects.yml +0 -41
  151. data/_data/publications.yml +0 -28
  152. data/_data/social.yml +0 -73
  153. data/_data/visualizations.yml +0 -51
  154. data/_includes/components/advanced-search.html +0 -682
  155. data/_includes/components/bookmark-system.html +0 -96
  156. data/_includes/components/comments.html +0 -244
  157. data/_includes/components/content-recommendations.html +0 -228
  158. data/_includes/components/email-preferences.html +0 -200
  159. data/_includes/components/enhanced-metadata.html +0 -228
  160. data/_includes/components/language-switcher.html +0 -396
  161. data/_includes/components/navigation-enhancements.html +0 -454
  162. data/_includes/components/newsletter-signup.html +0 -178
  163. data/_includes/components/popular-posts.html +0 -233
  164. data/_includes/components/reading-progress.html +0 -133
  165. data/_includes/components/reading-time.html +0 -121
  166. data/_includes/components/series-navigation.html +0 -124
  167. data/_includes/components/social-proof.html +0 -34
  168. data/_includes/components/user-preferences.html +0 -566
  169. data/_includes/meta/syntax-config.html +0 -19
  170. data/_layouts/archive.html +0 -282
  171. data/_layouts/post-sidebar.html +0 -183
  172. data/_sass/_phase3-enhancements.scss +0 -874
  173. data/_sass/_phase4-enhancements.scss +0 -1214
  174. data/_sass/_phase5-enhancements.scss +0 -414
  175. data/assets/js/academic.js +0 -262
  176. data/assets/js/analytics-dashboard.js +0 -382
  177. data/assets/js/core/dark-mode.js +0 -79
  178. data/assets/js/core/github-cards.js +0 -123
  179. data/assets/js/core/language-filter.js +0 -69
  180. data/assets/js/core/navigation.js +0 -184
  181. data/assets/js/core/scroll-progress.js +0 -45
  182. data/assets/js/core/search-hotkeys.js +0 -62
  183. data/assets/js/core/skip-links.js +0 -62
  184. data/assets/js/dist/manifest.json +0 -22
  185. data/assets/js/dist/meta.json +0 -371
  186. data/assets/js/main.js +0 -23
  187. data/assets/js/math.js +0 -818
  188. data/assets/js/notebook.js +0 -158
  189. data/assets/js/search/analytics.js +0 -91
  190. data/assets/js/search/app.js +0 -271
  191. data/assets/js/search/autocomplete.js +0 -120
  192. data/assets/js/search/engine.js +0 -260
  193. data/assets/js/search/filters.js +0 -45
  194. data/assets/js/search/render.js +0 -217
  195. data/assets/js/search/utils.js +0 -99
  196. data/assets/js/search.js +0 -354
  197. data/assets/js/visualizations.js +0 -816
  198. data/assets/publications/datalog-publications.bib +0 -8
  199. data/assets/publications/datalog-publications.ris +0 -9
  200. data/assets/publications/publications.bib +0 -30
  201. data/assets/templates/diogo-ribeiro-cv.md +0 -31
  202. data/assets/templates/diogo-ribeiro-cv.tex +0 -32
  203. data/lib/datalog/theme/theme.rb +0 -18
@@ -1,233 +0,0 @@
1
- {%- comment -%}
2
- Popular Posts Widget Component
3
- Displays trending and most popular content
4
-
5
- Parameters:
6
- - max_posts: Maximum posts to display (default: 5)
7
- - sort_by: Sorting method (recent, comments, trending) (default: recent)
8
- - show_stats: Show reading stats (default: true)
9
- - show_thumbnail: Show post thumbnail (default: true)
10
- - layout: Display layout (list, grid, compact) (default: list)
11
- - heading: Custom heading (default: "Popular Posts")
12
- {%- endcomment -%}
13
-
14
- {%- assign max_posts = include.max_posts | default: 5 -%}
15
- {%- assign sort_by = include.sort_by | default: "recent" -%}
16
- {%- assign show_stats = include.show_stats | default: true -%}
17
- {%- assign show_thumbnail = include.show_thumbnail | default: true -%}
18
- {%- assign layout = include.layout | default: "list" -%}
19
- {%- assign heading = include.heading | default: "Popular Posts" -%}
20
-
21
- {%- comment -%} Get posts and apply basic filtering {%- endcomment -%}
22
- {%- assign all_posts = site.posts -%}
23
-
24
- {%- comment -%} Simulate popularity scoring (in real app, use analytics data) {%- endcomment -%}
25
- {%- assign scored_posts = "" | split: "" -%}
26
-
27
- {%- for post in all_posts limit: 20 -%}
28
- {%- assign score = 0 -%}
29
-
30
- {%- comment -%} Recency score (prefer recent content) {%- endcomment -%}
31
- {%- assign days_old = "now" | date: "%s" | minus: post.date | date: "%s" | divided_by: 86400 -%}
32
- {%- if days_old < 7 -%}
33
- {%- assign score = score | plus: 50 -%}
34
- {%- elsif days_old < 30 -%}
35
- {%- assign score = score | plus: 30 -%}
36
- {%- elsif days_old < 90 -%}
37
- {%- assign score = score | plus: 10 -%}
38
- {%- endif -%}
39
-
40
- {%- comment -%} Tag count (more tags = more discoverable) {%- endcomment -%}
41
- {%- assign tag_score = post.tags.size | times: 5 -%}
42
- {%- assign score = score | plus: tag_score -%}
43
-
44
- {%- comment -%} Featured posts bonus {%- endcomment -%}
45
- {%- if post.featured -%}
46
- {%- assign score = score | plus: 25 -%}
47
- {%- endif -%}
48
-
49
- {%- comment -%} Difficulty-based scoring (beginner posts might be more popular) {%- endcomment -%}
50
- {%- if post.difficulty == "beginner" -%}
51
- {%- assign score = score | plus: 15 -%}
52
- {%- endif -%}
53
-
54
- {%- comment -%} Add randomness for variety (simulates view counts) {%- endcomment -%}
55
- {%- assign random_bonus = post.title | size | modulo: 30 -%}
56
- {%- assign score = score | plus: random_bonus -%}
57
-
58
- {%- comment -%} Store post with score {%- endcomment -%}
59
- {%- assign post_with_score = post.url | append: "||" | append: score -%}
60
- {%- assign scored_posts = scored_posts | push: post_with_score -%}
61
- {%- endfor -%}
62
-
63
- {%- comment -%} Simple bubble sort by score {%- endcomment -%}
64
- {%- assign sorted_posts = scored_posts -%}
65
- {%- for i in (1..scored_posts.size) -%}
66
- {%- for j in (1..scored_posts.size) -%}
67
- {%- assign current = sorted_posts[j] | split: "||" -%}
68
- {%- assign prev = sorted_posts[j-1] | split: "||" -%}
69
- {%- if current[1] and prev[1] -%}
70
- {%- assign current_score = current[1] | plus: 0 -%}
71
- {%- assign prev_score = prev[1] | plus: 0 -%}
72
- {%- if current_score > prev_score -%}
73
- {%- assign temp = sorted_posts[j] -%}
74
- {%- assign sorted_posts[j] = sorted_posts[j-1] -%}
75
- {%- assign sorted_posts[j-1] = temp -%}
76
- {%- endif -%}
77
- {%- endif -%}
78
- {%- endfor -%}
79
- {%- endfor -%}
80
-
81
- {%- comment -%} Build popular posts array {%- endcomment -%}
82
- {%- assign popular_posts = "" | split: "" -%}
83
- {%- for item in sorted_posts limit: max_posts -%}
84
- {%- assign item_parts = item | split: "||" -%}
85
- {%- assign post_url = item_parts[0] -%}
86
-
87
- {%- for post in site.posts -%}
88
- {%- if post.url == post_url -%}
89
- {%- assign popular_posts = popular_posts | push: post -%}
90
- {%- break -%}
91
- {%- endif -%}
92
- {%- endfor -%}
93
- {%- endfor -%}
94
-
95
- <aside class="popular-posts popular-posts--{{ layout }}"
96
- data-popular-posts
97
- aria-label="{{ heading }}">
98
-
99
- <div class="popular-posts__header">
100
- <h3 class="popular-posts__title">
101
- <svg xmlns="http://www.w3.org/2000/svg"
102
- width="20"
103
- height="20"
104
- viewBox="0 0 24 24"
105
- fill="none"
106
- stroke="currentColor"
107
- stroke-width="2"
108
- stroke-linecap="round"
109
- stroke-linejoin="round"
110
- aria-hidden="true">
111
- <polyline points="23 6 13.5 15.5 8.5 10.5 1 18"></polyline>
112
- <polyline points="17 6 23 6 23 12"></polyline>
113
- </svg>
114
- {{ heading }}
115
- </h3>
116
-
117
- {%- if show_stats -%}
118
- <span class="popular-posts__subtitle">
119
- Trending this month
120
- </span>
121
- {%- endif -%}
122
- </div>
123
-
124
- <div class="popular-posts__list">
125
- {%- for post in popular_posts -%}
126
- {%- assign loop_index = forloop.index -%}
127
-
128
- <article class="popular-post"
129
- itemscope
130
- itemtype="https://schema.org/BlogPosting">
131
-
132
- <div class="popular-post__rank" aria-label="Rank {{ loop_index }}">
133
- {{ loop_index }}
134
- </div>
135
-
136
- {%- if show_thumbnail and post.featured_image or post.image -%}
137
- <div class="popular-post__thumbnail">
138
- <a href="{{ post.url | relative_url }}"
139
- tabindex="-1"
140
- aria-hidden="true">
141
- <img src="{{ post.featured_image | default: post.image | relative_url }}"
142
- alt=""
143
- loading="lazy"
144
- itemprop="image">
145
- </a>
146
- </div>
147
- {%- endif -%}
148
-
149
- <div class="popular-post__content">
150
- <h4 class="popular-post__title" itemprop="headline">
151
- <a href="{{ post.url | relative_url }}"
152
- class="popular-post__link">
153
- {{ post.title }}
154
- </a>
155
- </h4>
156
-
157
- {%- if show_stats -%}
158
- <div class="popular-post__meta">
159
- {%- if post.date -%}
160
- <time class="popular-post__date"
161
- datetime="{{ post.date | date_to_xmlschema }}"
162
- itemprop="datePublished">
163
- {{ post.date | date: "%b %d, %Y" }}
164
- </time>
165
- {%- endif -%}
166
-
167
- <span class="popular-post__separator" aria-hidden="true">•</span>
168
-
169
- {%- comment -%} Reading time {%- endcomment -%}
170
- {% include components/reading-time.html
171
- content=post.content
172
- variant="inline"
173
- show_icon=false %}
174
-
175
- {%- comment -%} View count removed — was simulated. Re-enable with real analytics data. {%- endcomment -%}
176
- </div>
177
- {%- endif -%}
178
-
179
- {%- if post.excerpt and layout != "compact" -%}
180
- <p class="popular-post__excerpt" itemprop="description">
181
- {{ post.excerpt | strip_html | truncatewords: 15 }}
182
- </p>
183
- {%- endif -%}
184
-
185
- {%- if post.tags.size > 0 and layout == "grid" -%}
186
- <div class="popular-post__tags">
187
- {%- for tag in post.tags limit: 2 -%}
188
- <span class="popular-post__tag">{{ tag }}</span>
189
- {%- endfor -%}
190
- </div>
191
- {%- endif -%}
192
- </div>
193
-
194
- {%- comment -%} Trending indicator for top 3 {%- endcomment -%}
195
- {%- if loop_index <= 3 -%}
196
- <div class="popular-post__badge" aria-label="Trending">
197
- <svg xmlns="http://www.w3.org/2000/svg"
198
- width="16"
199
- height="16"
200
- viewBox="0 0 24 24"
201
- fill="none"
202
- stroke="currentColor"
203
- stroke-width="2"
204
- aria-hidden="true">
205
- <polyline points="23 6 13.5 15.5 8.5 10.5 1 18"></polyline>
206
- <polyline points="17 6 23 6 23 12"></polyline>
207
- </svg>
208
- </div>
209
- {%- endif -%}
210
- </article>
211
- {%- endfor -%}
212
- </div>
213
-
214
- {%- if site.phase4_features.popular_posts.show_view_all -%}
215
- <div class="popular-posts__footer">
216
- <a href="{{ '/archive/' | relative_url }}"
217
- class="popular-posts__view-all">
218
- View all posts
219
- <svg xmlns="http://www.w3.org/2000/svg"
220
- width="16"
221
- height="16"
222
- viewBox="0 0 24 24"
223
- fill="none"
224
- stroke="currentColor"
225
- stroke-width="2"
226
- aria-hidden="true">
227
- <line x1="5" y1="12" x2="19" y2="12"></line>
228
- <polyline points="12 5 19 12 12 19"></polyline>
229
- </svg>
230
- </a>
231
- </div>
232
- {%- endif -%}
233
- </aside>
@@ -1,133 +0,0 @@
1
- {%- comment -%}
2
- Reading Progress Tracker
3
- Displays a visual indicator of reading progress with time estimate
4
-
5
- Parameters:
6
- - position: Position on page (top, bottom, sticky)
7
- - show_time: Show time remaining estimate (default: true)
8
- - show_percentage: Show percentage complete (default: true)
9
- {%- endcomment -%}
10
-
11
- {%- assign position = include.position | default: "top" -%}
12
- {%- assign show_time = include.show_time | default: true -%}
13
- {%- assign show_percentage = include.show_percentage | default: true -%}
14
-
15
- <div class="reading-progress reading-progress--{{ position }}" data-reading-progress aria-label="Reading progress">
16
- {%- comment -%} Progress bar {%- endcomment -%}
17
- <div class="reading-progress__bar">
18
- <div class="reading-progress__fill"
19
- data-progress-fill
20
- role="progressbar"
21
- aria-valuenow="0"
22
- aria-valuemin="0"
23
- aria-valuemax="100"
24
- aria-label="Reading progress percentage">
25
- </div>
26
- </div>
27
-
28
- {%- comment -%} Progress stats {%- endcomment -%}
29
- <div class="reading-progress__stats">
30
- {%- if show_percentage -%}
31
- <span class="reading-progress__percentage" data-progress-percentage aria-live="polite">
32
- <span data-percentage-value>0</span>% complete
33
- </span>
34
- {%- endif -%}
35
-
36
- {%- if show_time -%}
37
- <span class="reading-progress__time" data-progress-time aria-live="polite">
38
- <svg class="reading-progress__icon" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
39
- <circle cx="12" cy="12" r="10"></circle>
40
- <polyline points="12 6 12 12 16 14"></polyline>
41
- </svg>
42
- <span data-time-value>...</span> remaining
43
- </span>
44
- {%- endif -%}
45
- </div>
46
- </div>
47
-
48
- <script>
49
- (function() {
50
- 'use strict';
51
-
52
- document.addEventListener('DOMContentLoaded', function() {
53
- const progressContainer = document.querySelector('[data-reading-progress]');
54
- if (!progressContainer) return;
55
-
56
- const progressFill = progressContainer.querySelector('[data-progress-fill]');
57
- const percentageValue = progressContainer.querySelector('[data-percentage-value]');
58
- const timeValue = progressContainer.querySelector('[data-time-value]');
59
-
60
- // Get article content for calculation
61
- const articleContent = document.querySelector('.post-content, .article-content, article, main');
62
- if (!articleContent) return;
63
-
64
- // Calculate reading time (words per minute)
65
- const wordsPerMinute = 200;
66
- const text = articleContent.textContent || articleContent.innerText;
67
- const wordCount = text.trim().split(/\s+/).length;
68
- const totalReadingTime = Math.ceil(wordCount / wordsPerMinute);
69
-
70
- // Track scroll position
71
- let ticking = false;
72
-
73
- function updateProgress() {
74
- // Get scroll position
75
- const windowHeight = window.innerHeight;
76
- const documentHeight = document.documentElement.scrollHeight;
77
- const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
78
-
79
- // Calculate progress percentage
80
- const scrollableDistance = documentHeight - windowHeight;
81
- const scrollPercentage = scrollableDistance > 0
82
- ? Math.min(Math.max((scrollTop / scrollableDistance) * 100, 0), 100)
83
- : 0;
84
-
85
- // Update progress bar
86
- if (progressFill) {
87
- progressFill.style.width = scrollPercentage + '%';
88
- progressFill.setAttribute('aria-valuenow', Math.round(scrollPercentage));
89
- }
90
-
91
- // Update percentage text
92
- if (percentageValue) {
93
- percentageValue.textContent = Math.round(scrollPercentage);
94
- }
95
-
96
- // Update time remaining
97
- if (timeValue) {
98
- const timeRemaining = Math.max(
99
- Math.ceil(totalReadingTime * (1 - scrollPercentage / 100)),
100
- 0
101
- );
102
-
103
- if (timeRemaining === 0) {
104
- timeValue.textContent = 'Complete!';
105
- } else if (timeRemaining === 1) {
106
- timeValue.textContent = '1 min';
107
- } else {
108
- timeValue.textContent = timeRemaining + ' min';
109
- }
110
- }
111
-
112
- ticking = false;
113
- }
114
-
115
- function requestTick() {
116
- if (!ticking) {
117
- window.requestAnimationFrame(updateProgress);
118
- ticking = true;
119
- }
120
- }
121
-
122
- // Listen to scroll events
123
- window.addEventListener('scroll', requestTick, { passive: true });
124
- window.addEventListener('resize', requestTick, { passive: true });
125
-
126
- // Initial update
127
- updateProgress();
128
-
129
- // Update when images load
130
- window.addEventListener('load', updateProgress);
131
- });
132
- })();
133
- </script>
@@ -1,121 +0,0 @@
1
- {%- comment -%}
2
- Reading Time Estimate Component
3
- Calculates and displays estimated reading time with metadata
4
-
5
- Parameters:
6
- - content: Content to analyze (default: page.content)
7
- - words_per_minute: Reading speed (default: 200)
8
- - show_word_count: Show word count (default: false)
9
- - show_icon: Show clock icon (default: true)
10
- - variant: Display variant (inline, badge, detailed) (default: inline)
11
- {%- endcomment -%}
12
-
13
- {%- assign content_to_analyze = include.content | default: page.content -%}
14
- {%- assign words_per_minute = include.words_per_minute | default: 200 -%}
15
- {%- assign show_word_count = include.show_word_count | default: false -%}
16
- {%- assign show_icon = include.show_icon | default: true -%}
17
- {%- assign variant = include.variant | default: "inline" -%}
18
-
19
- {%- comment -%} Calculate word count {%- endcomment -%}
20
- {%- assign words = content_to_analyze | strip_html | number_of_words -%}
21
-
22
- {%- comment -%} Calculate reading time in minutes {%- endcomment -%}
23
- {%- assign reading_time_float = words | divided_by: words_per_minute | plus: 0.0 -%}
24
- {%- assign reading_time = reading_time_float | ceil -%}
25
-
26
- {%- comment -%} Handle edge cases {%- endcomment -%}
27
- {%- if reading_time < 1 -%}
28
- {%- assign reading_time = 1 -%}
29
- {%- endif -%}
30
-
31
- {%- comment -%} Format reading time display {%- endcomment -%}
32
- {%- if reading_time == 1 -%}
33
- {%- assign time_label = "1 min read" -%}
34
- {%- else -%}
35
- {%- assign time_label = reading_time | append: " min read" -%}
36
- {%- endif -%}
37
-
38
- {%- comment -%} Calculate additional metrics for detailed variant {%- endcomment -%}
39
- {%- if variant == "detailed" -%}
40
- {%- assign hours = reading_time | divided_by: 60 -%}
41
- {%- assign remaining_minutes = reading_time | modulo: 60 -%}
42
-
43
- {%- comment -%} Estimate based on different reading speeds {%- endcomment -%}
44
- {%- assign slow_reading_time = words | divided_by: 150 | ceil -%}
45
- {%- assign fast_reading_time = words | divided_by: 250 | ceil -%}
46
- {%- endif -%}
47
-
48
- <span class="reading-time reading-time--{{ variant }}"
49
- data-reading-time
50
- data-words="{{ words }}"
51
- data-minutes="{{ reading_time }}"
52
- itemscope
53
- itemtype="https://schema.org/Article">
54
-
55
- {%- if show_icon -%}
56
- <svg class="reading-time__icon"
57
- xmlns="http://www.w3.org/2000/svg"
58
- width="16"
59
- height="16"
60
- viewBox="0 0 24 24"
61
- fill="none"
62
- stroke="currentColor"
63
- stroke-width="2"
64
- stroke-linecap="round"
65
- stroke-linejoin="round"
66
- aria-hidden="true">
67
- <circle cx="12" cy="12" r="10"></circle>
68
- <polyline points="12 6 12 12 16 14"></polyline>
69
- </svg>
70
- {%- endif -%}
71
-
72
- {%- if variant == "detailed" -%}
73
- <span class="reading-time__details">
74
- <span class="reading-time__primary">
75
- <strong class="reading-time__time" itemprop="timeRequired" content="PT{{ reading_time }}M">
76
- {{ time_label }}
77
- </strong>
78
- </span>
79
-
80
- <span class="reading-time__secondary">
81
- <span class="reading-time__word-count">
82
- {{ words }} words
83
- </span>
84
-
85
- {%- if hours >= 1 -%}
86
- <span class="reading-time__alternative">
87
- ({{ hours }}h {{ remaining_minutes }}m)
88
- </span>
89
- {%- endif -%}
90
- </span>
91
-
92
- <span class="reading-time__range">
93
- <small>{{ fast_reading_time }}-{{ slow_reading_time }} min depending on speed</small>
94
- </span>
95
- </span>
96
-
97
- {%- elsif variant == "badge" -%}
98
- <span class="reading-time__badge">
99
- <span class="reading-time__time" itemprop="timeRequired" content="PT{{ reading_time }}M">
100
- {{ time_label }}
101
- </span>
102
- {%- if show_word_count -%}
103
- <span class="reading-time__word-count">
104
- {{ words }} words
105
- </span>
106
- {%- endif -%}
107
- </span>
108
-
109
- {%- else -%}
110
- {%- comment -%} Inline variant (default) {%- endcomment -%}
111
- <span class="reading-time__time" itemprop="timeRequired" content="PT{{ reading_time }}M">
112
- {{ time_label }}
113
- </span>
114
- {%- if show_word_count -%}
115
- <span class="reading-time__separator" aria-hidden="true">•</span>
116
- <span class="reading-time__word-count">
117
- {{ words }} words
118
- </span>
119
- {%- endif -%}
120
- {%- endif -%}
121
- </span>
@@ -1,124 +0,0 @@
1
- {%- comment -%}
2
- Series/Collection Navigation Component
3
- Displays navigation for multi-part content series
4
-
5
- Parameters:
6
- - series: Series name/ID
7
- - show_progress: Show progress indicator (default: true)
8
- - compact: Compact layout (default: false)
9
- {%- endcomment -%}
10
-
11
- {%- assign series_name = include.series | default: page.series -%}
12
- {%- assign show_progress = include.show_progress | default: true -%}
13
- {%- assign compact = include.compact | default: false -%}
14
-
15
- {%- if series_name -%}
16
- {%- comment -%} Get all posts in this series {%- endcomment -%}
17
- {%- assign series_posts = site.posts | where: "series", series_name | sort: "series_order" -%}
18
- {%- assign series_count = series_posts | size -%}
19
-
20
- {%- if series_count > 0 -%}
21
- {%- comment -%} Find current post position {%- endcomment -%}
22
- {%- assign current_index = 0 -%}
23
- {%- for post in series_posts -%}
24
- {%- if post.url == page.url -%}
25
- {%- assign current_index = forloop.index -%}
26
- {%- endif -%}
27
- {%- endfor -%}
28
-
29
- <nav class="series-nav{% if compact %} series-nav--compact{% endif %}"
30
- aria-label="Series navigation: {{ series_name }}">
31
- <div class="series-nav__header">
32
- <svg class="series-nav__icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
33
- <path d="M12 2v20M2 12h20"></path>
34
- <path d="M16 8l4 4-4 4M8 16l-4-4 4-4"></path>
35
- </svg>
36
- <h3 class="series-nav__title">
37
- <span class="series-nav__label">Series:</span>
38
- {{ series_name }}
39
- </h3>
40
- </div>
41
-
42
- {%- if show_progress -%}
43
- <div class="series-nav__progress" aria-label="Series progress">
44
- <div class="series-nav__progress-bar">
45
- <div class="series-nav__progress-fill"
46
- style="width: {{ current_index | times: 100 | divided_by: series_count }}%"
47
- role="progressbar"
48
- aria-valuenow="{{ current_index }}"
49
- aria-valuemin="0"
50
- aria-valuemax="{{ series_count }}"
51
- aria-label="Part {{ current_index }} of {{ series_count }}">
52
- </div>
53
- </div>
54
- <span class="series-nav__progress-text">
55
- Part {{ current_index }} of {{ series_count }}
56
- </span>
57
- </div>
58
- {%- endif -%}
59
-
60
- <ol class="series-nav__list">
61
- {%- for post in series_posts -%}
62
- {%- assign is_current = false -%}
63
- {%- if post.url == page.url -%}
64
- {%- assign is_current = true -%}
65
- {%- endif -%}
66
-
67
- <li class="series-nav__item{% if is_current %} series-nav__item--current{% endif %}">
68
- <div class="series-nav__number">{{ forloop.index }}</div>
69
- {%- if is_current -%}
70
- <div class="series-nav__content">
71
- <strong class="series-nav__post-title">{{ post.title }}</strong>
72
- <span class="series-nav__badge">Current</span>
73
- </div>
74
- {%- else -%}
75
- <a href="{{ post.url | relative_url }}" class="series-nav__content">
76
- <span class="series-nav__post-title">{{ post.title }}</span>
77
- {%- if post.excerpt -%}
78
- <span class="series-nav__excerpt">{{ post.excerpt | strip_html | truncate: 80 }}</span>
79
- {%- endif -%}
80
- </a>
81
- {%- endif -%}
82
- </li>
83
- {%- endfor -%}
84
- </ol>
85
-
86
- {%- comment -%} Previous/Next navigation {%- endcomment -%}
87
- <div class="series-nav__controls">
88
- {%- if current_index > 1 -%}
89
- {%- assign prev_post = series_posts[current_index | minus: 2] -%}
90
- <a href="{{ prev_post.url | relative_url }}" class="series-nav__button series-nav__button--prev">
91
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
92
- <polyline points="15 18 9 12 15 6"></polyline>
93
- </svg>
94
- Previous
95
- </a>
96
- {%- else -%}
97
- <div class="series-nav__button series-nav__button--disabled">
98
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
99
- <polyline points="15 18 9 12 15 6"></polyline>
100
- </svg>
101
- Previous
102
- </div>
103
- {%- endif -%}
104
-
105
- {%- if current_index < series_count -%}
106
- {%- assign next_post = series_posts[current_index] -%}
107
- <a href="{{ next_post.url | relative_url }}" class="series-nav__button series-nav__button--next">
108
- Next
109
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
110
- <polyline points="9 18 15 12 9 6"></polyline>
111
- </svg>
112
- </a>
113
- {%- else -%}
114
- <div class="series-nav__button series-nav__button--disabled">
115
- Next
116
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
117
- <polyline points="9 18 15 12 9 6"></polyline>
118
- </svg>
119
- </div>
120
- {%- endif -%}
121
- </div>
122
- </nav>
123
- {%- endif -%}
124
- {%- endif -%}
@@ -1,34 +0,0 @@
1
- {%- comment -%}
2
- Social Proof Indicators Component
3
-
4
- This component requires REAL metrics passed via include parameters.
5
- It does NOT generate simulated/fabricated data.
6
-
7
- Usage (only when real analytics data is available):
8
- {% include components/social-proof.html views=page_views %}
9
-
10
- To enable real data, configure GA4 analytics and use the analytics
11
- dashboard plugin to fetch actual page view counts.
12
-
13
- See: https://github.com/DiogoRibeiro7/analytics-blog-jekyll/issues/23
14
- {%- endcomment -%}
15
-
16
- {%- if include.views or include.readers -%}
17
- <div class="social-proof{% if include.compact %} social-proof--compact{% endif %}"
18
- data-social-proof>
19
- <div class="social-proof__metrics">
20
- {%- if include.views -%}
21
- <div class="social-proof__metric" data-metric="views">
22
- <span class="social-proof__value">{{ include.views }}</span>
23
- <span class="social-proof__label">views</span>
24
- </div>
25
- {%- endif -%}
26
- {%- if include.readers -%}
27
- <div class="social-proof__metric" data-metric="readers">
28
- <span class="social-proof__value">{{ include.readers }}</span>
29
- <span class="social-proof__label">reading now</span>
30
- </div>
31
- {%- endif -%}
32
- </div>
33
- </div>
34
- {%- endif -%}