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
@@ -0,0 +1,90 @@
1
+ {%- comment -%}
2
+ ---
3
+ optional: []
4
+ ---
5
+ The comments thread of the `api` provider (lib/datalog/plugins/comments.rb
6
+ renders it through the datalog_comments tag; assets/js/comments/thread.js
7
+ fills it): the discussion under an article, read from and written to the
8
+ site's own backend through the dynamic services (docs/dynamic-services.md).
9
+ Reads `page.datalog_comments` (the plugin's resolved settings: `replies`,
10
+ `moderation`, `endpoint`) and `page.url`, which keys the thread.
11
+ {%- endcomment -%}
12
+ {%- assign settings = page.datalog_comments -%}
13
+ {%- capture labels_json -%}
14
+ {
15
+ "loading": {{ 'comments.loading' | t | jsonify }},
16
+ "empty": {{ 'comments.empty' | t | jsonify }},
17
+ "retry": {{ 'comments.retry' | t | jsonify }},
18
+ "reply": {{ 'comments.reply' | t | jsonify }},
19
+ "replying_to": {{ 'comments.replying_to' | t | jsonify }},
20
+ "anonymous": {{ 'comments.anonymous' | t | jsonify }},
21
+ "pending": {{ 'comments.pending' | t | jsonify }},
22
+ "sending": {{ 'comments.sending' | t | jsonify }},
23
+ "posted": {{ 'comments.posted' | t | jsonify }},
24
+ "posted_pending": {{ 'comments.posted_pending' | t | jsonify }},
25
+ "name_required": {{ 'comments.name_required' | t | jsonify }},
26
+ "email_invalid": {{ 'comments.email_invalid' | t | jsonify }},
27
+ "url_invalid": {{ 'comments.url_invalid' | t | jsonify }},
28
+ "body_short": {{ 'comments.body_short' | t | jsonify }},
29
+ "errors": {
30
+ {%- assign kinds = "disabled,unsupported,version,timeout,network,malformed,unauthorized,forbidden,not_found,conflict,invalid,rate_limited,server,http,aborted,reference" | split: "," -%}
31
+ {%- for kind in kinds -%}
32
+ {%- assign kind_key = "dynamic_services.errors." | append: kind -%}
33
+ {{ kind | jsonify }}: {{ kind_key | t | jsonify }}{% unless forloop.last %},{% endunless %}
34
+ {%- endfor -%}
35
+ }
36
+ }
37
+ {%- endcapture -%}
38
+ <div class="comments-thread" id="comments" data-comments-thread data-comments-path="{{ page.url }}" data-comments-replies="{% if settings.replies == false %}false{% else %}true{% endif %}"{% if settings.endpoint and settings.endpoint != '' %} data-comments-endpoint="{{ settings.endpoint }}"{% endif %} data-state="idle">
39
+ <script type="application/json" nonce="{{ page.csp_nonce }}" data-comments-labels>{{ labels_json | strip }}</script>
40
+ <p class="comments-thread__status" data-comments-status role="status" aria-live="polite">{% t 'comments.loading' %}</p>
41
+ <noscript><p class="comments-thread__noscript">{% t 'comments.noscript' %}</p></noscript>
42
+ <ol class="comments-thread__list" data-comments-list hidden></ol>
43
+ <form class="service-form comments-form" id="comments-form" data-comments-form data-state="idle" aria-busy="false" aria-labelledby="comments-form-heading" novalidate>
44
+ <h3 class="comments-form__heading" id="comments-form-heading">{% t 'comments.form_heading' %}</h3>
45
+ <p class="comments-form__replying" data-comments-replying hidden>
46
+ <span data-comments-replying-to></span>
47
+ <button type="button" class="post-tool" data-comments-cancel-reply>{% t 'comments.cancel_reply' %}</button>
48
+ </p>
49
+ <fieldset class="service-form__fields">
50
+ <legend class="visually-hidden">{% t 'comments.form_heading' %}</legend>
51
+ <div class="service-form__row">
52
+ <div class="service-form__field">
53
+ <label for="comments-name">{% t 'comments.name' %}</label>
54
+ <input id="comments-name" name="name" type="text" required autocomplete="name">
55
+ <p class="service-form__error" data-error-for="name" hidden></p>
56
+ </div>
57
+ <div class="service-form__field">
58
+ <label for="comments-email">{% t 'comments.email' %}</label>
59
+ <input id="comments-email" name="email" type="email" autocomplete="email" aria-describedby="comments-email-hint">
60
+ <p class="service-form__hint" id="comments-email-hint">{% t 'comments.email_hint' %}</p>
61
+ <p class="service-form__error" data-error-for="email" hidden></p>
62
+ </div>
63
+ </div>
64
+ <div class="service-form__field">
65
+ <label for="comments-url">{% t 'comments.url' %}</label>
66
+ <input id="comments-url" name="url" type="url" autocomplete="url">
67
+ <p class="service-form__error" data-error-for="url" hidden></p>
68
+ </div>
69
+ <div class="service-form__field">
70
+ <label for="comments-body">{% t 'comments.body' %}</label>
71
+ <textarea id="comments-body" name="body" rows="5" required></textarea>
72
+ <p class="service-form__error" data-error-for="body" hidden></p>
73
+ </div>
74
+ <input type="hidden" name="parent_id" value="">
75
+ <div class="service-form__trap" aria-hidden="true">
76
+ <label for="comments-website">Website</label>
77
+ <input id="comments-website" name="website" type="text" tabindex="-1" autocomplete="off">
78
+ </div>
79
+ </fieldset>
80
+ {%- if settings.moderation != false %}
81
+ <p class="service-form__privacy">{% t 'comments.moderated' %}</p>
82
+ {%- else %}
83
+ <p class="service-form__privacy">{% t 'comments.privacy' %}</p>
84
+ {%- endif %}
85
+ <div class="service-form__actions">
86
+ <button type="submit" class="post-tool">{% t 'comments.send' %}</button>
87
+ </div>
88
+ <p class="service-form__status" data-form-status role="status" aria-live="polite" hidden></p>
89
+ </form>
90
+ </div>
@@ -0,0 +1,120 @@
1
+ {%- comment -%}
2
+ ---
3
+ optional: []
4
+ ---
5
+ The contact and collaboration form (assets/js/contact/form.js): a structured
6
+ message to the author, research collaboration, consulting, speaking,
7
+ mentoring, a reproducibility question, a media request or anything else,
8
+ sent to the site's dynamic services (docs/dynamic-services.md). Rendered by
9
+ the page layout when the page says `contact_form: true`, or included
10
+ anywhere. Without a backend offering the feature it falls back to the
11
+ site's email address, so the page is never empty. Categories come from
12
+ `contact.categories`, their labels and prompts from `contact.categories.*`
13
+ and `contact.prompts.*` in _data/i18n; `contact.prompts` in _config.yml
14
+ overrides a prompt.
15
+ {%- endcomment -%}
16
+ {%- assign services = site.dynamic_services -%}
17
+ {%- assign contact = site.contact -%}
18
+ {%- if contact.enabled != false -%}
19
+ {%- assign mail = site.contact_email | default: site.author.email -%}
20
+ {%- if services.base_url and services.base_url != '' and services.features.contact != false -%}
21
+ {%- assign categories = contact.categories -%}
22
+ {%- if categories == nil or categories.size == 0 -%}
23
+ {%- assign categories = "research-collaboration,consulting,speaking,mentoring,reproducibility,media,other" | split: "," -%}
24
+ {%- endif -%}
25
+ {%- capture labels_json -%}
26
+ {
27
+ "pending": {{ 'contact.pending' | t | jsonify }},
28
+ "success": {{ 'contact.success' | t | jsonify }},
29
+ "name_required": {{ 'contact.name_required' | t | jsonify }},
30
+ "email_invalid": {{ 'contact.email_invalid' | t | jsonify }},
31
+ "subject_required": {{ 'contact.subject_required' | t | jsonify }},
32
+ "message_short": {{ 'contact.message_short' | t | jsonify }},
33
+ "errors": {
34
+ {%- assign kinds = "disabled,unsupported,version,timeout,network,malformed,unauthorized,forbidden,not_found,conflict,invalid,rate_limited,server,http,aborted,reference" | split: "," -%}
35
+ {%- for kind in kinds -%}
36
+ {%- assign kind_key = "dynamic_services.errors." | append: kind -%}
37
+ {{ kind | jsonify }}: {{ kind_key | t | jsonify }}{% unless forloop.last %},{% endunless %}
38
+ {%- endfor -%}
39
+ }
40
+ }
41
+ {%- endcapture -%}
42
+ {%- capture prompts_json -%}
43
+ {
44
+ {%- for category in categories -%}
45
+ {%- assign prompt = nil -%}
46
+ {%- if contact.prompts -%}{%- assign prompt = contact.prompts[category] -%}{%- endif -%}
47
+ {%- unless prompt -%}
48
+ {%- assign prompt_key = "contact.prompts." | append: category -%}
49
+ {%- assign prompt = prompt_key | t -%}
50
+ {%- if prompt == prompt_key -%}{%- assign prompt = nil -%}{%- endif -%}
51
+ {%- endunless -%}
52
+ {%- if prompt -%}{{ category | jsonify }}: {{ prompt | jsonify }},{%- endif -%}
53
+ {%- endfor -%}
54
+ "": ""
55
+ }
56
+ {%- endcapture -%}
57
+ {%- assign privacy = contact.privacy_notice | default: nil -%}
58
+ {%- unless privacy -%}{%- assign privacy = 'contact.privacy' | t -%}{%- endunless -%}
59
+ <section class="contact-form" id="contact" data-contact-form data-source-url="{{ page.url | absolute_url }}">
60
+ <script type="application/json" nonce="{{ page.csp_nonce }}" data-contact-labels>{{ labels_json | strip }}</script>
61
+ <script type="application/json" nonce="{{ page.csp_nonce }}" data-contact-prompts>{{ prompts_json | strip }}</script>
62
+ <form class="service-form contact-form__form" id="contact-form" data-state="idle" aria-busy="false" novalidate>
63
+ <p class="service-form__intro">{% t 'contact.intro' %}</p>
64
+ <fieldset class="service-form__fields">
65
+ <legend class="visually-hidden">{% t 'contact.legend' %}</legend>
66
+ <div class="service-form__field">
67
+ <label for="contact-category">{% t 'contact.category' %}</label>
68
+ <select id="contact-category" name="category">
69
+ {%- for category in categories %}
70
+ {%- assign category_key = "contact.categories." | append: category %}
71
+ <option value="{{ category | escape }}">{{ category_key | t }}</option>
72
+ {%- endfor %}
73
+ </select>
74
+ </div>
75
+ <div class="service-form__row">
76
+ <div class="service-form__field">
77
+ <label for="contact-name">{% t 'contact.name' %}</label>
78
+ <input id="contact-name" name="name" type="text" required autocomplete="name">
79
+ <p class="service-form__error" data-error-for="name" hidden></p>
80
+ </div>
81
+ <div class="service-form__field">
82
+ <label for="contact-email">{% t 'contact.email' %}</label>
83
+ <input id="contact-email" name="email" type="email" required autocomplete="email">
84
+ <p class="service-form__error" data-error-for="email" hidden></p>
85
+ </div>
86
+ </div>
87
+ <div class="service-form__field">
88
+ <label for="contact-affiliation">{% t 'contact.affiliation' %}</label>
89
+ <input id="contact-affiliation" name="affiliation" type="text" autocomplete="organization">
90
+ </div>
91
+ <div class="service-form__field">
92
+ <label for="contact-subject">{% t 'contact.subject' %}</label>
93
+ <input id="contact-subject" name="subject" type="text" required>
94
+ <p class="service-form__error" data-error-for="subject" hidden></p>
95
+ </div>
96
+ <div class="service-form__field">
97
+ <label for="contact-message">{% t 'contact.message' %}</label>
98
+ <textarea id="contact-message" name="message" rows="7" required minlength="20" aria-describedby="contact-message-hint"></textarea>
99
+ <p class="service-form__hint" id="contact-message-hint" data-contact-hint>{% t 'contact.message_hint' %}</p>
100
+ <p class="service-form__error" data-error-for="message" hidden></p>
101
+ </div>
102
+ <div class="service-form__trap" aria-hidden="true">
103
+ <label for="contact-website">Website</label>
104
+ <input id="contact-website" name="website" type="text" tabindex="-1" autocomplete="off">
105
+ </div>
106
+ </fieldset>
107
+ <p class="service-form__privacy">{{ privacy }}{% if contact.retention %} {{ contact.retention }}{% endif %}</p>
108
+ <div class="service-form__actions">
109
+ <button type="submit" class="post-tool">{% t 'contact.send' %}</button>
110
+ </div>
111
+ <p class="service-form__status" data-form-status role="status" aria-live="polite" hidden></p>
112
+ </form>
113
+ </section>
114
+ {%- elsif mail -%}
115
+ {%- capture mail_link -%}<a href="mailto:{{ mail }}">{{ mail }}</a>{%- endcapture -%}
116
+ <section class="contact-form contact-form--fallback" id="contact">
117
+ <p class="contact-form__fallback">{% t 'contact.fallback' email: mail_link %}</p>
118
+ </section>
119
+ {%- endif -%}
120
+ {%- endif -%}
@@ -0,0 +1,82 @@
1
+ {%- comment -%}
2
+ ---
3
+ optional: []
4
+ ---
5
+ "Report an error or suggest a correction" (assets/js/corrections/form.js):
6
+ structured feedback about an article, apart from comments, sent to the
7
+ site's dynamic services (docs/dynamic-services.md) and never shown on the
8
+ page. Renders only when the site has a backend with the corrections feature
9
+ on, `corrections.enabled` is not false, and the page does not say
10
+ `corrections: false`. The categories come from `corrections.categories`,
11
+ their labels from `corrections.categories.*` in _data/i18n.
12
+ {%- endcomment -%}
13
+ {%- assign services = site.dynamic_services -%}
14
+ {%- assign corrections = site.corrections -%}
15
+ {%- if services.base_url and services.base_url != '' and services.features.corrections != false and corrections.enabled != false and page.corrections != false -%}
16
+ {%- assign categories = corrections.categories -%}
17
+ {%- if categories == nil or categories.size == 0 -%}
18
+ {%- assign categories = "mathematical-error,factual-error,citation,code,reproducibility,typo,accessibility,other" | split: "," -%}
19
+ {%- endif -%}
20
+ {%- capture labels_json -%}
21
+ {
22
+ "pending": {{ 'corrections.pending' | t | jsonify }},
23
+ "success": {{ 'corrections.success' | t | jsonify }},
24
+ "message_short": {{ 'corrections.message_short' | t | jsonify }},
25
+ "email_invalid": {{ 'corrections.email_invalid' | t | jsonify }},
26
+ "errors": {
27
+ {%- assign kinds = "disabled,unsupported,version,timeout,network,malformed,unauthorized,forbidden,not_found,conflict,invalid,rate_limited,server,http,aborted,reference" | split: "," -%}
28
+ {%- for kind in kinds -%}
29
+ {%- assign kind_key = "dynamic_services.errors." | append: kind -%}
30
+ {{ kind | jsonify }}: {{ kind_key | t | jsonify }}{% unless forloop.last %},{% endunless %}
31
+ {%- endfor -%}
32
+ }
33
+ }
34
+ {%- endcapture -%}
35
+ <details class="correction-report" id="correction-report" data-correction-report data-article-url="{{ page.url | absolute_url }}" data-article-title="{{ page.title | escape }}">
36
+ <summary class="correction-report__summary">{% t 'corrections.summary' %}</summary>
37
+ <script type="application/json" nonce="{{ page.csp_nonce }}" data-correction-labels>{{ labels_json | strip }}</script>
38
+ <form class="service-form correction-report__form" id="correction-report-form" data-state="idle" aria-busy="false" novalidate>
39
+ <p class="service-form__intro">{% t 'corrections.intro' %}</p>
40
+ <p class="service-form__quote" data-correction-quote hidden></p>
41
+ <fieldset class="service-form__fields">
42
+ <legend class="visually-hidden">{% t 'corrections.summary' %}</legend>
43
+ <div class="service-form__field">
44
+ <label for="correction-category">{% t 'corrections.category' %}</label>
45
+ <select id="correction-category" name="category">
46
+ {%- for category in categories %}
47
+ {%- assign category_key = "corrections.categories." | append: category %}
48
+ <option value="{{ category | escape }}">{{ category_key | t }}</option>
49
+ {%- endfor %}
50
+ </select>
51
+ </div>
52
+ <div class="service-form__field">
53
+ <label for="correction-section">{% t 'corrections.section' %}</label>
54
+ <select id="correction-section" name="section">
55
+ <option value="">{% t 'corrections.section_any' %}</option>
56
+ </select>
57
+ </div>
58
+ <div class="service-form__field">
59
+ <label for="correction-message">{% t 'corrections.message' %}</label>
60
+ <textarea id="correction-message" name="message" rows="5" required minlength="20" aria-describedby="correction-message-hint"></textarea>
61
+ <p class="service-form__hint" id="correction-message-hint">{% t 'corrections.message_hint' %}</p>
62
+ <p class="service-form__error" data-error-for="message" hidden></p>
63
+ </div>
64
+ <div class="service-form__field">
65
+ <label for="correction-email">{% t 'corrections.contact_email' %}</label>
66
+ <input id="correction-email" name="contact_email" type="email" autocomplete="email" aria-describedby="correction-email-hint">
67
+ <p class="service-form__hint" id="correction-email-hint">{% t 'corrections.contact_hint' %}</p>
68
+ <p class="service-form__error" data-error-for="contact_email" hidden></p>
69
+ </div>
70
+ <input type="hidden" name="quote" value="">
71
+ <div class="service-form__trap" aria-hidden="true">
72
+ <label for="correction-website">Website</label>
73
+ <input id="correction-website" name="website" type="text" tabindex="-1" autocomplete="off">
74
+ </div>
75
+ </fieldset>
76
+ <div class="service-form__actions">
77
+ <button type="submit" class="post-tool">{% t 'corrections.send' %}</button>
78
+ </div>
79
+ <p class="service-form__status" data-form-status role="status" aria-live="polite" hidden></p>
80
+ </form>
81
+ </details>
82
+ {%- endif -%}
@@ -104,7 +104,7 @@ Parameters:
104
104
  </div>
