datalog-theme 0.7.0 → 0.8.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 (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +125 -0
  3. data/CITATION.cff +2 -2
  4. data/README.md +23 -17
  5. data/_data/cdn-integrity.yml +0 -30
  6. data/_includes/analytics/dashboard.html +3 -1
  7. data/_includes/components/api-function.html +20 -1
  8. data/_includes/components/enhanced-code-block.html +1 -1
  9. data/_includes/csp-meta.html +115 -11
  10. data/_includes/footer.html +19 -20
  11. data/_includes/head.html +60 -23
  12. data/_includes/header/navigation.html +12 -15
  13. data/_includes/header.html +28 -19
  14. data/_includes/layouts/default/article.html +5 -1
  15. data/_includes/meta/math-config.html +9 -5
  16. data/_includes/meta/schema.html +5 -2
  17. data/_includes/meta/scripts-loader.html +16 -32
  18. data/_includes/post/related-posts.html +4 -7
  19. data/_includes/search/index-data.json +9 -34
  20. data/_layouts/dataset.html +1 -0
  21. data/_layouts/default.html +16 -8
  22. data/_layouts/notebook.html +1 -0
  23. data/_layouts/package.html +1 -0
  24. data/_layouts/portfolio.html +1 -0
  25. data/_layouts/post.html +20 -5
  26. data/_layouts/project.html +2 -1
  27. data/_plugins/analytics_dashboard.rb +9 -3
  28. data/_plugins/config_validator.rb +12 -7
  29. data/_plugins/csp_generator.rb +18 -28
  30. data/_plugins/datalog_bibliography.rb +9 -7
  31. data/_plugins/datalog_comments.rb +8 -5
  32. data/_plugins/datalog_slides.rb +9 -8
  33. data/_plugins/i18n.rb +7 -8
  34. data/_plugins/image_optimizer.rb +17 -6
  35. data/_plugins/math_preprocessor.rb +33 -3
  36. data/_plugins/notebook_converter.rb +23 -4
  37. data/_plugins/plugin_loader.rb +3 -1
  38. data/_plugins/publications_generator.rb +8 -2
  39. data/_plugins/rouge_highlight_filter.rb +42 -0
  40. data/_plugins/search_code_blocks.rb +30 -0
  41. data/_plugins/search_normalizer.rb +15 -53
  42. data/_plugins/search_pages.rb +3 -4
  43. data/_sass/_academic-dashboard.scss +262 -0
  44. data/_sass/_base.scss +12 -0
  45. data/_sass/_components.scss +64 -1153
  46. data/_sass/_features.scss +17 -0
  47. data/_sass/_layout.scss +1 -1
  48. data/_sass/_notebooks.scss +322 -0
  49. data/_sass/_open-science-badges.scss +56 -0
  50. data/_sass/{_phase1-enhancements.scss → _post-components.scss} +1 -1
  51. data/_sass/_search-page.scss +530 -0
  52. data/_sass/_search.scss +46 -0
  53. data/_sass/_syntax-highlighting.scss +212 -97
  54. data/_sass/_theme.scss +29 -19
  55. data/_sass/_typography.scss +14 -0
  56. data/assets/css/main.scss +14 -0
  57. data/assets/js/dist/academic.js +1 -1
  58. data/assets/js/dist/analytics-dashboard.js +1 -1
  59. data/assets/js/dist/chunks/chunk-225H5YXE.js +1 -0
  60. data/assets/js/dist/core.js +1 -1
  61. data/assets/js/dist/loader.js +1 -1
  62. data/assets/js/dist/notebook.js +1 -1
  63. data/assets/js/dist/search.js +1 -1
  64. data/assets/js/dist/visualizations.js +11 -2
  65. data/assets/js/loader.js +3 -1
  66. data/datalog-theme.gemspec +36 -23
  67. data/lib/datalog/cli.rb +34 -14
  68. data/lib/datalog/plugin_system/dependency_resolver.rb +0 -2
  69. data/lib/datalog/theme/package.rb +57 -0
  70. data/lib/datalog/theme/repository_checkout.rb +90 -0
  71. data/lib/datalog/theme/version.rb +1 -1
  72. data/lib/datalog/warning_filter.rb +5 -11
  73. data/lib/datalog-theme.rb +5 -0
  74. metadata +48 -133
  75. data/_data/academic.yml +0 -217
  76. data/_data/config/author.yml +0 -121
  77. data/_data/datasets.yml +0 -28
  78. data/_data/js_meta.json +0 -371
  79. data/_data/navigation.yml +0 -145
  80. data/_data/projects.yml +0 -41
  81. data/_data/publications.yml +0 -28
  82. data/_data/social.yml +0 -73
  83. data/_data/visualizations.yml +0 -51
  84. data/_includes/components/advanced-search.html +0 -682
  85. data/_includes/components/bookmark-system.html +0 -96
  86. data/_includes/components/comments.html +0 -244
  87. data/_includes/components/content-recommendations.html +0 -228
  88. data/_includes/components/email-preferences.html +0 -200
  89. data/_includes/components/enhanced-metadata.html +0 -228
  90. data/_includes/components/language-switcher.html +0 -396
  91. data/_includes/components/navigation-enhancements.html +0 -454
  92. data/_includes/components/newsletter-signup.html +0 -178
  93. data/_includes/components/popular-posts.html +0 -233
  94. data/_includes/components/reading-progress.html +0 -133
  95. data/_includes/components/reading-time.html +0 -121
  96. data/_includes/components/series-navigation.html +0 -124
  97. data/_includes/components/social-proof.html +0 -34
  98. data/_includes/components/user-preferences.html +0 -566
  99. data/_includes/meta/syntax-config.html +0 -19
  100. data/_layouts/archive.html +0 -282
  101. data/_layouts/post-sidebar.html +0 -183
  102. data/_sass/_phase3-enhancements.scss +0 -874
  103. data/_sass/_phase4-enhancements.scss +0 -1214
  104. data/_sass/_phase5-enhancements.scss +0 -414
  105. data/assets/js/academic.js +0 -262
  106. data/assets/js/analytics-dashboard.js +0 -382
  107. data/assets/js/core/dark-mode.js +0 -79
  108. data/assets/js/core/github-cards.js +0 -123
  109. data/assets/js/core/language-filter.js +0 -69
  110. data/assets/js/core/navigation.js +0 -184
  111. data/assets/js/core/scroll-progress.js +0 -45
  112. data/assets/js/core/search-hotkeys.js +0 -62
  113. data/assets/js/core/skip-links.js +0 -62
  114. data/assets/js/dist/manifest.json +0 -22
  115. data/assets/js/dist/meta.json +0 -371
  116. data/assets/js/main.js +0 -23
  117. data/assets/js/math.js +0 -818
  118. data/assets/js/notebook.js +0 -158
  119. data/assets/js/search/analytics.js +0 -91
  120. data/assets/js/search/app.js +0 -271
  121. data/assets/js/search/autocomplete.js +0 -120
  122. data/assets/js/search/engine.js +0 -260
  123. data/assets/js/search/filters.js +0 -45
  124. data/assets/js/search/render.js +0 -217
  125. data/assets/js/search/utils.js +0 -99
  126. data/assets/js/search.js +0 -354
  127. data/assets/js/visualizations.js +0 -816
  128. data/assets/publications/datalog-publications.bib +0 -8
  129. data/assets/publications/datalog-publications.ris +0 -9
  130. data/assets/publications/publications.bib +0 -30
  131. data/assets/templates/diogo-ribeiro-cv.md +0 -31
  132. data/assets/templates/diogo-ribeiro-cv.tex +0 -32
  133. data/lib/datalog/theme/theme.rb +0 -18
data/_includes/head.html CHANGED
@@ -11,15 +11,15 @@
11
11
  {% assign keyword_list = raw_keywords | replace: ';', ',' | split: ',' %}
12
12
  {% endif %}
13
13
  {% include meta/math-config.html page=page %}
14
+ {%- comment -%} The math engine's URLs, which both the script tags below and the Content Security Policy use. {%- endcomment -%}
15
+ {% assign default_katex_cdn = 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/' %}
16
+ {% assign katex_cdn = math_settings.katex_cdn | default: default_katex_cdn %}
17
+ {% assign default_mathjax_url = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js' %}
18
+ {% assign mathjax_url = math_settings.mathjax_cdn | default: default_mathjax_url %}
14
19
  {% assign js_manifest = site.data.js_manifest %}
15
- {% assign syntax_config = site.theme_options.syntax_highlighting %}
16
- {% assign prism_cdn = syntax_config.cdn | default: 'https://cdn.jsdelivr.net/npm/prismjs@1.29.0' %}
17
- {% assign prism_theme = syntax_config.themes.dark | default: syntax_config.themes.light | default: 'prism-tomorrow' %}
18
- {% assign prism_theme_url = prism_cdn | append: '/themes/' | append: prism_theme | append: '.css' %}
19
- {% assign prism_line_numbers_url = prism_cdn | append: '/plugins/line-numbers/prism-line-numbers.min.css' %}
20
- {% include meta/syntax-config.html %}
21
20
  <meta charset="utf-8" />
22
21
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
22
+ {%- comment -%} Not include_cached: the CSP nonce is different on every page. {%- endcomment -%}
23
23
  {% include csp-meta.html %}
24
24
  <meta name="viewport" content="width=device-width, initial-scale=1" />
25
25
  <meta name="theme-color" content="#0b1d3d" />
@@ -30,7 +30,8 @@
30
30
  {% endif %}
31
31
  <meta name="generator" content="Jekyll" />
32
32
  <meta name="referrer" content="no-referrer-when-downgrade" />
33
- <meta name="robots" content="index,follow" />
33
+ {%- comment -%} `robots:` in front matter keeps a page out of search results, as the 404, search and admin pages do. {%- endcomment %}
34
+ <meta name="robots" content="{{ page.robots | default: 'index,follow' }}" />
34
35
  <meta name="format-detection" content="telephone=no" />
35
36
  <meta name="application-name" content="{{ site.title }}" />
36
37
  <meta property="og:site_name" content="{{ site.title }}" />
@@ -61,12 +62,31 @@
61
62
  <link rel="canonical" href="{{ canonical_url }}" />
62
63
  {% if js_manifest and js_manifest.core %}
63
64
  <link rel="modulepreload" href="{{ js_manifest.core | relative_url }}" crossorigin="anonymous" />
64
- {% if js_manifest.features and js_manifest.features.search and site.features.search %}
65
+ {%- comment -%} Only a page that renders the search app uses the search bundle. {%- endcomment -%}
66
+ {% if js_manifest.features and js_manifest.features.search and site.features.search and content contains 'data-search-app' %}
65
67
  <link rel="modulepreload" href="{{ js_manifest.features.search | relative_url }}" crossorigin="anonymous" />
66
68
  {% endif %}
67
69
  {% if js_manifest.features and js_manifest.features.math and math_enabled and math_engine == 'mathjax' %}
68
70
  <link rel="modulepreload" href="{{ js_manifest.features.math | relative_url }}" crossorigin="anonymous" />
69
71
  {% endif %}
72
+ {%- comment -%}
73
+ The loader requests a feature bundle only after the core bundle has loaded.
74
+ Preloading the bundles this page will use, on the same tests the loader
75
+ applies, starts those downloads alongside core.
76
+ {%- endcomment -%}
77
+ {% if js_manifest.features and js_manifest.features.visualizations and site.features.visualizations and content contains 'data-viz-type' %}
78
+ <link rel="modulepreload" href="{{ js_manifest.features.visualizations | relative_url }}" crossorigin="anonymous" />
79
+ {% endif %}
80
+ {% if js_manifest.features and js_manifest.features.notebook and page.notebook_post %}
81
+ <link rel="modulepreload" href="{{ js_manifest.features.notebook | relative_url }}" crossorigin="anonymous" />
82
+ {% endif %}
83
+ {% if js_manifest.features and js_manifest.features.academic %}
84
+ {% if site.features.academic_dashboard or site.features.citation_tracking %}
85
+ {% if content contains 'data-citation-metric' or content contains 'data-citation-table' or content contains 'data-citation-chart' %}
86
+ <link rel="modulepreload" href="{{ js_manifest.features.academic | relative_url }}" crossorigin="anonymous" />
87
+ {% endif %}
88
+ {% endif %}
89
+ {% endif %}
70
90
  {% endif %}
71
91
  {% assign alternate_translations = page.translations %}
72
92
  {% if alternate_translations %}
@@ -93,19 +113,20 @@
93
113
  <title>{{ site.title }}</title>
94
114
  {% endif %}
95
115
 
116
+ {%- comment -%}
117
+ Only the weights the stylesheet renders. Sans 700, Serif 400 and Mono 500
118
+ were requested as well, and no page of the demo site used them.
119
+ {%- endcomment -%}
120
+ {% assign google_fonts_url = 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600&family=IBM+Plex+Serif:wght@600;700&family=IBM+Plex+Mono:wght@400;600&display=swap' %}
96
121
  <link rel="preconnect" href="https://fonts.googleapis.com" />
97
122
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
98
- <link rel="preload" href="{{ 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600;700&family=IBM+Plex+Serif:wght@400;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap' | add_sri }}" as="style" />
99
- <link rel="stylesheet" href="{{ 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600;700&family=IBM+Plex+Serif:wght@400;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap' | add_sri }}" media="print" data-async-style media="print" data-async-style />
123
+ <link rel="preload" href="{{ google_fonts_url | add_sri }}" as="style" />
124
+ <link rel="stylesheet" href="{{ google_fonts_url | add_sri }}" media="print" data-async-style />
100
125
  <noscript>
101
- <link rel="stylesheet" href="{{ 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600;700&family=IBM+Plex+Serif:wght@400;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap' | add_sri }}" />
126
+ <link rel="stylesheet" href="{{ google_fonts_url | add_sri }}" />
102
127
  </noscript>
103
128
 
104
129
  <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
105
- {% if prism_enabled %}
106
- <link rel="stylesheet" href="{{ prism_theme_url | add_sri }}" />
107
- <link rel="stylesheet" href="{{ prism_line_numbers_url | add_sri }}" />
108
- {% endif %}
109
130
  {% assign critical_config = site.critical_css | default: {} %}
110
131
  {% assign critical_enabled = critical_config.enabled | default: false %}
111
132
  {% assign main_stylesheet = '/assets/css/main.css' | relative_url %}
@@ -166,13 +187,9 @@
166
187
  </script>
167
188
 
168
189
  {% if math_enabled %}
169
- {% assign default_katex_cdn = 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/' %}
170
- {% assign katex_cdn = math_settings.katex_cdn | default: default_katex_cdn %}
171
190
  {% assign katex_stylesheet_url = katex_cdn | append: 'katex.min.css' %}
172
191
  {% assign katex_js_url = katex_cdn | append: 'katex.min.js' %}
173
192
  {% assign katex_autorender_url = katex_cdn | append: 'contrib/auto-render.min.js' %}
174
- {% assign default_mathjax_url = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js' %}
175
- {% assign mathjax_url = math_settings.mathjax_cdn | default: default_mathjax_url %}
176
193
  {% if math_engine == 'katex' %}
177
194
  <link rel="stylesheet" href="{{ katex_stylesheet_url | add_sri }}" />
178
195
  <script defer src="{{ katex_js_url | add_sri }}"></script>
@@ -200,7 +217,10 @@
200
217
  displayMath: [['$$', '$$'], ['\\[', '\\]']],
201
218
  processEscapes: true,
202
219
  processEnvironments: true,
203
- packages: { '[+]': ['ams', 'mathtools', 'physics', 'mhchem', 'require', 'boldsymbol', 'braket', 'cancel'] },
220
+ // No 'require': MathJax 3.2 stopped starting up with "Illegal characters
221
+ // used in \require prefix", and nothing was typeset. Every extension the
222
+ // pages use is loaded below, so authors never need \require.
223
+ packages: { '[+]': ['ams', 'mathtools', 'physics', 'mhchem', 'boldsymbol', 'braket', 'cancel'] },
204
224
  tags: 'all',
205
225
  tagSide: 'right',
206
226
  tagIndent: '1.25em',
@@ -226,7 +246,7 @@
226
246
  },
227
247
  loader: {
228
248
  load: [
229
- '[tex]/require', '[tex]/ams', '[tex]/mathtools', '[tex]/physics', '[tex]/mhchem',
249
+ '[tex]/ams', '[tex]/mathtools', '[tex]/physics', '[tex]/mhchem',
230
250
  '[tex]/boldsymbol', '[tex]/braket', '[tex]/cancel', 'ui/menu', 'a11y/semantic-enrich', 'a11y/assistive-mml'
231
251
  ]
232
252
  },
@@ -239,7 +259,10 @@
239
259
  settings: {
240
260
  assistiveMml: true,
241
261
  collapsible: true,
242
- explorer: true
262
+ // MathJax's explorer gives each equation tabindex="1" when it
263
+ // attaches, which takes the equations out of the page's tab order.
264
+ // The assistive MathML above is what screen readers read.
265
+ explorer: false
243
266
  }
244
267
  }
245
268
  },
@@ -268,7 +291,11 @@
268
291
  if (window.DatalogMath && typeof window.DatalogMath.onPageReady === 'function') {
269
292
  window.DatalogMath.onPageReady();
270
293
  }
271
- return window.MathJax.startup.defaultPageReady();
294
+ // Scripts that act on typeset math wait for this event: until MathJax
295
+ // loads, window.MathJax is only this configuration object.
296
+ return window.MathJax.startup.defaultPageReady().then(() => {
297
+ document.dispatchEvent(new CustomEvent('datalog:math-ready'));
298
+ });
272
299
  }
273
300
  }
274
301
  };
