hexo-theme-gnix 12.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 +4 -14
- 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 -13
- 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 +58 -58
- 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/x-info-card.js +297 -0
- package/source/js/main.js +24 -21
- package/source/js/mdit/mermaid.js +10 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Friends list custom elements.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* <friends-list id="friends">
|
|
6
|
+
* <friend-card
|
|
7
|
+
* name="Example"
|
|
8
|
+
* href="https://example.com"
|
|
9
|
+
* display-url="example.com"
|
|
10
|
+
* avatar="https://example.com/avatar.png"
|
|
11
|
+
* description="Personal site"
|
|
12
|
+
* feed="https://example.com/atom.xml"
|
|
13
|
+
* open-label="Open"
|
|
14
|
+
* ></friend-card>
|
|
15
|
+
* </friends-list>
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
let friendsListStyleSheetInjected = false;
|
|
19
|
+
|
|
20
|
+
function escapeHtml(value) {
|
|
21
|
+
const span = document.createElement("span");
|
|
22
|
+
span.textContent = value == null ? "" : String(value);
|
|
23
|
+
return span.innerHTML;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function escapeAttribute(value) {
|
|
27
|
+
return escapeHtml(value).replace(/"/g, """);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function displayUrlFromHref(href) {
|
|
31
|
+
try {
|
|
32
|
+
const url = new URL(href);
|
|
33
|
+
return url.hostname.replace(/^www\./, "www.");
|
|
34
|
+
} catch {
|
|
35
|
+
return href.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function injectFriendsListStyles() {
|
|
40
|
+
if (friendsListStyleSheetInjected) return;
|
|
41
|
+
|
|
42
|
+
const style = `
|
|
43
|
+
friends-list {
|
|
44
|
+
display: grid;
|
|
45
|
+
grid-template-columns: repeat(6, 1fr);
|
|
46
|
+
padding: 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
friend-card {
|
|
50
|
+
position: relative;
|
|
51
|
+
grid-column: span 2;
|
|
52
|
+
padding: 1.5rem;
|
|
53
|
+
background: var(--base);
|
|
54
|
+
border: .5px solid var(--surface0);
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
overflow: hidden;
|
|
57
|
+
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
58
|
+
min-height: 5.25rem;
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
friend-card:focus-within,
|
|
65
|
+
friend-card:hover {
|
|
66
|
+
border-color: var(--lavender);
|
|
67
|
+
box-shadow: 0 0.75rem 2.5rem -0.75rem hsl(from var(--text) h s l / 0.1);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
friend-card:nth-last-child(1):nth-child(3n+1) {
|
|
71
|
+
grid-column: span 6;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
friend-card:nth-last-child(1):nth-child(3n+2),
|
|
75
|
+
friend-card:nth-last-child(2):nth-child(3n+1) {
|
|
76
|
+
grid-column: span 3;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
friend-card::before {
|
|
80
|
+
content: '';
|
|
81
|
+
position: absolute;
|
|
82
|
+
inset: 0;
|
|
83
|
+
background:
|
|
84
|
+
radial-gradient(circle at 100% 100%, hsl(from var(--lavender) h s l / 0.14), transparent 55%);
|
|
85
|
+
opacity: 0;
|
|
86
|
+
transition: opacity 0.3s ease;
|
|
87
|
+
z-index: 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
friend-card:focus-within::before,
|
|
91
|
+
friend-card:hover::before {
|
|
92
|
+
opacity: 1;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.friend-hit {
|
|
96
|
+
position: absolute;
|
|
97
|
+
inset: 0;
|
|
98
|
+
z-index: 2;
|
|
99
|
+
border-radius: inherit;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.friend-avatar {
|
|
103
|
+
position: absolute;
|
|
104
|
+
bottom: -0.5rem;
|
|
105
|
+
right: -0.5rem;
|
|
106
|
+
width: 6rem;
|
|
107
|
+
height: 6rem;
|
|
108
|
+
border-radius: 50%;
|
|
109
|
+
background-color: var(--surface0);
|
|
110
|
+
object-fit: cover;
|
|
111
|
+
opacity: 0.08;
|
|
112
|
+
filter: grayscale(0.5);
|
|
113
|
+
transform: rotate(-8deg);
|
|
114
|
+
transition: all 0.3s ease;
|
|
115
|
+
z-index: 0;
|
|
116
|
+
pointer-events: none;
|
|
117
|
+
border: none;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
friend-card:focus-within .friend-avatar,
|
|
121
|
+
friend-card:hover .friend-avatar {
|
|
122
|
+
opacity: 0.15;
|
|
123
|
+
filter: grayscale(0);
|
|
124
|
+
transform: scale(1.1) rotate(-8deg);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.friend-detail {
|
|
128
|
+
position: relative;
|
|
129
|
+
z-index: 1;
|
|
130
|
+
display: flex;
|
|
131
|
+
flex-direction: column;
|
|
132
|
+
gap: 0.25rem;
|
|
133
|
+
margin: 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
h3.friend-name {
|
|
137
|
+
font-family: var(--font-serif);
|
|
138
|
+
font-style: italic;
|
|
139
|
+
font-synthesis: none;
|
|
140
|
+
font-size: 0.92rem;
|
|
141
|
+
font-weight: bolder;
|
|
142
|
+
color: var(--text);
|
|
143
|
+
display: flex;
|
|
144
|
+
align-items: center;
|
|
145
|
+
gap: 0.5rem;
|
|
146
|
+
line-height: 1.3;
|
|
147
|
+
margin: 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.friend-name .rss-link {
|
|
151
|
+
display: inline-flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
justify-content: center;
|
|
154
|
+
width: 1.25rem;
|
|
155
|
+
height: 1.25rem;
|
|
156
|
+
color: var(--subtext0);
|
|
157
|
+
text-decoration: none;
|
|
158
|
+
transition: all 0.2s ease;
|
|
159
|
+
border: 1px solid var(--surface0);
|
|
160
|
+
border-radius: 50%;
|
|
161
|
+
font-size: 0.75rem;
|
|
162
|
+
position: relative;
|
|
163
|
+
z-index: 3;
|
|
164
|
+
flex: 0 0 auto;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.friend-name .rss-link:hover {
|
|
168
|
+
color: var(--lavender);
|
|
169
|
+
border-color: var(--lavender);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.friend-url {
|
|
173
|
+
font-family: var(--font-mono);
|
|
174
|
+
font-size: 0.75rem;
|
|
175
|
+
font-weight: 400;
|
|
176
|
+
color: var(--subtext0);
|
|
177
|
+
opacity: 0.7;
|
|
178
|
+
transition: all 0.2s ease;
|
|
179
|
+
white-space: nowrap;
|
|
180
|
+
overflow: hidden;
|
|
181
|
+
text-overflow: ellipsis;
|
|
182
|
+
margin: 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
friend-card:hover .friend-url {
|
|
186
|
+
opacity: 1;
|
|
187
|
+
color: var(--lavender);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.friend-desc {
|
|
191
|
+
font-family: var(--font-sans-serif);
|
|
192
|
+
font-size: 0.8rem;
|
|
193
|
+
color: var(--subtext1);
|
|
194
|
+
margin: 0.5rem 0 0;
|
|
195
|
+
padding: 0;
|
|
196
|
+
overflow: hidden;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@media (max-width: 768px) {
|
|
200
|
+
friends-list {
|
|
201
|
+
grid-template-columns: 1fr;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
friend-card,
|
|
205
|
+
friend-card:nth-last-child(1):nth-child(3n+1),
|
|
206
|
+
friend-card:nth-last-child(1):nth-child(3n+2),
|
|
207
|
+
friend-card:nth-last-child(2):nth-child(3n+1) {
|
|
208
|
+
grid-column: span 1;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
friend-card:nth-child(1) {
|
|
212
|
+
min-height: 6.25rem;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@media (max-width: 480px) {
|
|
217
|
+
friend-card {
|
|
218
|
+
padding: 1.1rem;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
`;
|
|
222
|
+
|
|
223
|
+
const styleEl = document.createElement("style");
|
|
224
|
+
styleEl.textContent = style;
|
|
225
|
+
document.head.appendChild(styleEl);
|
|
226
|
+
friendsListStyleSheetInjected = true;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
class FriendsList extends HTMLElement {
|
|
230
|
+
connectedCallback() {
|
|
231
|
+
injectFriendsListStyles();
|
|
232
|
+
if (!this.hasAttribute("role")) this.setAttribute("role", "list");
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
class FriendCard extends HTMLElement {
|
|
237
|
+
connectedCallback() {
|
|
238
|
+
injectFriendsListStyles();
|
|
239
|
+
this.render();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
render() {
|
|
243
|
+
const name = this.getAttribute("name") || "";
|
|
244
|
+
const href = this.getAttribute("href") || "";
|
|
245
|
+
const displayUrl = this.getAttribute("display-url") || displayUrlFromHref(href);
|
|
246
|
+
const avatar = this.getAttribute("avatar") || "";
|
|
247
|
+
const description = this.getAttribute("description") || "";
|
|
248
|
+
const feed = this.getAttribute("feed") || "";
|
|
249
|
+
const openLabel = this.getAttribute("open-label") || "Open";
|
|
250
|
+
const ariaLabel = href && name ? `${openLabel} ${name}` : "";
|
|
251
|
+
|
|
252
|
+
this.setAttribute("role", "listitem");
|
|
253
|
+
this.innerHTML = `
|
|
254
|
+
${href ? `<a class="friend-hit" href="${escapeAttribute(href)}" target="_blank" rel="noopener noreferrer" aria-label="${escapeAttribute(ariaLabel)}"></a>` : ""}
|
|
255
|
+
${avatar ? `<img class="friend-avatar" src="${escapeAttribute(avatar)}" alt="" width="96" height="96" loading="lazy" decoding="async">` : ""}
|
|
256
|
+
<article class="friend-detail">
|
|
257
|
+
<h3 class="friend-name">
|
|
258
|
+
${escapeHtml(name)}
|
|
259
|
+
${feed ? `<a class="rss-link" target="_blank" rel="noopener noreferrer" href="${escapeAttribute(feed)}" title="RSS Feed">◎</a>` : ""}
|
|
260
|
+
</h3>
|
|
261
|
+
${displayUrl ? `<div class="friend-url">${escapeHtml(displayUrl)}</div>` : ""}
|
|
262
|
+
${description ? `<div class="friend-desc">${escapeHtml(description)}</div>` : ""}
|
|
263
|
+
</article>
|
|
264
|
+
`;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!customElements.get("friends-list")) customElements.define("friends-list", FriendsList);
|
|
269
|
+
if (!customElements.get("friend-card")) customElements.define("friend-card", FriendCard);
|
|
270
|
+
|
|
271
|
+
export { FriendCard, FriendsList };
|
|
@@ -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,3 +1,27 @@
|
|
|
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);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
window.showSiteToast = showSiteToast;
|
|
24
|
+
|
|
1
25
|
function twikoo_handler() {
|
|
2
26
|
const el = document.getElementById("tko");
|
|
3
27
|
if (!el) return;
|
|
@@ -573,26 +597,6 @@ function initArticleCommentPopover() {
|
|
|
573
597
|
});
|
|
574
598
|
}
|
|
575
599
|
|
|
576
|
-
function getComparablePath(url) {
|
|
577
|
-
const path = new URL(url, window.location.href).pathname.replace(/\/index\.html$/, "/").replace(/\/+$/, "");
|
|
578
|
-
return path || "/";
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
function updateNavbarCurrentPage() {
|
|
582
|
-
const currentPath = getComparablePath(window.location.href);
|
|
583
|
-
document.querySelectorAll(".navbar-start .navbar-item").forEach((item) => {
|
|
584
|
-
const itemPath = getComparablePath(item.href);
|
|
585
|
-
const active = itemPath === currentPath || (itemPath !== "/" && currentPath.startsWith(`${itemPath}/`));
|
|
586
|
-
|
|
587
|
-
item.classList.toggle("is-active", active);
|
|
588
|
-
if (active) {
|
|
589
|
-
item.setAttribute("aria-current", "page");
|
|
590
|
-
} else {
|
|
591
|
-
item.removeAttribute("aria-current");
|
|
592
|
-
}
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
|
|
596
600
|
function refreshNavbarIcons() {
|
|
597
601
|
document.querySelectorAll(".navbar-end iconify-icon").forEach((el) => {
|
|
598
602
|
if (!el.getAttribute("icon")) return;
|
|
@@ -614,7 +618,6 @@ function initPage() {
|
|
|
614
618
|
document.querySelectorAll(".content img").forEach((img) => zoomImgs.add(img));
|
|
615
619
|
mediumZoom([...zoomImgs], zoomOpts);
|
|
616
620
|
initArticleCommentPopover();
|
|
617
|
-
updateNavbarCurrentPage();
|
|
618
621
|
refreshNavbarIcons();
|
|
619
622
|
}
|
|
620
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");
|