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.
- package/README.md +2 -0
- package/include/hexo/i18n.js +11 -1
- package/include/hexo/obsidian-callouts.js +210 -0
- package/include/hexo/renderer.js +199 -16
- package/include/hexo/shiki.js +191 -0
- package/include/hexo/sitemap.js +184 -0
- package/languages/en.yml +4 -10
- package/languages/zh-CN.yml +4 -10
- package/layout/common/article.jsx +49 -24
- package/layout/common/article_info.jsx +11 -48
- package/layout/common/footer.jsx +14 -106
- package/layout/common/head.jsx +3 -15
- package/layout/common/navbar.jsx +14 -80
- package/layout/layout.jsx +33 -16
- package/layout/plugin/goatcounter.jsx +25 -0
- package/package.json +7 -14
- package/scripts/index.js +1 -0
- package/source/css/archive.css +3 -5
- package/source/css/callout_blocks.css +41 -21
- package/source/css/default.css +72 -117
- package/source/css/optional/mermaid.css +12 -6
- package/source/css/shiki/shiki.css +5 -4
- package/source/js/components/friends-list.js +271 -0
- package/source/js/components/tab.js +242 -0
- package/source/js/components/x-info-card.js +297 -0
- package/source/js/main.js +23 -119
- package/source/js/mdit/mermaid.js +10 -0
|
@@ -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, """);
|
|
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
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
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");
|