@@ -319,14 +346,24 @@
319
346
 
320
347
  <link rel="alternate" type="application/atom+xml" title="{{ site.title }}" href="{{ '/feed.xml' | absolute_url }}" />
321
348
  <meta http-equiv="Permissions-Policy" content="interest-cohort=()" />
349
+ {%- comment -%} A verification tag renders only when the site sets its code: an empty one verifies nothing. {%- endcomment %}
350
+ {% if site.google_site_verification and site.google_site_verification != "" %}
322
351
  <meta name="google-site-verification" content="{{ site.google_site_verification }}" />
352
+ {% endif %}
353
+ {% if site.bing_site_verification and site.bing_site_verification != "" %}
323
354
  <meta name="msvalidate.01" content="{{ site.bing_site_verification }}" />
355
+ {% endif %}
356
+ {% if site.yandex_site_verification and site.yandex_site_verification != "" %}
324
357
  <meta name="yandex-verification" content="{{ site.yandex_site_verification }}" />
358
+ {% endif %}
359
+ {% if site.baidu_site_verification and site.baidu_site_verification != "" %}
325
360
  <meta name="baidu-site-verification" content="{{ site.baidu_site_verification }}" />
361
+ {% endif %}
326
362
  <meta name="plausible-domain" content="{{ site.url | replace: 'https://', '' | replace: 'http://', '' }}" />
