All checks were successful
docker-images / resolve-build-targets (push) Successful in 5s
ui-regression / playwright-regression (push) Successful in 3m51s
docker-images / build-and-push (admin) (push) Successful in 4s
docker-images / build-and-push (backend) (push) Successful in 3s
docker-images / build-and-push (frontend) (push) Successful in 1m10s
docker-images / submit-indexnow (push) Successful in 19s
- Introduced `compactJsonLd` utility to filter out falsy values from JSON-LD arrays. - Updated various pages to utilize `compactJsonLd` for cleaner JSON-LD handling. - Refactored music playlist configuration in Header component. - Enhanced BaseLayout with inline script for JSON-LD and removed unnecessary media attributes from stylesheets. - Improved error handling in category and tag pages by simplifying response logic. - Added new styles for home hero section and sidebar components to enhance UI. - Adjusted layout components for better responsiveness and visual consistency.
301 lines
12 KiB
Plaintext
301 lines
12 KiB
Plaintext
---
|
|
import BaseLayout from '../../layouts/BaseLayout.astro';
|
|
import DiscoveryBrief from '../../components/seo/DiscoveryBrief.astro';
|
|
import PageViewTracker from '../../components/seo/PageViewTracker.astro';
|
|
import SharePanel from '../../components/seo/SharePanel.astro';
|
|
import TerminalWindow from '../../components/ui/TerminalWindow.astro';
|
|
import CommandPrompt from '../../components/ui/CommandPrompt.astro';
|
|
import StatsList from '../../components/StatsList.astro';
|
|
import TechStackList from '../../components/TechStackList.astro';
|
|
import { api, DEFAULT_SITE_SETTINGS } from '../../lib/api/client';
|
|
import { getI18n } from '../../lib/i18n';
|
|
import { buildDiscoveryHighlights, buildFaqJsonLd, buildPageFaqs, compactJsonLd } from '../../lib/seo';
|
|
|
|
export const prerender = false;
|
|
|
|
let siteSettings = DEFAULT_SITE_SETTINGS;
|
|
let systemStats = [];
|
|
let techStack = [];
|
|
const { locale, t } = getI18n(Astro);
|
|
const isEnglish = locale.startsWith('en');
|
|
|
|
try {
|
|
const [settings, posts, tags, friendLinks] = await Promise.all([
|
|
api.getSiteSettings(),
|
|
api.getPosts(),
|
|
api.getTags(),
|
|
api.getFriendLinks(),
|
|
]);
|
|
|
|
siteSettings = settings;
|
|
techStack = siteSettings.techStack.map(name => ({ name }));
|
|
systemStats = [
|
|
{ label: t('common.posts'), value: String(posts.length) },
|
|
{ label: t('common.tags'), value: String(tags.length) },
|
|
{ label: t('common.friends'), value: String(friendLinks.filter(friend => friend.status === 'approved').length) },
|
|
];
|
|
} catch (error) {
|
|
console.error('Failed to load about data:', error);
|
|
techStack = siteSettings.techStack.map(name => ({ name }));
|
|
systemStats = [
|
|
{ label: t('common.posts'), value: '0' },
|
|
{ label: t('common.tags'), value: '0' },
|
|
{ label: t('common.friends'), value: '0' },
|
|
];
|
|
}
|
|
|
|
const ownerInitial = siteSettings.ownerName.charAt(0) || 'T';
|
|
const siteBaseUrl = (siteSettings.siteUrl || new URL(Astro.request.url).origin).replace(/\/$/, '');
|
|
const aboutCanonicalUrl = new URL('/about', siteBaseUrl).toString();
|
|
const sharePanelCopy = isEnglish
|
|
? {
|
|
badge: 'profile source',
|
|
title: 'Share the profile page',
|
|
description:
|
|
'Use this page as the canonical identity and capability profile so social sharing and AI search can cite one stable source.',
|
|
}
|
|
: {
|
|
badge: '个人介绍',
|
|
title: '分享个人介绍',
|
|
description: '把这页作为个人介绍页分享,方便快速了解作者信息、技术栈和联系方式。',
|
|
};
|
|
const aboutHighlights = buildDiscoveryHighlights([
|
|
siteSettings.ownerTitle,
|
|
siteSettings.ownerBio,
|
|
siteSettings.techStack.slice(0, 4).join(' / '),
|
|
]);
|
|
const aboutFaqs = buildPageFaqs({
|
|
locale,
|
|
pageTitle: t('about.pageTitle'),
|
|
summary: siteSettings.ownerBio || siteSettings.siteDescription,
|
|
primaryLabel: t('about.pageTitle'),
|
|
primaryUrl: aboutCanonicalUrl,
|
|
relatedLinks: [
|
|
{ label: t('nav.articles'), url: `${siteBaseUrl}/articles` },
|
|
{ label: t('nav.timeline'), url: `${siteBaseUrl}/timeline` },
|
|
{ label: t('nav.ask'), url: `${siteBaseUrl}/ask` },
|
|
],
|
|
signals: aboutHighlights,
|
|
});
|
|
const aboutFaqJsonLd = buildFaqJsonLd(aboutFaqs);
|
|
const aboutJsonLd = [
|
|
{
|
|
'@context': 'https://schema.org',
|
|
'@type': 'AboutPage',
|
|
name: `${siteSettings.ownerName} / ${siteSettings.siteName}`,
|
|
description: siteSettings.siteDescription,
|
|
url: aboutCanonicalUrl,
|
|
inLanguage: locale,
|
|
},
|
|
{
|
|
'@context': 'https://schema.org',
|
|
'@type': 'ProfilePage',
|
|
name: siteSettings.ownerName,
|
|
url: aboutCanonicalUrl,
|
|
mainEntity: {
|
|
'@type': 'Person',
|
|
name: siteSettings.ownerName,
|
|
jobTitle: siteSettings.ownerTitle,
|
|
description: siteSettings.ownerBio,
|
|
image: siteSettings.ownerAvatarUrl || undefined,
|
|
sameAs: [
|
|
siteSettings.social.github,
|
|
siteSettings.social.twitter,
|
|
].filter(Boolean),
|
|
},
|
|
},
|
|
{
|
|
'@context': 'https://schema.org',
|
|
'@type': 'BreadcrumbList',
|
|
itemListElement: [
|
|
{
|
|
'@type': 'ListItem',
|
|
position: 1,
|
|
name: siteSettings.siteName,
|
|
item: siteBaseUrl,
|
|
},
|
|
{
|
|
'@type': 'ListItem',
|
|
position: 2,
|
|
name: t('about.pageTitle'),
|
|
item: aboutCanonicalUrl,
|
|
},
|
|
],
|
|
},
|
|
aboutFaqJsonLd,
|
|
];
|
|
---
|
|
|
|
<BaseLayout
|
|
title={`${t('about.pageTitle')} - ${siteSettings.siteShortName}`}
|
|
description={siteSettings.siteDescription}
|
|
siteSettings={siteSettings}
|
|
jsonLd={compactJsonLd(aboutJsonLd)}
|
|
>
|
|
<PageViewTracker pageType="about" entityId="about" />
|
|
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
<TerminalWindow title="~/about" class="w-full">
|
|
<div class="mb-6 px-4">
|
|
<CommandPrompt command="id -un" />
|
|
<div class="terminal-panel ml-4 mt-4">
|
|
<div class="terminal-kicker">identity profile</div>
|
|
<div class="terminal-section-title mt-4">
|
|
<span class="terminal-section-icon">
|
|
<i class="fas fa-user-circle"></i>
|
|
</span>
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-[var(--title-color)]">{t('about.title')}</h1>
|
|
<p class="mt-2 max-w-2xl text-sm leading-6 text-[var(--text-secondary)]">
|
|
{t('about.intro')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="mt-5 flex flex-wrap gap-2">
|
|
<span class="terminal-stat-pill">
|
|
<i class="fas fa-layer-group text-[var(--primary)]"></i>
|
|
<span>{t('about.techStackCount', { count: techStack.length })}</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ml-4 mt-4">
|
|
<SharePanel
|
|
shareTitle={`${siteSettings.ownerName} / ${t('about.pageTitle')}`}
|
|
summary={siteSettings.ownerBio || siteSettings.siteDescription}
|
|
canonicalUrl={aboutCanonicalUrl}
|
|
badge={sharePanelCopy.badge}
|
|
kicker="geo / profile"
|
|
title={sharePanelCopy.title}
|
|
description={sharePanelCopy.description}
|
|
stats={systemStats.slice(0, 4)}
|
|
wechatShareQrEnabled={siteSettings.seo.wechatShareQrEnabled}
|
|
/>
|
|
</div>
|
|
|
|
<div class="ml-4 mt-4">
|
|
<DiscoveryBrief
|
|
badge={isEnglish ? 'profile brief' : '身份摘要'}
|
|
kicker="geo / profile"
|
|
title={isEnglish ? 'AI-readable profile brief' : '给 AI 看的身份摘要'}
|
|
summary={siteSettings.ownerBio || siteSettings.siteDescription}
|
|
highlights={aboutHighlights}
|
|
faqs={aboutFaqs}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="px-4">
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
<div>
|
|
<CommandPrompt command="sed -n '1,80p' ~/profile.md" />
|
|
<div class="ml-4 mt-4">
|
|
<div class="terminal-panel p-6">
|
|
<div class="flex items-center gap-4 mb-4">
|
|
{siteSettings.ownerAvatarUrl ? (
|
|
<img
|
|
src={siteSettings.ownerAvatarUrl}
|
|
alt={siteSettings.ownerName}
|
|
class="w-16 h-16 rounded-full object-cover border border-[var(--border-color)]"
|
|
/>
|
|
) : (
|
|
<div class="w-16 h-16 rounded-full bg-[var(--primary)] flex items-center justify-center text-[var(--terminal-bg)] text-2xl font-bold shadow-lg shadow-[var(--primary)]/20">
|
|
{ownerInitial}
|
|
</div>
|
|
)}
|
|
<div>
|
|
<h2 class="text-xl font-bold text-[var(--title-color)]">{siteSettings.ownerName}</h2>
|
|
<p class="text-sm text-[var(--text-secondary)] mt-1">{siteSettings.ownerTitle}</p>
|
|
</div>
|
|
</div>
|
|
<p class="text-[var(--text-secondary)] leading-7">{siteSettings.ownerBio}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-6">
|
|
<CommandPrompt command="ls ~/.local/share/stack" />
|
|
<div class="ml-4 mt-4">
|
|
<TechStackList items={techStack} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<CommandPrompt command="uname -a" />
|
|
<div class="ml-4 mt-4">
|
|
<StatsList stats={systemStats} />
|
|
</div>
|
|
|
|
<div class="mt-6">
|
|
<CommandPrompt command="printenv | grep -E 'MAIL|WEB'" />
|
|
<div class="ml-4 mt-4">
|
|
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
|
{siteSettings.social.github && (
|
|
<a
|
|
href={siteSettings.social.github}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="terminal-interactive-card flex items-center gap-3 rounded-2xl border border-[var(--border-color)] bg-[linear-gradient(135deg,rgba(15,23,42,0.04),rgba(var(--primary-rgb),0.08))] px-4 py-3 shadow-[0_10px_28px_rgba(15,23,42,0.08)]"
|
|
>
|
|
<span class="flex h-10 w-10 items-center justify-center rounded-xl bg-[#111827] text-white">
|
|
<i class="fab fa-github text-sm"></i>
|
|
</span>
|
|
<span class="min-w-0">
|
|
<span class="block text-[11px] uppercase tracking-[0.2em] text-[var(--text-tertiary)]">{t('about.contact')}</span>
|
|
<span class="mt-1 block text-sm font-semibold text-[var(--title-color)]">GitHub</span>
|
|
</span>
|
|
</a>
|
|
)}
|
|
{siteSettings.social.twitter && (
|
|
<a
|
|
href={siteSettings.social.twitter}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="terminal-interactive-card flex items-center gap-3 rounded-2xl border border-[var(--border-color)] bg-[linear-gradient(135deg,rgba(15,23,42,0.04),rgba(var(--secondary-rgb),0.1))] px-4 py-3 shadow-[0_10px_28px_rgba(15,23,42,0.08)]"
|
|
>
|
|
<span class="flex h-10 w-10 items-center justify-center rounded-xl bg-[#1d9bf0] text-white">
|
|
<i class="fab fa-twitter text-sm"></i>
|
|
</span>
|
|
<span class="min-w-0">
|
|
<span class="block text-[11px] uppercase tracking-[0.2em] text-[var(--text-tertiary)]">{t('about.contact')}</span>
|
|
<span class="mt-1 block text-sm font-semibold text-[var(--title-color)]">Twitter</span>
|
|
</span>
|
|
</a>
|
|
)}
|
|
{siteSettings.social.email && (
|
|
<a
|
|
href={siteSettings.social.email}
|
|
class="terminal-interactive-card flex items-center gap-3 rounded-2xl border border-[var(--border-color)] bg-[linear-gradient(135deg,rgba(15,23,42,0.04),rgba(59,130,246,0.08))] px-4 py-3 shadow-[0_10px_28px_rgba(15,23,42,0.08)]"
|
|
>
|
|
<span class="flex h-10 w-10 items-center justify-center rounded-xl bg-[var(--primary)] text-white">
|
|
<i class="fas fa-envelope text-sm"></i>
|
|
</span>
|
|
<span class="min-w-0">
|
|
<span class="block text-[11px] uppercase tracking-[0.2em] text-[var(--text-tertiary)]">{t('about.contact')}</span>
|
|
<span class="mt-1 block text-sm font-semibold text-[var(--title-color)]">{t('comments.email')}</span>
|
|
</span>
|
|
</a>
|
|
)}
|
|
<a
|
|
href={siteSettings.siteUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="terminal-interactive-card flex items-center gap-3 rounded-2xl border border-[var(--border-color)] bg-[linear-gradient(135deg,rgba(15,23,42,0.04),rgba(16,185,129,0.1))] px-4 py-3 shadow-[0_10px_28px_rgba(15,23,42,0.08)]"
|
|
>
|
|
<span class="flex h-10 w-10 items-center justify-center rounded-xl bg-emerald-500 text-white">
|
|
<i class="fas fa-globe text-sm"></i>
|
|
</span>
|
|
<span class="min-w-0">
|
|
<span class="block text-[11px] uppercase tracking-[0.2em] text-[var(--text-tertiary)]">{t('about.contact')}</span>
|
|
<span class="mt-1 block text-sm font-semibold text-[var(--title-color)]">{t('about.website')}</span>
|
|
</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</TerminalWindow>
|
|
</div>
|
|
</BaseLayout>
|