hexo-theme-gnix 8.0.0 → 10.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 (66) hide show
  1. package/README.md +4 -2
  2. package/include/hexo/feed.js +6 -5
  3. package/include/hexo/filter.js +25 -1
  4. package/include/hexo/generator/archive.js +116 -0
  5. package/include/hexo/generator/home.js +64 -0
  6. package/include/hexo/generator/index.js +82 -0
  7. package/include/hexo/generator/md_generator.js +87 -0
  8. package/include/hexo/generator/page.js +55 -0
  9. package/include/hexo/generator/tag.js +84 -0
  10. package/include/hexo/helper.js +38 -0
  11. package/include/hexo/i18n.js +183 -0
  12. package/include/util/article_font.js +132 -0
  13. package/include/util/i18n.js +280 -0
  14. package/include/util/theme.js +84 -0
  15. package/languages/en.yml +28 -0
  16. package/languages/zh-CN.yml +28 -0
  17. package/layout/archive.jsx +131 -127
  18. package/layout/common/article.jsx +316 -34
  19. package/layout/common/article_info.jsx +339 -0
  20. package/layout/common/article_media.jsx +11 -4
  21. package/layout/common/comment.jsx +15 -7
  22. package/layout/common/footer.jsx +6 -5
  23. package/layout/common/head.jsx +122 -33
  24. package/layout/common/navbar.jsx +195 -65
  25. package/layout/common/theme_selector.jsx +16 -14
  26. package/layout/layout.jsx +43 -5
  27. package/layout/misc/open_graph.jsx +162 -66
  28. package/layout/misc/paginator.jsx +2 -8
  29. package/layout/plugin/cookie_consent.jsx +252 -53
  30. package/layout/plugin/swup.jsx +1 -1
  31. package/layout/search/insight.jsx +1 -1
  32. package/layout/tag.jsx +3 -2
  33. package/layout/tags.jsx +81 -73
  34. package/package.json +5 -5
  35. package/scripts/index.js +1 -0
  36. package/source/css/archive.css +225 -180
  37. package/source/css/default.css +1223 -126
  38. package/source/css/responsive.css +426 -0
  39. package/source/css/shiki/shiki.css +12 -2081
  40. package/source/css/tags.css +183 -0
  41. package/source/css/twikoo.css +1053 -1049
  42. package/source/img/favicon.svg +1 -6
  43. package/source/img/og_image.webp +0 -0
  44. package/source/js/article-font-utils.js +99 -0
  45. package/source/js/busuanzi.js +91 -24
  46. package/source/js/components/chat.js +169 -50
  47. package/source/js/components/image-carousel.js +152 -108
  48. package/source/js/components/sidenote.js +210 -0
  49. package/source/js/components/text-image-section.js +78 -90
  50. package/source/js/components/theme-stacked.js +65 -33
  51. package/source/js/components/tree.js +30 -16
  52. package/source/js/decrypt.js +7 -2
  53. package/source/js/main.js +428 -5
  54. package/source/js/swup.js +39 -0
  55. package/source/js/theme-selector.js +26 -16
  56. package/include/hexo/generator.js +0 -53
  57. package/layout/misc/article_licensing.jsx +0 -99
  58. package/source/css/responsive/desktop.css +0 -36
  59. package/source/css/responsive/mobile.css +0 -38
  60. package/source/css/responsive/tablet.css +0 -43
  61. package/source/css/responsive/touch.css +0 -155
  62. package/source/img/logo.svg +0 -9
  63. package/source/js/archive-breadcrumb.js +0 -132
  64. package/source/js/host/cookieconsent/3.1.1/build/cookieconsent.min.css +0 -6
  65. package/source/js/host/cookieconsent/3.1.1/build/cookieconsent.min.js +0 -1
  66. package/source/js/swup.bundle.js +0 -1
@@ -1,18 +1,24 @@
1
1
  const { Component, Fragment, cacheComponent } = require("../../include/util/common");