327
363
 
328
364
  <script nonce="{{ page.csp_nonce }}">
329
365
  document.documentElement.classList.add('js');
330
366
  </script>
331
367
 
368
+ {%- comment -%} Not include_cached: its inline script carries the page's CSP nonce. {%- endcomment -%}
332
369
  {% include analytics.html %}
@@ -3,37 +3,34 @@
3
3
  required:
4
4
  - items
5
5
  optional:
6
- - page
7
- - contexts
6
+ - active
7
+ - lang
8
8
  ---
9
- Renders the primary navigation list with active state awareness.
9
+ Renders the primary navigation list. `active` lists the indexes of the items that
10
+ mark the current section, each between commas (",2,"); header.html works them out
11
+ from the page. Nothing else here depends on the page, which is what lets
12
+ header.html render this with include_cached. `lang` is passed only so that each
13
+ page language gets its own copy of any translated descriptions.
10
14
  {%- endcomment -%}
11
15
  {%- assign nav_items = include.items | default: '' -%}
12
16
  {%- if nav_items == '' -%}
13
17
  {%- assign nav_items = '' | split: '' -%}
14
18
  {%- endif -%}
15
- {%- assign active_page = include.page | default: page -%}
16
- {%- assign contexts = include.contexts | default: '' -%}
17
- {%- if contexts == '' -%}
18
- {%- assign contexts = '' | split: '' -%}
19
- {%- endif -%}
19
+ {%- assign active_items = include.active | default: '' -%}
20
20
  <ul class="nav-list">
