hexo-theme-gnix 11.0.0 → 13.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.
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Info card custom element.
3
+ *
4
+ * Usage:
5
+ * <x-info-card
6
+ * name="GnixAij"
7
+ * description="Life Tracking & Tech Sharing"
8
+ * avatar="https://assets.vluv.space/avatar.webp"
9
+ * website="https://vluv.space"
10
+ * feed="/atom.xml"
11
+ * links='{"Github":{"icon":"mdi:github","url":"https://github.com/Efterklang"}}'
12
+ * quicklinks='{"Now":"now"}'
13
+ * ></x-info-card>
14
+ */
15
+
16
+ let infoCardStyleSheetInjected = false;
17
+
18
+ function escapeHtml(value) {
19
+ const span = document.createElement("span");
20
+ span.textContent = value == null ? "" : String(value);
21
+ return span.innerHTML;
22
+ }
23
+
24
+ function escapeAttribute(value) {
25
+ return escapeHtml(value).replace(/"/g, "&quot;");
26
+ }
27
+
28
+ function parseJsonAttribute(element, name) {
29
+ const value = element.getAttribute(name);
30
+ if (!value) return {};
31
+
32
+ try {
33
+ const parsed = JSON.parse(value);
34
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
35
+ } catch {
36
+ return {};
37
+ }
38
+ }
39
+
40
+ function normalizeLinkEntry(entry) {
41
+ if (typeof entry === "string") return { url: entry };
42
+ if (entry && typeof entry === "object" && typeof entry.url === "string") return entry;
43
+ return null;
44
+ }
45
+
46
+ class InfoCard extends HTMLElement {
47
+ connectedCallback() {
48
+ this.injectStyles();
49
+ this.render();
50
+ }
51
+
52
+ injectStyles() {
53
+ if (infoCardStyleSheetInjected) return;
54
+
55
+ const style = `
56
+ x-info-card {
57
+ display: block;
58
+ margin: 2rem 0;
59
+ }
60
+
61
+ .x-info-card {
62
+ background: var(--base);
63
+ border: 1px solid var(--surface0);
64
+ border-radius: var(--radius);
65
+ padding: clamp(1.25rem, 4vw, 2rem);
66
+ position: relative;
67
+ overflow: hidden;
68
+ }
69
+
70
+ .x-info-card::before {
71
+ content: "";
72
+ position: absolute;
73
+ inset: auto -20% -45% 35%;
74
+ height: 70%;
75
+ pointer-events: none;
76
+ }
77
+
78
+ .x-info-header {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 1rem;
82
+ margin-bottom: 1.25rem;
83
+ position: relative;
84
+ z-index: 1;
85
+ }
86
+
87
+ .x-info-avatar {
88
+ width: 3.5rem;
89
+ height: 3.5rem;
90
+ border-radius: 50%;
91
+ object-fit: cover;
92
+ border: 2px solid var(--surface0);
93
+ flex: 0 0 auto;
94
+ }
95
+
96
+ .x-info-name {
97
+ font-family: var(--font-serif);
98
+ font-style: italic;
99
+ font-size: 1.35rem;
100
+ font-weight: 700;
101
+ color: var(--rosewater);
102
+ margin: 0;
103
+ line-height: 1.2;
104
+ }
105
+
106
+ .x-info-desc {
107
+ font-family: var(--font-sans-serif);
108
+ font-size: 0.8rem;
109
+ font-weight: 300;
110
+ color: var(--subtext0);
111
+ margin: 0.2rem 0 0;
112
+ }
113
+
114
+ .x-info-details {
115
+ display: grid;
116
+ grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
117
+ gap: 0.75rem;
118
+ padding-top: 1.25rem;
119
+ border-top: 1px solid var(--surface0);
120
+ position: relative;
121
+ z-index: 1;
122
+ }
123
+
124
+ .x-info-item {
125
+ display: flex;
126
+ flex-direction: column;
127
+ gap: 0.15rem;
128
+ min-width: 0;
129
+ }
130
+
131
+ .x-info-label {
132
+ font-family: var(--font-mono);
133
+ font-size: 0.65rem;
134
+ font-weight: 500;
135
+ color: var(--subtext0);
136
+ text-transform: uppercase;
137
+ letter-spacing: 0.1em;
138
+ }
139
+
140
+ .x-info-value {
141
+ font-family: var(--font-mono);
142
+ font-size: 0.8125rem;
143
+ color: var(--text);
144
+ text-decoration: none;
145
+ word-break: break-word;
146
+ transition: color 0.2s ease;
147
+ }
148
+
149
+ .x-info-value:hover {
150
+ color: var(--lavender);
151
+ }
152
+
153
+ .x-info-links {
154
+ display: flex;
155
+ flex-wrap: wrap;
156
+ align-items: center;
157
+ gap: 0.6rem 0.9rem;
158
+ padding-top: 1.25rem;
159
+ margin-top: 0.25rem;
160
+ border-top: 1px solid var(--surface0);
161
+ position: relative;
162
+ z-index: 1;
163
+ }
164
+
165
+ .x-info-link {
166
+ font-family: var(--font-mono);
167
+ font-size: 0.8rem;
168
+ color: var(--subtext0);
169
+ text-decoration: none;
170
+ transition: color 0.2s ease;
171
+ }
172
+
173
+ .x-info-link:hover {
174
+ color: var(--lavender);
175
+ }
176
+
177
+ .x-info-link--icon {
178
+ display: inline-flex;
179
+ align-items: center;
180
+ justify-content: center;
181
+ width: 1.6rem;
182
+ height: 1.6rem;
183
+ border-radius: 50%;
184
+ transition: color 0.2s ease, background 0.2s ease;
185
+ }
186
+
187
+ .x-info-link--icon:hover {
188
+ color: var(--lavender);
189
+ }
190
+
191
+ .x-info-link--icon svg {
192
+ width: 1.1rem;
193
+ height: 1.1rem;
194
+ }
195
+
196
+ .x-info-sep {
197
+ width: 1px;
198
+ height: 1rem;
199
+ background: var(--surface0);
200
+ margin: 0 0.15rem;
201
+ }
202
+
203
+ @media (max-width: 520px) {
204
+ .x-info-header {
205
+ align-items: flex-start;
206
+ }
207
+
208
+ .x-info-details {
209
+ grid-template-columns: 1fr;
210
+ }
211
+ }
212
+ `;
213
+
214
+ const styleEl = document.createElement("style");
215
+ styleEl.textContent = style;
216
+ document.head.appendChild(styleEl);
217
+ infoCardStyleSheetInjected = true;
218
+ }
219
+
220
+ brandIconSVG(label) {
221
+ const svg = (d) => `<svg aria-hidden="true" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="${d}"/></svg>`;
222
+
223
+ const ICONS = {
224
+ Bluesky: "M12 11.388c-.906-1.761-3.372-5.044-5.665-6.662c-2.197-1.55-3.034-1.283-3.583-1.033C2.116 3.978 2 4.955 2 5.528c0 .575.315 4.709.52 5.4c.68 2.28 3.094 3.05 5.32 2.803c-3.26.483-6.157 1.67-2.36 5.898c4.178 4.325 5.726-.927 6.52-3.59c.794 2.663 1.708 7.726 6.444 3.59c3.556-3.59.977-5.415-2.283-5.898c2.225.247 4.64-.523 5.319-2.803c.205-.69.52-4.825.52-5.399c0-.575-.116-1.55-.752-1.838c-.549-.248-1.386-.517-3.583 1.033c-2.293 1.621-4.76 4.904-5.665 6.664",
225
+ Github: "M12 2A10 10 0 0 0 2 12c0 4.42 2.87 8.17 6.84 9.5c.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34c-.46-1.16-1.11-1.47-1.11-1.47c-.91-.62.07-.6.07-.6c1 .07 1.53 1.03 1.53 1.03c.87 1.52 2.34 1.07 2.91.83c.09-.65.35-1.09.63-1.34c-2.22-.25-4.55-1.11-4.55-4.92c0-1.11.38-2 1.03-2.71c-.1-.25-.45-1.29.1-2.64c0 0 .84-.27 2.75 1.02c.79-.22 1.65-.33 2.5-.33s1.71.11 2.5.33c1.91-1.29 2.75-1.02 2.75-1.02c.55 1.35.2 2.39.1 2.64c.65.71 1.03 1.6 1.03 2.71c0 3.82-2.34 4.66-4.57 4.91c.36.31.69.92.69 1.85V21c0 .27.16.59.67.5C19.14 20.16 22 16.42 22 12A10 10 0 0 0 12 2",
226
+ Youtube: "M12 4c.855 0 1.732.022 2.582.058l1.004.048l.961.057l.9.061l.822.064a3.8 3.8 0 0 1 3.494 3.423l.04.425l.075.91c.07.943.122 1.971.122 2.954s-.052 2.011-.122 2.954l-.075.91l-.04.425a3.8 3.8 0 0 1-3.495 3.423l-.82.063l-.9.062l-.962.057l-1.004.048A62 62 0 0 1 12 20a62 62 0 0 1-2.582-.058l-1.004-.048l-.961-.057l-.9-.062l-.822-.063a3.8 3.8 0 0 1-3.494-3.423l-.04-.425l-.075-.91A41 41 0 0 1 2 12c0-.983.052-2.011.122-2.954l.075-.91l.04-.425A3.8 3.8 0 0 1 5.73 4.288l.821-.064l.9-.061l.962-.057l1.004-.048A62 62 0 0 1 12 4m-2 5.575v4.85c0 .462.5.75.9.52l4.2-2.425a.6.6 0 0 0 0-1.04l-4.2-2.424a.6.6 0 0 0-.9.52Z",
227
+ Bilibili: "M17.555 3.168a1 1 0 0 1 .277 1.387L16.87 6H18a4 4 0 0 1 4 4v7a4 4 0 0 1-4 4H6a4 4 0 0 1-4-4v-7a4 4 0 0 1 4-4h1.131l-.963-1.445a1 1 0 0 1 1.664-1.11L9.535 6h4.93l1.703-2.555a1 1 0 0 1 1.387-.277M9 11a1 1 0 0 0-.993.883L8 12v2a1 1 0 0 0 1.993.117L10 14v-2a1 1 0 0 0-1-1m6 0a1 1 0 0 0-1 1v2a1 1 0 1 0 2 0v-2a1 1 0 0 0-1-1",
228
+ Threads: "M5.086 4.28c1.65-1.76 4.031-2.78 6.93-2.78c4.792 0 8.017 2.784 9.033 6.65a1.5 1.5 0 0 1-2.902.762C17.48 6.37 15.45 4.5 12.017 4.5c-2.164 0-3.72.742-4.742 1.832C6.239 7.438 5.64 9.02 5.64 10.875v2.25c0 1.855.598 3.437 1.634 4.543c1.022 1.09 2.578 1.832 4.741 1.832c1.576 0 2.795-.365 3.714-.93c.92-.568 1.42-1.285 1.56-2.068c.173-.972-.044-1.579-.364-2.001a2 2 0 0 0-.116-.14a5.3 5.3 0 0 1-.623 1.382c-1.514 2.3-4.369 2.46-6.203 1.728c-1.091-.435-1.972-1.583-2.247-2.788a3.5 3.5 0 0 1 .168-2.147c.312-.75.884-1.37 1.662-1.828c.8-.472 1.927-.665 2.979-.694a11 11 0 0 1 1.254.04c-.09-.2-.187-.343-.274-.425c-.384-.357-1.06-.632-1.746-.628c-.647.005-1.126.247-1.41.7a1.5 1.5 0 1 1-2.544-1.59c.948-1.515 2.507-2.1 3.933-2.11c1.388-.01 2.821.512 3.81 1.432c.954.888 1.373 2.254 1.513 3.485c.836.403 1.63.974 2.234 1.77c.874 1.15 1.233 2.624.927 4.34c-.32 1.793-1.45 3.178-2.94 4.096c-1.457.898-3.239 1.376-5.287 1.376c-2.899 0-5.28-1.02-6.93-2.78c-1.636-1.746-2.445-4.1-2.445-6.595v-2.25c0-2.494.81-4.85 2.445-6.594Zm8.947 8.823a8 8 0 0 0-1.405-.09c-.86.024-1.384.188-1.537.279c-.305.18-.39.333-.417.398a.53.53 0 0 0-.011.327c.036.16.121.333.238.48a.8.8 0 0 0 .194.186q.008.006 0 .002c.985.393 2.105.14 2.586-.592c.137-.207.265-.553.352-.99",
229
+ X: "M19.753 4.659a1 1 0 0 0-1.506-1.317l-5.11 5.84L8.8 3.4A1 1 0 0 0 8 3H4a1 1 0 0 0-.8 1.6l6.437 8.582l-5.39 6.16a1 1 0 0 0 1.506 1.317l5.11-5.841L15.2 20.6a1 1 0 0 0 .8.4h4a1 1 0 0 0 .8-1.6l-6.437-8.582l5.39-6.16ZM16.5 19L6 5h1.5L18 19",
230
+ Steam: "M12 2a10 10 0 0 1 10 10a10 10 0 0 1-10 10c-4.6 0-8.45-3.08-9.64-7.27l3.83 1.58a2.84 2.84 0 0 0 2.78 2.27c1.56 0 2.83-1.27 2.83-2.83v-.13l3.4-2.43h.08c2.08 0 3.77-1.69 3.77-3.77s-1.69-3.77-3.77-3.77s-3.78 1.69-3.78 3.77v.05l-2.37 3.46l-.16-.01c-.59 0-1.14.18-1.59.49L2 11.2C2.43 6.05 6.73 2 12 2M8.28 17.17c.8.33 1.72-.04 2.05-.84s-.05-1.71-.83-2.04l-1.28-.53c.49-.18 1.04-.19 1.56.03c.53.21.94.62 1.15 1.15c.22.52.22 1.1 0 1.62c-.43 1.08-1.7 1.6-2.78 1.15c-.5-.21-.88-.59-1.09-1.04zm9.52-7.75c0 1.39-1.13 2.52-2.52 2.52a2.52 2.52 0 0 1-2.51-2.52a2.5 2.5 0 0 1 2.51-2.51a2.52 2.52 0 0 1 2.52 2.51m-4.4 0c0 1.04.84 1.89 1.89 1.89c1.04 0 1.88-.85 1.88-1.89s-.84-1.89-1.88-1.89c-1.05 0-1.89.85-1.89 1.89",
231
+ RSS: "M5 17a2 2 0 1 1 0 4a2 2 0 0 1 0-4M5 3c8.837 0 16 7.163 16 16q0 .277-.01.55a1.5 1.5 0 1 1-2.997-.1A13 13 0 0 0 18 19c0-7.18-5.82-13-13-13q-.225 0-.45.008a1.5 1.5 0 0 1-.1-2.999Q4.722 3 5 3m0 7a9 9 0 0 1 8.98 9.599a1.5 1.5 0 1 1-2.993-.198a6 6 0 0 0-6.388-6.388a1.5 1.5 0 0 1-.197-2.993Q4.699 10 5 10",
232
+ };
233
+
234
+ return ICONS[label] ? svg(ICONS[label]) : svg("M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14l11-11");
235
+ }
236
+
237
+ render() {
238
+ const name = this.getAttribute("name") || "";
239
+ const description = this.getAttribute("description") || "";
240
+ const avatar = this.getAttribute("avatar") || "";
241
+ const website = this.getAttribute("website") || "";
242
+ const feed = this.getAttribute("feed") || "";
243
+ const avatarLink = this.getAttribute("avatar-link") || avatar;
244
+ const links = parseJsonAttribute(this, "links");
245
+ const quicklinks = parseJsonAttribute(this, "quicklinks");
246
+
247
+ const details = [
248
+ website
249
+ ? `<div class="x-info-item"><span class="x-info-label">Website</span><a href="${escapeAttribute(website)}" class="x-info-value" target="_blank" rel="noopener noreferrer">${escapeHtml(website.replace(/^https?:\/\//, ""))}</a></div>`
250
+ : "",
251
+ feed
252
+ ? `<div class="x-info-item"><span class="x-info-label">Feed</span><a href="${escapeAttribute(feed)}" class="x-info-value" target="_blank" rel="noopener noreferrer">${escapeHtml(feed.replace(/^https?:\/\//, ""))}</a></div>`
253
+ : "",
254
+ avatarLink
255
+ ? `<div class="x-info-item"><span class="x-info-label">Avatar</span><a href="${escapeAttribute(avatarLink)}" class="x-info-value" target="_blank" rel="noopener noreferrer">${escapeHtml(avatarLink.replace(/^https?:\/\//, ""))}</a></div>`
256
+ : "",
257
+ ]
258
+ .filter(Boolean)
259
+ .join("");
260
+
261
+ const quicklinkItems = Object.keys(quicklinks)
262
+ .map((label) => {
263
+ const link = normalizeLinkEntry(quicklinks[label]);
264
+ if (!link) return "";
265
+ return `<a class="x-info-link" href="${escapeAttribute(link.url)}" target="_self" rel="noopener noreferrer">${escapeHtml(label)}</a>`;
266
+ })
267
+ .filter(Boolean);
268
+
269
+ const socialItems = Object.keys(links)
270
+ .map((label) => {
271
+ const link = normalizeLinkEntry(links[label]);
272
+ if (!link) return "";
273
+ const iconSvg = this.brandIconSVG(label);
274
+ return `<a class="x-info-link x-info-link--icon" href="${escapeAttribute(link.url)}" target="_blank" rel="noopener noreferrer" title="${escapeAttribute(label)}">${iconSvg}</a>`;
275
+ })
276
+ .filter(Boolean);
277
+
278
+ const hasDivider = quicklinkItems.length > 0 && socialItems.length > 0;
279
+ const linksHtml = [...quicklinkItems, hasDivider ? `<span class="x-info-sep" aria-hidden="true"></span>` : "", ...socialItems].filter(Boolean).join("");
280
+
281
+ this.innerHTML = `
282
+ <div class="x-info-card">
283
+ <div class="x-info-header">
284
+ ${avatar ? `<img src="${escapeAttribute(avatar)}" alt="${escapeAttribute(name)}" class="x-info-avatar" width="56" height="56" loading="lazy" decoding="async">` : ""}
285
+ <div>
286
+ ${name ? `<h3 class="x-info-name">${escapeHtml(name)}</h3>` : ""}
287
+ ${description ? `<p class="x-info-desc">${escapeHtml(description)}</p>` : ""}
288
+ </div>
289
+ </div>
290
+ ${details ? `<div class="x-info-details">${details}</div>` : ""}
291
+ ${linksHtml ? `<div class="x-info-links">${linksHtml}</div>` : ""}
292
+ </div>
293
+ `;
294
+ }
295
+ }
296
+
297
+ if (!customElements.get("x-info-card")) customElements.define("x-info-card", InfoCard);
package/source/js/main.js CHANGED
@@ -1,19 +1,27 @@
1
- function tableWrapFix() {
2
- document.querySelectorAll(".content table").forEach((table) => {
3
- if (table.hasAttribute("data-nowrap") || table.parentElement.classList.contains("table-wrapper")) {
4
- return;
5
- }
6
- // if width exceeds container, wrap it
7
- const wrapper = document.createElement("div");
8
- Object.assign(wrapper.style, {
9
- width: "100%",
10
- overflowX: "auto",
11
- });
12
- table.parentNode.insertBefore(wrapper, table);
13
- wrapper.appendChild(table);
14
- });
1
+ function showSiteToast(message) {
2
+ if (!message) return;
3
+
4
+ let toast = document.getElementById("site-toast");
5
+ if (!toast) {
6
+ toast = document.createElement("div");
7
+ toast.id = "site-toast";
8
+ toast.setAttribute("role", "status");
9
+ toast.setAttribute("aria-live", "polite");
10
+ toast.className = "site-toast";
11
+ document.body.appendChild(toast);
12
+ }
13
+
14
+ toast.textContent = message;
15
+ toast.classList.add("is-visible");
16
+
17
+ clearTimeout(toast._hideTimer);
18
+ toast._hideTimer = setTimeout(() => {
19
+ toast.classList.remove("is-visible");
20
+ }, 1800);
15
21
  }
16
22
 
23
+ window.showSiteToast = showSiteToast;
24
+
17
25
  function twikoo_handler() {
18
26
  const el = document.getElementById("tko");
19
27
  if (!el) return;
@@ -42,87 +50,6 @@ function twikoo_handler() {
42
50
  delete el.dataset.initializing;
43
51
  });
44
52
  }
45
- // #region mdit@tab-plugin
46
- /**
47
- * 初始化页面上所有的 Tab 组件
48
- */
49
-
50
- function initializeTabs() {
51
- document.querySelectorAll(".tabs-tabs-wrapper").forEach((container) => {
52
- const buttons = container.querySelectorAll(".tabs-tab-button");
53
- buttons.forEach((button) => {
54
- button.removeEventListener("click", handleTabClick);
55
- button.addEventListener("click", handleTabClick);
56
- });
57
- });
58
- }
59
-
60
- function handleTabClick() {
61
- const tabContainer = this.closest(".tabs-tabs-wrapper");
62
- const targetIndex = this.getAttribute("data-tab");
63
- const syncId = this.getAttribute("data-id");
64
- activateTab(tabContainer, targetIndex);
65
- if (syncId) {
66
- syncRelatedTabs(syncId);
67
- }
68
- }
69
-
70
- /**
71
- * 激活指定容器中的特定 Tab
72
- * @param {HTMLElement} container - Tab 容器元素
73
- * @param {string} targetIndex - 要激活的 Tab 的 data-tab 值
74
- */
75
- function activateTab(container, targetIndex) {
76
- // 先重置该容器内所有 Tab 的状态
77
- resetTabsState(container);
78
-
79
- const buttonToActivate = container.querySelector(`.tabs-tab-button[data-tab="${targetIndex}"]`);
80
- const contentToActivate = container.querySelector(`.tabs-tab-content[data-index="${targetIndex}"]`);
81
-
82
- if (buttonToActivate) {
83
- buttonToActivate.classList.add("active");
84
- buttonToActivate.setAttribute("data-active", "");
85
- }
86
- if (contentToActivate) {
87
- contentToActivate.classList.add("active");
88
- contentToActivate.setAttribute("data-active", "");
89
- }
90
- }
91
-
92
- /**
93
- * 重置指定容器内所有 Tab 按钮和内容面板的状态
94
- * @param {HTMLElement} container - Tab 容器元素
95
- */
96
- function resetTabsState(container) {
97
- const buttons = container.querySelectorAll(".tabs-tab-button");
98
- const contents = container.querySelectorAll(".tabs-tab-content");
99
-
100
- buttons.forEach((btn) => {
101
- btn.classList.remove("active");
102
- btn.removeAttribute("data-active");
103
- });
104
- contents.forEach((content) => {
105
- content.classList.remove("active");
106
- content.removeAttribute("data-active");
107
- });
108
- }
109
-
110
- /**
111
- * 同步所有具有相同 data-id 的关联 Tab
112
- * @param {string} syncId - 用于同步的 data-id
113
- */
114
- function syncRelatedTabs(syncId) {
115
- const relatedButtons = document.querySelectorAll(`.tabs-tab-button[data-id="${syncId}"]`);
116
-
117
- relatedButtons.forEach((button) => {
118
- const container = button.closest(".tabs-tabs-wrapper");
119
- const targetIndex = button.getAttribute("data-tab");
120
- activateTab(container, targetIndex);
121
- });
122
- }
123
-
124
- // #endregion
125
-
126
53
  // #region markdown-exit shiki
127
54
  const SELECTORS = {
128
55
  figure: "figure.shiki",
@@ -649,7 +576,7 @@ function initArticleCommentPopover() {
649
576
 
650
577
  // Preload twikoo JS during idle time so comments render faster on click
651
578
  const tko = document.getElementById("tko");
652
- if (tko && tko.dataset.jsUrl) {
579
+ if (tko?.dataset.jsUrl) {
653
580
  const preload = () => loadScriptOnce(tko.dataset.jsUrl, () => {});
654
581
  if (typeof requestIdleCallback === "function") {
655
582
  requestIdleCallback(preload, { timeout: 3000 });
@@ -670,26 +597,6 @@ function initArticleCommentPopover() {
670
597
  });
671
598
  }
672
599
 
673
- function getComparablePath(url) {
674
- const path = new URL(url, window.location.href).pathname.replace(/\/index\.html$/, "/").replace(/\/+$/, "");
675
- return path || "/";
676
- }
677
-
678
- function updateNavbarCurrentPage() {
679
- const currentPath = getComparablePath(window.location.href);
680
- document.querySelectorAll(".navbar-start .navbar-item").forEach((item) => {
681
- const itemPath = getComparablePath(item.href);
682
- const active = itemPath === currentPath || (itemPath !== "/" && currentPath.startsWith(`${itemPath}/`));
683
-
684
- item.classList.toggle("is-active", active);
685
- if (active) {
686
- item.setAttribute("aria-current", "page");
687
- } else {
688
- item.removeAttribute("aria-current");
689
- }
690
- });
691
- }
692
-
693
600
  function refreshNavbarIcons() {
694
601
  document.querySelectorAll(".navbar-end iconify-icon").forEach((el) => {
695
602
  if (!el.getAttribute("icon")) return;
@@ -703,8 +610,6 @@ function refreshNavbarIcons() {
703
610
  }
704
611
 
705
612
  function initPage() {
706
- tableWrapFix();
707
- initializeTabs();
708
613
  handleMermaid();
709
614
  addHighlightTool();
710
615
  initArticleSettings();
@@ -713,7 +618,6 @@ function initPage() {
713
618
  document.querySelectorAll(".content img").forEach((img) => zoomImgs.add(img));
714
619
  mediumZoom([...zoomImgs], zoomOpts);
715
620
  initArticleCommentPopover();
716
- updateNavbarCurrentPage();
717
621
  refreshNavbarIcons();
718
622
  }
719
623
 
@@ -60,6 +60,7 @@
60
60
  this.isDragging = false;
61
61
  this.startX = 0;
62
62
  this.startY = 0;
63
+ this.wrapper = container.querySelector(".mermaid-wrapper");
63
64
  this.initEvents();
64
65
  }
65
66
 
@@ -70,6 +71,15 @@
70
71
  }
71
72
 
72
73
  initEvents() {
74
+ this.container.addEventListener("pointerdown", () => {
75
+ this.wrapper?.classList.add("is-controls-visible");
76
+ });
77
+
78
+ document.addEventListener("pointerdown", (e) => {
79
+ if (this.container.contains(e.target)) return;
80
+ this.wrapper?.classList.remove("is-controls-visible");
81
+ });
82
+
73
83
  // Toolbar & Grid Panel
74
84
  this.container.addEventListener("click", (e) => {
75
85
  const btn = e.target.closest("button");