105
105
  </div>
106
106
 
107
- <pre class="enhanced-code-block__pre{% if show_line_numbers %} enhanced-code-block__pre--numbered{% endif %}"><code id="{{ code_id }}" class="language-{{ language }}" data-code-content>{{ code_content }}</code></pre>
107
+ <pre class="highlight enhanced-code-block__pre{% if show_line_numbers %} enhanced-code-block__pre--numbered{% endif %}" tabindex="0"><code id="{{ code_id }}" class="language-{{ language }}" data-code-content>{{ code_content | rouge_highlight: language }}</code></pre>
108
108
 
109
109
  {%- comment -%} Feedback message {%- endcomment -%}
110
110
  <div class="enhanced-code-block__feedback" data-feedback role="status" aria-live="polite"></div>
@@ -27,7 +27,8 @@ Parameters:
27
27
  {%- endcapture -%}
28
28
 
29
29
  {%- if toc_markup contains '<li' -%}
30
- <aside class="enhanced-toc{% if collapsible %} enhanced-toc--collapsible{% endif %}"
30
+ {%- comment -%} A <nav>: doc-toc refines the navigation landmark, and is not a role an <aside> may carry. {%- endcomment -%}
31
+ <nav class="enhanced-toc{% if collapsible %} enhanced-toc--collapsible{% endif %}"
31
32
  data-toc-enhanced