21
21
  {%- for item in nav_items -%}
22
22
  {%- assign item_url = item.url | relative_url -%}
23
+ {%- capture item_marker -%},{{ forloop.index0 }},{%- endcapture -%}
23
24
  {%- assign is_active = false -%}
25
+ {%- if active_items contains item_marker -%}
26
+ {%- assign is_active = true -%}
27
+ {%- endif -%}
24
28
  {%- assign nav_title = item.title | default: 'Untitled' | strip -%}
25
29
  {%- assign nav_section = item.section | default: nav_title | replace: '_', ' ' | replace: '-', ' ' | strip -%}
26
30
  {%- assign nav_description = item.description | default: '' -%}
27
31
  {%- if nav_description == '' and item.description_key -%}
28
32
  {%- capture nav_description -%}{% t item.description_key %}{%- endcapture -%}
29
33
  {%- endif -%}
30
- {%- if active_page.url == item.url or active_page.url == item_url -%}
31
- {%- assign is_active = true -%}
32
- {%- elsif item.section and contexts contains item.section -%}
33
- {%- assign is_active = true -%}
34
- {%- elsif item.url != '/' and active_page.url contains item.url -%}
35
- {%- assign is_active = true -%}
36
- {%- endif -%}
37
34
  {%- if nav_description == '' -%}
38
35
  {%- assign nav_description = nav_section | default: nav_title -%}
39
36
  {%- assign nav_description = nav_description | append: ' resources' -%}
@@ -1,10 +1,31 @@
1
1
  {% assign localization_config = site.theme_options.localization %}
2
2
  {% assign navigation_data = site.data.navigation %}
