hexo-theme-gnix 12.0.0 → 14.0.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 (45) hide show
  1. package/README.md +2 -0
  2. package/include/hexo/generator/archive.js +14 -1
  3. package/include/hexo/generator/index.js +0 -5
  4. package/include/hexo/generator/page.js +18 -4
  5. package/include/hexo/generator/tag.js +1 -1
  6. package/include/hexo/helper.js +0 -4
  7. package/include/hexo/i18n.js +31 -136
  8. package/include/hexo/obsidian-callouts.js +210 -0
  9. package/include/hexo/renderer.js +4 -14
  10. package/include/hexo/shiki.js +191 -0
  11. package/include/hexo/sitemap.js +184 -0
  12. package/include/util/i18n.js +92 -106
  13. package/languages/en.yml +4 -10
  14. package/languages/zh-CN.yml +4 -10
  15. package/layout/archive.jsx +155 -78
  16. package/layout/common/article.jsx +94 -108
  17. package/layout/common/article_cover.jsx +3 -3
  18. package/layout/common/article_info.jsx +11 -48
  19. package/layout/common/article_media.jsx +9 -2
  20. package/layout/common/footer.jsx +17 -106
  21. package/layout/common/head.jsx +3 -15
  22. package/layout/common/navbar.jsx +24 -87
  23. package/layout/common/scripts.jsx +1 -1
  24. package/layout/layout.jsx +37 -19
  25. package/layout/plugin/goatcounter.jsx +25 -0
  26. package/layout/tag.jsx +3 -70
  27. package/layout/tags.jsx +26 -23
  28. package/package.json +7 -13
  29. package/scripts/index.js +1 -0
  30. package/source/css/archive.css +287 -168
  31. package/source/css/callout_blocks.css +41 -21
  32. package/source/css/default.css +154 -132
  33. package/source/css/optional/mermaid.css +12 -6
  34. package/source/css/responsive.css +1 -45
  35. package/source/css/shiki/shiki.css +5 -4
  36. package/source/css/tags.css +53 -59
  37. package/source/js/components/archive-popup.js +313 -0
  38. package/source/js/components/friends-list.js +270 -0
  39. package/source/js/components/x-info-card.js +297 -0
  40. package/source/js/main.js +38 -34
  41. package/source/js/mdit/mermaid.js +10 -0
  42. package/include/hexo/generator/home.js +0 -64
  43. package/layout/index.jsx +0 -19
  44. package/layout/misc/paginator.jsx +0 -69
  45. package/source/js/host/iconify-icon/3.0.2/iconify-icon.min.js +0 -12
@@ -1,36 +1,11 @@
1
1
  const { Component } = require("../../include/util/common");
2
2
 
