hexo-theme-gnix 6.0.2 → 6.0.3

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.
@@ -8,10 +8,9 @@ const {
8
8
  class Twikoo extends Component {
9
9
  render() {
10
10
  const { envId, region, lang, jsUrl } = this.props;
11
- const js = `
12
- window.twikooConfig = { envId: '${envId}', ${region ? `region: ${JSON.stringify(region)},` : ""} ${lang ? `lang: ${JSON.stringify(lang)},` : ""} };
13
- twikoo.init(window.twikooConfig);
14
- `;
11
+ const configJs = `window.twikooConfig = { envId: '${envId}', ${
12
+ region ? `region: ${JSON.stringify(region)},` : ""
13
+ } ${lang ? `lang: ${JSON.stringify(lang)},` : ""} };`;
15
14
  const lazy_load_css_script = lazy_load_css("/css/twikoo.css");
16
15
  return (
17
16
  <Fragment>
@@ -19,7 +18,8 @@ twikoo.init(window.twikooConfig);
19
18
  <script
20
19
  dangerouslySetInnerHTML={{ __html: lazy_load_css_script }}
21
20
  ></script>
22
- <script defer src={jsUrl} onload={`${js}`}></script>
21
+ <script dangerouslySetInnerHTML={{ __html: configJs }}></script>
22
+ <script defer src={jsUrl}></script>
23
23
  </Fragment>
24
24
  );
25
25
  }
@@ -10,13 +10,9 @@ module.exports = class extends Component {
10
10
 
11
11
  return (
12
12
  <a href={url_for(page.link || page.path)} class="cover-image">
13
+ <img class="cover-lqip" src={lqip_src} alt="placeholder" />
13
14
  <img
14
- class="cover-image-placeholder"
15
- src={lqip_src}
16
- alt="placeholder"
17
- />
18
- <img
19
- class="fill"
15
+ class="cover-origin"
20
16
  src={cover}
21
17
  alt={page.title || cover}
22
18
  srcset={imageSrcset}
@@ -12,8 +12,8 @@ module.exports = class extends Component {
12
12
  return (
13
13
  <Fragment>
14
14
  {Object.keys(plugins).map((name) => {
15
- // plugin is not enabled
16
- if (!plugins[name]) {
15
+ // plugin is not enabled or is 'swup' (which is now built-in)
16
+ if (!plugins[name] || name === "swup") {
17
17
  return null;
18
18
  }
19
19
  const Plugin = loadComponent(`plugin/${name}`);
@@ -1,5 +1,6 @@
1
1
  const { Component, Fragment } = require("../../include/util/common");
2
2
  const Plugins = require("./plugins");
3
+ const Swup = require("../plugin/swup");
3
4
 
4
5
  module.exports = class extends Component {
5
6
  render() {
@@ -14,6 +15,7 @@ module.exports = class extends Component {
14
15
  helper={helper}
15
16
  head={false}
16
17
  />
18
+ <Swup head={false} />
17
19
  <script
18
20
  defer
19
21
  src="/js/host/iconify-icon/3.0.2/iconify-icon.min.js"
@@ -23,7 +25,7 @@ module.exports = class extends Component {
23
25
  defer
24
26
  src="/js/host/medium-zoom/dist/medium-zoom.min.js"
25
27
  ></script>
26
- <script defer src="/js/shiki/shiki.js"></script>
28
+ <script defer src="/js/mdit/shiki.js"></script>
27
29
  <script defer src="/js/main.js"></script>
28
30
  <script async src="/js/instant-page.min.js" type="module"></script>
29
31
  </Fragment>
package/layout/layout.jsx CHANGED
@@ -19,7 +19,11 @@ module.exports = class extends Component {
19
19
  <Navbar config={config} helper={helper} page={page} />
20
20
  <ThemeSelector />
21
21
  <section class="section">
22
- <div class="main-content" id="swup" dangerouslySetInnerHTML={{ __html: body }} ></div>
22
+ <div
23
+ class="main-content transition-fade"
24
+ id="swup"
25
+ dangerouslySetInnerHTML={{ __html: body }}
26
+ ></div>
23
27
  </section>
24
28
  <Footer site={site} config={config} helper={helper} />
25
29
  <Scripts site={site} config={config} helper={helper} page={page} />
@@ -10,6 +10,7 @@ class Swup extends Component {
10
10
  const swup = new Swup({
11
11
  containers: ["#swup"],
12
12
  cache: true,
13
+ native: true,
13
14
  plugins: [
14
15
  new SwupHeadPlugin({
15
16
  persistTags: true
@@ -26,7 +26,7 @@ class Insight extends Component {
26
26
  <div class="searchbox-body"></div>
27
27
  </div>
28
28
  </div>
29
- <script src={jsUrl} defer={true}></script>
29
+ <script defer src={jsUrl}></script>
30
30
  <script dangerouslySetInnerHTML={{ __html: js }}></script>
31
31
  </>
32
32
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hexo-theme-gnix",
3
- "version": "6.0.2",
3
+ "version": "6.0.3",
4
4
  "author": "Efterklang <gaojiaxing0220@gmail.com>",
5
5
  "license": "MIT",
6
6
  "description": "Second generation of Hexo theme Icarus, now with Catppuccin flavor and night mode support.",
@@ -1244,18 +1244,20 @@ section {
1244
1244
  margin-bottom: 0.25rem;
1245
1245
  }
1246
1246
 
1247
- .licensing-meta .level-item {
1248
- margin-right: 1.5rem;
1249
- font-size: 0.75rem;
1250
- }
1247
+ .licensing-meta {
1248
+ .level-item {
1249
+ margin-right: 1.5rem;
1250
+ font-size: 0.75rem;
1251
+ }
1251
1252
 
1252
- .licensing-meta iconify-icon {
1253
- font-size: 1.5em;
1254
- vertical-align: bottom;
1255
- }
1253
+ iconify-icon {
1254
+ font-size: 1.5em;
1255
+ vertical-align: bottom;
1256
+ }
1256
1257
 
1257
- .licensing-meta a {
1258
- color: inherit;
1258
+ a {
1259
+ color: inherit;
1260
+ }
1259
1261
  }
1260
1262
 
1261
1263
  /* #endregion Licensing */
@@ -1274,12 +1276,12 @@ section {
1274
1276
  opacity: 0;
1275
1277
  visibility: hidden;
1276
1278
  display: flex;
1277
- }
1278
1279
 
1279
- .searchbox.show {
1280
- opacity: 1;
1281
- visibility: visible;
1282
- backdrop-filter: blur(5px);
1280
+ &.show {
1281
+ opacity: 1;
1282
+ visibility: visible;
1283
+ backdrop-filter: blur(5px);
1284
+ }
1283
1285
  }
1284
1286
 
1285
1287
  input.searchbox-input {
@@ -1359,6 +1361,7 @@ input.searchbox-input {
1359
1361
 
1360
1362
  .searchbox-result-preview {
1361
1363
  color: var(--subtext0);
1364
+ margin-top: 0.25em;
1362
1365
  }
1363
1366
 
1364
1367
  .searchbox-result-preview,
@@ -1369,14 +1372,8 @@ input.searchbox-input {
1369
1372
  text-overflow: ellipsis;
1370
1373
  }
1371
1374
 
1372
- .searchbox-result-preview {
1373
- margin-top: 0.25em;
1374
- }
1375
-
1376
1375
  /* #endregion Search */
1377
1376
 
1378
- /* #region Article Cover */
1379
-
1380
1377
  .cover-image {
1381
1378
  position: relative;
1382
1379
  display: block;
@@ -1384,7 +1381,14 @@ input.searchbox-input {
1384
1381
  border-radius: var(--radius) var(--radius) 0 0;
1385
1382
  height: 380px;
1386
1383
 
1387
- .cover-image-placeholder {
1384
+ img {
1385
+ display: block;
1386
+ width: 100%;
1387
+ height: 100%;
1388
+ object-fit: cover;
1389
+ }
1390
+
1391
+ .cover-lqip {
1388
1392
  position: absolute;
1389
1393
  top: 0;
1390
1394
  left: 0;
@@ -1392,17 +1396,29 @@ input.searchbox-input {
1392
1396
  filter: blur(10px);
1393
1397
  }
1394
1398
 
1395
- .fill {
1399
+ .cover-origin {
1396
1400
  position: relative;
1397
1401
  z-index: 2;
1398
1402
  }
1399
1403
  }
1400
1404
 
1401
- .cover-image img {
1402
- display: block;
1403
- width: 100%;
1404
- height: 100%;
1405
- object-fit: cover;
1405
+ /* html.is-changing .transition-fade {
1406
+ view-transition-name: main;
1406
1407
  }
1407
1408
 
1408
- /* #endregion Article Cover */
1409
+ ::view-transition-old(main) {
1410
+ animation: fade 0.5s ease-in-out both;
1411
+ }
1412
+
1413
+ ::view-transition-new(main) {
1414
+ animation: fade 0.5s ease-in-out both reverse;
1415
+ }
1416
+
1417
+ @keyframes fade {
1418
+ from {
1419
+ opacity: 1;
1420
+ }
1421
+ to {
1422
+ opacity: 0;
1423
+ }
1424
+ } */
@@ -0,0 +1,123 @@
1
+ .mermaid-container {
2
+ margin: 1em 0;
3
+ position: relative;
4
+ }
5
+ .mermaid-wrapper {
6
+ border: 1px solid var(--surface0);
7
+ border-radius: 6px;
8
+ overflow: hidden;
9
+ position: relative;
10
+ background: var(--mantle);
11
+ }
12
+ .mermaid-toolbar {
13
+ position: absolute;
14
+ top: 8px;
15
+ right: 8px;
16
+ z-index: 20;
17
+ display: flex;
18
+ gap: 4px;
19
+ opacity: 0;
20
+ transition: opacity 0.2s ease;
21
+ }
22
+ .mermaid-wrapper:hover .mermaid-toolbar {
23
+ opacity: 1;
24
+ }
25
+ .mermaid-view-container {
26
+ position: relative;
27
+ width: 100%;
28
+ height: 100%;
29
+ min-height: 200px;
30
+ overflow: hidden;
31
+ cursor: grab;
32
+ display: flex;
33
+ flex-direction: column;
34
+ }
35
+ .mermaid-view-container:active {
36
+ cursor: grabbing;
37
+ }
38
+ .mermaid-content {
39
+ width: 100%;
40
+ flex: 1;
41
+ display: flex;
42
+ justify-content: center;
43
+ align-items: center;
44
+ padding: 20px;
45
+ box-sizing: border-box;
46
+ transform-origin: center;
47
+ transition: transform 0.2s ease;
48
+ }
49
+ .mermaid-viewer-grid-panel {
50
+ position: absolute;
51
+ bottom: 8px;
52
+ right: 8px;
53
+ z-index: 10;
54
+ opacity: 0;
55
+ transition: opacity 0.2s ease;
56
+ display: grid;
57
+ grid-template-rows: repeat(3, 1fr);
58
+ gap: 4px;
59
+ }
60
+ .mermaid-wrapper:hover .mermaid-viewer-grid-panel {
61
+ opacity: 1;
62
+ }
63
+ .mermaid-viewer-grid-panel .grid-row {
64
+ display: grid;
65
+ grid-template-columns: repeat(3, 1fr);
66
+ gap: 4px;
67
+ }
68
+ .mermaid-viewer-grid-panel .empty-cell {
69
+ width: 32px;
70
+ height: 32px;
71
+ }
72
+ .btn {
73
+ display: flex;
74
+ align-items: center;
75
+ justify-content: center;
76
+ width: 32px;
77
+ height: 32px;
78
+ background: var(--base);
79
+ border: 1px solid var(--surface0);
80
+ border-radius: 6px;
81
+ cursor: pointer;
82
+ transition: all 0.2s ease;
83
+ backdrop-filter: blur(4px);
84
+ color: var(--text);
85
+ padding: 0;
86
+ }
87
+ .btn:hover {
88
+ transform: scale(1.05);
89
+ }
90
+ .btn:active {
91
+ transform: scale(0.95);
92
+ }
93
+ .btn svg {
94
+ fill: currentColor;
95
+ }
96
+ .mermaid-loading {
97
+ display: flex;
98
+ align-items: center;
99
+ justify-content: center;
100
+ position: absolute;
101
+ top: 0;
102
+ left: 0;
103
+ width: 100%;
104
+ height: 100%;
105
+ color: #586069;
106
+ font-style: italic;
107
+ background: rgba(255, 255, 255, 0.5);
108
+ z-index: 5;
109
+ }
110
+
111
+ @media (max-width: 768px) {
112
+ .mermaid-wrapper {
113
+ border-radius: 3px;
114
+ }
115
+ .mermaid-toolbar,
116
+ .mermaid-viewer-grid-panel {
117
+ opacity: 1;
118
+ }
119
+ .btn {
120
+ width: 28px;
121
+ height: 28px;
122
+ }
123
+ }
package/source/js/main.js CHANGED
@@ -111,17 +111,75 @@ function handleKeyDown(e) {
111
111
  }
112
112
  }
113
113
 
114
+ function handleMermaid() {
115
+ const containers = document.querySelectorAll(".mermaid-container");
116
+ if (containers.length === 0) return;
117
+
118
+ const cssUrl = "/css/optional/mermaid.css";
119
+ const adapterUrl = "/js/mdit/mermaid.js";
120
+
121
+ if (!document.querySelector(`link[href="${cssUrl}"]`)) {
122
+ const link = document.createElement("link");
123
+ link.rel = "stylesheet";
124
+ link.href = cssUrl;
125
+ document.head.appendChild(link);
126
+ }
127
+
128
+ const runInit = () => {
129
+ const isNight = document.documentElement.classList.contains("night");
130
+ const theme = isNight ? "dark" : "default";
131
+ const libUrl = "/js/host/mermaid/mermaid.min.js";
132
+
133
+ containers.forEach((container, index) => {
134
+ if (!container.id) {
135
+ container.id = `mermaid-${Date.now()}-${index}`;
136
+ }
137
+ if (window.initMermaidDiagram) {
138
+ window.initMermaidDiagram(container.id, libUrl, theme, {});
139
+ }
140
+ });
141
+ };
142
+
143
+ if (window.initMermaidDiagram) {
144
+ runInit();
145
+ } else {
146
+ const existingScript = document.querySelector(
147
+ `script[src="${adapterUrl}"]`,
148
+ );
149
+ if (existingScript) {
150
+ existingScript.addEventListener("load", runInit);
151
+ } else {
152
+ const script = document.createElement("script");
153
+ script.src = adapterUrl;
154
+ script.onload = runInit;
155
+ document.head.appendChild(script);
156
+ }
157
+ }
158
+ }
159
+
114
160
  // #endregion
115
161
 
116
162
  function initLogic() {
117
163
  initializeTabs();
164
+ handleMermaid();
118
165
  mediumZoom(".article img", {
119
166
  background: "hsla(from var(--mantle) / 0.9)",
120
167
  });
121
168
  if (document.getElementById("twikoo")) {
122
- setTimeout(() => {
123
- twikoo?.init(window.twikooConfig);
124
- }, 600);
169
+ const initTwikoo = () => {
170
+ if (window.twikoo && window.twikooConfig) {
171
+ window.twikoo.init(window.twikooConfig);
172
+ }
173
+ };
174
+
175
+ if (typeof twikoo !== "undefined") {
176
+ initTwikoo();
177
+ } else {
178
+ const script = document.querySelector('script[src*="twikoo.all.min.js"]');
179
+ if (script) {
180
+ script.addEventListener("load", initTwikoo);
181
+ }
182
+ }
125
183
  }
126
184
  }
127
185
 
@@ -131,7 +189,8 @@ document.addEventListener("DOMContentLoaded", () => {
131
189
 
132
190
  // Re-initialize on page changes when using swup
133
191
  if (typeof swup !== "undefined") {
134
- swup.hooks.on("page:view", () => {
192
+ swup.hooks.on("page:view", (visit) => {
193
+ console.log("New page loaded:", visit.to.url);
135
194
  initLogic();
136
195
  });
137
196
  }
@@ -0,0 +1,169 @@
1
+ (() => {
2
+ const instances = new Map();
3
+ let mermaidPromise = null;
4
+ const loadMermaid = (jsUrl) => {
5
+ if (mermaidPromise) return mermaidPromise;
6
+ if (window.mermaid) {
7
+ mermaidPromise = Promise.resolve(window.mermaid);
8
+ return mermaidPromise;
9
+ }
10
+ return (mermaidPromise = new Promise((resolve, reject) => {
11
+ const script = document.createElement("script");
12
+ script.src = jsUrl;
13
+ script.onload = () => resolve(window.mermaid);
14
+ script.onerror = reject;
15
+ document.head.appendChild(script);
16
+ }));
17
+ };
18
+
19
+ const renderDiagram = async (id, code, container, themeVariables) => {
20
+ const content = container.querySelector(".mermaid-content");
21
+ const mermaid = await mermaidPromise;
22
+ if (!content || !mermaid) return;
23
+
24
+ // Initialize with current theme
25
+ mermaid.initialize({
26
+ startOnLoad: false,
27
+ theme: document.documentElement.classList.contains("night")
28
+ ? "dark"
29
+ : "default",
30
+ darkMode: document.documentElement.classList.contains("night"),
31
+ themeVariables,
32
+ securityLevel: "strict",
33
+ fontSize: 16,
34
+ });
35
+
36
+ try {
37
+ content.innerHTML = "";
38
+ const { svg } = await mermaid.render(`${id}-svg`, code);
39
+ content.insertAdjacentHTML("beforeend", svg);
40
+ } catch (error) {
41
+ console.error("Mermaid rendering error:", error);
42
+ content.innerHTML = `<p style="color: red;">Failed to render diagram: ${error.message}</p>`;
43
+ }
44
+ };
45
+
46
+ class PanZoomHandler {
47
+ constructor(container) {
48
+ this.container = container;
49
+ this.content = container.querySelector(".mermaid-content");
50
+ this.viewContainer = container.querySelector(".mermaid-view-container");
51
+ this.scale = 1;
52
+ this.tx = 0;
53
+ this.ty = 0;
54
+ this.isDragging = false;
55
+ this.startX = 0;
56
+ this.startY = 0;
57
+ this.initEvents();
58
+ }
59
+
60
+ apply() {
61
+ if (this.content) {
62
+ this.content.style.transform = `scale(${this.scale}) translate(${this.tx}px, ${this.ty}px)`;
63
+ }
64
+ }
65
+
66
+ initEvents() {
67
+ // Toolbar & Grid Panel
68
+ this.container.addEventListener("click", (e) => {
69
+ const btn = e.target.closest("button");
70
+ if (!btn) return;
71
+ const cl = btn.classList;
72
+
73
+ if (cl.contains("zoom-in")) this.scale = Math.min(this.scale * 1.2, 5);
74
+ else if (cl.contains("zoom-out"))
75
+ this.scale = Math.max(this.scale / 1.2, 0.2);
76
+ else if (cl.contains("reset")) {
77
+ this.scale = 1;
78
+ this.tx = 0;
79
+ this.ty = 0;
80
+ } else if (cl.contains("up")) this.ty += 40;
81
+ else if (cl.contains("down")) this.ty -= 40;
82
+ else if (cl.contains("left")) this.tx += 40;
83
+ else if (cl.contains("right")) this.tx -= 40;
84
+ else if (cl.contains("copy-code")) this.copyCode(btn);
85
+
86
+ this.apply();
87
+ });
88
+
89
+ // Dragging
90
+ if (this.viewContainer) {
91
+ this.viewContainer.addEventListener("mousedown", (e) => {
92
+ this.isDragging = true;
93
+ this.startX = e.clientX - this.tx;
94
+ this.startY = e.clientY - this.ty;
95
+ this.viewContainer.style.cursor = "grabbing";
96
+ });
97
+ }
98
+
99
+ window.addEventListener("mousemove", (e) => {
100
+ if (!this.isDragging) return;
101
+ e.preventDefault();
102
+ this.tx = e.clientX - this.startX;
103
+ this.ty = e.clientY - this.startY;
104
+ this.apply();
105
+ });
106
+
107
+ window.addEventListener("mouseup", () => {
108
+ if (this.isDragging) {
109
+ this.isDragging = false;
110
+ if (this.viewContainer) this.viewContainer.style.cursor = "grab";
111
+ }
112
+ });
113
+ }
114
+
115
+ copyCode(btn) {
116
+ const codeEl = this.container.querySelector(".mermaid-code");
117
+ if (!codeEl) return;
118
+ navigator.clipboard.writeText(codeEl.textContent).then(() => {
119
+ const originalTitle = btn.getAttribute("title");
120
+ btn.setAttribute("title", "Copied!");
121
+ setTimeout(() => btn.setAttribute("title", originalTitle), 2000);
122
+ });
123
+ }
124
+ }
125
+
126
+ const pruneInstances = () => {
127
+ for (const [id, { container }] of instances) {
128
+ if (!document.body.contains(container)) {
129
+ instances.delete(id);
130
+ }
131
+ }
132
+ };
133
+
134
+ window.initMermaidDiagram = (id, jsUrl, _theme, themeVariables) => {
135
+ const container = document.getElementById(id);
136
+ if (!container) return;
137
+
138
+ pruneInstances();
139
+ if (instances.has(id)) return;
140
+
141
+ const codeEl = container.querySelector(".mermaid-code");
142
+ if (!codeEl) return;
143
+
144
+ new PanZoomHandler(container);
145
+ instances.set(id, { container, code: codeEl.textContent, themeVariables });
146
+
147
+ loadMermaid(jsUrl).then(() => {
148
+ renderDiagram(id, codeEl.textContent, container, themeVariables);
149
+ });
150
+ };
151
+
152
+ // Theme Observer
153
+ const observer = new MutationObserver((mutations) => {
154
+ const isThemeChange = mutations.some(
155
+ (m) => m.type === "attributes" && m.attributeName === "class",
156
+ );
157
+ if (isThemeChange) {
158
+ pruneInstances();
159
+ instances.forEach(({ container, code, themeVariables }, id) => {
160
+ renderDiagram(id, code, container, themeVariables);
161
+ });
162
+ }
163
+ });
164
+
165
+ observer.observe(document.documentElement, {
166
+ attributes: true,
167
+ attributeFilter: ["class"],
168
+ });
169
+ })();
File without changes