3
+ {%- comment -%} `header:` (or `main:`) in _data/navigation.yml lists the links. Without it the header has no navigation, rather than links to sections the site may not have. {%- endcomment -%}
4
+ {% assign nav_items = navigation_data.header | default: navigation_data.main %}
5
+ {%- comment -%}
6
+ The navigation is the same on every page except for which links mark the current
7
+ section, so header/navigation.html renders from the indexes of those links (",2,")
8
+ and is cached for each distinct set.
9
+ {%- endcomment -%}
10
+ {% assign page_contexts = '' | split: '' %}
11
+ {% if page.section %}{% assign page_contexts = page_contexts | push: page.section %}{% endif %}
12
+ {% if page.layout %}{% assign page_contexts = page_contexts | push: page.layout %}{% endif %}
13
+ {% if page.collection %}{% assign page_contexts = page_contexts | push: page.collection %}{% endif %}
14
+ {% if page.type %}{% assign page_contexts = page_contexts | push: page.type %}{% endif %}
15
+ {%- capture active_items -%}
16
+ {%- for item in nav_items -%}
17
+ {%- assign item_url = item.url | relative_url -%}
18
+ {%- if page.url == item.url or page.url == item_url -%},{{ forloop.index0 }}
19
+ {%- elsif item.section and page_contexts contains item.section -%},{{ forloop.index0 }}
20
+ {%- elsif item.url != '/' and page.url contains item.url -%},{{ forloop.index0 }}
21
+ {%- endif -%}
22
+ {%- endfor -%},
23
+ {%- endcapture -%}
3
24
  <header class="site-header" role="banner" data-sticky-header>
4
25
  <div class="header-progress" aria-hidden="true">
5
26
  <progress class="header-progress__bar" value="0" max="100" data-scroll-progress></progress>
6
27
  </div>
7
- <div class="visually-hidden" aria-live="polite" id="progress-announcement">Reading progress: <span data-scroll-progress-value>0</span>%</div>
28
+ {%- comment -%} No live region for the percentage: screen readers announced it on every scroll event. {%- endcomment %}
8
29
  <div class="container header-container">
9
30
  <div class="header-primary">
10
31
  {% assign brand_symbol = site.brand.accent_symbol | default: '∀ data · Σ insights' | strip %}
@@ -18,7 +39,7 @@
18
39
  </svg>
19
40
  </span>
20
41
  <span class="site-title-group">
21
- <span class="site-title">{{ site.name | default: 'DataLog' }}</span>
42
+ <span class="site-title">{{ site.name | default: site.title }}</span>
22
43
  <span class="site-tagline">{{ brand_symbol }}</span>
23
44
  </span>
24
45
  </a>
@@ -41,30 +62,18 @@
41
62
  </svg>
42
63
  </button>
43
64
  {% endif %}
65
+ {% if nav_items.size > 0 %}
44
66
  <button class="header-control header-control--icon nav-toggle" type="button" aria-expanded="false" aria-controls="site-nav" aria-label="Toggle navigation">
45
67
  <span aria-hidden="true">☰</span>
46
68
  </button>
69
+ {% endif %}
47
70
  </div>
48
71
  </div>
49
72
  </div>
50
- {% assign nav_items = navigation_data.header | default: navigation_data.main %}
51
- {% if nav_items == nil %}
52
- {% assign nav_items = '' | split: '' %}
53
- {% assign nav_items = nav_items | push: {"title": "Blog", "url": "/blog/", "section": "blog"} %}
54
- {% assign nav_items = nav_items | push: {"title": "Research", "url": "/research/", "section": "research"} %}
55
- {% assign nav_items = nav_items | push: {"title": "Projects", "url": "/portfolio/", "section": "portfolio"} %}
56
- {% assign nav_items = nav_items | push: {"title": "Datasets", "url": "/datasets/", "section": "datasets"} %}
57
- {% assign nav_items = nav_items | push: {"title": "Visualizations", "url": "/visualizations/", "section": "visualizations"} %}
58
- {% assign nav_items = nav_items | push: {"title": "Academic Ops", "url": "/academic/", "section": "academic"} %}
59
- {% assign nav_items = nav_items | push: {"title": "About", "url": "/about/", "section": "about"} %}
60
- {% endif %}
61
- {% assign page_contexts = '' | split: '' %}
62
- {% if page.section %}{% assign page_contexts = page_contexts | push: page.section %}{% endif %}
63
- {% if page.layout %}{% assign page_contexts = page_contexts | push: page.layout %}{% endif %}
64
- {% if page.collection %}{% assign page_contexts = page_contexts | push: page.collection %}{% endif %}
65
- {% if page.type %}{% assign page_contexts = page_contexts | push: page.type %}{% endif %}
73
+ {% if nav_items.size > 0 %}
66
74
  <nav id="site-nav" class="site-nav" role="navigation" aria-label="Primary navigation" data-open="false">
67
- {% include header/navigation.html items=nav_items page=page contexts=page_contexts %}
75
+ {% include_cached header/navigation.html items=nav_items active=active_items lang=page.lang %}
68
76
  </nav>
77
+ {% endif %}
69
78
  </div>
70
79
  </header>
@@ -19,7 +19,11 @@ Renders the standard article scaffold used by the default layout.
19
19
  {%- endif -%}
20
20
  <header class="article-header">
21
21
  {%- unless hero_overlay_title -%}
22
- {%- if article_page.title -%}
22
+ {%- comment -%}
23
+ `show_title: false`, in a page's front matter or a layout's, leaves the <h1> to a
24
+ page or layout that renders its own heading, so the page has one.
25
+ {%- endcomment -%}
26
+ {%- if article_page.title and article_page.show_title != false and layout.show_title != false -%}
23
27
  <h1 class="article-title" itemprop="headline">{{ article_page.title }}</h1>