3
- function getTranslatedValue(helper, key, fallback) {
4
- const translated = helper.__(key);
5
- return translated === key ? fallback : translated;
6
- }
7
-
8
- function getTranslationMethodLabel(method, helper) {
9
- if (!method) return "";
10
- const key = `article.translation_methods.${method}`;
11
- return getTranslatedValue(helper, key, method);
12
- }
13
-
14
- function getOriginalWorkValue(page, helper) {
15
- const value = page.i18n?.original ?? page.original;
16
- if (typeof value !== "boolean") return null;
17
- return value ? helper.__("article.yes") : helper.__("article.no");
18
- }
19
-
20
- function getTranslationNote(page, helper) {
21
- const translation = page.i18n?.translation || page.translation;
22
- if (!translation) return null;
23
- if (typeof translation === "string") return translation;
24
-
25
- const parts = [];
26
- const method = getTranslationMethodLabel(translation.method, helper);
27
- if (method) parts.push(method);
28
- if (typeof translation.reviewed === "boolean") {
29
- parts.push(helper.__(translation.reviewed ? "article.translation_reviewed" : "article.translation_not_reviewed"));
30
- }
31
- if (translation.note) parts.push(translation.note);
32
-
33
- return parts.length ? parts.join(" · ") : null;
3
+ function getTranslationInfo(page, helper) {
4
+ if (!page.i18n) return null;
5
+ const t = page.i18n.translation;
6
+ if (t === 1) return helper.__("article.translation_llm_reviewed");
7
+ if (t === 2) return helper.__("article.translation_llm_unreviewed");
8
+ return helper.__("article.translation_original");
34
9
  }
35
10
 
36
11
  function LanguageIcon({ title }) {
@@ -51,8 +26,7 @@ module.exports = class extends Component {
51
26
  render() {
52
27
  const { page, config, helper } = this.props;
53
28
  const { article } = config;
54
- const originalWorkValue = getOriginalWorkValue(page, helper);
55
- const translationNote = getTranslationNote(page, helper);
29
+ const translationInfo = getTranslationInfo(page, helper);
56
30
 
57
31
  const markdownSourceUrl = page.markdown_path ? helper.url_for(page.markdown_path) : null;
58
32
  const markdownSourceLabel = helper.__("article.markdown_source");
@@ -171,25 +145,14 @@ module.exports = class extends Component {
171
145
  </div>
172
146
  </div>
173
147
  )}
174
- {originalWorkValue && (
175
- <div class="article-info-item">
176
- <div class="article-info-icon">
177
- <LanguageIcon title={helper.__("article.original_work")} />
178
- </div>
179
- <div class="article-info-content">
180
- <span class="article-info-label">{helper.__("article.original_work")}</span>
181
- <span class="article-info-value">{originalWorkValue}</span>
182
- </div>
183
- </div>
184
- )}
185
- {translationNote && (
148
+ {translationInfo && (
186
149
  <div class="article-info-item">
187
150
  <div class="article-info-icon">
188
- <LanguageIcon title={helper.__("article.translation_note")} />
151
+ <LanguageIcon title={helper.__("article.translation_info")} />
189
152
  </div>
190
153
  <div class="article-info-content">
191
- <span class="article-info-label">{helper.__("article.translation_note")}</span>
192
- <span class="article-info-value">{translationNote}</span>
154
+ <span class="article-info-label">{helper.__("article.translation_info")}</span>
155
+ <span class="article-info-value">{translationInfo}</span>
193
156
  </div>
194
157
  </div>
195
158
  )}
@@ -12,11 +12,15 @@ function formatDate(date, dateXml) {
12
12
 
13
13
  module.exports = class extends Component {
14
14
  render() {
15
- const { url, title, date, dateXml } = this.props;
15
+ const { url, title, date, dateXml, excerpt, readTime } = this.props;
16
16
  const formattedDate = formatDate(date, dateXml);
17
+ const hasPreview = excerpt || readTime;
17
18
 
18
19
  return (
19
- <article class="archive-item">
20
+ <article
21
+ class={hasPreview ? "archive-item has-preview" : "archive-item"}
22
+ data-read-time={readTime || null}
23
+ >
20
24
  <div>
21
25
  <p class="article-meta">
22
26
  <time dateTime={dateXml || null}>{formattedDate}</time>
@@ -25,6 +29,9 @@ module.exports = class extends Component {
25
29
  {title}
26
30
  </a>
27
31
  </div>
32
+ {excerpt && (
33
+ <template class="archive-item__excerpt" dangerouslySetInnerHTML={{ __html: excerpt }}></template>
34
+ )}
28
35
  </article>
29
36
  );
30
37
  }
@@ -2,92 +2,28 @@ const { Component, cacheComponent } = require("../../include/util/common");
2
2
 
3
3
  class Footer extends Component {
4
4
  render() {
5
- const { siteTitle, siteYear, author, links, subdomains, copyright, showVisitorCounter, visitorCounterTitle, ICPRecord } = this.props;
5
+ const { copyright, showVisitorCounter, visitorCounterTitle, ICPRecord } = this.props;
6
6
 
7
- const svg_line = (
8
- <svg aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
9
- <pattern id="a" width="91" height="8" patternUnits="userSpaceOnUse">
10
- <g clip-path="url(#clip0_2426_11367)">
11
- <path
12
- d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0"
13
- stroke="var(--lavender)"
14
- stroke-linecap="square"
15
- ></path>
16
- </g>
17
- </pattern>
18
- <rect width="100%" height="100%" fill="url(#a)"></rect>
19
- </svg>
20
- );
21
-
22
- const footer_brand = (
23
- <div class="footer-column footer-brand">
24
- <p class="footer-title">{author || siteTitle}</p>
25
- <p class="footer-meta">
26
- <span
27
- dangerouslySetInnerHTML={{
28
- __html: `&copy; 2022 - ${siteYear}`,
29
- }}
30
- ></span>
31
- &nbsp;Powered by Hexo
7
+ return (
8
+ <footer class="footer">
9
+ <div class="footer-brand">
10
+ <p class="footer-credit">
11
+ 2022&ndash;PRESENT&ensp;<span class="footer-author">© GnixAij Oag</span>&ensp;
12
+ <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="license noopener noreferrer">
13
+ CC BY-NC-SA 4.0
14
+ </a>
15
+ </p>
32
16
  {showVisitorCounter ? (
33
- <>
34
- <br />
17
+ <p class="footer-meta">
35
18
  <span id="busuanzi_container_site_uv" dangerouslySetInnerHTML={{ __html: visitorCounterTitle }}></span>
36
- </>
19
+ </p>
37
20
  ) : null}
38
21
  {ICPRecord ? (
39
- <>
40
- <br />
41
- <a href="https://beian.miit.gov.cn/" style={"color: inherit"} target="_blank" rel="noopener" dangerouslySetInnerHTML={{ __html: ICPRecord }}></a>
42
- </>
22
+ <p class="footer-meta">
23
+ <a href="https://beian.miit.gov.cn/" class="footer-icp" target="_blank" rel="noopener" dangerouslySetInnerHTML={{ __html: ICPRecord }}></a>
24
+ </p>
43
25
  ) : null}
44
- </p>
45
- {copyright ? <p class="footer-meta" dangerouslySetInnerHTML={{ __html: copyright }}></p> : null}
46
- </div>
47
- );
48
-
49
- const footer_social = (
50
- <div class="footer-column footer-social">
51
- <p class="footer-heading">Social Media</p>
52
- <div class="footer-links">
53
- {Object.keys(links).length
54
- ? Object.keys(links).map((name) => {
55
- const link = links[name];
56
- return (
57
- <a class="footer-link" target="_blank" rel="noopener" title={name} href={link.url}>
58
- {link.icon ? <iconify-icon icon={link.icon}></iconify-icon> : name}
59
- </a>
60
- );
61
- })
62
- : null}
63
- </div>
64
- </div>
65
- );
66
-
67
- const footer_subdomains = (
68
- <div class="footer-column footer-subdomains">
69
- <p class="footer-heading">Quick Links</p>
70
- <div class="footer-links">
71
- {Object.keys(subdomains).length
72
- ? Object.keys(subdomains).map((name) => {
73
- const link = subdomains[name];
74
- return (
75
- <a class="footer-link" target="_self" rel="noopener" title={name} href={link.url}>
76
- {name}
77
- </a>
78
- );
79
- })
80
- : null}
81
- </div>
82
- </div>
83
- );
84
-
85
- return (
86
- <footer class="footer">
87
- <div class="footer-grid">
88
- {footer_brand}
89
- {footer_subdomains}
90
- {footer_social}
26
+ {copyright ? <p class="footer-meta" dangerouslySetInnerHTML={{ __html: copyright }}></p> : null}
91
27
  </div>
92
28
  </footer>
93
29
  );
@@ -96,33 +32,10 @@ class Footer extends Component {
96
32
 
97
33
  module.exports = cacheComponent(Footer, "common.footer", (props) => {
98
34
  const { config, helper, page, site } = props;
99
- const { url_for, _p, date } = helper;
35
+ const { _p, date } = helper;
100
36
  const { title, author, footer, plugins } = config;
101
37
  const langKey = helper.language_key(page);
102
38
 
103
- const links = {};
104
- if (footer?.links) {
105
- Object.keys(footer.links).forEach((name) => {
106
- const link = footer.links[name];
107
- links[name] = {
108
- url: url_for(typeof link === "string" ? link : link.url),
109
- icon: link.icon,
110
- };
111
- });
112
- }
113
-
114
- const subdomains = {};
115
- if (footer?.subdomains) {
116
- Object.keys(footer.subdomains).forEach((name) => {
117
- const link = footer.subdomains[name];
118
- const targetUrl = typeof link === "string" ? link : link.url;
119
- subdomains[name] = {
120
- url: helper.localized_url_for(targetUrl, langKey),
121
- };
122
- });
123
- }
124
-
125
- // Build archives grouped by year from site posts
126
39
  let archives = [];
127
40
  if (site?.posts?.length) {
128
41
  const archiveDir = config.archive_dir || "archives";
@@ -148,8 +61,6 @@ module.exports = cacheComponent(Footer, "common.footer", (props) => {
148
61
  siteTitle: title,
149
62
  siteYear: date(new Date(), "YYYY"),
150
63
  author,
151
- links,
152
- subdomains,
153
64
  archives,
154
65
  copyright: footer?.copyright ?? "",
155
66
  showVisitorCounter: plugins && plugins.busuanzi === true,
@@ -5,7 +5,7 @@ const StructuredData = require("../../layout/misc/structured_data");
5
5
  const Plugins = require("./plugins");
6
6
  const { getArticleFontInitScript } = require("../../include/util/article_font");
7
7
  const { getThemeInitScript } = require("../../include/util/theme");
8
- const { getDefaultLanguageKey, getI18nKey, getLanguage, getPageLanguageKey, getPageLocale, isI18nEnabled, normalizeLocale, toArray } = require("../../include/util/i18n");
8
+ const { getDefaultLanguageKey, getLanguage, getPageLanguageKey, getPageLocale, isI18nEnabled, normalizeLocale } = require("../../include/util/i18n");
9
9
  const fs = require("node:fs");
10
10
  const path = require("node:path");
11
11
 
@@ -82,27 +82,15 @@ function addExplicitAlternates(links, alternates, helper, config) {
82
82
  });
83
83
  }
84
84
 
85
- function collectDocuments(site) {
86
- return [...toArray(site?.posts), ...toArray(site?.pages)];
87
- }
88
-
89
85
  function getHreflangLinks(site, page, config, helper) {
90
86
  if (!isI18nEnabled(config)) return [];
91
87
 
92
88
  const links = new Map();
93
- const pageKey = getI18nKey(page);
94
89
  const langKey = getPageLanguageKey(page, config);
95
90
  const locale = getPageLocale(page, config);
96
91
 
97
92
  addAlternateLink(links, locale, page.permalink || page.path, helper, config);
98
- addExplicitAlternates(links, page.i18n?.alternates || page.alternates || page.hreflang, helper, config);
99
-
100
- if (pageKey) {
101
- collectDocuments(site).forEach((item) => {
102
- if (!item || getI18nKey(item) !== pageKey) return;
103
- addAlternateLink(links, getPageLocale(item, config), item.permalink || item.path, helper, config);
104
- });
105
- }
93
+ addExplicitAlternates(links, page.i18n, helper, config);
106
94
 
107
95
  if (links.size > 1 && !links.has("x-default")) {
108
96
  const defaultLanguage = getLanguage(config, getDefaultLanguageKey(config));
@@ -174,10 +162,10 @@ module.exports = class extends Component {
174
162
 
175
163
  return (
176
164
  <head>
165
+ <meta charset="utf-8" />
177
166
  <script dangerouslySetInnerHTML={{ __html: themeInitScript }}></script>
178
167
  <script dangerouslySetInnerHTML={{ __html: articleFontUtilsScript }}></script>
179
168
  <script dangerouslySetInnerHTML={{ __html: articleFontInitScript }}></script>
180
- <meta charset="utf-8" />
181
169
  <meta name="viewport" content="width=device-width, initial-scale=1" />
182
170
  {noIndex ? <meta name="robots" content="noindex" /> : null}
183
171
  {meta?.length ? <MetaTags meta={meta} /> : null}
@@ -1,25 +1,5 @@
1
1
  const { Component, Fragment, cacheComponent } = require("../../include/util/common");
2
- const { getI18nKey, getLanguage, getLanguageKeys, getPageLanguageKey, isExternalUrl, isI18nEnabled, toArray } = require("../../include/util/i18n");
3
-
4
- function isActiveMenuLink(menuUrl, pageUrl) {
5
- const menuPath = sanitizeLink(menuUrl);
6
- const pagePath = sanitizeLink(pageUrl);
7
- if (menuPath === pagePath) return true;
8
- if (!menuPath) return false;
9
- return pagePath.startsWith(`${menuPath}/`);
10
- }
11
-
12
- function sanitizeLink(url) {
13
- let paths = url
14
- .replace(/(^\w+:|^)\/\//, "")
15
- .split("#")[0]
16
- .split("/")
17
- .filter((p) => p.trim() !== "");
18
- if (paths.length > 0 && paths[paths.length - 1].trim() === "index.html") {
19
- paths = paths.slice(0, paths.length - 1);
20
- }
21
- return paths.join("/");
22
- }
2
+ const { getLanguage, getLanguageKeys, getPageLanguageKey, isExternalUrl, isI18nEnabled } = require("../../include/util/i18n");
23
3
 
24
4
  const renderLinkIcon = (link) => {
25
5
  if (!link.icon) return null;
@@ -33,19 +13,8 @@ const renderLinkIcon = (link) => {
33
13
  </svg>
34
14
  );
35
15
  }
36
- return <iconify-icon icon={link.icon}></iconify-icon>;
37
16
  };
38
17
 
39
- function collectDocuments(site) {
40
- return [...toArray(site?.posts), ...toArray(site?.pages)];
41
- }
42
-
43
- function getDocumentUrl(helper, document) {
44
- const href = document?.path || document?.permalink;
45
- if (!href) return null;
46
- return isExternalUrl(href) ? href : helper.url_for(href);
47
- }
48
-
49
18
  function getTargetLanguageKey(config, currentLanguageKey) {
50
19
  const languageKeys = getLanguageKeys(config);
51
20
  if (currentLanguageKey === "en" && languageKeys.includes("cn")) return "cn";
@@ -71,22 +40,18 @@ function getLanguageSwitch(site, page, config, helper) {
71
40
  if (!targetLanguage || targetLanguageKey === currentLanguageKey) return null;
72
41
  const targetLanguageName = getTargetLanguageDisplayName(currentLanguageKey, targetLanguageKey, targetLanguage);
73
42
 
74
- const pageKey = getI18nKey(page);
75
43
  let url = null;
76
-
77
- if (pageKey) {
78
- const alternate = collectDocuments(site).find((item) => getI18nKey(item) === pageKey && getPageLanguageKey(item, config) === targetLanguageKey);
79
- url = getDocumentUrl(helper, alternate);
44
+ const pageI18n = page.i18n;
45
+ if (pageI18n && typeof pageI18n === "object" && targetLanguageKey) {
46
+ const altUrl = pageI18n[targetLanguageKey] || (targetLanguage ? pageI18n[targetLanguage.locale] : null);
47
+ if (altUrl) {
48
+ url = isExternalUrl(altUrl) ? altUrl : helper.url_for(altUrl);
49
+ }
80
50
  }
81
51
 
82
52
  const isDocumentPage = ["page", "post"].includes(page?.layout);
83
53
  const title = helper.__("navbar.language_switch", targetLanguageName);
84
- const homeUrl = helper.localized_url_for("/", currentLanguageKey);
85
- const unavailableTitle = helper.__("navbar.language_unavailable_title");
86
54
  const unavailableMessage = helper.__("navbar.language_unavailable", targetLanguageName);
87
- const stayLabel = helper.__("navbar.language_stay");
88
- const homeLabel = helper.__("navbar.language_home");
89
- const closeLabel = helper.__("navbar.language_close");
90
55
 
91
56
  const mode = !url && isDocumentPage ? "missing" : "link";
92
57
 
@@ -100,12 +65,7 @@ function getLanguageSwitch(site, page, config, helper) {
100
65
  locale: targetLanguage.locale,
101
66
  label: targetLanguageName,
102
67
  title,
103
- homeUrl,
104
- unavailableTitle,
105
68
  unavailableMessage,
106
- stayLabel,
107
- homeLabel,
108
- closeLabel,
109
69
  };
110
70
  }
111
71
 
@@ -127,7 +87,7 @@ class Navbar extends Component {
127
87
  <Fragment>
128
88
  <nav class="navbar navbar-main">
129
89
  <div class="navbar-container" onclick="toggleNav(event)">
130
- <a href={siteUrl} style={"font-family: homemade-apple; color: var(--text); display: flex; align-items: center; padding: 0 1em;"}>
90
+ <a id="navbar-logo-link" class="navbar-logo" href={siteUrl}>
131
91
  GnixAij
132
92
  </a>
133
93
  <div class="navbar-menu">
@@ -135,9 +95,8 @@ class Navbar extends Component {
135
95
  <div class="navbar-start">
136
96
  {Object.keys(menu).map((name) => {
137
97
  const item = menu[name];
138
- const navbar_item_class = `navbar-item ${item.active ? "is-active" : ""}`;
139
98
  return (
140
- <a class={navbar_item_class} href={item.url} aria-current={item.active ? "page" : null}>
99
+ <a class="navbar-item" href={item.url} data-navbar-menu={name}>
141
100
  {name}
142
101
  </a>
143
102
  );
@@ -158,14 +117,19 @@ class Navbar extends Component {
158
117
  </Fragment>
159
118
  ) : null}
160
119
  {languageSwitch ? (
161
- <Fragment>
162
- <a id="language-switch-link" class="navbar-item" href={languageSwitch.url || '#'} title={languageSwitch.title} aria-label={languageSwitch.title} lang={languageSwitch.locale} hreflang={languageSwitch.locale} style={languageSwitch.mode === 'link' ? '' : 'display:none'}>
163
- {languageIcon}
164
- </a>
165
- <button id="language-switch-button" type="button" class="navbar-item" title={languageSwitch.title} aria-label={languageSwitch.title} lang={languageSwitch.locale} popovertarget="language-switch-popover" style={languageSwitch.mode === 'missing' ? '' : 'display:none'}>
166
- {languageIcon}
167
- </button>
168
- </Fragment>
120
+ <a
121
+ id="language-switch"
122
+ class="navbar-item"
123
+ href={languageSwitch.url || "#"}
124
+ title={languageSwitch.title}
125
+ aria-label={languageSwitch.title}
126
+ lang={languageSwitch.locale}
127
+ hreflang={languageSwitch.locale}
128
+ data-mode={languageSwitch.mode}
129
+ data-toast-message={languageSwitch.unavailableMessage}
130
+ >
131
+ {languageIcon}
132
+ </a>
169
133
  ) : null}
170
134
  <button type="button" class="navbar-item" title="Choose Theme" popovertarget="theme-selector-popover">
171
135
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
@@ -192,31 +156,6 @@ class Navbar extends Component {
192
156
  </button>
193
157
  </div>
194
158
  </nav>
195
- {languageSwitch ? (
196
- <div id="language-switch-popover" class="article-popover language-switch-popover" popover="auto" tabindex="-1">
197
- <div class="article-popover-header">
198
- <h3>{languageSwitch.unavailableTitle}</h3>
199
- <button type="button" class="article-popover-close" popovertarget="language-switch-popover" popovertargetaction="hide" aria-label={languageSwitch.closeLabel}>
200
- <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label={languageSwitch.closeLabel}>
201
- <title>{languageSwitch.closeLabel}</title>
202
- <path d="M18 6 6 18" />
203
- <path d="m6 6 12 12" />
204
- </svg>
205
- </button>
206
- </div>
207
- <div class="article-popover-body language-switch-popover-body">
208
- <p>{languageSwitch.unavailableMessage}</p>
209
- <div class="language-switch-popover-actions">
210
- <button type="button" class="language-switch-popover-action" popovertarget="language-switch-popover" popovertargetaction="hide">
211
- {languageSwitch.stayLabel}
212
- </button>
213
- <a id="language-switch-home-link" class="language-switch-popover-action is-primary" href={languageSwitch.homeUrl} onclick="document.getElementById('language-switch-popover')?.hidePopover()">
214
- {languageSwitch.homeLabel}
215
- </a>
216
- </div>
217
- </div>
218
- </div>
219
- ) : null}
220
159
  </Fragment>
221
160
  );
222
161
  }
@@ -233,11 +172,9 @@ module.exports = cacheComponent(Navbar, "common.navbar", (props) => {
233
172
 
234
173
  const menu = {};
235
174
  if (navbar?.menu) {
236
- const pageUrl = typeof page.path !== "undefined" ? url_for(page.path) : "";
237
175
  Object.keys(navbar.menu).forEach((name) => {
238
- const url = helper.localized_url_for(navbar.menu[name], langKey);
239
- const active = isActiveMenuLink(url, pageUrl);
240
- menu[name] = { url, active };
176
+ const rawValue = navbar.menu[name];
177
+ menu[name] = { url: helper.localized_url_for(rawValue, langKey) };
241
178
  });
242
179
  }
243
180
 
@@ -10,9 +10,9 @@ module.exports = class extends Component {
10
10
  <Fragment>
11
11
  <Plugins site={site} config={config} page={page} helper={helper} head={false} />
12
12
  <Swup head={false} />
13
- <script defer src="/js/host/iconify-icon/3.0.2/iconify-icon.min.js"></script>
14
13
  <script defer src="/js/theme-selector.js"></script>
15
14
  <script defer src="/js/host/medium-zoom/dist/medium-zoom.min.js"></script>
15
+ <script defer src="/js/components/archive-popup.js"></script>
16
16
  <script defer src="/js/main.js"></script>
17
17
  {page.encrypt ? <script src="/js/decrypt.js" type="module"></script> : null}
18
18
  <script async src="/js/instant-page.min.js" type="module"></script>
package/layout/layout.jsx CHANGED
@@ -9,32 +9,50 @@ const { DEFAULT_SETTINGS: ARTICLE_FONT_DEFAULT_SETTINGS } = require("../include/
9
9
 
10
10
  function buildLangSwitchScript(site, page, config, helper) {
11
11
  const lswitch = Navbar.getLanguageSwitch(site, page, config, helper);
12
- if (!lswitch) return "";
13
12
 
14
- const payload = JSON.stringify({
15
- mode: lswitch.mode,
16
- url: lswitch.url || "",
17
- locale: lswitch.locale,
18
- homeUrl: lswitch.homeUrl,
19
- });
13
+ const langKey = helper.language_key(page);
14
+ const menu = {};
15
+ if (config.navbar?.menu) {
16
+ Object.keys(config.navbar.menu).forEach((name) => {
17
+ const rawValue = config.navbar.menu[name];
18
+ menu[name] = { url: helper.localized_url_for(rawValue, langKey) };
19
+ });
20
+ }
21
+ const siteUrl = helper.localized_url_for("/", langKey);
22
+
23
+ const payload = lswitch
24
+ ? JSON.stringify({
25
+ mode: lswitch.mode,
26
+ url: lswitch.url || "",
27
+ locale: lswitch.locale,
28
+ title: lswitch.title,
29
+ unavailableMessage: lswitch.unavailableMessage,
30
+ })
31
+ : "null";
20
32
 
21
33
  return `<script data-swup-reload-script>
22
34
  (function() {
23
35
  var d = ${payload};
24
- var l = document.getElementById('language-switch-link');
25
- var b = document.getElementById('language-switch-button');
26
- if (!l && !b) return;
27
- if (d.mode === 'link' && d.url) {
28
- if (l) { l.href = d.url; l.style.display = ''; l.setAttribute('lang', d.locale); l.setAttribute('hreflang', d.locale); }
29
- if (b) b.style.display = 'none';
30
- } else {
31
- if (l) l.style.display = 'none';
32
- if (b) { b.style.display = ''; b.setAttribute('lang', d.locale); }
33
- var h = document.getElementById('language-switch-home-link');
34
- if (h) h.href = d.homeUrl;
36
+ var el = document.getElementById('language-switch');
37
+ if (el && d) {
38
+ el.href = d.mode === 'link' && d.url ? d.url : '#';
39
+ el.title = d.title;
40
+ el.setAttribute('aria-label', d.title);
41
+ el.setAttribute('lang', d.locale);
42
+ el.setAttribute('hreflang', d.locale);
43
+ el.dataset.mode = d.mode;
44
+ el.dataset.toastMessage = d.unavailableMessage;
35
45
  }
46
+ var menu = ${JSON.stringify(menu)};
47
+ Object.keys(menu).forEach(function(name) {
48
+ var link = document.querySelector('a[data-navbar-menu="' + name + '"]');
49
+ if (!link) return;
50
+ link.href = menu[name].url;
51
+ });
52
+ var logo = document.getElementById('navbar-logo-link');
53
+ if (logo) logo.href = ${JSON.stringify(siteUrl)};
36
54
  })();
37
- <\/script>`;
55
+ </script>`;
38
56
  }
39
57
 
40
58
  module.exports = class extends Component {
@@ -0,0 +1,25 @@
1
+ const { Component, Fragment, cacheComponent } = require("../../include/util/common");
2
+
3
+ class Goatcounter extends Component {
4
+ render() {
5
+ const { url } = this.props;
6
+
7
+ return (
8
+ <Fragment>
9
+ <script data-goatcounter={url} async src="//gc.zgo.at/count.js"></script>
10
+ </Fragment>
11
+ );
12
+ }
13
+ }
14
+
15
+ Goatcounter.Cacheable = cacheComponent(Goatcounter, "plugin.goatcounter", (props) => {
16
+ const { head, plugin } = props;
17
+ if (!head || !plugin.url) {
18
+ return null;
19
+ }
20
+ return {
21
+ url: plugin.url,
22
+ };
23
+ });
24
+
25
+ module.exports = Goatcounter;