jekyll-theme-zer0 1.21.0 → 1.23.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 +57 -0
- data/_data/ai.yml +10 -0
- data/_data/backlog.yml +173 -23
- data/_data/navigation/docs.yml +4 -0
- data/_data/theme_backgrounds.yml +24 -0
- data/_includes/components/component-showcase.html +83 -14
- data/_includes/components/js-cdn.html +10 -2
- data/_includes/components/post-card.html +18 -4
- data/_includes/components/search-modal.html +28 -4
- data/_includes/content/backlinks.html +47 -18
- data/_includes/content/giscus.html +30 -44
- data/_includes/core/color-mode-init.html +35 -0
- data/_includes/core/footer-fabs.html +5 -2
- data/_includes/core/head.html +9 -0
- data/_includes/core/tokens-inline.html +5 -0
- data/_includes/navigation/local-graph-fab.html +1 -1
- data/_includes/navigation/local-graph.html +4 -3
- data/_includes/navigation/section-sidebar.html +10 -3
- data/_includes/obsidian/full-graph.html +7 -5
- data/_layouts/article.html +23 -6
- data/_layouts/author.html +22 -1
- data/_layouts/root.html +15 -1
- data/_plugins/obsidian_links.rb +84 -15
- data/_sass/core/_obsidian.scss +59 -8
- data/assets/data/wiki-index.json +50 -3
- data/assets/js/obsidian-graph.js +66 -21
- data/assets/js/obsidian-local-graph.js +125 -32
- data/assets/js/obsidian-wiki-links.js +118 -21
- data/assets/js/search-modal.js +45 -9
- data/scripts/bin/giscus-discussions +509 -0
- data/scripts/bin/validate +1 -0
- data/scripts/ci/classify_changes.py +129 -0
- data/scripts/issues/dispatch.py +478 -0
- data/scripts/issues/test_verify_close.py +118 -0
- data/scripts/issues/triage.py +1046 -0
- data/scripts/issues/verify_close.py +179 -0
- metadata +10 -2
|
@@ -2,19 +2,43 @@
|
|
|
2
2
|
===================================================================
|
|
3
3
|
SEARCH MODAL - Site-wide Search Popup
|
|
4
4
|
===================================================================
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
File: search-modal.html
|
|
7
7
|
Path: _includes/components/search-modal.html
|
|
8
8
|
Purpose: Provide a modal search experience with keyboard shortcut support
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
Notes:
|
|
11
|
-
-
|
|
11
|
+
- Form action targets /sitemap/ when that page exists in the build;
|
|
12
|
+
falls back to /sitemap.xml if available, otherwise '#' (safe no-op).
|
|
13
|
+
JS (search-modal.js) always intercepts submit and keeps results
|
|
14
|
+
in-modal, so the action is only a no-JS fallback. The gate here
|
|
15
|
+
prevents a dead link on remote-theme Pages builds that lack /sitemap/.
|
|
12
16
|
- Keyboard shortcut handled by navigation keyboard module ("/")
|
|
13
17
|
===================================================================
|
|
14
18
|
-->
|
|
15
19
|
|
|
16
20
|
{%- assign ui_text = site.data.ui-text[site.locale] | default: site.data.ui-text.en -%}
|
|
17
21
|
{%- assign search_placeholder = ui_text.search_placeholder_text | default: "Enter your search term..." -%}
|
|
22
|
+
{%- comment -%}
|
|
23
|
+
Existence-gate the form action to /sitemap/ only when that page is present
|
|
24
|
+
in the build (mirroring the footer Quick Links guard, and the section-sidebar
|
|
25
|
+
/tags/ gate). Remote-theme Pages consumers that haven't committed a /sitemap/
|
|
26
|
+
stub get a safe '#' fallback; the JS always intercepts submit anyway, so this
|
|
27
|
+
only affects the no-JS code path.
|
|
28
|
+
{%- endcomment -%}
|
|
29
|
+
{%- assign _search_sitemap_url = "/sitemap/" -%}
|
|
30
|
+
{%- assign _search_sitemap_page = site.html_pages | where: "url", _search_sitemap_url | first -%}
|
|
31
|
+
{%- unless _search_sitemap_page -%}
|
|
32
|
+
{%- for col in site.collections -%}
|
|
33
|
+
{%- assign _search_sitemap_page = col.docs | where: "url", _search_sitemap_url | first -%}
|
|
34
|
+
{%- if _search_sitemap_page -%}{%- break -%}{%- endif -%}
|
|
35
|
+
{%- endfor -%}
|
|
36
|
+
{%- endunless -%}
|
|
37
|
+
{%- if _search_sitemap_page -%}
|
|
38
|
+
{%- assign _search_form_action = "/sitemap/" | relative_url -%}
|
|
39
|
+
{%- else -%}
|
|
40
|
+
{%- assign _search_form_action = "#" -%}
|
|
41
|
+
{%- endif -%}
|
|
18
42
|
|
|
19
43
|
<div class="modal fade search-modal" id="siteSearchModal" tabindex="-1" aria-labelledby="siteSearchModalLabel" aria-hidden="true">
|
|
20
44
|
<div class="modal-dialog modal-dialog-centered">
|
|
@@ -26,7 +50,7 @@
|
|
|
26
50
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
27
51
|
</div>
|
|
28
52
|
<div class="modal-body">
|
|
29
|
-
<form action="{{
|
|
53
|
+
<form action="{{ _search_form_action }}" method="get" data-search-form>
|
|
30
54
|
<label class="visually-hidden" for="site-search-input">Search</label>
|
|
31
55
|
<div class="input-group">
|
|
32
56
|
<span class="input-group-text">
|
|
@@ -21,11 +21,26 @@
|
|
|
21
21
|
===================================================================
|
|
22
22
|
{%- endcomment -%}
|
|
23
23
|
|
|
24
|
-
{%- if page.backlinks != false -%}
|
|
24
|
+
{%- if page.backlinks != false and site.obsidian.enabled != false -%}
|
|
25
25
|
{%- assign _self_url = page.url -%}
|
|
26
26
|
{%- assign _self_title = page.title | default: "" | downcase | strip -%}
|
|
27
27
|
{%- assign _self_basename = page.path | split: "/" | last | replace: ".md", "" | replace: ".markdown", "" | downcase -%}
|
|
28
28
|
|
|
29
|
+
{%- comment -%}
|
|
30
|
+
Needles = every name another page could wiki-link this page by: title,
|
|
31
|
+
file basename, and any frontmatter aliases. Normalized to lowercase + strip
|
|
32
|
+
to match the resolver. A backlink is a [[needle]] with a terminator
|
|
33
|
+
(]] | # ^) so [[Foobar]] does NOT match the needle "foo".
|
|
34
|
+
{%- endcomment -%}
|
|
35
|
+
{%- assign _needles = "" | split: "" -%}
|
|
36
|
+
{%- if _self_title != "" -%}{%- assign _needles = _needles | push: _self_title -%}{%- endif -%}
|
|
37
|
+
{%- if _self_basename != "" -%}{%- assign _needles = _needles | push: _self_basename -%}{%- endif -%}
|
|
38
|
+
{%- for _alias in page.aliases -%}
|
|
39
|
+
{%- assign _an = _alias | downcase | strip -%}
|
|
40
|
+
{%- if _an != "" -%}{%- assign _needles = _needles | push: _an -%}{%- endif -%}
|
|
41
|
+
{%- endfor -%}
|
|
42
|
+
{%- assign _needles = _needles | uniq -%}
|
|
43
|
+
|
|
29
44
|
{%- assign _candidates = "" | split: "" -%}
|
|
30
45
|
{%- for _coll in site.collections -%}
|
|
31
46
|
{%- for _doc in _coll.docs -%}
|
|
@@ -46,31 +61,44 @@
|
|
|
46
61
|
{%- if _doc.published == false or _doc.draft == true -%}{%- continue -%}{%- endif -%}
|
|
47
62
|
{%- endunless -%}
|
|
48
63
|
{%- assign _body = _doc.content | default: "" -%}
|
|
49
|
-
{%- assign _body_lower = _body | downcase -%}
|
|
50
64
|
{%- assign _matched = false -%}
|
|
51
65
|
|
|
66
|
+
{%- comment -%} A markdown link to this page's permalink is a backlink. {%- endcomment -%}
|
|
52
67
|
{%- if _self_url and _self_url != "" and _self_url != "/" -%}
|
|
53
68
|
{%- if _body contains _self_url -%}
|
|
54
69
|
{%- assign _matched = true -%}
|
|
55
70
|
{%- endif -%}
|
|
56
71
|
{%- endif -%}
|
|
57
72
|
|
|
73
|
+
{%- comment -%}
|
|
74
|
+
Wiki-link match: strip fenced (```) and inline (`) code first so a page
|
|
75
|
+
that merely *documents* [[syntax]] doesn't register as a backlink, then
|
|
76
|
+
require a terminator after the needle.
|
|
77
|
+
{%- endcomment -%}
|
|
58
78
|
{%- unless _matched -%}
|
|
59
|
-
{%-
|
|
60
|
-
|
|
61
|
-
|
|
79
|
+
{%- assign _nb = "" -%}
|
|
80
|
+
{%- assign _fp = _body | split: "```" -%}
|
|
81
|
+
{%- for _p in _fp -%}
|
|
82
|
+
{%- assign _m = forloop.index0 | modulo: 2 -%}
|
|
83
|
+
{%- if _m == 0 -%}{%- assign _nb = _nb | append: _p -%}{%- endif -%}
|
|
84
|
+
{%- endfor -%}
|
|
85
|
+
{%- assign _nb2 = "" -%}
|
|
86
|
+
{%- assign _ip = _nb | split: "`" -%}
|
|
87
|
+
{%- for _p in _ip -%}
|
|
88
|
+
{%- assign _m = forloop.index0 | modulo: 2 -%}
|
|
89
|
+
{%- if _m == 0 -%}{%- assign _nb2 = _nb2 | append: _p -%}{%- endif -%}
|
|
90
|
+
{%- endfor -%}
|
|
91
|
+
{%- assign _body_lower = _nb2 | downcase -%}
|
|
92
|
+
{%- for _n in _needles -%}
|
|
93
|
+
{%- assign _v1 = "[[" | append: _n | append: "]]" -%}
|
|
94
|
+
{%- assign _v2 = "[[" | append: _n | append: "|" -%}
|
|
95
|
+
{%- assign _v3 = "[[" | append: _n | append: "#" -%}
|
|
96
|
+
{%- assign _v4 = "[[" | append: _n | append: "^" -%}
|
|
97
|
+
{%- if _body_lower contains _v1 or _body_lower contains _v2 or _body_lower contains _v3 or _body_lower contains _v4 -%}
|
|
62
98
|
{%- assign _matched = true -%}
|
|
99
|
+
{%- break -%}
|
|
63
100
|
{%- endif -%}
|
|
64
|
-
{%-
|
|
65
|
-
{%- endunless -%}
|
|
66
|
-
|
|
67
|
-
{%- unless _matched -%}
|
|
68
|
-
{%- if _self_basename != "" -%}
|
|
69
|
-
{%- assign _needle2 = "[[" | append: _self_basename -%}
|
|
70
|
-
{%- if _body_lower contains _needle2 -%}
|
|
71
|
-
{%- assign _matched = true -%}
|
|
72
|
-
{%- endif -%}
|
|
73
|
-
{%- endif -%}
|
|
101
|
+
{%- endfor -%}
|
|
74
102
|
{%- endunless -%}
|
|
75
103
|
|
|
76
104
|
{%- if _matched -%}
|
|
@@ -92,11 +120,12 @@
|
|
|
92
120
|
<strong>{{ _ref.title | default: _ref.path | split: "/" | last }}</strong>
|
|
93
121
|
</a>
|
|
94
122
|
{%- if _ref.collection -%}
|
|
95
|
-
<span class="badge
|
|
123
|
+
<span class="badge rounded-pill border text-body-secondary ms-1">{{ _ref.collection }}</span>
|
|
96
124
|
{%- endif -%}
|
|
97
|
-
{%-
|
|
125
|
+
{%- assign _ref_excerpt = _ref.description | default: _ref.excerpt | strip_html | strip | truncate: 160 -%}
|
|
126
|
+
{%- if _ref_excerpt and _ref_excerpt != "" -%}
|
|
98
127
|
<div class="obsidian-backlink-excerpt small text-muted">
|
|
99
|
-
{{
|
|
128
|
+
{{ _ref_excerpt }}
|
|
100
129
|
</div>
|
|
101
130
|
{%- endif -%}
|
|
102
131
|
</li>
|
|
@@ -1,49 +1,35 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
║ Configuration Options: ║
|
|
21
|
-
║ • data-repo: GitHub repository (site.repository) ║
|
|
22
|
-
║ • data-repo-id: GitHub repository ID (site.giscus.data-repo-id) ║
|
|
23
|
-
║ • data-category-id: Discussions category ID (site.giscus.data-category-id) ║
|
|
24
|
-
║ • data-mapping: "pathname" - maps comments to page URL ║
|
|
25
|
-
║ • data-strict: "1" - enables strict mapping mode ║
|
|
26
|
-
║ ║
|
|
27
|
-
║ Setup Requirements: ║
|
|
28
|
-
║ • GitHub repository with Discussions feature enabled ║
|
|
29
|
-
║ • Giscus app installed on the repository ║
|
|
30
|
-
║ • Correct repo-id and category-id in _config.yml ║
|
|
31
|
-
║ ║
|
|
32
|
-
║ Reference: Feature Request #FR000001 ║
|
|
33
|
-
║ Documentation: https://giscus.app/ ║
|
|
34
|
-
║ ║
|
|
35
|
-
╚═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
|
|
36
|
-
-->
|
|
2
|
+
giscus.html — GitHub Discussions comment system
|
|
3
|
+
===============================================
|
|
4
|
+
|
|
5
|
+
Purpose: Embeds the Giscus widget so page comments are stored as
|
|
6
|
+
GitHub Discussions (no database, privacy-friendly).
|
|
7
|
+
Location: _includes/content/giscus.html
|
|
8
|
+
Usage: Rendered by the article / note / notebook layouts, gated on
|
|
9
|
+
`page.comments != false and site.giscus.enabled`.
|
|
10
|
+
To include manually elsewhere, use the FULL path:
|
|
11
|
+
include content/giscus.html (wrap in Liquid tags)
|
|
12
|
+
Dependencies: site.repository, site.giscus.* (see _config.yml), a public
|
|
13
|
+
repo with Discussions enabled + the Giscus app installed.
|
|
14
|
+
|
|
15
|
+
Features:
|
|
16
|
+
• GitHub Discussions-based commenting
|
|
17
|
+
• Automatic light/dark theme (preferred_color_scheme)
|
|
18
|
+
• Pathname-based mapping for one thread per page (data-strict)
|
|
19
|
+
• Reactions enabled, input positioned at the top
|
|
37
20
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<!-- Feature Request: #FR000001 -->
|
|
43
|
-
<!-- Documentation: https://giscus.app/ -->
|
|
44
|
-
<!-- Async loading: Non-blocking comment system initialization -->
|
|
21
|
+
Config-driven attributes (from _config.yml `giscus:` block):
|
|
22
|
+
• data-repo ← site.repository
|
|
23
|
+
• data-repo-id ← site.giscus.data-repo-id
|
|
24
|
+
• data-category-id ← site.giscus.data-category-id
|
|
45
25
|
|
|
46
|
-
|
|
26
|
+
Docs: pages/_docs/features/giscus-comments.md
|
|
27
|
+
Reference: https://giscus.app/ (Feature Request #FR000001)
|
|
28
|
+
|
|
29
|
+
NOTE: do not write a bare `include giscus.html` example with live Liquid
|
|
30
|
+
braces inside this file — Liquid evaluates tags even inside HTML comments,
|
|
31
|
+
which would recursively (and wrongly) try to include "giscus.html".
|
|
32
|
+
-->
|
|
47
33
|
|
|
48
34
|
<script src="https://giscus.app/client.js"
|
|
49
35
|
data-repo="{{ site.repository }}"
|
|
@@ -58,4 +44,4 @@
|
|
|
58
44
|
data-lang="en"
|
|
59
45
|
crossorigin="anonymous"
|
|
60
46
|
async>
|
|
61
|
-
</script>
|
|
47
|
+
</script>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
Component: color-mode-init
|
|
3
|
+
Path: _includes/core/color-mode-init.html
|
|
4
|
+
Purpose: Early inline script that applies data-bs-theme BEFORE Bootstrap CSS
|
|
5
|
+
loads, preventing FOUC (flash of unstyled/wrong-themed content).
|
|
6
|
+
Params: none — reads data-color-mode-default from the <html> element,
|
|
7
|
+
which root.html sets from site.color_mode_default.
|
|
8
|
+
Depends on: root.html (sets data-color-mode-default attribute server-side)
|
|
9
|
+
Notes: Must be included BEFORE any Bootstrap CSS link tag. The script
|
|
10
|
+
is tiny, synchronous, and runs inline before the browser requests
|
|
11
|
+
external stylesheets, so the correct theme is already applied
|
|
12
|
+
when Bootstrap's [data-bs-theme] selectors are first evaluated.
|
|
13
|
+
|
|
14
|
+
Priority order (highest → lowest):
|
|
15
|
+
1. localStorage["theme"] — user's explicit override via Appearance panel
|
|
16
|
+
2. data-color-mode-default — site.color_mode_default config value
|
|
17
|
+
3. "auto" fallback — resolves via prefers-color-scheme
|
|
18
|
+
{%- endcomment -%}
|
|
19
|
+
<script>
|
|
20
|
+
(function () {
|
|
21
|
+
'use strict';
|
|
22
|
+
try {
|
|
23
|
+
var root = document.documentElement;
|
|
24
|
+
var configDefault = root.getAttribute('data-color-mode-default') || 'auto';
|
|
25
|
+
var stored = window.localStorage.getItem('theme');
|
|
26
|
+
var mode = stored || configDefault;
|
|
27
|
+
if (mode === 'auto') {
|
|
28
|
+
root.setAttribute('data-bs-theme',
|
|
29
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
|
30
|
+
} else {
|
|
31
|
+
root.setAttribute('data-bs-theme', mode);
|
|
32
|
+
}
|
|
33
|
+
} catch (e) { /* localStorage / matchMedia unavailable — server-rendered value stands */ }
|
|
34
|
+
})();
|
|
35
|
+
</script>
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
{%- assign _root_only_layouts = "landing,welcome,stats,sitemap-collection,setup,section,news,index,home,admin" | split: "," -%}
|
|
24
24
|
{%- unless _root_only_layouts contains page.layout -%}
|
|
25
25
|
{% include navigation/toc-fab.html %}
|
|
26
|
-
{
|
|
27
|
-
{
|
|
26
|
+
{%- comment -%} Local-graph FAB/panel are part of the Obsidian feature. {%- endcomment -%}
|
|
27
|
+
{%- if site.obsidian.enabled != false -%}
|
|
28
|
+
{% include navigation/local-graph-fab.html %}
|
|
29
|
+
{% include navigation/local-graph.html %}
|
|
30
|
+
{%- endif -%}
|
|
28
31
|
{%- endunless -%}
|
data/_includes/core/head.html
CHANGED
|
@@ -127,6 +127,15 @@ window.MathJax = {
|
|
|
127
127
|
<!-- CSS FRAMEWORKS AND LIBRARIES -->
|
|
128
128
|
<!-- ================================ -->
|
|
129
129
|
|
|
130
|
+
<!-- ========================== -->
|
|
131
|
+
<!-- COLOR MODE INIT (FOUC FIX) -->
|
|
132
|
+
<!-- ========================== -->
|
|
133
|
+
<!-- Must run BEFORE Bootstrap CSS so the correct data-bs-theme is applied
|
|
134
|
+
when [data-bs-theme=dark/light] selectors are first evaluated.
|
|
135
|
+
Reads site.color_mode_default (via data-color-mode-default attr on <html>)
|
|
136
|
+
and falls back to localStorage["theme"] for the user's explicit preference. -->
|
|
137
|
+
{% include core/color-mode-init.html %}
|
|
138
|
+
|
|
130
139
|
<!-- ========================== -->
|
|
131
140
|
<!-- BOOTSTRAP 5 FRAMEWORK -->
|
|
132
141
|
<!-- ========================== -->
|
|
@@ -35,6 +35,11 @@
|
|
|
35
35
|
{%- endif -%}
|
|
36
36
|
|
|
37
37
|
{%- comment -%}
|
|
38
|
+
NOTE: The data-bs-theme FOUC-prevention script lives in
|
|
39
|
+
_includes/core/color-mode-init.html (included from head.html BEFORE Bootstrap
|
|
40
|
+
CSS) so the correct theme is applied before any [data-bs-theme] selectors are
|
|
41
|
+
evaluated. Do not duplicate it here.
|
|
42
|
+
|
|
38
43
|
Restore any user-saved Appearance overrides before paint to avoid a flash
|
|
39
44
|
of the default palette. The Appearance panel (Track 6) writes JSON like
|
|
40
45
|
{"primary":"#ff5722"} to localStorage["zer0-appearance"].
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
{%- endif -%}
|
|
12
12
|
{%- endif -%}
|
|
13
13
|
{%- if page.local_graph != false and _effective_sidebar != false -%}
|
|
14
|
-
<div class="obsidian-local-graph-fab d-print-none" data-obsidian-local-graph-toggle>
|
|
14
|
+
<div id="obsidianLocalGraphFab" class="obsidian-local-graph-fab d-print-none" data-obsidian-local-graph-toggle hidden>
|
|
15
15
|
<button class="btn btn-primary rounded-circle shadow-lg p-0 obsidian-local-graph-toggle"
|
|
16
16
|
type="button"
|
|
17
17
|
data-bs-toggle="offcanvas"
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
aria-labelledby="obsidianLocalGraphLabel"
|
|
41
41
|
data-bs-scroll="true"
|
|
42
42
|
data-bs-backdrop="false"
|
|
43
|
-
data-obsidian-local-graph-panel
|
|
43
|
+
data-obsidian-local-graph-panel
|
|
44
|
+
hidden>
|
|
44
45
|
<div class="offcanvas-header">
|
|
45
46
|
<h2 class="offcanvas-title h5 mb-0" id="obsidianLocalGraphLabel">
|
|
46
47
|
<i class="bi bi-diagram-3 me-2" aria-hidden="true"></i>Local graph
|
|
@@ -80,7 +81,7 @@
|
|
|
80
81
|
<div id="obsidian-local-graph"
|
|
81
82
|
data-depth="{{ lg_depth }}"
|
|
82
83
|
data-index-url="{{ '/assets/data/wiki-index.json' | relative_url }}"
|
|
83
|
-
role="
|
|
84
|
+
role="group"
|
|
84
85
|
aria-label="Local graph of pages linked to this page"></div>
|
|
85
86
|
<p class="obsidian-local-graph-status small text-secondary mt-2 mb-0"
|
|
86
87
|
data-obsidian-local-graph-status
|
|
@@ -88,5 +89,5 @@
|
|
|
88
89
|
</div>
|
|
89
90
|
</div>
|
|
90
91
|
</aside>
|
|
91
|
-
<script src="{{ '/assets/js/obsidian-local-graph.js' | relative_url }}" defer></script>
|
|
92
|
+
<script src="{{ '/assets/js/obsidian-local-graph.js' | relative_url }}?v={{ site.time | date: '%s' }}" defer></script>
|
|
92
93
|
{%- endif -%}
|
|
@@ -64,9 +64,14 @@
|
|
|
64
64
|
{% endfor %}
|
|
65
65
|
</nav>
|
|
66
66
|
</div>
|
|
67
|
-
{%
|
|
67
|
+
{% comment %} Existence-gate the tags index: a remote-theme consumer without
|
|
68
|
+
the (plugin-generated) /tags/ page must not get a 404 link. Mirrors the
|
|
69
|
+
footer Quick-Links guard and honours the documented site.tags_page. {% endcomment %}
|
|
70
|
+
{% assign _tags_url = site.tags_page | default: "/tags/" %}
|
|
71
|
+
{% assign _tags_page = site.html_pages | where: "url", _tags_url | first %}
|
|
72
|
+
{% if sub_categories.size > 15 and _tags_page %}
|
|
68
73
|
<div class="card-footer bg-transparent">
|
|
69
|
-
<a href="{{
|
|
74
|
+
<a href="{{ _tags_url | relative_url }}" class="btn btn-sm btn-outline-secondary w-100">
|
|
70
75
|
View All Tags <i class="bi bi-arrow-right ms-1"></i>
|
|
71
76
|
</a>
|
|
72
77
|
</div>
|
|
@@ -123,11 +128,13 @@
|
|
|
123
128
|
{% endif %}
|
|
124
129
|
{% endfor %}
|
|
125
130
|
</nav>
|
|
131
|
+
{% if _tags_page %}
|
|
126
132
|
<div class="p-3">
|
|
127
|
-
<a href="{{
|
|
133
|
+
<a href="{{ _tags_url | relative_url }}" class="btn btn-outline-primary w-100">
|
|
128
134
|
<i class="bi bi-tags me-2"></i>Browse All Tags
|
|
129
135
|
</a>
|
|
130
136
|
</div>
|
|
137
|
+
{% endif %}
|
|
131
138
|
</div>
|
|
132
139
|
</div>
|
|
133
140
|
|
|
@@ -136,7 +136,7 @@ and [backlinks panel]({{ "/docs/obsidian/syntax-reference/#backlinks-panel" | re
|
|
|
136
136
|
<span id="obsidian-graph-status" role="status"></span>
|
|
137
137
|
</div>
|
|
138
138
|
|
|
139
|
-
<div id="obsidian-graph" role="
|
|
139
|
+
<div id="obsidian-graph" role="group" aria-label="Site knowledge graph"></div>
|
|
140
140
|
|
|
141
141
|
<div class="obsidian-graph-legend" aria-label="Graph legend">
|
|
142
142
|
<span><span class="swatch" style="background:#0d6efd"></span>Posts</span>
|
|
@@ -167,10 +167,12 @@ unresolved targets show up as dashed red nodes so you can find dangling
|
|
|
167
167
|
links at a glance. The graph is regenerated every Jekyll build; nothing
|
|
168
168
|
runs client-side except cytoscape's force layout.
|
|
169
169
|
|
|
170
|
-
<!-- Cytoscape.js
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
170
|
+
<!-- Cytoscape.js is vendored (no runtime CDN) and lazy-loaded by
|
|
171
|
+
obsidian-graph.js from window.OBSIDIAN_CONFIG.cytoscapeUrl, only on this
|
|
172
|
+
page, with an in-page recovery message if the fetch fails. -->
|
|
173
|
+
{% if site.obsidian.enabled != false %}
|
|
174
|
+
<script src="{{ '/assets/js/obsidian-graph.js' | relative_url }}?v={{ site.time | date: '%s' }}" defer></script>
|
|
175
|
+
{% endif %}
|
|
174
176
|
|
|
175
177
|
## See also
|
|
176
178
|
|
data/_layouts/article.html
CHANGED
|
@@ -160,16 +160,33 @@ layout: default
|
|
|
160
160
|
</div>
|
|
161
161
|
|
|
162
162
|
<!-- Category Link -->
|
|
163
|
-
{% comment %} Category base is configurable (`category_base`, default `/news`)
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
{% comment %} Category base is configurable (`category_base`, default `/news`).
|
|
164
|
+
Only link the badge when that category index page actually exists in the
|
|
165
|
+
build; on a remote-theme Pages consumer the category section is
|
|
166
|
+
plugin/page-generated and absent, so we render a plain badge instead of a
|
|
167
|
+
link that 404s (mirrors the tag-badge and footer Quick-Links guard).
|
|
168
|
+
See issue #204. {% endcomment %}
|
|
166
169
|
{% if page.categories.size > 0 %}
|
|
167
170
|
{% assign _category_base = site.category_base | default: '/news' %}
|
|
171
|
+
{% assign _primary_category = page.categories | first %}
|
|
172
|
+
{% capture _category_url %}{{ _category_base }}/{{ _primary_category | slugify }}/{% endcapture %}
|
|
173
|
+
{% comment %} Category index pages can live in site.pages (a regular page) or
|
|
174
|
+
site.posts (a dated section index), so check both. {% endcomment %}
|
|
175
|
+
{% assign _category_page = site.html_pages | where: "url", _category_url | first %}
|
|
176
|
+
{% unless _category_page %}
|
|
177
|
+
{% assign _category_page = site.posts | where: "url", _category_url | first %}
|
|
178
|
+
{% endunless %}
|
|
168
179
|
<div class="mb-3">
|
|
169
|
-
|
|
180
|
+
{% if _category_page %}
|
|
181
|
+
<a href="{{ site.baseurl }}{{ _category_url }}"
|
|
170
182
|
class="badge bg-primary text-decoration-none fs-6">
|
|
171
|
-
<i class="bi bi-folder me-1"></i>{{
|
|
183
|
+
<i class="bi bi-folder me-1"></i>{{ _primary_category }}
|
|
172
184
|
</a>
|
|
185
|
+
{% else %}
|
|
186
|
+
<span class="badge bg-primary fs-6">
|
|
187
|
+
<i class="bi bi-folder me-1"></i>{{ _primary_category }}
|
|
188
|
+
</span>
|
|
189
|
+
{% endif %}
|
|
173
190
|
</div>
|
|
174
191
|
{% endif %}
|
|
175
192
|
|
|
@@ -512,7 +529,7 @@ layout: default
|
|
|
512
529
|
<!-- ================================ -->
|
|
513
530
|
<!-- COMMENT SYSTEM -->
|
|
514
531
|
<!-- ================================ -->
|
|
515
|
-
{% if page.comments != false and site.giscus %}
|
|
532
|
+
{% if page.comments != false and site.giscus.enabled %}
|
|
516
533
|
<section class="post-comments mt-5 pt-4 border-top" id="comments">
|
|
517
534
|
<h2 class="h4 mb-4">
|
|
518
535
|
<i class="bi bi-chat-dots me-2"></i>Comments
|
data/_layouts/author.html
CHANGED
|
@@ -68,10 +68,31 @@ hide_intro: true
|
|
|
68
68
|
<!-- ========================== -->
|
|
69
69
|
<!-- BREADCRUMB -->
|
|
70
70
|
<!-- ========================== -->
|
|
71
|
+
{%- comment -%}
|
|
72
|
+
Only link the /authors/ breadcrumb crumb when that index page actually exists
|
|
73
|
+
in the build. A pure remote-theme Pages consumer doesn't get the
|
|
74
|
+
plugin-generated /authors/ index, so rendering the link would produce a 404.
|
|
75
|
+
Mirror the existence guard used in _includes/navigation/breadcrumbs.html
|
|
76
|
+
and _includes/components/author-bio.html. See issue #204.
|
|
77
|
+
{%- endcomment -%}
|
|
78
|
+
{%- assign _authors_url = '/authors/' -%}
|
|
79
|
+
{%- assign _authors_page = site.html_pages | where: "url", _authors_url | first -%}
|
|
80
|
+
{%- unless _authors_page -%}
|
|
81
|
+
{%- for _col in site.collections -%}
|
|
82
|
+
{%- assign _authors_page = _col.docs | where: "url", _authors_url | first -%}
|
|
83
|
+
{%- if _authors_page -%}{%- break -%}{%- endif -%}
|
|
84
|
+
{%- endfor -%}
|
|
85
|
+
{%- endunless -%}
|
|
71
86
|
<nav aria-label="breadcrumb" class="mb-3">
|
|
72
87
|
<ol class="breadcrumb">
|
|
73
88
|
<li class="breadcrumb-item"><a href="{{ '/' | relative_url }}">Home</a></li>
|
|
74
|
-
<li class="breadcrumb-item"
|
|
89
|
+
<li class="breadcrumb-item">
|
|
90
|
+
{%- if _authors_page -%}
|
|
91
|
+
<a href="{{ _authors_url | relative_url }}">Authors</a>
|
|
92
|
+
{%- else -%}
|
|
93
|
+
Authors
|
|
94
|
+
{%- endif -%}
|
|
95
|
+
</li>
|
|
75
96
|
<li class="breadcrumb-item active" aria-current="page">{{ author_name }}</li>
|
|
76
97
|
</ol>
|
|
77
98
|
</nav>
|
data/_layouts/root.html
CHANGED
|
@@ -31,7 +31,21 @@
|
|
|
31
31
|
-->
|
|
32
32
|
|
|
33
33
|
<!doctype html>
|
|
34
|
-
|
|
34
|
+
{%- comment -%}
|
|
35
|
+
Resolve the server-side data-bs-theme:
|
|
36
|
+
dark → dark
|
|
37
|
+
light → light
|
|
38
|
+
auto → dark (inline script in tokens-inline.html corrects this before first paint)
|
|
39
|
+
We also store the raw config value in data-color-mode-default so the inline
|
|
40
|
+
script can read it without a Liquid evaluation.
|
|
41
|
+
{%- endcomment -%}
|
|
42
|
+
{%- assign _cm = site.color_mode_default | default: 'auto' -%}
|
|
43
|
+
{%- if _cm == 'light' -%}
|
|
44
|
+
{%- assign _bs_theme = 'light' -%}
|
|
45
|
+
{%- else -%}
|
|
46
|
+
{%- assign _bs_theme = 'dark' -%}
|
|
47
|
+
{%- endif -%}
|
|
48
|
+
<html lang="{{ site.locale | slice: 0,2 | default: 'en' }}" class="no-js" data-bs-theme="{{ _bs_theme }}" data-color-mode-default="{{ _cm }}" data-theme-skin="{{ site.theme_skin | default: 'dark' }}" data-zer0-bg="{{ site.theme_background.enabled | default: true }}">
|
|
35
49
|
<head>
|
|
36
50
|
<!-- =============================================== -->
|
|
37
51
|
<!-- HEAD SECTION: Meta tags, styles, and SEO setup -->
|