24
28
  {%- endif -%}
25
29
  {%- if article_page.subtitle -%}
@@ -10,10 +10,14 @@ Exposes math_enabled, math_engine, math_status_id, and math_has_expressions vari
10
10
 
11
11
  math_enabled resolves, in order: the page's `mathjax` or `math` front matter,
12
12
  then `theme_options.math.render_on_load` (falling back to `features.mathjax`).
13
- With `render_on_load: auto` the engine is only loaded when the rendered page
14
- contains math (`$`, `\(`, `\[` or kramdown's `math/tex` scripts) or declares
15
- `math_expressions`; `true` loads it on every page; `false` only on pages that
16
- opt in.
13
+ With `render_on_load: auto` the engine is only loaded on pages with math: the
14
+ math preprocessor records each expression it wraps in `math_expressions`, and
15
+ leaves code alone. Kramdown's `math/tex` scripts and the preprocessor's
16
+ `math-expression--source` wrappers in the rendered page count too: notebook
17
+ pages carry the wrappers but record no expressions of their own. Searching the
18
+ rendered page for `$`, `\(` or `\[` instead loaded MathJax for shell variables,
19
+ prices and escaped parentheses in code. `true` loads it on every page; `false`
20
+ only on pages that opt in.
17
21
  {%- endcomment -%}
18
22
  {%- assign math_settings = site.theme_options.math | default: {} -%}
19
23
  {%- assign math_engine = include.page.math_engine | default: math_settings.engine | default: 'mathjax' -%}
@@ -35,7 +39,7 @@ opt in.
35
39
  {%- assign math_enabled = include.page.math -%}
36
40
  {%- elsif math_load == 'auto' -%}
37
41
  {%- assign math_enabled = math_has_expressions -%}
38
- {%- if content contains 'math/tex' or content contains '$' or content contains '\(' or content contains '\[' -%}
42
+ {%- if content contains 'math/tex' or content contains 'math-expression--source' -%}
39
43
  {%- assign math_enabled = true -%}
40
44
  {%- endif -%}
41
45
  {%- else -%}
@@ -41,8 +41,9 @@ Defaults to TechnicalArticle for posts, WebPage for other pages.
41
41
  "@type": "WebPage",
42
42
  "@id": {{ page_url | jsonify }}
43
43
  },
44
- "headline": {{ schema_page.title | truncate: 110 | jsonify }},
45
- {%- if description -%}
44
+ "headline": {{ schema_page.title | default: site.title | truncate: 110 | jsonify }},
45
+ {%- comment -%} Values the page doesn't have are left out: "description": "" and "dateModified": "" are invalid structured data. {%- endcomment -%}
46
+ {%- if description and description != "" -%}
46
47
  "description": {{ description | jsonify }},
47
48
  {%- endif -%}
48
49
  "url": {{ page_url | jsonify }},
@@ -64,7 +65,9 @@ Defaults to TechnicalArticle for posts, WebPage for other pages.
64
65
  {%- if schema_page.date -%}
65
66
  "datePublished": "{{ schema_page.date | date_to_xmlschema }}",
66
67
  {%- endif -%}
68
+ {%- if modified -%}
67
69
  "dateModified": "{{ modified | date_to_xmlschema }}",
70
+ {%- endif -%}
68
71
  {%- if word_count > 0 -%}
69
72
  "wordCount": {{ word_count }},
70
73
  {%- endif -%}
@@ -2,26 +2,18 @@
2
2
  ---
3
3
  required:
4
4
  - location
5
- optional:
6
- - syntax_config
7
5
  ---
8
- Outputs Prism assets in the head and shared data bootstrapping scripts in the body.
9
- Pass location="head" or location="body" to control which block renders.
6
+ Outputs the page data scripts and the feature loader at the end of the body
7
+ (location="body"). location="head" used to render Prism's scripts. Code is now
8
+ highlighted by Rouge when the site builds, so the head renders nothing.
10
9
  {%- endcomment -%}
11
- {%- assign syntax = include.syntax_config | default: site.theme_options.syntax_highlighting | default: {} -%}
12
- {%- assign prism_cdn = syntax.cdn | default: 'https://cdn.jsdelivr.net/npm/prismjs@1.29.0' -%}
13
- {%- assign default_components = 'python|r|sql|julia|javascript' | split: '|' -%}
14
- {%- assign prism_components = syntax.components | default: default_components -%}
15
- {%- include meta/syntax-config.html -%}
16
- {%- if include.location == 'head' and prism_enabled -%}
17
- <script defer src="{{ (prism_cdn | append: '/prism.min.js') | add_sri }}"></script>
18
- {%- for component in prism_components -%}
19
- {%- unless component == 'core' -%}
20
- <script defer src="{{ (prism_cdn | append: '/components/prism-' | append: component | append: '.min.js') | add_sri }}"></script>
21
- {%- endunless -%}
22
- {%- endfor -%}
23
- <script defer src="{{ (prism_cdn | append: '/plugins/normalize-whitespace/prism-normalize-whitespace.min.js') | add_sri }}"></script>
24
- {%- elsif include.location == 'body' -%}
10
+ {%- if include.location == 'body' -%}
11
+ {%- comment -%}
12
+ Only academic.js reads the academic and publication data, and the loader
13
+ runs it only on pages with citation metrics, tables or charts. Inlined on
14
+ every page, it was a quarter of the home page's HTML.
15
+ {%- endcomment -%}
16
+ {%- if content contains 'data-citation-metric' or content contains 'data-citation-table' or content contains 'data-citation-chart' -%}
25
17
  {%- assign publications = site.data.publications | default: {} -%}