2
+ const { getI18nKey, getLanguage, getLanguageKeys, getPageLanguageKey, isExternalUrl, isI18nEnabled, toArray } = require("../../include/util/i18n");
2
3
 
3
- function isSameLink(a, b) {
4
- function santize(url) {
5
- let paths = url
6
- .replace(/(^\w+:|^)\/\//, "")
7
- .split("#")[0]
8
- .split("/")
9
- .filter((p) => p.trim() !== "");
10
- if (paths.length > 0 && paths[paths.length - 1].trim() === "index.html") {
11
- paths = paths.slice(0, paths.length - 1);
12
- }
13
- return paths.join("/");
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);
14
20
  }
15
- return santize(a) === santize(b);
21
+ return paths.join("/");
16
22
  }
17
23
 
18
24
  const renderLinkIcon = (link) => {
@@ -30,76 +36,197 @@ const renderLinkIcon = (link) => {
30
36
  return <iconify-icon icon={link.icon}></iconify-icon>;
31
37
  };
32
38
 
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
+ function getTargetLanguageKey(config, currentLanguageKey) {
50
+ const languageKeys = getLanguageKeys(config);
51
+ if (currentLanguageKey === "en" && languageKeys.includes("cn")) return "cn";
52
+ if (currentLanguageKey !== "en" && languageKeys.includes("en")) return "en";
53
+ return languageKeys.find((key) => key !== currentLanguageKey) || null;
54
+ }
55
+
56
+ function getTargetLanguageDisplayName(currentLanguageKey, targetLanguageKey, targetLanguage) {
57
+ if (currentLanguageKey === "en" && targetLanguageKey === "cn") return "Chinese";
58
+ if (currentLanguageKey !== "en" && targetLanguageKey === "en") return "英文";
59
+ return targetLanguage.label;
60
+ }
61
+
62
+ function getLanguageSwitch(site, page, config, helper) {
63
+ if (!isI18nEnabled(config)) return null;
64
+
65
+ const languageKeys = getLanguageKeys(config);
66
+ if (languageKeys.length < 2) return null;
67
+
68
+ const currentLanguageKey = getPageLanguageKey(page, config);
69
+ const targetLanguageKey = getTargetLanguageKey(config, currentLanguageKey);
70
+ const targetLanguage = getLanguage(config, targetLanguageKey);
71
+ if (!targetLanguage || targetLanguageKey === currentLanguageKey) return null;
72
+ const targetLanguageName = getTargetLanguageDisplayName(currentLanguageKey, targetLanguageKey, targetLanguage);
73
+
74
+ const pageKey = getI18nKey(page);
75
+ 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);
80
+ }
81
+
82
+ const isDocumentPage = ["page", "post"].includes(page?.layout);
83
+ const title = helper.__("navbar.language_switch", targetLanguageName);
84
+ const homeUrl = helper.localized_url_for("/", currentLanguageKey);
85
+ const unavailableTitle = helper.__("navbar.language_unavailable_title");
86
+ 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
+
91
+ const mode = !url && isDocumentPage ? "missing" : "link";
92
+
93
+ if (!url && mode === "link") {
94
+ url = helper.localized_url_for(page?.path || "/", targetLanguageKey);
95
+ }
96
+
97
+ return {
98
+ mode,
99
+ url: url || null,
100
+ locale: targetLanguage.locale,
101
+ label: targetLanguageName,
102
+ title,
103
+ homeUrl,
104
+ unavailableTitle,
105
+ unavailableMessage,
106
+ stayLabel,
107
+ homeLabel,
108
+ closeLabel,
109
+ };
110
+ }
111
+
33
112
  class Navbar extends Component {
34
113
  render() {
35
- const { siteUrl, menu, links, searchTitle } = this.props;
114
+ const { siteUrl, menu, links, languageSwitch, searchTitle } = this.props;
115
+
116
+ const languageIcon = (
117
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
118
+ <title>translate_2_line</title>
119
+ <g id="translate_2_line" fill="currentColor">
120
+ <path d="M12.87 15.07c-1.01-.28-1.93-.79-2.72-1.48l-.16-.14-.19.18a9.96 9.96 0 0 1-3.27 1.98 1 1 0 0 1-.71-1.87 7.96 7.96 0 0 0 2.58-1.55 9.5 9.5 0 0 1-1.54-2.69 1 1 0 1 1 1.88-.69c.29.79.71 1.52 1.24 2.16A7.95 7.95 0 0 0 11.66 8H5a1 1 0 1 1 0-2h4V4a1 1 0 1 1 2 0v2h4a1 1 0 1 1 0 2h-1.29a9.95 9.95 0 0 1-2.27 4.44l.05.04c.55.38 1.18.67 1.86.86a1 1 0 1 1-.48 1.94z"></path>
121
+ <path d="M17.5 10a1 1 0 0 1 .93.63l3.5 8.75a1 1 0 0 1-1.86.74L19.42 18h-3.84l-.65 2.12a1 1 0 0 1-1.86-.74l3.5-8.75A1 1 0 0 1 17.5 10m-1.15 6h2.3l-1.15-3.03z"></path>
122
+ </g>
123
+ </svg>
124
+ );
36
125
 
37
126
  return (
38
- <nav class="navbar navbar-main">
39
- <div class="navbar-container" onclick="toggleNav(event)">
40
- <a href={siteUrl} style={"font-family: homemade-apple; color: var(--text); display: flex; align-items: center; padding: 0 1em;"}>
41
- GnixAij
42
- </a>
43
- <div class="navbar-menu">
44
- {Object.keys(menu).length ? (
45
- <div class="navbar-start">
46
- {Object.keys(menu).map((name) => {
47
- const item = menu[name];
48
- const navbar_item_class = `navbar-item ${item.active ? "is-active" : ""}`;
49
- return (
50
- <a class={navbar_item_class} href={item.url}>
51
- {name}
52
- </a>
53
- );
54
- })}
55
- </div>
56
- ) : null}
57
- <div class="navbar-end">
58
- {Object.keys(links).length ? (
59
- <Fragment>
60
- {Object.keys(links).map((name) => {
61
- const link = links[name];
127
+ <Fragment>
128
+ <nav class="navbar navbar-main">
129
+ <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;"}>
131
+ GnixAij
132
+ </a>
133
+ <div class="navbar-menu">
134
+ {Object.keys(menu).length ? (
135
+ <div class="navbar-start">
136
+ {Object.keys(menu).map((name) => {
137
+ const item = menu[name];
138
+ const navbar_item_class = `navbar-item ${item.active ? "is-active" : ""}`;
62
139
  return (
63
- <a class="navbar-item" target="_blank" rel="noopener" title={name} href={link.url}>
64
- {renderLinkIcon(link)}
140
+ <a class={navbar_item_class} href={item.url} aria-current={item.active ? "page" : null}>
141
+ {name}
65
142
  </a>
66
143
  );
67
144
  })}
68
- </Fragment>
145
+ </div>
69
146
  ) : null}
70
- <button type="button" class="navbar-item" title="Choose Theme" popovertarget="theme-selector-popover">
71
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
72
- <title>brightness_fill</title>
73
- <g id="brightness_fill" fill="currentColor">
74
- <path d="M12 18.5a1.5 1.5 0 0 1 1.493 1.356L13.5 20v1a1.5 1.5 0 0 1-2.993.144L10.5 21v-1a1.5 1.5 0 0 1 1.5-1.5m4.596-1.904a1.5 1.5 0 0 1 2.008-.103l.114.103.707.707a1.5 1.5 0 0 1-2.008 2.225l-.114-.103-.707-.707a1.5 1.5 0 0 1 0-2.122m-11.314 0a1.5 1.5 0 0 1 2.225 2.008l-.103.114-.707.707a1.5 1.5 0 0 1-2.225-2.008l.103-.114zM12 6a6 6 0 1 1 0 12 6 6 0 0 1 0-12m0 3a3 3 0 0 0-.176 5.995L12 15zm-8 1.5a1.5 1.5 0 0 1 .144 2.993L4 13.5H3a1.5 1.5 0 0 1-.144-2.993L3 10.5zm17 0a1.5 1.5 0 0 1 .144 2.993L21 13.5h-1a1.5 1.5 0 0 1-.144-2.993L20 10.5zM4.575 4.575a1.5 1.5 0 0 1 2.008-.103l.114.103.707.707a1.5 1.5 0 0 1-2.008 2.225l-.114-.103-.707-.707a1.5 1.5 0 0 1 0-2.122m12.728 0a1.5 1.5 0 0 1 2.225 2.008l-.103.114-.707.707a1.5 1.5 0 0 1-2.225-2.008l.103-.114zM12 1.5a1.5 1.5 0 0 1 1.493 1.356L13.5 3v1a1.5 1.5 0 0 1-2.993.144L10.5 4V3A1.5 1.5 0 0 1 12 1.5"></path>
75
- </g>
76
- </svg>
77
- </button>
78
- <button type="button" class="navbar-item search" popovertarget="searchbox" title={searchTitle}>
79
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
80
- <title>search_line</title>
81
- <g id="search_line" fill="currentColor">
82
- <path d="M10.5 2a8.5 8.5 0 1 0 5.262 15.176l3.652 3.652a1 1 0 0 0 1.414-1.414l-3.652-3.652A8.5 8.5 0 0 0 10.5 2M4 10.5a6.5 6.5 0 1 1 13 0 6.5 6.5 0 0 1-13 0"></path>
83
- </g>
147
+ <div class="navbar-end">
148
+ {Object.keys(links).length ? (
149
+ <Fragment>
150
+ {Object.keys(links).map((name) => {
151
+ const link = links[name];
152
+ return (
153
+ <a class="navbar-item" target="_blank" rel="noopener" title={name} href={link.url}>
154
+ {renderLinkIcon(link)}
155
+ </a>
156
+ );
157
+ })}
158
+ </Fragment>
159
+ ) : null}
160
+ {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>
169
+ ) : null}
170
+ <button type="button" class="navbar-item" title="Choose Theme" popovertarget="theme-selector-popover">
171
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
172
+ <title>brightness_fill</title>
173
+ <g id="brightness_fill" fill="currentColor">
174
+ <path d="M12 18.5a1.5 1.5 0 0 1 1.493 1.356L13.5 20v1a1.5 1.5 0 0 1-2.993.144L10.5 21v-1a1.5 1.5 0 0 1 1.5-1.5m4.596-1.904a1.5 1.5 0 0 1 2.008-.103l.114.103.707.707a1.5 1.5 0 0 1-2.008 2.225l-.114-.103-.707-.707a1.5 1.5 0 0 1 0-2.122m-11.314 0a1.5 1.5 0 0 1 2.225 2.008l-.103.114-.707.707a1.5 1.5 0 0 1-2.225-2.008l.103-.114zM12 6a6 6 0 1 1 0 12 6 6 0 0 1 0-12m0 3a3 3 0 0 0-.176 5.995L12 15zm-8 1.5a1.5 1.5 0 0 1 .144 2.993L4 13.5H3a1.5 1.5 0 0 1-.144-2.993L3 10.5zm17 0a1.5 1.5 0 0 1 .144 2.993L21 13.5h-1a1.5 1.5 0 0 1-.144-2.993L20 10.5zM4.575 4.575a1.5 1.5 0 0 1 2.008-.103l.114.103.707.707a1.5 1.5 0 0 1-2.008 2.225l-.114-.103-.707-.707a1.5 1.5 0 0 1 0-2.122m12.728 0a1.5 1.5 0 0 1 2.225 2.008l-.103.114-.707.707a1.5 1.5 0 0 1-2.225-2.008l.103-.114zM12 1.5a1.5 1.5 0 0 1 1.493 1.356L13.5 3v1a1.5 1.5 0 0 1-2.993.144L10.5 4V3A1.5 1.5 0 0 1 12 1.5"></path>
175
+ </g>
176
+ </svg>
177
+ </button>
178
+ <button type="button" class="navbar-item search" popovertarget="searchbox" title={searchTitle}>
179
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
180
+ <title>search_line</title>
181
+ <g id="search_line" fill="currentColor">
182
+ <path d="M10.5 2a8.5 8.5 0 1 0 5.262 15.176l3.652 3.652a1 1 0 0 0 1.414-1.414l-3.652-3.652A8.5 8.5 0 0 0 10.5 2M4 10.5a6.5 6.5 0 1 1 13 0 6.5 6.5 0 0 1-13 0"></path>
183
+ </g>
184
+ </svg>
185
+ </button>
186
+ </div>
187
+ </div>
188
+ <button type="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
189
+ <span aria-hidden="true"></span>
190
+ <span aria-hidden="true"></span>
191
+ <span aria-hidden="true"></span>
192
+ </button>
193
+ </div>
194
+ </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" />
84
204
  </svg>
85
205
  </button>
86
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>
87
218
  </div>
88
- <button type="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
89
- <span aria-hidden="true"></span>
90
- <span aria-hidden="true"></span>
91
- <span aria-hidden="true"></span>
92
- </button>
93
- </div>
94
- </nav>
219
+ ) : null}
220
+ </Fragment>
95
221
  );
96
222
  }
97
223
  }
98
224
 
99
225
  module.exports = cacheComponent(Navbar, "common.navbar", (props) => {
100
- const { config, helper, page } = props;
226
+ const { site, config, helper, page } = props;
101
227
  const { url_for, _p, __ } = helper;
102
228
  const { logo, title, navbar, widgets } = config;
229
+ const langKey = helper.language_key(page);
103
230
 
104
231
  const hasTocWidget = Array.isArray(widgets) && widgets.find((widget) => widget.type === "toc");
105
232
  const showToc = (config.toc === true || page.toc) && hasTocWidget && ["page", "post"].includes(page.layout);
@@ -108,8 +235,8 @@ module.exports = cacheComponent(Navbar, "common.navbar", (props) => {
108
235
  if (navbar?.menu) {
109
236
  const pageUrl = typeof page.path !== "undefined" ? url_for(page.path) : "";
110
237
  Object.keys(navbar.menu).forEach((name) => {
111
- const url = url_for(navbar.menu[name]);
112
- const active = isSameLink(url, pageUrl);
238
+ const url = helper.localized_url_for(navbar.menu[name], langKey);
239
+ const active = isActiveMenuLink(url, pageUrl);
113
240
  menu[name] = { url, active };
114
241
  });
115
242
  }
@@ -127,12 +254,15 @@ module.exports = cacheComponent(Navbar, "common.navbar", (props) => {
127
254
 
128
255
  return {
129
256
  logo: url_for(logo),
130
- siteUrl: url_for("/"),
257
+ siteUrl: helper.localized_url_for("/", langKey),
131
258
  siteTitle: title,
132
259
  menu,
133
260
  links,
261
+ languageSwitch: getLanguageSwitch(site, page, config, helper),
134
262
  showToc,
135
263
  tocTitle: _p("widget.catalogue", Infinity),
136
264
  searchTitle: __("search.search"),
137
265
  };
138
266
  });
267
+
268
+ module.exports.getLanguageSwitch = getLanguageSwitch;
@@ -1,23 +1,25 @@
1
1
  const { Component, cacheComponent } = require("../../include/util/common");
2
-
3
- const themes = [
4
- { name: "🖥️ SYSTEM", value: "system" },
5
- { name: "🌻 LATTE", value: "latte" },
6
- { name: "🦭 NORD", value: "nord" },
7
- { name: "🐻‍❄️ NORD NIGHT", value: "nord_night" },
8
- { name: "🌹 ROSE PINE", value: "rose_pine" },
9
- { name: "🌿 MOCHA", value: "mocha" },
10
- { name: "🏙 TOKYO NIGHT", value: "tokyo_night" },
11
- ];
2
+ const { THEME_OPTIONS } = require("../../include/util/theme");
12
3
 
13
4
  class ThemeSelector extends Component {
14
5
  render() {
15
6
  return (
16
7
  <div id="theme-selector-popover" popover="auto" tabindex="-1">
17
- <div class="theme-selector-list" role="listbox" aria-label="Select theme">
18
- {themes.map((theme, index) => (
19
- <button class="theme-option" type="submit" data-theme-option={theme.value} data-index={index} onclick={`window.selectThemeOption?.(${index})`}>
20
- {theme.name}
8
+ <div
9
+ class="theme-selector-list"
10
+ role="listbox"
11
+ aria-label="Select theme"
12
+ >
13
+ {THEME_OPTIONS.map((theme, index) => (
14
+ <button
15
+ class="theme-option"
16
+ type="submit"
17
+ role="option"
18
+ data-theme-option={theme.value}
19
+ data-index={index}
20
+ onclick={`window.selectThemeOption?.(${index})`}
21
+ >
22
+ {theme.label}
21
23
  </button>
22
24
  ))}
23
25
  </div>
package/layout/layout.jsx CHANGED
@@ -5,23 +5,61 @@ const Footer = require("./common/footer");
5
5
  const Scripts = require("./common/scripts");
6
6
  const Search = require("./common/search");
7
7
  const ThemeSelector = require("./common/theme_selector");
8
+ const { DEFAULT_SETTINGS: ARTICLE_FONT_DEFAULT_SETTINGS } = require("../include/util/article_font");
9
+
10
+ function buildLangSwitchScript(site, page, config, helper) {
11
+ const lswitch = Navbar.getLanguageSwitch(site, page, config, helper);
12
+ if (!lswitch) return "";
13
+
14
+ const payload = JSON.stringify({
15
+ mode: lswitch.mode,
16
+ url: lswitch.url || "",
17
+ locale: lswitch.locale,
18
+ homeUrl: lswitch.homeUrl,
19
+ });
20
+
21
+ return `<script data-swup-reload-script>
22
+ (function() {
23
+ 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;
35
+ }
36
+ })();
37
+ <\/script>`;
38
+ }
8
39
 
9
40
  module.exports = class extends Component {
10
41
  render() {
11
42
  const { site, config, page, helper, body } = this.props;
12
43
 
13
- const language = page.lang || page.language || config.language || "en";
44
+ const language = helper.language_locale ? helper.language_locale(page) : page.lang || page.language || config.language || "en";
45
+ const langSwitchScript = buildLangSwitchScript(site, page, config, helper);
14
46
 
15
47
  return (
16
- <html lang={language ? language : ""}>
48
+ <html
49
+ lang={language ? language : ""}
50
+ data-article-font-size={ARTICLE_FONT_DEFAULT_SETTINGS.size}
51
+ data-article-font-family={ARTICLE_FONT_DEFAULT_SETTINGS.type}
52
+ data-article-line-height={String(ARTICLE_FONT_DEFAULT_SETTINGS.lineHeight)}
53
+ data-article-font-weight={ARTICLE_FONT_DEFAULT_SETTINGS.weight}
54
+ >
17
55
  <Head site={site} config={config} helper={helper} page={page} />
18
56
  <body>
19
- <Navbar config={config} helper={helper} page={page} />
57
+ <Navbar site={site} config={config} helper={helper} page={page} />
20
58
  <ThemeSelector />
21
59
  <section class="section">
22
- <div class="main-content transition-fade" id="swup" dangerouslySetInnerHTML={{ __html: body }}></div>
60
+ <div class="main-content transition-fade" id="swup" dangerouslySetInnerHTML={{ __html: body + langSwitchScript }}></div>
23
61
  </section>
24
- <Footer site={site} config={config} helper={helper} />
62
+ <Footer site={site} config={config} helper={helper} page={page} />
25
63
  <Scripts site={site} config={config} helper={helper} page={page} />
26
64
  <Search config={config} helper={helper} />
27
65
  </body>