32
33
  aria-label="{{ toc_heading }}"
33
34
  role="doc-toc">
@@ -87,7 +88,7 @@ Parameters:
87
88
  <span data-progress-percent>0</span>% complete
88
89
  </span>
89
90
  </div>
90
- </aside>
91
+ </nav>
91
92
 
92
93
  <script>
93
94
  (function() {
@@ -154,7 +155,8 @@ Parameters:
154
155
  }
155
156
  }
156
157
 
157
- // Smooth scroll for TOC links
158
+ // Scroll to TOC targets. The stylesheet's scroll-behavior decides whether
159
+ // the scroll is smooth, so reduced-motion preferences are respected.
158
160
  tocLinks.forEach(link => {
159
161
  link.addEventListener('click', function(e) {
160
162
  const targetId = this.getAttribute('href');
@@ -163,10 +165,7 @@ Parameters:
163
165
  const targetElement = document.querySelector(targetId);
164
166
  if (targetElement) {
165
167
  const offsetTop = targetElement.offsetTop - 80; // Account for sticky header
166
- window.scrollTo({
167
- top: offsetTop,
168
- behavior: 'smooth'
169
- });
168
+ window.scrollTo({ top: offsetTop });
170
169
  // Update URL without jumping
171
170
  if (history.pushState) {
172
171
  history.pushState(null, null, targetId);
@@ -0,0 +1,13 @@
1
+ {%- comment -%}
2
+ ---
3
+ required:
4
+ - license
5
+ ---
6
+ A licence's name (from the page_license or page_code_license filter), linked
7
+ with rel="license" when the licence has a URL.
8
+ {%- endcomment -%}
9
+ {%- if include.license.url -%}
10
+ <a class="license-link" rel="license" href="{{ include.license.url | escape }}">{{ include.license.name | escape }}</a>
11
+ {%- else -%}
12
+ <span class="license-link">{{ include.license.name | escape }}</span>
13
+ {%- endif -%}
@@ -0,0 +1,28 @@
1
+ {%- comment -%}
2
+ ---
3
+ required:
4
+ - page
5
+ ---
6
+ The reuse terms of an article: its copyright holder and year, the licence of
7
+ its text and figures and, when it differs, of its code samples
8
+ (_plugins/licenses.rb). Renders nothing for a page without a licence.
9
+ {%- endcomment -%}
10
+ {%- assign content_license = include.page | page_license -%}
11
+ {%- assign code_license = include.page | page_code_license -%}
12
+ {%- if content_license or code_license -%}
13
+ {%- capture content_link -%}{% include components/license-link.html license=content_license %}{%- endcapture -%}
14
+ {%- capture code_link -%}{% include components/license-link.html license=code_license %}{%- endcapture -%}
15
+ {%- assign content_link = content_link | strip -%}
16
+ {%- assign code_link = code_link | strip -%}
17
+ <p class="content-license">
18
+ {%- if content_license and content_license.holder %}
19
+ <span class="content-license__holder">©{% if content_license.year %} {{ content_license.year }}{% endif %} {{ content_license.holder | escape }}.</span>
20
+ {%- endif %}
21
+ {%- if content_license %}
22
+ {% if content_license.reserved %}{% t 'post.license.reserved' %}{% else %}{% t 'post.license.content' license: content_link %}{% endif %}
23
+ {%- endif %}
24
+ {%- if code_license %}
25
+ {% t 'post.license.code' license: code_link %}
26
+ {%- endif %}
27
+ </p>
28
+ {%- endif -%}
@@ -0,0 +1,139 @@
1
+ {%- comment -%}
2
+ ---
3
+ optional: []
4
+ ---
5
+ The moderation inbox (assets/js/moderation/inbox.js, docs/moderation.md):
6
+ what the self-hosted features received, comments awaiting approval or
7
+ flagged as spam, correction reports, abuse reports, with the actions a
8
+ moderator can take. Rendered by the page layout when the page says
9
+ `moderation_inbox: true`, and only when `moderation.enabled` is true and
10
+ there is a moderation API to talk to (`moderation.endpoint`, or the
11
+ dynamic services' base_url).
12
+
13
+ This markup is a shell and protects nothing: a static site cannot hold an
14
+ admin secret. Every request is authenticated and authorized by the service
15
+ or the identity-aware proxy in front of it; the settings here (an address,
16
+ a fetch credentials mode, a sign-in link) are public.
17
+ {%- endcomment -%}
18
+ {%- assign moderation = site.moderation -%}
19
+ {%- assign services = site.dynamic_services -%}
20
+ {%- assign endpoint = moderation.endpoint | default: '' -%}
21
+ {%- assign has_api = false -%}
22
+ {%- if endpoint != '' -%}{%- assign has_api = true -%}{%- endif -%}
23
+ {%- if services.base_url and services.base_url != '' and services.features.moderation != false -%}{%- assign has_api = true -%}{%- endif -%}
24
+ {%- if moderation.enabled == true and has_api -%}
25
+ {%- assign categories = site.corrections.categories -%}
26
+ {%- if categories == nil or categories.size == 0 -%}
27
+ {%- assign categories = "mathematical-error,factual-error,citation,code,reproducibility,typo,accessibility,other" | split: "," -%}
28
+ {%- endif -%}
29
+ {%- assign statuses = "pending,approved,spam,hidden,deleted,new,reviewed,accepted,rejected,resolved,open,dismissed" | split: "," -%}
30
+ {%- assign actions = "approve,spam,hide,delete,reviewed,accept,reject,resolve,dismiss" | split: "," -%}
31
+ {%- assign types = "comment,correction,abuse" | split: "," -%}
32
+ {%- capture labels_json -%}
33
+ {
34
+ "loading": {{ 'moderation.loading' | t | jsonify }},
35
+ "empty": {{ 'moderation.empty' | t | jsonify }},
36
+ "working": {{ 'moderation.working' | t | jsonify }},
37
+ "done": {{ 'moderation.done' | t | jsonify }},
38
+ "unauthorized": {{ 'moderation.unauthorized' | t | jsonify }},
39
+ "forbidden": {{ 'moderation.forbidden' | t | jsonify }},
40
+ "link_required": {{ 'moderation.link_required' | t | jsonify }},
41
+ "note": {{ 'moderation.note' | t | jsonify }},
42
+ "link": {{ 'moderation.link' | t | jsonify }},
43
+ "in_reply_to": {{ 'moderation.in_reply_to' | t | jsonify }},
44
+ "types": {
45
+ {%- for type in types -%}{%- assign key = "moderation.types." | append: type -%}
46
+ {{ type | jsonify }}: {{ key | t | jsonify }}{% unless forloop.last %},{% endunless %}
47
+ {%- endfor -%}
48
+ },
49
+ "statuses": {
50
+ {%- for status in statuses -%}{%- assign key = "moderation.statuses." | append: status -%}
51
+ {{ status | jsonify }}: {{ key | t | jsonify }}{% unless forloop.last %},{% endunless %}
52
+ {%- endfor -%}
53
+ },
54
+ "actions": {
55
+ {%- for action in actions -%}{%- assign key = "moderation.actions." | append: action -%}
56
+ {{ action | jsonify }}: {{ key | t | jsonify }}{% unless forloop.last %},{% endunless %}
57
+ {%- endfor -%}
58
+ },
59
+ "categories": {
60
+ {%- for category in categories -%}{%- assign key = "corrections.categories." | append: category -%}
61
+ {{ category | jsonify }}: {{ key | t | jsonify }}{% unless forloop.last %},{% endunless %}
62
+ {%- endfor -%}
63
+ },
64
+ "errors": {
65
+ {%- assign kinds = "disabled,unsupported,version,timeout,network,malformed,unauthorized,forbidden,not_found,conflict,invalid,rate_limited,server,http,aborted,reference" | split: "," -%}
66
+ {%- for kind in kinds -%}
67
+ {%- assign kind_key = "dynamic_services.errors." | append: kind -%}
68
+ {{ kind | jsonify }}: {{ kind_key | t | jsonify }}{% unless forloop.last %},{% endunless %}
69
+ {%- endfor -%}
70
+ }
71
+ }
72
+ {%- endcapture -%}
73
+ <section class="moderation" id="moderation" data-moderation-inbox data-moderation-site="{{ '' | absolute_url }}"{% if endpoint != '' %} data-moderation-endpoint="{{ endpoint }}"{% endif %} data-moderation-credentials="{{ moderation.credentials | default: 'include' }}" data-state="idle" aria-busy="false">
74
+ <script type="application/json" nonce="{{ page.csp_nonce }}" data-moderation-labels>{{ labels_json | strip }}</script>
75
+ <p class="moderation__boundary">{% t 'moderation.boundary' %}</p>
76
+ <form class="service-form moderation__filters" id="moderation-filters" data-moderation-filters>
77
+ <fieldset class="service-form__fields moderation__filter-grid">
78
+ <legend class="visually-hidden">{% t 'moderation.filters' %}</legend>
79
+ <div class="service-form__field">
80
+ <label for="moderation-type">{% t 'moderation.type' %}</label>
81
+ <select id="moderation-type" name="type">
82
+ <option value="">{% t 'moderation.any_type' %}</option>
83
+ {%- for type in types %}{%- assign key = "moderation.types." | append: type %}
84
+ <option value="{{ type }}">{{ key | t }}</option>
85
+ {%- endfor %}
86
+ </select>
87
+ </div>
88
+ <div class="service-form__field">
89
+ <label for="moderation-status">{% t 'moderation.status' %}</label>
90
+ <select id="moderation-status" name="status">
91
+ <option value="">{% t 'moderation.queue' %}</option>
92
+ {%- for status in statuses %}{%- assign key = "moderation.statuses." | append: status %}
93
+ <option value="{{ status }}">{{ key | t }}</option>
94
+ {%- endfor %}
95
+ </select>
96
+ </div>
97
+ <div class="service-form__field">
98
+ <label for="moderation-category">{% t 'moderation.category' %}</label>
99
+ <select id="moderation-category" name="category">
100
+ <option value="">{% t 'moderation.any_category' %}</option>
101
+ {%- for category in categories %}{%- assign key = "corrections.categories." | append: category %}
102
+ <option value="{{ category | escape }}">{{ key | t }}</option>
103
+ {%- endfor %}
104
+ </select>
105
+ </div>
106
+ <div class="service-form__field">
107
+ <label for="moderation-path">{% t 'moderation.path' %}</label>
108
+ <input id="moderation-path" name="path" type="text" autocomplete="off">
109
+ </div>
110
+ <div class="service-form__field">
111
+ <label for="moderation-since">{% t 'moderation.since' %}</label>
112
+ <input id="moderation-since" name="since" type="date">
113
+ </div>
114
+ <div class="service-form__field">
115
+ <label for="moderation-search">{% t 'moderation.search' %}</label>
116
+ <input id="moderation-search" name="q" type="search" autocomplete="off">
117
+ </div>
118
+ </fieldset>
119
+ <div class="service-form__actions">
120
+ <button type="submit" class="post-tool">{% t 'moderation.apply' %}</button>
121
+ <button type="reset" class="post-tool">{% t 'moderation.reset' %}</button>
122
+ </div>
123
+ </form>
124
+ <p class="moderation__status" data-moderation-status role="status" aria-live="polite" hidden></p>
125
+ <p class="moderation__sign-in" data-moderation-sign-in hidden>
126
+ {%- if moderation.sign_in_url and moderation.sign_in_url != '' %}
127
+ <a href="{{ moderation.sign_in_url }}" rel="noopener">{% t 'moderation.sign_in' %}</a>
128
+ {%- else %}
129
+ {% t 'moderation.sign_in_hint' %}
130
+ {%- endif %}
131
+ </p>
132
+ <ol class="moderation__list" data-moderation-list></ol>
133
+ <button type="button" class="post-tool moderation__more" data-moderation-more hidden>{% t 'moderation.more' %}</button>
134
+ </section>
135
+ {%- else -%}
136
+ <section class="moderation" id="moderation">
137
+ <p class="moderation__boundary">{% t 'moderation.unavailable' %}</p>
138
+ </section>
139
+ {%- endif -%}
@@ -0,0 +1,49 @@
1
+ {%- comment -%}
2
+ ---
3
+ optional: []
4
+ ---
5
+ "Was this useful?" (assets/js/reactions/widget.js): a small set of
6
+ reactions a reader can give an article without writing a comment, read
7
+ from and sent to the site's dynamic services (docs/dynamic-services.md).
8
+ Renders only when the site has a backend with the reactions feature on,
9
+ `reactions.enabled` is not false, and the page does not say
10
+ `reactions: false`. The set comes from `reactions.types`, the labels from
11
+ `reactions.types.*` in _data/i18n. The counts on the buttons are filled by
12
+ the script from the service's answer, never here.
13
+ {%- endcomment -%}
14
+ {%- assign services = site.dynamic_services -%}
15
+ {%- assign reactions = site.reactions -%}
16
+ {%- if services.base_url and services.base_url != '' and services.features.reactions != false and reactions.enabled != false and page.reactions != false -%}
17
+ {%- assign types = reactions.types -%}
18
+ {%- if types == nil or types.size == 0 -%}
19
+ {%- assign types = "useful,clear,interesting,needs-clarification" | split: "," -%}
20
+ {%- endif -%}
21
+ {%- capture labels_json -%}
22
+ {
23
+ "sending": {{ 'reactions.sending' | t | jsonify }},
24
+ "thanks": {{ 'reactions.thanks' | t | jsonify }},
25
+ "already": {{ 'reactions.already' | t | jsonify }},
26
+ "errors": {
27
+ {%- assign kinds = "disabled,unsupported,version,timeout,network,malformed,unauthorized,forbidden,not_found,conflict,invalid,rate_limited,server,http,aborted,reference" | split: "," -%}
28
+ {%- for kind in kinds -%}
29
+ {%- assign kind_key = "dynamic_services.errors." | append: kind -%}
30
+ {{ kind | jsonify }}: {{ kind_key | t | jsonify }}{% unless forloop.last %},{% endunless %}
31
+ {%- endfor -%}
32
+ }
33
+ }
34
+ {%- endcapture -%}
35
+ <section class="reactions" id="reactions" data-reactions data-reactions-path="{{ page.url }}" data-reactions-counts="{% if reactions.counts == false %}false{% else %}true{% endif %}" data-state="idle" aria-busy="false" aria-labelledby="reactions-prompt">
36
+ <script type="application/json" nonce="{{ page.csp_nonce }}" data-reactions-labels>{{ labels_json | strip }}</script>
37
+ <p class="reactions__prompt" id="reactions-prompt">{% t 'reactions.prompt' %}</p>
38
+ <div class="reactions__choices" role="group" aria-labelledby="reactions-prompt">
39
+ {%- for type in types %}
40
+ {%- assign type_key = "reactions.types." | append: type %}
41
+ <button type="button" class="reactions__choice post-tool" data-reaction="{{ type | escape }}" aria-pressed="false">
42
+ <span class="reactions__label">{{ type_key | t }}</span>
43
+ <span class="reactions__count" data-reaction-count hidden></span>
44
+ </button>
45
+ {%- endfor %}
46
+ </div>
47
+ <p class="reactions__status" data-reactions-status role="status" aria-live="polite" hidden></p>
48
+ </section>
49
+ {%- endif -%}
@@ -0,0 +1,31 @@
1
+ {%- comment -%}
2
+ ---
3
+ optional: []
4
+ ---
5
+ The articles a reader saved, with their reading progress and highlights, on
6
+ a page of the site (assets/js/reading-state/ui.js): `{% include
7
+ components/reading-list.html %}` on a page, named in
8
+ theme_options.reading_state.list_url so posts link to it. Filled from this
9
+ browser's storage; nothing comes from a server.
10
+ {%- endcomment -%}
11
+ {%- capture progress_label %}{% t 'post.reading_state.progress' %}{% endcapture -%}
12
+ {%- capture highlights_label %}{% t 'post.reading_state.highlights_count' %}{% endcapture -%}
13
+ {%- capture saved_label %}{% t 'post.reading_state.saved' %}{% endcapture -%}
14
+ {%- capture remove_label %}{% t 'post.reading_state.remove' %}{% endcapture -%}
15
+ {%- capture erase_confirm %}{% t 'post.reading_state.erase_confirm' %}{% endcapture -%}
16
+ {%- capture imported %}{% t 'post.reading_state.imported' %}{% endcapture -%}
17
+ {%- capture import_failed %}{% t 'post.reading_state.import_failed' %}{% endcapture -%}
18
+ {%- capture exported %}{% t 'post.reading_state.exported' %}{% endcapture -%}
19
+ {%- capture erased_all %}{% t 'post.reading_state.erased_all' %}{% endcapture -%}
20
+ <div class="reading-list" data-reading-list data-progress-label="{{ progress_label | strip | escape }}" data-highlights-label="{{ highlights_label | strip | escape }}" data-saved-label="{{ saved_label | strip | escape }}" data-remove-label="{{ remove_label | strip | escape }}">
21
+ <p class="reading-notes__intro">{% t 'post.reading_state.intro' %}</p>
22
+ <p class="reading-notes__unavailable" data-reading-unavailable hidden>{% t 'post.reading_state.unavailable' %}</p>
23
+ <p class="reading-list__empty" data-reading-list-empty>{% t 'post.reading_state.list_empty' %}</p>
24
+ <ul class="reading-list__items" data-reading-list-items></ul>
25
+ <div class="reading-data__actions">
26
+ <button type="button" class="post-tool" data-reading-export data-done="{{ exported | strip | escape }}">{% t 'post.reading_state.export' %}</button>
27
+ <label class="post-tool reading-data__import">{% t 'post.reading_state.import' %}<input type="file" accept="application/json,.json" data-reading-import data-done="{{ imported | strip | escape }}" data-failed="{{ import_failed | strip | escape }}"></label>
28
+ <button type="button" class="post-tool" data-reading-erase-all data-confirm="{{ erase_confirm | strip | escape }}" data-done="{{ erased_all | strip | escape }}">{% t 'post.reading_state.erase_all' %}</button>
29
+ </div>
30
+ <p class="reading-data__status" data-reading-status aria-live="polite"></p>
31
+ </div>