26
18
  <script nonce="{{ page.csp_nonce }}">
27
19
  window.DatalogAcademic = {{ site.data.academic | jsonify }};
@@ -32,22 +24,14 @@ Pass location="head" or location="body" to control which block renders.
32
24
  "yearly_totals": {{ publications.yearly_totals | jsonify }}
33
25
  };
34
26
  </script>
27
+ {%- endif -%}
28
+ {%- comment -%} Not include_cached: scripts.html reads the page's math and notebook settings and CSP nonce. {%- endcomment -%}
35
29
  {% include scripts.html %}
30
+ {%- comment -%}
31
+ core/github-cards.js reads the integration settings. The theme options and
32
+ content taxonomy were inlined here as well, and no script read them.
33
+ {%- endcomment -%}
36
34
  <script nonce="{{ page.csp_nonce }}">
37
- window.DatalogTheme = {{ site.theme_options | jsonify }};
38
35
  window.DatalogIntegrations = {{ site.integrations | jsonify }};
39
- window.DatalogContent = {{ site.content | jsonify }};
40
- </script>
41
- <script nonce="{{ page.csp_nonce }}">
42
- document.addEventListener('DOMContentLoaded', function () {
43
- if (window.Prism && window.Prism.plugins && window.Prism.plugins.NormalizeWhitespace) {
44
- Prism.plugins.NormalizeWhitespace.setDefaults({
45
- 'remove-trailing': true,
46
- 'remove-indent': true,
47
- 'left-trim': true,
48
- 'right-trim': true
49
- });
50
- }
51
- });
52
36
  </script>
53
37
  {%- endif -%}
@@ -10,17 +10,14 @@ Builds a list of related posts based on overlapping tags or categories.
10
10
  Falls back to a friendly message when no related content is available.
11
11
  {%- endcomment -%}
12
12
  {%- assign related_limit = include.limit | default: 3 -%}
13
- {%- assign source_posts = site.collections.posts.docs | default: site.posts | default: '' | split: '' -%}
14
- {%- assign layout_filtered = source_posts | where: 'layout', 'post' -%}
15
- {%- assign collection_filtered = source_posts | where: 'collection', 'posts' -%}
16
- {%- assign candidate_posts = layout_filtered | concat: collection_filtered -%}
17
- {%- assign sorted_candidates = candidate_posts | sort: 'date' | reverse -%}
13
+ {%- comment -%} site.posts is already newest first. {%- endcomment -%}
18
14
  {%- assign rendered_count = 0 -%}
19
15
  <section class="post-related" aria-labelledby="post-related-heading">
20
16
  <h2 id="post-related-heading">Related posts</h2>
21
17
  <ul class="post-related-list">
22
- {%- for candidate in sorted_candidates -%}
23
- {%- if candidate.url != include.page.url and rendered_count < related_limit -%}
18
+ {%- for candidate in site.posts -%}
19
+ {%- if rendered_count >= related_limit -%}{%- break -%}{%- endif -%}
20
+ {%- if candidate.url != include.page.url -%}
24
21
  {%- assign overlap = 0 -%}
25
22
  {%- if include.page.tags and candidate.tags -%}
26
23
  {%- for tag in include.page.tags -%}
@@ -2,6 +2,7 @@
2
2
  "generated_at": "{{ 'now' | date_to_xmlschema }}",
