datalog-theme 0.6.1 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +175 -0
- data/CITATION.cff +2 -2
- data/README.md +26 -19
- data/_data/cdn-integrity.yml +0 -30
- data/_includes/analytics/dashboard.html +3 -1
- data/_includes/components/api-function.html +20 -1
- data/_includes/components/author-bio.html +2 -2
- data/_includes/components/enhanced-code-block.html +1 -1
- data/_includes/components/hero.html +19 -1
- data/_includes/csp-meta.html +115 -11
- data/_includes/footer.html +19 -20
- data/_includes/head.html +61 -29
- data/_includes/header/navigation.html +12 -15
- data/_includes/header.html +29 -20
- data/_includes/layouts/default/article.html +5 -1
- data/_includes/meta/math-config.html +27 -1
- data/_includes/meta/schema.html +5 -2
- data/_includes/meta/scripts-loader.html +16 -31
- data/_includes/post/related-posts.html +4 -7
- data/_includes/scripts.html +1 -9
- data/_includes/search/index-data.json +131 -0
- data/_includes/search/page.html +136 -0
- data/_layouts/dataset.html +1 -0
- data/_layouts/default.html +16 -8
- data/_layouts/home.html +6 -0
- data/_layouts/notebook.html +1 -0
- data/_layouts/package.html +1 -0
- data/_layouts/portfolio.html +1 -0
- data/_layouts/post.html +20 -5
- data/_layouts/project.html +2 -1
- data/_plugins/analytics_dashboard.rb +9 -3
- data/_plugins/config_validator.rb +12 -7
- data/_plugins/csp_generator.rb +18 -28
- data/_plugins/datalog_bibliography.rb +9 -7
- data/_plugins/datalog_comments.rb +8 -5
- data/_plugins/datalog_slides.rb +9 -8
- data/_plugins/i18n.rb +7 -8
- data/_plugins/image_optimizer.rb +17 -6
- data/_plugins/math_preprocessor.rb +36 -3
- data/_plugins/notebook_converter.rb +23 -43
- data/_plugins/plugin_loader.rb +3 -1
- data/_plugins/publications_generator.rb +8 -2
- data/_plugins/rouge_highlight_filter.rb +42 -0
- data/_plugins/search_code_blocks.rb +30 -0
- data/_plugins/search_normalizer.rb +15 -53
- data/_plugins/search_pages.rb +72 -0
- data/_sass/_academic-dashboard.scss +262 -0
- data/_sass/_base.scss +19 -1
- data/_sass/_components.scss +77 -1144
- data/_sass/_features.scss +17 -0
- data/_sass/_font-fallbacks.scss +43 -0
- data/_sass/_header.scss +10 -0
- data/_sass/_layout.scss +18 -12
- data/_sass/_mathematical.scss +1 -1
- data/_sass/_notebooks.scss +322 -0
- data/_sass/_open-science-badges.scss +56 -0
- data/_sass/{_phase1-enhancements.scss → _post-components.scss} +46 -1
- data/_sass/_search-page.scss +530 -0
- data/_sass/_search.scss +47 -1
- data/_sass/_syntax-highlighting.scss +212 -97
- data/_sass/_theme.scss +42 -18
- data/_sass/_typography.scss +14 -0
- data/_sass/_variables.scss +7 -4
- data/assets/css/main.scss +14 -0
- data/assets/img/hero-detail-640.webp +0 -0
- data/assets/img/hero-detail.webp +0 -0
- data/assets/img/social-card.png +0 -0
- data/assets/js/dist/academic.js +1 -0
- data/assets/js/dist/analytics-dashboard.js +1 -0
- data/assets/js/dist/chunks/chunk-225H5YXE.js +1 -0
- data/assets/js/dist/core.js +1 -0
- data/assets/js/dist/loader.js +1 -0
- data/assets/js/dist/math.js +1 -0
- data/assets/js/dist/notebook.js +1 -0
- data/assets/js/dist/search.js +1 -0
- data/assets/js/dist/visualizations.js +11 -0
- data/assets/js/loader.js +3 -1
- data/datalog-theme.gemspec +43 -22
- data/lib/datalog/cli.rb +53 -15
- data/lib/datalog/plugin_system/dependency_resolver.rb +0 -2
- data/lib/datalog/theme/package.rb +57 -0
- data/lib/datalog/theme/repository_checkout.rb +90 -0
- data/lib/datalog/theme/version.rb +1 -1
- data/lib/datalog/warning_filter.rb +5 -11
- data/lib/datalog-theme.rb +21 -0
- metadata +75 -146
- data/_data/academic.yml +0 -217
- data/_data/config/author.yml +0 -121
- data/_data/config/features.yml +0 -262
- data/_data/config/site.yml +0 -42
- data/_data/config/theme.yml +0 -181
- data/_data/datasets.yml +0 -28
- data/_data/js_meta.json +0 -371
- data/_data/navigation.yml +0 -145
- data/_data/projects.yml +0 -41
- data/_data/publications.yml +0 -28
- data/_data/social.yml +0 -73
- data/_data/visualizations.yml +0 -51
- data/_includes/components/advanced-search.html +0 -682
- data/_includes/components/bookmark-system.html +0 -96
- data/_includes/components/comments.html +0 -244
- data/_includes/components/content-recommendations.html +0 -228
- data/_includes/components/email-preferences.html +0 -200
- data/_includes/components/enhanced-metadata.html +0 -228
- data/_includes/components/language-switcher.html +0 -396
- data/_includes/components/navigation-enhancements.html +0 -454
- data/_includes/components/newsletter-signup.html +0 -178
- data/_includes/components/popular-posts.html +0 -233
- data/_includes/components/reading-progress.html +0 -133
- data/_includes/components/reading-time.html +0 -121
- data/_includes/components/series-navigation.html +0 -124
- data/_includes/components/social-proof.html +0 -34
- data/_includes/components/user-preferences.html +0 -566
- data/_layouts/archive.html +0 -282
- data/_layouts/post-sidebar.html +0 -183
- data/_sass/_phase3-enhancements.scss +0 -874
- data/_sass/_phase4-enhancements.scss +0 -1214
- data/_sass/_phase5-enhancements.scss +0 -414
- data/assets/img/20220607123041_detail.001.png +0 -0
- data/assets/js/academic.js +0 -262
- data/assets/js/analytics-dashboard.js +0 -382
- data/assets/js/core/dark-mode.js +0 -79
- data/assets/js/core/github-cards.js +0 -123
- data/assets/js/core/language-filter.js +0 -69
- data/assets/js/core/navigation.js +0 -184
- data/assets/js/core/scroll-progress.js +0 -45
- data/assets/js/core/search-hotkeys.js +0 -62
- data/assets/js/core/skip-links.js +0 -62
- data/assets/js/main.js +0 -23
- data/assets/js/math.js +0 -818
- data/assets/js/notebook.js +0 -158
- data/assets/js/search/analytics.js +0 -91
- data/assets/js/search/app.js +0 -271
- data/assets/js/search/autocomplete.js +0 -120
- data/assets/js/search/engine.js +0 -260
- data/assets/js/search/filters.js +0 -38
- data/assets/js/search/render.js +0 -217
- data/assets/js/search/utils.js +0 -99
- data/assets/js/search.js +0 -354
- data/assets/js/visualizations.js +0 -816
- data/assets/publications/datalog-publications.bib +0 -8
- data/assets/publications/datalog-publications.ris +0 -9
- data/assets/publications/publications.bib +0 -30
- data/assets/templates/diogo-ribeiro-cv.md +0 -31
- data/assets/templates/diogo-ribeiro-cv.tex +0 -32
- data/lib/datalog/theme/theme.rb +0 -18
|
@@ -2,25 +2,18 @@
|
|
|
2
2
|
---
|
|
3
3
|
required:
|
|
4
4
|
- location
|
|
5
|
-
optional:
|
|
6
|
-
- syntax_config
|
|
7
5
|
---
|
|
8
|
-
Outputs
|
|
9
|
-
|
|
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
|
-
{%-
|
|
12
|
-
{%-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{%-
|
|
18
|
-
{%- unless component == 'core' -%}
|
|
19
|
-
<script defer src="{{ (prism_cdn | append: '/components/prism-' | append: component | append: '.min.js') | add_sri }}"></script>
|
|
20
|
-
{%- endunless -%}
|
|
21
|
-
{%- endfor -%}
|
|
22
|
-
<script defer src="{{ (prism_cdn | append: '/plugins/normalize-whitespace/prism-normalize-whitespace.min.js') | add_sri }}"></script>
|
|
23
|
-
{%- 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' -%}
|
|
24
17
|
{%- assign publications = site.data.publications | default: {} -%}
|
|
25
18
|
<script nonce="{{ page.csp_nonce }}">
|
|
26
19
|
window.DatalogAcademic = {{ site.data.academic | jsonify }};
|
|
@@ -31,22 +24,14 @@ Pass location="head" or location="body" to control which block renders.
|
|
|
31
24
|
"yearly_totals": {{ publications.yearly_totals | jsonify }}
|
|
32
25
|
};
|
|
33
26
|
</script>
|
|
27
|
+
{%- endif -%}
|
|
28
|
+
{%- comment -%} Not include_cached: scripts.html reads the page's math and notebook settings and CSP nonce. {%- endcomment -%}
|
|
34
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 -%}
|
|
35
34
|
<script nonce="{{ page.csp_nonce }}">
|
|
36
|
-
window.DatalogTheme = {{ site.theme_options | jsonify }};
|
|
37
35
|
window.DatalogIntegrations = {{ site.integrations | jsonify }};
|
|
38
|
-
window.DatalogContent = {{ site.content | jsonify }};
|
|
39
|
-
</script>
|
|
40
|
-
<script nonce="{{ page.csp_nonce }}">
|
|
41
|
-
document.addEventListener('DOMContentLoaded', function () {
|
|
42
|
-
if (window.Prism && window.Prism.plugins && window.Prism.plugins.NormalizeWhitespace) {
|
|
43
|
-
Prism.plugins.NormalizeWhitespace.setDefaults({
|
|
44
|
-
'remove-trailing': true,
|
|
45
|
-
'remove-indent': true,
|
|
46
|
-
'left-trim': true,
|
|
47
|
-
'right-trim': true
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
36
|
</script>
|
|
52
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
|
-
{%-
|
|
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
|
|
23
|
-
{%- if
|
|
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 -%}
|
data/_includes/scripts.html
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
{%
|
|
2
|
-
{% assign math_engine = page.math_engine | default: math_settings.engine | default: 'mathjax' %}
|
|
3
|
-
{% assign math_enabled = page.mathjax %}
|
|
4
|
-
{% if math_enabled == nil %}
|
|
5
|
-
{% assign math_enabled = page.math %}
|
|
6
|
-
{% endif %}
|
|
7
|
-
{% if math_enabled == nil %}
|
|
8
|
-
{% assign math_enabled = math_settings.render_on_load | default: site.features.mathjax %}
|
|
9
|
-
{% endif %}
|
|
1
|
+
{% include meta/math-config.html page=page %}
|
|
10
2
|
{% assign js_manifest = site.data.js_manifest %}
|
|
11
3
|
|
|
12
4
|
{% if site.features.search %}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generated_at": "{{ 'now' | date_to_xmlschema }}",
|
|
3
|
+
"documents": [
|
|
4
|
+
{% assign known_langs = 'python,r,sql,julia,javascript,scala,go,java,typescript,html,css' | split: ',' %}
|
|
5
|
+
{% assign no_code = '' | split: '' %}
|
|
6
|
+
{% assign searchable = '' | split: '' %}
|
|
7
|
+
{% for post in site.posts %}
|
|
8
|
+
{% unless post.exclude_from_search %}
|
|
9
|
+
{% assign searchable = searchable | push: post %}
|
|
10
|
+
{% endunless %}
|
|
11
|
+
{% endfor %}
|
|
12
|
+
{% for page in site.pages %}
|
|
13
|
+
{% unless page.exclude_from_search or page.layout == nil or page.url contains 'search.json' %}
|
|
14
|
+
{% assign searchable = searchable | push: page %}
|
|
15
|
+
{% endunless %}
|
|
16
|
+
{% endfor %}
|
|
17
|
+
{% if site.portfolio %}
|
|
18
|
+
{% for project in site.portfolio %}
|
|
19
|
+
{% unless project.exclude_from_search %}
|
|
20
|
+
{% assign searchable = searchable | push: project %}
|
|
21
|
+
{% endunless %}
|
|
22
|
+
{% endfor %}
|
|
23
|
+
{% endif %}
|
|
24
|
+
{% if site.datasets %}
|
|
25
|
+
{% for dataset in site.datasets %}
|
|
26
|
+
{% unless dataset.exclude_from_search %}
|
|
27
|
+
{% assign searchable = searchable | push: dataset %}
|
|
28
|
+
{% endunless %}
|
|
29
|
+
{% endfor %}
|
|
30
|
+
{% endif %}
|
|
31
|
+
{% for doc in searchable %}
|
|
32
|
+
{% assign raw_tags = doc.tags | default: doc.topics %}
|
|
33
|
+
{% if raw_tags %}
|
|
34
|
+
{% assign tags = raw_tags %}
|
|
35
|
+
{% else %}
|
|
36
|
+
{% assign tags = '' | split: '' %}
|
|
37
|
+
{% endif %}
|
|
38
|
+
{% assign languages = '' | split: '' %}
|
|
39
|
+
{% if doc.languages %}
|
|
40
|
+
{% for lang in doc.languages %}
|
|
41
|
+
{% assign lang_normalized = lang | downcase %}
|
|
42
|
+
{% if known_langs contains lang_normalized %}
|
|
43
|
+
{% unless languages contains lang_normalized %}
|
|
44
|
+
{% assign languages = languages | push: lang_normalized %}
|
|
45
|
+
{% endunless %}
|
|
46
|
+
{% endif %}
|
|
47
|
+
{% endfor %}
|
|
48
|
+
{% elsif doc.language %}
|
|
49
|
+
{% assign lang_normalized = doc.language | downcase %}
|
|
50
|
+
{% if known_langs contains lang_normalized %}
|
|
51
|
+
{% assign languages = languages | push: lang_normalized %}
|
|
52
|
+
{% endif %}
|
|
53
|
+
{% elsif doc.tools %}
|
|
54
|
+
{% for tool in doc.tools %}
|
|
55
|
+
{% assign lang_normalized = tool | downcase %}
|
|
56
|
+
{% if known_langs contains lang_normalized %}
|
|
57
|
+
{% unless languages contains lang_normalized %}
|
|
58
|
+
{% assign languages = languages | push: lang_normalized %}
|
|
59
|
+
{% endunless %}
|
|
60
|
+
{% endif %}
|
|
61
|
+
{% endfor %}
|
|
62
|
+
{% elsif tags %}
|
|
63
|
+
{% for tag in tags %}
|
|
64
|
+
{% assign lang_normalized = tag | downcase %}
|
|
65
|
+
{% if known_langs contains lang_normalized %}
|
|
66
|
+
{% unless languages contains lang_normalized %}
|
|
67
|
+
{% assign languages = languages | push: lang_normalized %}
|
|
68
|
+
{% endunless %}
|
|
69
|
+
{% endif %}
|
|
70
|
+
{% endfor %}
|
|
71
|
+
{% endif %}
|
|
72
|
+
{% assign raw_content = doc.content | default: '' %}
|
|
73
|
+
{% assign content_text = raw_content | markdownify | strip_html | normalize_whitespace %}
|
|
74
|
+
{% assign summary = doc.summary | default: doc.excerpt | default: content_text | truncate: 280, '' %}
|
|
75
|
+
{% assign inline_math_segments = raw_content | split: '$' %}
|
|
76
|
+
{% assign display_math_segments = raw_content | split: '$$' %}
|
|
77
|
+
{% capture doc_type %}
|
|
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 %}
|
|
79
|
+
{% endcapture %}
|
|
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 %}
|
|
86
|
+
{
|
|
87
|
+
"title": {{ doc.title | default: doc.name | jsonify }},
|
|
88
|
+
"url": {{ doc.url | relative_url | jsonify }},
|
|
89
|
+
"type": {{ doc_type | strip | jsonify }},
|
|
90
|
+
"summary": {{ summary | strip | jsonify }},
|
|
91
|
+
"content": {{ content_text | jsonify }},
|
|
92
|
+
"code": {{ doc.search_code | default: no_code | jsonify }},
|
|
93
|
+
"math": [
|
|
94
|
+
{% assign math_counter = 0 %}
|
|
95
|
+
{% for piece in inline_math_segments %}
|
|
96
|
+
{% assign piece_index = forloop.index0 | modulo: 2 %}
|
|
97
|
+
{% if piece_index == 1 %}
|
|
98
|
+
{% assign cleaned = piece | strip %}
|
|
99
|
+
{% if cleaned != '' %}
|
|
100
|
+
{% if math_counter > 0 %},{% endif %}
|
|
101
|
+
{{ cleaned | jsonify }}
|
|
102
|
+
{% assign math_counter = math_counter | plus: 1 %}
|
|
103
|
+
{% endif %}
|
|
104
|
+
{% endif %}
|
|
105
|
+
{% endfor %}
|
|
106
|
+
{% for piece in display_math_segments %}
|
|
107
|
+
{% assign piece_index = forloop.index0 | modulo: 2 %}
|
|
108
|
+
{% if piece_index == 1 %}
|
|
109
|
+
{% assign cleaned = piece | strip %}
|
|
110
|
+
{% if cleaned != '' %}
|
|
111
|
+
{% if math_counter > 0 %},{% endif %}
|
|
112
|
+
{{ cleaned | jsonify }}
|
|
113
|
+
{% assign math_counter = math_counter | plus: 1 %}
|
|
114
|
+
{% endif %}
|
|
115
|
+
{% endif %}
|
|
116
|
+
{% endfor %}
|
|
117
|
+
],
|
|
118
|
+
"tags": {{ tags | uniq | jsonify }},
|
|
119
|
+
"languages": {{ languages | uniq | jsonify }},
|
|
120
|
+
"difficulty": {{ doc.difficulty | default: doc.level | default: '' | jsonify }},
|
|
121
|
+
"topics": {{ doc.topics | default: tags | uniq | jsonify }},
|
|
122
|
+
"date": {% if doc.date %}{{ doc.date | date_to_xmlschema | jsonify }}{% else %}null{% endif %},
|
|
123
|
+
"reading_time": {{ doc.reading_time | default: doc.time | default: '' | jsonify }}
|
|
124
|
+
{% assign search_extensions = doc.datalog_search_extensions %}
|
|
125
|
+
{% if search_extensions and search_extensions != empty %}
|
|
126
|
+
,"extensions": {{ search_extensions | jsonify }}
|
|
127
|
+
{% endif %}
|
|
128
|
+
}{% unless forloop.last %},{% endunless %}
|
|
129
|
+
{% endfor %}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<div class="search-app" data-search-app data-search-index="{{ '/search.json' | relative_url }}">
|
|
2
|
+
<div class="search-app__panel">
|
|
3
|
+
<form class="search-app__form" data-search-form novalidate role="search" aria-label="Technical content search">
|
|
4
|
+
<div class="search-app__query">
|
|
5
|
+
<label class="search-app__label" for="advanced-search-input">Query</label>
|
|
6
|
+
<div class="search-app__input-group">
|
|
7
|
+
<input
|
|
8
|
+
id="advanced-search-input"
|
|
9
|
+
class="search-app__input"
|
|
10
|
+
type="search"
|
|
11
|
+
name="q"
|
|
12
|
+
placeholder="Search code, LaTeX, datasets, and more"
|
|
13
|
+
autocomplete="off"
|
|
14
|
+
spellcheck="false"
|
|
15
|
+
data-search-input
|
|
16
|
+
aria-describedby="search-shortcuts"
|
|
17
|
+
aria-activedescendant=""
|
|
18
|
+
/>
|
|
19
|
+
<button type="submit" class="search-app__submit">Search</button>
|
|
20
|
+
</div>
|
|
21
|
+
<p id="search-shortcuts" class="search-app__shortcuts" data-search-shortcuts>
|
|
22
|
+
Keyboard shortcuts: <kbd>/</kbd> or <kbd>Ctrl</kbd> + <kbd>K</kbd> to focus, <kbd>Enter</kbd> to run, <kbd>Esc</kbd> to clear.
|
|
23
|
+
</p>
|
|
24
|
+
<div class="search-app__autocomplete" data-search-autocomplete role="listbox" aria-label="Search suggestions"></div>
|
|
25
|
+
</div>
|
|
26
|
+
<fieldset class="search-app__filters" data-search-filters>
|
|
27
|
+
<legend>Advanced filters</legend>
|
|
28
|
+
<div class="search-app__filters-grid">
|
|
29
|
+
<label class="search-filter">
|
|
30
|
+
<span class="search-filter__label">Content type</span>
|
|
31
|
+
<select data-filter-type>
|
|
32
|
+
<option value="">All content</option>
|
|
33
|
+
<option value="post">Tutorials & blog posts</option>
|
|
34
|
+
<option value="research">Research outputs</option>
|
|
35
|
+
<option value="project">Projects & portfolio</option>
|
|
36
|
+
<option value="dataset">Datasets</option>
|
|
37
|
+
<option value="page">Pages</option>
|
|
38
|
+
</select>
|
|
39
|
+
</label>
|
|
40
|
+
<label class="search-filter">
|
|
41
|
+
<span class="search-filter__label">Programming language</span>
|
|
42
|
+
<select data-filter-language>
|
|
43
|
+
<option value="">Any language</option>
|
|
44
|
+
<option value="python">Python</option>
|
|
45
|
+
<option value="r">R</option>
|
|
46
|
+
<option value="sql">SQL</option>
|
|
47
|
+
<option value="julia">Julia</option>
|
|
48
|
+
<option value="javascript">JavaScript</option>
|
|
49
|
+
</select>
|
|
50
|
+
</label>
|
|
51
|
+
<label class="search-filter">
|
|
52
|
+
<span class="search-filter__label">Difficulty</span>
|
|
53
|
+
<select data-filter-difficulty>
|
|
54
|
+
<option value="">All levels</option>
|
|
55
|
+
<option value="beginner">Beginner</option>
|
|
56
|
+
<option value="intermediate">Intermediate</option>
|
|
57
|
+
<option value="advanced">Advanced</option>
|
|
58
|
+
</select>
|
|
59
|
+
</label>
|
|
60
|
+
<div class="search-filter">
|
|
61
|
+
<span class="search-filter__label">Topics</span>
|
|
62
|
+
<div class="search-filter__tags" data-filter-tags role="group" aria-label="Filter by topic tags"></div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</fieldset>
|
|
66
|
+
</form>
|
|
67
|
+
<aside class="search-app__analytics" data-search-analytics aria-live="polite">
|
|
68
|
+
<h2 class="search-app__analytics-title">Popular technical queries</h2>
|
|
69
|
+
<p class="search-app__analytics-empty">Search activity will appear here once queries are made.</p>
|
|
70
|
+
<ol class="search-app__analytics-list"></ol>
|
|
71
|
+
</aside>
|
|
72
|
+
</div>
|
|
73
|
+
<section class="search-app__results" aria-live="polite" data-search-region>
|
|
74
|
+
<header class="search-app__results-header">
|
|
75
|
+
<h2>Results</h2>
|
|
76
|
+
<p class="search-app__results-meta" data-search-meta>Enter a query to begin.</p>
|
|
77
|
+
<div class="search-app__loading" data-search-loading hidden>
|
|
78
|
+
<span class="search-app__spinner" aria-hidden="true"></span>
|
|
79
|
+
<span class="search-app__loading-text">Searching…</span>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="visually-hidden" data-search-announcements>
|
|
82
|
+
<p aria-live="polite" data-search-live="count" hidden></p>
|
|
83
|
+
<p aria-live="polite" data-search-live="selection" hidden></p>
|
|
84
|
+
<p aria-live="polite" data-search-live="status" hidden></p>
|
|
85
|
+
</div>
|
|
86
|
+
</header>
|
|
87
|
+
<ol class="search-app__results-list" data-search-results role="listbox" aria-label="Search results"></ol>
|
|
88
|
+
<template id="search-result-template">
|
|
89
|
+
<li class="search-result" role="option" data-result-item>
|
|
90
|
+
<article class="search-result__card">
|
|
91
|
+
<header class="search-result__header">
|
|
92
|
+
<p class="search-result__meta">
|
|
93
|
+
<span class="search-result__type" data-result-type></span>
|
|
94
|
+
<span class="search-result__difficulty" data-result-difficulty></span>
|
|
95
|
+
<span class="search-result__language" data-result-languages></span>
|
|
96
|
+
</p>
|
|
97
|
+
<h3 class="search-result__title"><a data-result-link></a></h3>
|
|
98
|
+
<p class="search-result__summary" data-result-summary></p>
|
|
99
|
+
</header>
|
|
100
|
+
<div class="search-result__body">
|
|
101
|
+
<pre class="search-result__code language-none line-numbers" data-result-code><code></code></pre>
|
|
102
|
+
<div class="search-result__math" data-result-math hidden>
|
|
103
|
+
<div class="search-result__math-preview" data-result-math-preview aria-hidden="true"></div>
|
|
104
|
+
<pre class="search-result__math-source"><code data-result-math-code></code></pre>
|
|
105
|
+
<button type="button" class="search-result__math-copy" data-result-math-copy>
|
|
106
|
+
<span aria-hidden="true">⧉</span>
|
|
107
|
+
<span class="visually-hidden">Copy LaTeX</span>
|
|
108
|
+
</button>
|
|
109
|
+
</div>
|
|
110
|
+
<p class="search-result__excerpt" data-result-excerpt></p>
|
|
111
|
+
</div>
|
|
112
|
+
<footer class="search-result__footer">
|
|
113
|
+
<ul class="search-result__tags" data-result-tags></ul>
|
|
114
|
+
<time class="search-result__date" data-result-date></time>
|
|
115
|
+
</footer>
|
|
116
|
+
</article>
|
|
117
|
+
</li>
|
|
118
|
+
</template>
|
|
119
|
+
<div class="search-app__no-results" data-search-empty hidden>
|
|
120
|
+
<div class="search-app__no-results-illustration" aria-hidden="true">
|
|
121
|
+
<svg viewBox="0 0 120 80" role="presentation" focusable="false">
|
|
122
|
+
<g fill="none" stroke="currentColor" stroke-width="2">
|
|
123
|
+
<circle cx="40" cy="34" r="18" opacity="0.35" />
|
|
124
|
+
<circle cx="72" cy="46" r="14" opacity="0.2" />
|
|
125
|
+
<path d="M28 58h64" stroke-linecap="round" opacity="0.45" />
|
|
126
|
+
<path d="M48 33l8 9 16-18" stroke-linecap="round" stroke-linejoin="round" />
|
|
127
|
+
</g>
|
|
128
|
+
</svg>
|
|
129
|
+
</div>
|
|
130
|
+
<p>No matching entries were found. Try refining your filters or adjusting the query.</p>
|
|
131
|
+
</div>
|
|
132
|
+
</section>
|
|
133
|
+
</div>
|
|
134
|
+
<noscript>
|
|
135
|
+
<p class="search-app__noscript">JavaScript is required for the interactive search experience. Please enable JavaScript to explore technical content.</p>
|
|
136
|
+
</noscript>
|
data/_layouts/dataset.html
CHANGED
data/_layouts/default.html
CHANGED
|
@@ -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
|
-
{
|
|
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
|
-
{%
|
|
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>
|
data/_layouts/home.html
CHANGED
|
@@ -31,6 +31,11 @@ layout: default
|
|
|
31
31
|
</div>
|
|
32
32
|
</div>
|
|
33
33
|
</section>
|
|
34
|
+
{% comment %}
|
|
35
|
+
The portfolio collection is optional: a site that has not declared it gets
|
|
36
|
+
nil here, and sorting nil raises rather than rendering an empty section.
|
|
37
|
+
{% endcomment %}
|
|
38
|
+
{% if site.portfolio and site.portfolio.size > 0 %}
|
|
34
39
|
<section class="section section-portfolio">
|
|
35
40
|
<div class="container">
|
|
36
41
|
<h2 class="section-title">{% t 'home.featured_projects' %}</h2>
|
|
@@ -46,3 +51,4 @@ layout: default
|
|
|
46
51
|
</div>
|
|
47
52
|
</div>
|
|
48
53
|
</section>
|
|
54
|
+
{% endif %}
|
data/_layouts/notebook.html
CHANGED
data/_layouts/package.html
CHANGED
data/_layouts/portfolio.html
CHANGED
data/_layouts/post.html
CHANGED
|
@@ -272,7 +272,12 @@ schema_type: TechnicalArticle
|
|
|
272
272
|
const metaBar = document.createElement('div');
|
|
273
273
|
metaBar.className = 'code-block-meta';
|
|
274
274
|
|
|
275
|
-
|
|
275
|
+
// Rouge puts the language on the block's wrapper rather than on the code.
|
|
276
|
+
// With line numbers on, the code is a table whose first column holds the
|
|
277
|
+
// numbers, and the copy leaves them out.
|
|
278
|
+
const langHolder = codeBlock.matches('[class*="language-"]') ? codeBlock : codeBlock.closest('.highlighter-rouge');
|
|
279
|
+
const lang = langHolder && langHolder.getAttribute('class');
|
|
280
|
+
const source = codeBlock.querySelector('.rouge-code') || codeBlock;
|
|
276
281
|
if (lang) {
|
|
277
282
|
const m = lang.match(/language-([a-z0-9+#]+)/i);
|
|
278
283
|
if (m) {
|
|
@@ -291,7 +296,7 @@ schema_type: TechnicalArticle
|
|
|
291
296
|
|
|
292
297
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
293
298
|
button.addEventListener('click', function () {
|
|
294
|
-
navigator.clipboard.writeText(
|
|
299
|
+
navigator.clipboard.writeText(source.textContent).then(function () {
|
|
295
300
|
button.textContent = 'Copied!';
|
|
296
301
|
button.classList.add('is-copied');
|
|
297
302
|
setTimeout(function () {
|
|
@@ -311,9 +316,19 @@ schema_type: TechnicalArticle
|
|
|
311
316
|
wrapper.insertBefore(metaBar, pre);
|
|
312
317
|
});
|
|
313
318
|
|
|
314
|
-
// Make equation references focus their targets for accessibility.
|
|
315
|
-
|
|
316
|
-
|
|
319
|
+
// Make equation references focus their targets for accessibility. This runs
|
|
320
|
+
// on DOMContentLoaded, before MathJax has loaded, when window.MathJax is
|
|
321
|
+
// still the configuration object and has no startup.promise; reading it
|
|
322
|
+
// threw. The head's MathJax configuration announces typeset math instead.
|
|
323
|
+
const onMathReady = function (callback) {
|
|
324
|
+
if (document.body.classList.contains('math-ready') && document.querySelector('mjx-container')) {
|
|
325
|
+
callback();
|
|
326
|
+
} else {
|
|
327
|
+
document.addEventListener('datalog:math-ready', callback, { once: true });
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
if (window.MathJax) {
|
|
331
|
+
onMathReady(function () {
|
|
317
332
|
const equations = document.querySelectorAll('.post-content mjx-container[display="true"]');
|
|
318
333
|
let eqIndex = 1;
|
|
319
334
|
equations.forEach(function (container) {
|
data/_layouts/project.html
CHANGED
|
@@ -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 = localization_config.date_format | default: 'long' %}
|
|
@@ -30,7 +31,7 @@ layout: default
|
|
|
30
31
|
{% if page.github %}
|
|
31
32
|
<section class="project-case__github" aria-labelledby="github-heading">
|
|
32
33
|
<h2 id="github-heading">{% t 'project.header.github_repository' %}</h2>
|
|
33
|
-
<div class="project-case__github-card" data-github-repo="{{ page.github.repo }}" data-github-owner="{{ page.github.owner }}">
|
|
34
|
+
<div class="project-case__github-card" data-github-repo="{{ page.github.repo }}" data-github-owner="{{ page.github.owner }}" data-github-unavailable="{% t 'project.github.unavailable' %}">
|
|
34
35
|
<div class="project-case__github-meta">
|
|
35
36
|
<p class="project-case__github-name">
|
|
36
37
|
<a href="https://github.com/{{ page.github.owner }}/{{ page.github.repo }}" itemprop="url">{{ page.github.owner }}/{{ page.github.repo }}</a>
|
|
@@ -31,7 +31,10 @@ module Datalog
|
|
|
31
31
|
|
|
32
32
|
data = query_analytics(site)
|
|
33
33
|
data["fetched_at"] = Time.now.utc.iso8601
|
|
34
|
-
|
|
34
|
+
# Only a successful report is cached. A missing-configuration or error
|
|
35
|
+
# payload used to be kept for CACHE_TTL as well, so a site that had just
|
|
36
|
+
# set GA4_PROPERTY_ID went on reporting the old problem for a day.
|
|
37
|
+
write_cache(cache_path, data) if data["status"] == "ok"
|
|
35
38
|
data
|
|
36
39
|
rescue StandardError => e
|
|
37
40
|
Jekyll.logger.warn("Analytics", "Falling back to cached analytics data: #{e.message}")
|
|
@@ -43,7 +46,7 @@ module Datalog
|
|
|
43
46
|
end
|
|
44
47
|
|
|
45
48
|
def fresh?(payload)
|
|
46
|
-
return false unless payload.is_a?(Hash)
|
|
49
|
+
return false unless payload.is_a?(Hash) && payload["status"] == "ok"
|
|
47
50
|
|
|
48
51
|
fetched_at = payload["fetched_at"]
|
|
49
52
|
return false if fetched_at.to_s.empty?
|
|
@@ -95,7 +98,10 @@ module Datalog
|
|
|
95
98
|
results["status"] = "ok"
|
|
96
99
|
results
|
|
97
100
|
rescue LoadError => e
|
|
98
|
-
|
|
101
|
+
# The theme does not depend on googleauth: only a site with a GA4 property
|
|
102
|
+
# configured needs it, and adds it to its own Gemfile.
|
|
103
|
+
fallback_payload("missing_dependency",
|
|
104
|
+
"Add gem \"googleauth\" to the site's Gemfile to enable the GA4 integration: #{e.message}")
|
|
99
105
|
rescue StandardError => e
|
|
100
106
|
fallback_payload("error", e.message)
|
|
101
107
|
end
|
|
@@ -12,8 +12,9 @@ module Datalog
|
|
|
12
12
|
SCHEMA = {
|
|
13
13
|
title: { type: :string, required: true },
|
|
14
14
|
url: { type: :string, required: true, format: :url },
|
|
15
|
+
# `author: Jane Doe` is a common Jekyll setting; the hash adds an email and profile links.
|
|
15
16
|
author: {
|
|
16
|
-
type:
|
|
17
|
+
type: %i[string hash],
|
|
17
18
|
required: true,
|
|
18
19
|
schema: {
|
|
19
20
|
name: { type: :string, required: true },
|
|
@@ -68,12 +69,6 @@ module Datalog
|
|
|
68
69
|
engine: { type: :string, enum: %w[mathjax katex] },
|
|
69
70
|
enabled: { type: :boolean }
|
|
70
71
|
}
|
|
71
|
-
},
|
|
72
|
-
syntax_highlighting: {
|
|
73
|
-
type: :hash,
|
|
74
|
-
schema: {
|
|
75
|
-
cdn: { type: :string }
|
|
76
|
-
}
|
|
77
72
|
}
|
|
78
73
|
}
|
|
79
74
|
}
|
|
@@ -84,6 +79,10 @@ module Datalog
|
|
|
84
79
|
replacement: "theme_options.math.engine",
|
|
85
80
|
message: "'math_engine' has moved under theme_options.math.engine.",
|
|
86
81
|
auto_migrate: true
|
|
82
|
+
},
|
|
83
|
+
"theme_options.syntax_highlighting" => {
|
|
84
|
+
message: "Code is highlighted by Rouge when the site builds and the theme no longer loads Prism, " \
|
|
85
|
+
"so these settings have no effect. Remove them."
|
|
87
86
|
}
|
|
88
87
|
}.freeze
|
|
89
88
|
|
|
@@ -204,6 +203,8 @@ module Datalog
|
|
|
204
203
|
end
|
|
205
204
|
|
|
206
205
|
def type_valid?(value, expected_type)
|
|
206
|
+
return expected_type.any? { |type| type_valid?(value, type) } if expected_type.is_a?(Array)
|
|
207
|
+
|
|
207
208
|
case expected_type
|
|
208
209
|
when :string
|
|
209
210
|
value.is_a?(String)
|
|
@@ -222,6 +223,8 @@ module Datalog
|
|
|
222
223
|
|
|
223
224
|
def url_valid?(value)
|
|
224
225
|
return false unless value.is_a?(String)
|
|
226
|
+
# `url: ""` is what `jekyll new` writes, and what a site built locally keeps.
|
|
227
|
+
return true if value.strip.empty?
|
|
225
228
|
|
|
226
229
|
uri = URI.parse(value)
|
|
227
230
|
uri.is_a?(URI::HTTP) && !uri.host.nil?
|
|
@@ -311,6 +314,8 @@ module Datalog
|
|
|
311
314
|
end
|
|
312
315
|
|
|
313
316
|
def human_type(type)
|
|
317
|
+
return type.map { |entry| human_type(entry) }.join(" or ") if type.is_a?(Array)
|
|
318
|
+
|
|
314
319
|
case type
|
|
315
320
|
when :string then "a String"
|
|
316
321
|
when :integer then "an Integer"
|