@anglefeint/astro-theme 0.1.8 → 0.1.10
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/package.json +1 -1
- package/public/scripts/about-effects.js +35 -9
- package/public/scripts/blogpost-effects.js +39 -13
- package/public/styles/blog-list.css +35 -0
- package/src/cli-new-post.mjs +3 -3
- package/src/components/FormattedDate.astro +3 -2
- package/src/components/shared/CommonFooter.astro +2 -30
- package/src/components/shared/CommonHeader.astro +4 -49
- package/src/components/shared/Icon.astro +39 -0
- package/src/components/shared/LangSwitcher.astro +35 -0
- package/src/components/shared/SocialMenu.astro +20 -0
- package/src/i18n/messages.ts +0 -8
- package/src/layouts/BlogPost.astro +1 -1
- package/src/layouts/HomePage.astro +1 -1
package/package.json
CHANGED
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
runtimeConfig = {};
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
var prefersReducedMotion = false;
|
|
13
|
+
try {
|
|
14
|
+
prefersReducedMotion = !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);
|
|
15
|
+
} catch (_e) {
|
|
16
|
+
prefersReducedMotion = false;
|
|
17
|
+
}
|
|
12
18
|
// ── Terminal background: dir + 可输入 (点击背景聚焦,回车仅换行) ──
|
|
13
19
|
var bgCanvas = document.querySelector('.hacker-bg-canvas');
|
|
14
20
|
if (bgCanvas) {
|
|
@@ -107,14 +113,19 @@
|
|
|
107
113
|
cancelAnimationFrame(bgAnimationId);
|
|
108
114
|
bgAnimationId = 0;
|
|
109
115
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (document.hidden) {
|
|
116
|
+
if (prefersReducedMotion) {
|
|
117
|
+
renderBg(performance.now());
|
|
113
118
|
stopBackgroundLoop();
|
|
114
|
-
|
|
119
|
+
} else {
|
|
120
|
+
startBackgroundLoop();
|
|
121
|
+
document.addEventListener('visibilitychange', function() {
|
|
122
|
+
if (document.hidden) {
|
|
123
|
+
stopBackgroundLoop();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
startBackgroundLoop();
|
|
127
|
+
});
|
|
115
128
|
}
|
|
116
|
-
startBackgroundLoop();
|
|
117
|
-
});
|
|
118
129
|
|
|
119
130
|
// 点击背景聚焦,点击内容失焦
|
|
120
131
|
document.addEventListener('click', function(e) {
|
|
@@ -207,7 +218,7 @@
|
|
|
207
218
|
var backTop = document.querySelector('.hacker-back-to-top');
|
|
208
219
|
if (backTop) {
|
|
209
220
|
backTop.addEventListener('click', function() {
|
|
210
|
-
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
221
|
+
window.scrollTo({ top: 0, behavior: prefersReducedMotion ? 'auto' : 'smooth' });
|
|
211
222
|
});
|
|
212
223
|
}
|
|
213
224
|
|
|
@@ -235,6 +246,7 @@
|
|
|
235
246
|
return s;
|
|
236
247
|
}
|
|
237
248
|
function startDecryptorFlash() {
|
|
249
|
+
if (prefersReducedMotion) return;
|
|
238
250
|
if (decryptorInterval) clearInterval(decryptorInterval);
|
|
239
251
|
var keys = 0, sec = 1;
|
|
240
252
|
decryptorInterval = setInterval(function() {
|
|
@@ -444,7 +456,7 @@
|
|
|
444
456
|
|
|
445
457
|
// ── Mouse glow ──
|
|
446
458
|
var glow = document.querySelector('.hacker-mouse-glow');
|
|
447
|
-
if (glow) {
|
|
459
|
+
if (glow && !prefersReducedMotion) {
|
|
448
460
|
var glowRaf;
|
|
449
461
|
var mx = 0, my = 0;
|
|
450
462
|
document.addEventListener('mousemove', function(e) {
|
|
@@ -476,7 +488,14 @@
|
|
|
476
488
|
|
|
477
489
|
// ── Typewriter section titles ──
|
|
478
490
|
var titles = document.querySelectorAll('.about-section-title');
|
|
479
|
-
if (
|
|
491
|
+
if (prefersReducedMotion && titles.length) {
|
|
492
|
+
titles.forEach(function(el) {
|
|
493
|
+
var fullText = el.textContent || '';
|
|
494
|
+
el.setAttribute('data-full-text', fullText);
|
|
495
|
+
el.textContent = fullText;
|
|
496
|
+
el.style.minHeight = '1.2em';
|
|
497
|
+
});
|
|
498
|
+
} else if (window.IntersectionObserver && titles.length) {
|
|
480
499
|
titles.forEach(function(el) {
|
|
481
500
|
var fullText = el.textContent || '';
|
|
482
501
|
el.setAttribute('data-full-text', fullText);
|
|
@@ -510,6 +529,13 @@
|
|
|
510
529
|
var scan = document.querySelector('.hacker-load-scan');
|
|
511
530
|
if (regen && article) {
|
|
512
531
|
regen.addEventListener('click', function() {
|
|
532
|
+
if (prefersReducedMotion) {
|
|
533
|
+
article.classList.add('hacker-flash');
|
|
534
|
+
setTimeout(function() {
|
|
535
|
+
article.classList.remove('hacker-flash');
|
|
536
|
+
}, 120);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
513
539
|
regen.disabled = true;
|
|
514
540
|
article.classList.add('hacker-flash');
|
|
515
541
|
if (scan) {
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
(function() {
|
|
2
|
+
function init() {
|
|
3
|
+
var prefersReducedMotion = false;
|
|
4
|
+
try {
|
|
5
|
+
prefersReducedMotion = !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);
|
|
6
|
+
} catch (_e) {
|
|
7
|
+
prefersReducedMotion = false;
|
|
8
|
+
}
|
|
9
|
+
// 阅读进度条
|
|
4
10
|
var progress = document.querySelector('.ai-read-progress');
|
|
5
11
|
var article = document.querySelector('.ai-article');
|
|
6
12
|
var toast = document.querySelector('.ai-stage-toast');
|
|
@@ -42,13 +48,13 @@
|
|
|
42
48
|
onScroll();
|
|
43
49
|
window.addEventListener('scroll', onScroll, { passive: true });
|
|
44
50
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
var backTop = document.querySelector('.ai-back-to-top');
|
|
52
|
+
if (backTop) {
|
|
53
|
+
backTop.addEventListener('click', function() {
|
|
54
|
+
window.scrollTo({ top: 0, behavior: prefersReducedMotion ? 'auto' : 'smooth' });
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function initHeroCanvas() {
|
|
52
58
|
var shell = document.querySelector('.hero-shell');
|
|
53
59
|
if (!shell) return;
|
|
54
60
|
var canvas = shell.querySelector('.hero-canvas');
|
|
@@ -283,6 +289,11 @@
|
|
|
283
289
|
sizeCanvas();
|
|
284
290
|
buildEdge(img);
|
|
285
291
|
wrap.classList.add('ready');
|
|
292
|
+
if (prefersReducedMotion) {
|
|
293
|
+
var staticCtx = canvas.getContext('2d');
|
|
294
|
+
if (staticCtx) staticCtx.drawImage(baseCanvas, 0, 0);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
286
297
|
heroRaf = requestAnimationFrame(heroRender);
|
|
287
298
|
};
|
|
288
299
|
img.src = new URL(src, window.location.href).href;
|
|
@@ -294,6 +305,7 @@
|
|
|
294
305
|
}
|
|
295
306
|
}, { passive: true });
|
|
296
307
|
function onHeroVisibilityChange() {
|
|
308
|
+
if (prefersReducedMotion) return;
|
|
297
309
|
if (document.hidden) {
|
|
298
310
|
if (heroRaf) cancelAnimationFrame(heroRaf);
|
|
299
311
|
heroRaf = 0;
|
|
@@ -305,7 +317,7 @@
|
|
|
305
317
|
window.addEventListener('beforeunload', function() { cancelAnimationFrame(heroRaf); }, { once: true });
|
|
306
318
|
}
|
|
307
319
|
initHeroCanvas();
|
|
308
|
-
|
|
320
|
+
function initRedQueenTv() {
|
|
309
321
|
var shell = document.querySelector('.rq-tv');
|
|
310
322
|
var stage = document.querySelector('.rq-tv-stage');
|
|
311
323
|
var toggle = document.querySelector('.rq-tv-toggle');
|
|
@@ -361,8 +373,22 @@
|
|
|
361
373
|
return new URL(item.url, window.location.href).href;
|
|
362
374
|
}
|
|
363
375
|
|
|
364
|
-
|
|
365
|
-
|
|
376
|
+
var playlist = [{ url: source, type: guessImageType(source), holdLast: 360 }];
|
|
377
|
+
if (source2) playlist.push({ url: source2, type: guessImageType(source2), holdLast: 500 });
|
|
378
|
+
if (prefersReducedMotion) {
|
|
379
|
+
setCollapsed(false);
|
|
380
|
+
var staticImg = new Image();
|
|
381
|
+
staticImg.className = 'rq-tv-screen';
|
|
382
|
+
staticImg.alt = '';
|
|
383
|
+
staticImg.decoding = 'async';
|
|
384
|
+
staticImg.loading = 'lazy';
|
|
385
|
+
staticImg.src = resolveItemUrl(playlist[0]);
|
|
386
|
+
stage.innerHTML = '';
|
|
387
|
+
stage.appendChild(staticImg);
|
|
388
|
+
toggle.hidden = true;
|
|
389
|
+
toggle.setAttribute('aria-hidden', 'true');
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
366
392
|
|
|
367
393
|
function setCollapsed(collapsed) {
|
|
368
394
|
shell.classList.toggle('rq-tv-collapsed', collapsed);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/* Pagination styles for cyber blog list pages */
|
|
2
|
+
body.cyber-page .pagination {
|
|
3
|
+
display: flex;
|
|
4
|
+
justify-content: center;
|
|
5
|
+
align-items: center;
|
|
6
|
+
gap: 0.5rem;
|
|
7
|
+
margin-top: 2.5rem;
|
|
8
|
+
padding: 1.5rem 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
body.cyber-page .pagination a {
|
|
12
|
+
padding: 0.5rem 1rem;
|
|
13
|
+
color: rgb(212, 196, 232);
|
|
14
|
+
text-decoration: none;
|
|
15
|
+
border: 1px solid rgba(198, 156, 224, 0.42);
|
|
16
|
+
border-radius: 4px;
|
|
17
|
+
transition: 0.25s ease;
|
|
18
|
+
text-shadow: 0 0 10px rgba(214, 152, 226, 0.2);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
body.cyber-page .pagination a:hover {
|
|
22
|
+
border-color: rgba(226, 166, 236, 0.88);
|
|
23
|
+
text-shadow: 0 0 15px rgba(232, 174, 242, 0.5);
|
|
24
|
+
box-shadow: 0 0 15px rgba(222, 154, 228, 0.26);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
body.cyber-page .pagination a.current {
|
|
28
|
+
background: rgba(188, 122, 214, 0.2);
|
|
29
|
+
border-color: rgb(224, 170, 236);
|
|
30
|
+
color: rgb(244, 224, 252);
|
|
31
|
+
text-shadow: 0 0 15px rgba(232, 174, 242, 0.48);
|
|
32
|
+
box-shadow:
|
|
33
|
+
0 0 20px rgba(118, 198, 255, 0.24),
|
|
34
|
+
0 0 12px rgba(220, 152, 230, 0.22);
|
|
35
|
+
}
|
package/src/cli-new-post.mjs
CHANGED
|
@@ -56,21 +56,21 @@ function templateFor(locale, slug, pubDate, heroImage) {
|
|
|
56
56
|
en: toTitleFromSlug(slug),
|
|
57
57
|
ja: '新しい記事タイトル',
|
|
58
58
|
ko: '새 글 제목',
|
|
59
|
-
es: '
|
|
59
|
+
es: 'Título del nuevo artículo',
|
|
60
60
|
zh: '新文章标题',
|
|
61
61
|
};
|
|
62
62
|
const descriptionByLocale = {
|
|
63
63
|
en: `A short EN post scaffold for "${slug}".`,
|
|
64
64
|
ja: `「${slug}」用の短い日本語記事テンプレートです。`,
|
|
65
65
|
ko: `"${slug}"용 한국어 글 템플릿입니다.`,
|
|
66
|
-
es: `Plantilla breve en
|
|
66
|
+
es: `Plantilla breve en español para "${slug}".`,
|
|
67
67
|
zh: `“${slug}”的中文文章模板。`,
|
|
68
68
|
};
|
|
69
69
|
const bodyByLocale = {
|
|
70
70
|
en: `Write your EN content for "${slug}" here.`,
|
|
71
71
|
ja: `ここに「${slug}」の日本語本文を書いてください。`,
|
|
72
72
|
ko: `여기에 "${slug}" 한국어 본문을 작성하세요.`,
|
|
73
|
-
es: `Escribe
|
|
73
|
+
es: `Escribe aquí el contenido en español para "${slug}".`,
|
|
74
74
|
zh: `请在这里填写“${slug}”的中文正文。`,
|
|
75
75
|
};
|
|
76
76
|
return `---
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
interface Props {
|
|
3
3
|
date: Date;
|
|
4
|
+
locale?: string;
|
|
4
5
|
}
|
|
5
6
|
|
|
6
|
-
const { date } = Astro.props;
|
|
7
|
+
const { date, locale = 'en-US' } = Astro.props as Props;
|
|
7
8
|
---
|
|
8
9
|
|
|
9
10
|
<time datetime={date.toISOString()}>
|
|
10
11
|
{
|
|
11
|
-
date.toLocaleDateString(
|
|
12
|
+
date.toLocaleDateString(locale, {
|
|
12
13
|
year: 'numeric',
|
|
13
14
|
month: 'short',
|
|
14
15
|
day: 'numeric',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { SITE_TAGLINE, SITE_TITLE } from '@anglefeint/site-config/site';
|
|
3
3
|
import { SOCIAL_LINKS } from '@anglefeint/site-config/social';
|
|
4
|
+
import SocialMenu from './SocialMenu.astro';
|
|
4
5
|
|
|
5
6
|
const today = new Date();
|
|
6
7
|
interface Props {
|
|
@@ -16,36 +17,7 @@ const { tagline = SITE_TAGLINE, scanlines = false } = Astro.props as Props;
|
|
|
16
17
|
© {today.getFullYear()} {SITE_TITLE}. {tagline}
|
|
17
18
|
{SOCIAL_LINKS.length > 0 && (
|
|
18
19
|
<div class="social-links">
|
|
19
|
-
{SOCIAL_LINKS
|
|
20
|
-
<a href={link.href} target="_blank" rel="noopener noreferrer">
|
|
21
|
-
<span class="sr-only">{link.label}</span>
|
|
22
|
-
{link.icon === 'mastodon' && (
|
|
23
|
-
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32">
|
|
24
|
-
<path
|
|
25
|
-
fill="currentColor"
|
|
26
|
-
d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z"
|
|
27
|
-
/>
|
|
28
|
-
</svg>
|
|
29
|
-
)}
|
|
30
|
-
{link.icon === 'twitter' && (
|
|
31
|
-
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32">
|
|
32
|
-
<path
|
|
33
|
-
fill="currentColor"
|
|
34
|
-
d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"
|
|
35
|
-
/>
|
|
36
|
-
</svg>
|
|
37
|
-
)}
|
|
38
|
-
{link.icon === 'github' && (
|
|
39
|
-
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32">
|
|
40
|
-
<path
|
|
41
|
-
fill="currentColor"
|
|
42
|
-
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
|
43
|
-
/>
|
|
44
|
-
</svg>
|
|
45
|
-
)}
|
|
46
|
-
{!link.icon && <span>{link.label}</span>}
|
|
47
|
-
</a>
|
|
48
|
-
))}
|
|
20
|
+
<SocialMenu links={SOCIAL_LINKS} />
|
|
49
21
|
</div>
|
|
50
22
|
)}
|
|
51
23
|
{scanlines && <div class="ai-scanlines" aria-hidden="true"></div>}
|
|
@@ -3,6 +3,8 @@ import { SITE_TITLE } from '@anglefeint/site-config/site';
|
|
|
3
3
|
import { SOCIAL_LINKS } from '@anglefeint/site-config/social';
|
|
4
4
|
import { THEME } from '@anglefeint/site-config/theme';
|
|
5
5
|
import HeaderLink from '../HeaderLink.astro';
|
|
6
|
+
import LangSwitcher from './LangSwitcher.astro';
|
|
7
|
+
import SocialMenu from './SocialMenu.astro';
|
|
6
8
|
import {
|
|
7
9
|
DEFAULT_LOCALE,
|
|
8
10
|
LOCALE_LABELS,
|
|
@@ -67,59 +69,12 @@ const localeOptions = SUPPORTED_LOCALES.map((targetLocale) => ({
|
|
|
67
69
|
</div>
|
|
68
70
|
<div class="nav-right">
|
|
69
71
|
<div class="social-links">
|
|
70
|
-
<
|
|
71
|
-
<label class="lang-label" for="lang-select">{labels.language}</label>
|
|
72
|
-
<select
|
|
73
|
-
id="lang-select"
|
|
74
|
-
class="lang-select"
|
|
75
|
-
aria-label={labels.language}
|
|
76
|
-
onchange="if (this.value) window.location.href = this.value;"
|
|
77
|
-
>
|
|
78
|
-
{
|
|
79
|
-
localeOptions.map((option) => (
|
|
80
|
-
<option value={option.href} selected={option.locale === locale}>
|
|
81
|
-
{option.label}
|
|
82
|
-
</option>
|
|
83
|
-
))
|
|
84
|
-
}
|
|
85
|
-
</select>
|
|
86
|
-
</div>
|
|
72
|
+
<LangSwitcher label={labels.language} currentLocale={locale} options={localeOptions} />
|
|
87
73
|
<div class="nav-status" aria-label="System status">
|
|
88
74
|
<span class="nav-status-dot" aria-hidden="true"></span>
|
|
89
75
|
<span class="nav-status-text">{labels.status}</span>
|
|
90
76
|
</div>
|
|
91
|
-
{
|
|
92
|
-
SOCIAL_LINKS.map((link) => (
|
|
93
|
-
<a href={link.href} target="_blank" rel="noopener noreferrer">
|
|
94
|
-
<span class="sr-only">{link.label}</span>
|
|
95
|
-
{link.icon === 'mastodon' && (
|
|
96
|
-
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32">
|
|
97
|
-
<path
|
|
98
|
-
fill="currentColor"
|
|
99
|
-
d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z"
|
|
100
|
-
/>
|
|
101
|
-
</svg>
|
|
102
|
-
)}
|
|
103
|
-
{link.icon === 'twitter' && (
|
|
104
|
-
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32">
|
|
105
|
-
<path
|
|
106
|
-
fill="currentColor"
|
|
107
|
-
d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"
|
|
108
|
-
/>
|
|
109
|
-
</svg>
|
|
110
|
-
)}
|
|
111
|
-
{link.icon === 'github' && (
|
|
112
|
-
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32">
|
|
113
|
-
<path
|
|
114
|
-
fill="currentColor"
|
|
115
|
-
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
|
116
|
-
/>
|
|
117
|
-
</svg>
|
|
118
|
-
)}
|
|
119
|
-
{!link.icon && <span>{link.label}</span>}
|
|
120
|
-
</a>
|
|
121
|
-
))
|
|
122
|
-
}
|
|
77
|
+
<SocialMenu links={SOCIAL_LINKS} />
|
|
123
78
|
</div>
|
|
124
79
|
</div>
|
|
125
80
|
</nav>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
name?: 'mastodon' | 'twitter' | 'github';
|
|
4
|
+
size?: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const { name, size = 32 } = Astro.props as Props;
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
name === 'mastodon' && (
|
|
12
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" width={size} height={size}>
|
|
13
|
+
<path
|
|
14
|
+
fill="currentColor"
|
|
15
|
+
d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z"
|
|
16
|
+
/>
|
|
17
|
+
</svg>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
{
|
|
21
|
+
name === 'twitter' && (
|
|
22
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" width={size} height={size}>
|
|
23
|
+
<path
|
|
24
|
+
fill="currentColor"
|
|
25
|
+
d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"
|
|
26
|
+
/>
|
|
27
|
+
</svg>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
{
|
|
31
|
+
name === 'github' && (
|
|
32
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" width={size} height={size}>
|
|
33
|
+
<path
|
|
34
|
+
fill="currentColor"
|
|
35
|
+
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
|
36
|
+
/>
|
|
37
|
+
</svg>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Locale } from '@anglefeint/site-i18n/config';
|
|
3
|
+
|
|
4
|
+
interface LocaleOption {
|
|
5
|
+
locale: Locale;
|
|
6
|
+
label: string;
|
|
7
|
+
href: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
label: string;
|
|
12
|
+
currentLocale: Locale;
|
|
13
|
+
options: LocaleOption[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { label, currentLocale, options } = Astro.props as Props;
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
<div class="lang-switcher" aria-label={label}>
|
|
20
|
+
<label class="lang-label" for="lang-select">{label}</label>
|
|
21
|
+
<select
|
|
22
|
+
id="lang-select"
|
|
23
|
+
class="lang-select"
|
|
24
|
+
aria-label={label}
|
|
25
|
+
onchange="if (this.value) window.location.href = this.value;"
|
|
26
|
+
>
|
|
27
|
+
{
|
|
28
|
+
options.map((option) => (
|
|
29
|
+
<option value={option.href} selected={option.locale === currentLocale}>
|
|
30
|
+
{option.label}
|
|
31
|
+
</option>
|
|
32
|
+
))
|
|
33
|
+
}
|
|
34
|
+
</select>
|
|
35
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SOCIAL_LINKS, type SocialLink } from '@anglefeint/site-config/social';
|
|
3
|
+
import Icon from './Icon.astro';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
links?: SocialLink[];
|
|
7
|
+
iconSize?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { links = SOCIAL_LINKS, iconSize = 32 } = Astro.props as Props;
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
links.map((link) => (
|
|
15
|
+
<a href={link.href} target="_blank" rel="noopener noreferrer">
|
|
16
|
+
<span class="sr-only">{link.label}</span>
|
|
17
|
+
{link.icon ? <Icon name={link.icon} size={iconSize} /> : <span>{link.label}</span>}
|
|
18
|
+
</a>
|
|
19
|
+
))
|
|
20
|
+
}
|
package/src/i18n/messages.ts
CHANGED
|
@@ -37,9 +37,6 @@ type Messages = {
|
|
|
37
37
|
related: string;
|
|
38
38
|
regenerate: string;
|
|
39
39
|
};
|
|
40
|
-
footer: {
|
|
41
|
-
tagline: string;
|
|
42
|
-
};
|
|
43
40
|
};
|
|
44
41
|
|
|
45
42
|
const MESSAGES: Record<Locale, Messages> = {
|
|
@@ -75,7 +72,6 @@ const MESSAGES: Record<Locale, Messages> = {
|
|
|
75
72
|
related: 'Related',
|
|
76
73
|
regenerate: 'Regenerate',
|
|
77
74
|
},
|
|
78
|
-
footer: { tagline: 'Built with Astro.' },
|
|
79
75
|
},
|
|
80
76
|
ja: {
|
|
81
77
|
siteTitle: 'Angle Feint',
|
|
@@ -109,7 +105,6 @@ const MESSAGES: Record<Locale, Messages> = {
|
|
|
109
105
|
related: '関連記事',
|
|
110
106
|
regenerate: '再生成',
|
|
111
107
|
},
|
|
112
|
-
footer: { tagline: 'Astro で構築。' },
|
|
113
108
|
},
|
|
114
109
|
ko: {
|
|
115
110
|
siteTitle: 'Angle Feint',
|
|
@@ -143,7 +138,6 @@ const MESSAGES: Record<Locale, Messages> = {
|
|
|
143
138
|
related: '관련 글',
|
|
144
139
|
regenerate: '재생성',
|
|
145
140
|
},
|
|
146
|
-
footer: { tagline: 'Astro로 제작됨.' },
|
|
147
141
|
},
|
|
148
142
|
es: {
|
|
149
143
|
siteTitle: 'Angle Feint',
|
|
@@ -177,7 +171,6 @@ const MESSAGES: Record<Locale, Messages> = {
|
|
|
177
171
|
related: 'Relacionados',
|
|
178
172
|
regenerate: 'Regenerar',
|
|
179
173
|
},
|
|
180
|
-
footer: { tagline: 'Construido con Astro.' },
|
|
181
174
|
},
|
|
182
175
|
zh: {
|
|
183
176
|
siteTitle: 'Angle Feint',
|
|
@@ -211,7 +204,6 @@ const MESSAGES: Record<Locale, Messages> = {
|
|
|
211
204
|
related: '相关文章',
|
|
212
205
|
regenerate: '重新生成',
|
|
213
206
|
},
|
|
214
|
-
footer: { tagline: 'Built with Astro.' },
|
|
215
207
|
},
|
|
216
208
|
};
|
|
217
209
|
|
|
@@ -243,7 +243,7 @@ for (let i = 0; i < pts.length; i++) {
|
|
|
243
243
|
)}
|
|
244
244
|
<h3 class="ai-related-card-title">{p.data.title}</h3>
|
|
245
245
|
<p class="ai-related-card-date">
|
|
246
|
-
<FormattedDate date={p.data.pubDate} />
|
|
246
|
+
<FormattedDate date={p.data.pubDate} locale={resolvedLocale} />
|
|
247
247
|
</p>
|
|
248
248
|
</a>
|
|
249
249
|
))}
|
|
@@ -32,7 +32,7 @@ const heroText = SITE_HERO_BY_LOCALE[locale] ?? messages.home.hero;
|
|
|
32
32
|
{post.data.title}
|
|
33
33
|
</a>
|
|
34
34
|
<div class="home-post-meta">
|
|
35
|
-
<FormattedDate date={post.data.pubDate} />
|
|
35
|
+
<FormattedDate date={post.data.pubDate} locale={locale} />
|
|
36
36
|
{post.data.description && <> · {post.data.description}</>}
|
|
37
37
|
</div>
|
|
38
38
|
</li>
|