3
3
  "documents": [
4
4
  {% assign known_langs = 'python,r,sql,julia,javascript,scala,go,java,typescript,html,css' | split: ',' %}
5
+ {% assign no_code = '' | split: '' %}
5
6
  {% assign searchable = '' | split: '' %}
6
7
  {% for post in site.posts %}
7
8
  {% unless post.exclude_from_search %}
@@ -76,37 +77,19 @@
76
77
  {% capture doc_type %}
77
78
  {% if doc.collection == 'posts' %}post{% elsif doc.collection == 'portfolio' %}project{% elsif doc.collection == 'datasets' %}dataset{% elsif doc.layout == 'research' %}research{% elsif doc.layout == 'project' %}project{% elsif doc.layout == 'post' %}post{% else %}{{ doc.collection | default: doc.layout | default: 'page' }}{% endif %}
78
79
  {% endcapture %}
79
- {% assign normalized_title = doc.title | default: doc.name | normalize_search %}
80
- {% assign normalized_summary = summary | normalize_search %}
81
- {% assign normalized_content = content_text | normalize_search %}
82
- {% assign normalized_tags = tags | normalize_search_array %}
83
- {% assign normalized_languages = languages | normalize_search_array %}
84
- {% assign normalized_difficulty = doc.difficulty | default: doc.level | normalize_search | default: '' %}
80
+ {%- comment -%}
81
+ Each text field is written once. The search engine normalizes it in the browser with the
82
+ function it applies to queries; a precomputed normalized copy doubled the size of the file.
83
+ The code blocks come from the page's source, collected before rendering by
84
+ _plugins/search_code_blocks.rb: by the time this renders, a post's content is already HTML.
85
+ {%- endcomment %}
85
86
  {
86
87
  "title": {{ doc.title | default: doc.name | jsonify }},
87
88
  "url": {{ doc.url | relative_url | jsonify }},
88
89
  "type": {{ doc_type | strip | jsonify }},
89
90
  "summary": {{ summary | strip | jsonify }},
90
91
  "content": {{ content_text | jsonify }},
91
- "code": [
92
- {% assign raw_blocks = raw_content | split: '```' %}
93
- {% assign code_counter = 0 %}
94
- {% for block in raw_blocks %}
95
- {% assign block_index = forloop.index0 | modulo: 2 %}
96
- {% if block_index == 1 %}
97
- {% assign lines = block | split: '\n' %}
98
- {% assign first_line = lines[0] | default: '' %}
99
- {% assign language_hint = first_line | strip | downcase | default: 'text' %}
100
- {% assign code_body = block | remove_first: first_line | strip %}
101
- {% if code_counter > 0 %},{% endif %}
102
- {
103
- "language": {{ language_hint | jsonify }},
104
- "code": {{ code_body | jsonify }}
105
- }
106
- {% assign code_counter = code_counter | plus: 1 %}
107
- {% endif %}
108
- {% endfor %}
109
- ],
92
+ "code": {{ doc.search_code | default: no_code | jsonify }},
110
93
  "math": [
111
94
  {% assign math_counter = 0 %}
112
95
  {% for piece in inline_math_segments %}
@@ -137,15 +120,7 @@
137
120
  "difficulty": {{ doc.difficulty | default: doc.level | default: '' | jsonify }},
138
121
  "topics": {{ doc.topics | default: tags | uniq | jsonify }},
139
122
  "date": {% if doc.date %}{{ doc.date | date_to_xmlschema | jsonify }}{% else %}null{% endif %},
140
- "reading_time": {{ doc.reading_time | default: doc.time | default: '' | jsonify }},
141
- "normalized": {
142
- "title": {{ normalized_title | jsonify }},
143
- "summary": {{ normalized_summary | jsonify }},
144
- "content": {{ normalized_content | jsonify }},
145
- "tags": {{ normalized_tags | uniq | jsonify }},
146
- "languages": {{ normalized_languages | uniq | jsonify }},
147
- "difficulty": {{ normalized_difficulty | jsonify }}
148
- }
123
+ "reading_time": {{ doc.reading_time | default: doc.time | default: '' | jsonify }}
149
124
  {% assign search_extensions = doc.datalog_search_extensions %}
150
125
  {% if search_extensions and search_extensions != empty %}
151
126
  ,"extensions": {{ search_extensions | jsonify }}
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  layout: default
3
+ show_title: false # this layout renders the page's <h1>
3
4
  ---
4
5
  {% assign localization_config = site.theme_options.localization %}
5
6
  {% assign date_format = page.date_format | default: localization_config.date_format | default: 'long' %}
@@ -8,25 +8,33 @@
8
8
  <head>
9
9
  {% include head.html %}
10
10
  <meta name="twitter:creator" content="{{ site.author.twitter | default: site.social.twitter }}" />
11
- {% if page.tags and page.tags.size > 0 %}
12
- <meta name="keywords" content="{% include helpers/array-to-sentence.html items=page.tags conjunction='and' %}" />
13
- {% endif %}
14
- <meta name="format-detection" content="telephone=no" />
15
- <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
16
11
  {% include meta/schema.html page=page %}
17
- {% include meta/scripts-loader.html location='head' %}
18
12
  </head>
19
13
  {% capture body_classes %}
20
14
  site-body{% if page.layout %} layout-{{ page.layout | replace: ' ', '-' }}{% endif %}{% if page.page_class %} page-{{ page.page_class | replace: ' ', '-' | downcase }}{% endif %}{% if page.classes %} {{ page.classes | join: ' ' }}{% endif %}{% if site.features.dark_mode_toggle %} has-dark-toggle{% endif %}{% if html_dir == 'rtl' %} rtl{% endif %}
21
15
  {% endcapture %}
22
16
  <body class="{{ body_classes | strip }}" data-theme="{{ page.theme | default: 'system' }}">
23
- {% include skip-link.html %}
17
+ {%- comment -%}
18
+ Applies the saved or system color scheme before anything is drawn. core/dark-mode.js
19
+ applies the same rule, but only once the script bundles have loaded, so readers who
20
+ chose dark saw the light theme first; keep the two in step.
21
+ {%- endcomment %}
22
+ <script nonce="{{ page.csp_nonce }}">
23
+ try {
24
+ var mode = localStorage.getItem("datalog-color-mode");
25
+ if (mode === "dark" || (mode !== "light" && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
26
+ document.body.classList.add("dark-mode");
27
+ }
28
+ } catch (error) {}
29
+ </script>
30
+ {%- comment -%} The skip link and the footer take nothing from the page, so each is rendered once per build (the skip link once per language). {%- endcomment -%}
31
+ {% include_cached skip-link.html lang=page.lang %}
24
32
  {% include header.html %}
25
33
  <main id="main-content" class="site-main container" role="main" tabindex="-1" aria-describedby="{{ math_status_id }}">
26
34
  {% include components/math-fallback.html expressions=page.math_expressions %}
27
35
  {% include layouts/default/article.html page=page content=content date_format=date_format %}
28
36
  </main>
29
- {% include footer.html %}
37
+ {% include_cached footer.html %}
30
38
  <div class="visually-hidden" aria-live="polite" id="{{ math_status_id }}">{% t 'global.math_loading' %}</div>
31
39
  {% include meta/scripts-loader.html location='body' %}
32
40
  </body>
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  layout: default
3
+ show_title: false # this layout renders the page's <h1>
3
4
  ---
4
5
 
5
6
  {%- comment -%}
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  layout: default
3
+ show_title: false # this layout renders the page's <h1>
3
4
  ---
4
5
 
5
6
  <div class="package-docs">
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  layout: default
3
+ show_title: false # this layout renders the page's <h1>
3
4
  ---
4
5
  <article class="portfolio-item">
5
6
  <header class="portfolio-header">