feat: update tag and timeline share panel copy for clarity and conciseness
Some checks failed
docker-images / resolve-build-targets (push) Successful in 7s
ui-regression / playwright-regression (push) Failing after 13m4s
docker-images / build-and-push (admin) (push) Successful in 1m17s
docker-images / build-and-push (backend) (push) Successful in 28m13s
docker-images / build-and-push (frontend) (push) Successful in 47s
docker-images / submit-indexnow (push) Successful in 13s
Some checks failed
docker-images / resolve-build-targets (push) Successful in 7s
ui-regression / playwright-regression (push) Failing after 13m4s
docker-images / build-and-push (admin) (push) Successful in 1m17s
docker-images / build-and-push (backend) (push) Successful in 28m13s
docker-images / build-and-push (frontend) (push) Successful in 47s
docker-images / submit-indexnow (push) Successful in 13s
style: enhance global CSS for better responsiveness of terminal chips and navigation pills test: remove inline subscription test and add maintenance mode access code test feat: implement media library picker dialog for selecting images from the media library feat: add media URL controls for uploading and managing media assets feat: add migration for music_enabled and maintenance_mode settings in site settings feat: implement maintenance mode functionality with access control feat: create maintenance page with access code input and error handling chore: add TypeScript declaration for QR code module
This commit is contained in:
@@ -92,20 +92,20 @@ const articleMarkdown = contentText.replace(/^#\s+.+\r?\n+/, '');
|
||||
const paragraphCommentsEnabled = siteSettings.comments.paragraphsEnabled;
|
||||
const articleCopy = isEnglish
|
||||
? {
|
||||
digestBadge: 'featured digest',
|
||||
digestKicker: 'ai digest',
|
||||
digestTitle: 'AI / search summary',
|
||||
digestBadge: 'quick brief',
|
||||
digestKicker: 'reading preview',
|
||||
digestTitle: 'Read this first',
|
||||
digestDescription:
|
||||
'This block exposes a compact summary, key takeaways, and canonical follow-up paths for AI search and human skimming.',
|
||||
'A short overview of the article so readers can quickly grasp the key points before sharing or saving it.',
|
||||
highlightsTitle: 'Key takeaways',
|
||||
faqTitle: 'Quick FAQ',
|
||||
sourceTitle: 'Canonical source signals',
|
||||
sourceTitle: 'Page details',
|
||||
readTime: 'Read time',
|
||||
insightCount: 'Key points',
|
||||
faqCount: 'FAQ',
|
||||
updated: 'Updated',
|
||||
category: 'Category',
|
||||
canonical: 'Canonical',
|
||||
canonical: 'Permalink',
|
||||
keywords: 'Keywords',
|
||||
copySummary: 'Copy digest',
|
||||
copySuccess: 'Digest copied',
|
||||
@@ -114,39 +114,39 @@ const articleCopy = isEnglish
|
||||
shareSuccess: 'Share panel opened',
|
||||
shareFallback: 'Share text copied',
|
||||
shareFailed: 'Share failed',
|
||||
shareChannelsTitle: 'Quick share',
|
||||
shareChannelsTitle: 'Share options',
|
||||
shareChannelsDescription:
|
||||
'Push this article to social channels with a shorter path, so people and AI search tools can pick up the canonical link faster.',
|
||||
'Copy the overview or permalink, or continue sharing through the channels below.',
|
||||
shareToX: 'Share to X',
|
||||
shareToTelegram: 'Share to Telegram',
|
||||
shareToWeChat: 'WeChat QR',
|
||||
qrModalTitle: 'WeChat scan share',
|
||||
qrModalDescription: 'Scan this local QR code in WeChat to open the canonical article URL on mobile.',
|
||||
qrModalHint: 'Prefer sharing the canonical link so users and AI engines can fold signals back to one source.',
|
||||
shareToWeChat: 'WeChat scan',
|
||||
qrModalTitle: 'Scan with WeChat',
|
||||
qrModalDescription: 'Scan this QR code in WeChat to continue reading the article on mobile.',
|
||||
qrModalHint: 'When you want to send the article to someone else, copying the permalink below is usually the easiest option.',
|
||||
downloadQr: 'Download QR',
|
||||
downloadQrStarted: 'QR download started',
|
||||
qrOpened: 'WeChat QR ready',
|
||||
floatingToolsTitle: 'Digest tools',
|
||||
floatingToolsTitle: 'Quick actions',
|
||||
copyPermalinkSuccess: 'Permalink copied',
|
||||
copyPermalinkFailed: 'Permalink copy failed',
|
||||
toastSuccessTitle: 'Done',
|
||||
toastErrorTitle: 'Action failed',
|
||||
toastInfoTitle: 'Share ready',
|
||||
toastInfoTitle: 'Ready',
|
||||
}
|
||||
: {
|
||||
digestBadge: '精选摘要',
|
||||
digestKicker: 'ai digest',
|
||||
digestTitle: 'AI / 搜索摘要',
|
||||
digestDescription: '这块内容会把页面结论、重点摘录和规范入口显式写出来,方便 AI 搜索和用户快速理解。',
|
||||
digestBadge: '文章导读',
|
||||
digestKicker: '阅读前速览',
|
||||
digestTitle: '先看重点',
|
||||
digestDescription: '先用几句话帮你抓住这篇文章的重点,方便快速浏览、收藏或转发。',
|
||||
highlightsTitle: '关键信息',
|
||||
faqTitle: '快速问答',
|
||||
sourceTitle: '规范来源信号',
|
||||
sourceTitle: '页面信息',
|
||||
readTime: '阅读时长',
|
||||
insightCount: '重点条数',
|
||||
faqCount: '问答条数',
|
||||
updated: '最近更新',
|
||||
category: '归档分类',
|
||||
canonical: '规范地址',
|
||||
canonical: '固定链接',
|
||||
keywords: '关键词',
|
||||
copySummary: '复制摘要',
|
||||
copySuccess: '摘要已复制',
|
||||
@@ -155,23 +155,23 @@ const articleCopy = isEnglish
|
||||
shareSuccess: '已打开分享面板',
|
||||
shareFallback: '分享文案已复制',
|
||||
shareFailed: '分享失败',
|
||||
shareChannelsTitle: '快速分发',
|
||||
shareChannelsDescription: '用更短路径把这篇内容发到社交渠道,方便二次传播和 AI 引用回链。',
|
||||
shareChannelsTitle: '分享方式',
|
||||
shareChannelsDescription: '可以直接复制摘要、固定链接,或通过常用渠道继续转发。',
|
||||
shareToX: '分享到 X',
|
||||
shareToTelegram: '分享到 Telegram',
|
||||
shareToWeChat: '微信扫码',
|
||||
qrModalTitle: '微信扫码分享',
|
||||
qrModalDescription: '使用本地生成的二维码,在微信里扫一扫,就能直接打开这篇文章的规范链接。',
|
||||
qrModalHint: '尽量分享规范地址,方便用户回访,也方便 AI 搜索把信号聚合回同一篇内容。',
|
||||
shareToWeChat: '微信扫一扫',
|
||||
qrModalTitle: '微信扫一扫',
|
||||
qrModalDescription: '用微信扫一扫,就能在手机上继续阅读这篇文章。',
|
||||
qrModalHint: '发给别人时,优先复制固定链接,对方打开会更方便。',
|
||||
downloadQr: '下载二维码',
|
||||
downloadQrStarted: '二维码开始下载',
|
||||
qrOpened: '微信二维码已打开',
|
||||
floatingToolsTitle: '摘要工具',
|
||||
floatingToolsTitle: '快捷操作',
|
||||
copyPermalinkSuccess: '固定链接已复制',
|
||||
copyPermalinkFailed: '固定链接复制失败',
|
||||
toastSuccessTitle: '操作完成',
|
||||
toastErrorTitle: '操作失败',
|
||||
toastInfoTitle: '分享渠道已就绪',
|
||||
toastInfoTitle: '已准备好',
|
||||
};
|
||||
|
||||
const markdownProcessor = await createMarkdownProcessor();
|
||||
@@ -236,7 +236,7 @@ if (wechatShareQrEnabled) {
|
||||
wechatShareQrSvg = await QRCode.toString(canonicalUrl, {
|
||||
type: 'svg',
|
||||
margin: 1,
|
||||
width: 220,
|
||||
width: 240,
|
||||
color: {
|
||||
dark: '#111827',
|
||||
light: '#ffffff',
|
||||
@@ -244,7 +244,7 @@ if (wechatShareQrEnabled) {
|
||||
});
|
||||
wechatShareQrPngDataUrl = await QRCode.toDataURL(canonicalUrl, {
|
||||
margin: 1,
|
||||
width: 360,
|
||||
width: 420,
|
||||
color: {
|
||||
dark: '#111827',
|
||||
light: '#ffffff',
|
||||
@@ -434,55 +434,7 @@ const breadcrumbJsonLd = {
|
||||
<div class="absolute inset-y-0 left-0 w-1 rounded-full bg-[var(--primary)]/80"></div>
|
||||
<div class="absolute right-0 top-0 h-36 w-36 rounded-full bg-[var(--primary)]/10 blur-3xl"></div>
|
||||
<div class="absolute bottom-0 right-10 h-24 w-24 rounded-full bg-[var(--secondary)]/10 blur-3xl"></div>
|
||||
<div class="pointer-events-none absolute right-4 top-4 z-10 hidden xl:block">
|
||||
<div class="pointer-events-auto rounded-2xl border border-[var(--border-color)]/70 bg-[var(--terminal-bg)]/88 px-2 py-2 shadow-[0_10px_28px_rgba(15,23,42,0.08)] backdrop-blur">
|
||||
<div class="mb-2 px-2 text-[10px] font-semibold uppercase tracking-[0.22em] text-[var(--text-tertiary)]">
|
||||
{articleCopy.floatingToolsTitle}
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="flex h-10 w-10 items-center justify-center rounded-xl border border-[var(--border-color)]/70 bg-[var(--bg)]/75 text-[var(--text-secondary)] transition hover:border-[var(--primary)] hover:text-[var(--primary)]"
|
||||
data-article-floating-action="digest-copy"
|
||||
title={articleCopy.copySummary}
|
||||
aria-label={articleCopy.copySummary}
|
||||
>
|
||||
<i class="fas fa-copy text-sm"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="flex h-10 w-10 items-center justify-center rounded-xl border border-[var(--border-color)]/70 bg-[var(--bg)]/75 text-[var(--text-secondary)] transition hover:border-[var(--primary)] hover:text-[var(--primary)]"
|
||||
data-article-floating-action="digest-share"
|
||||
title={articleCopy.shareSummary}
|
||||
aria-label={articleCopy.shareSummary}
|
||||
>
|
||||
<i class="fas fa-share-nodes text-sm"></i>
|
||||
</button>
|
||||
{wechatShareQrEnabled && wechatShareQrSvg && (
|
||||
<button
|
||||
type="button"
|
||||
class="flex h-10 w-10 items-center justify-center rounded-xl border border-[var(--border-color)]/70 bg-[var(--bg)]/75 text-[var(--text-secondary)] transition hover:border-[var(--primary)] hover:text-[var(--primary)]"
|
||||
data-article-floating-action="wechat-qr"
|
||||
title={articleCopy.shareToWeChat}
|
||||
aria-label={articleCopy.shareToWeChat}
|
||||
>
|
||||
<i class="fab fa-weixin text-sm"></i>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
class="flex h-10 w-10 items-center justify-center rounded-xl border border-[var(--border-color)]/70 bg-[var(--bg)]/75 text-[var(--text-secondary)] transition hover:border-[var(--primary)] hover:text-[var(--primary)]"
|
||||
data-article-floating-action="permalink-copy"
|
||||
title={t('common.copyPermalink')}
|
||||
aria-label={t('common.copyPermalink')}
|
||||
>
|
||||
<i class="fas fa-link text-sm"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative grid gap-5 lg:grid-cols-[minmax(0,1.4fr)_minmax(19rem,0.95fr)]">
|
||||
<div class="relative grid gap-5 xl:grid-cols-[minmax(0,1.4fr)_minmax(19rem,0.95fr)]">
|
||||
<div class="space-y-5">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="inline-flex items-center gap-2 rounded-full border border-[var(--primary)]/20 bg-[var(--primary)]/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.24em] text-[var(--primary)]">
|
||||
@@ -490,7 +442,7 @@ const breadcrumbJsonLd = {
|
||||
{articleCopy.digestBadge}
|
||||
</span>
|
||||
<span class="terminal-kicker">
|
||||
<i class="fas fa-robot"></i>
|
||||
<i class="fas fa-book-open"></i>
|
||||
{articleCopy.digestKicker}
|
||||
</span>
|
||||
</div>
|
||||
@@ -591,25 +543,6 @@ const breadcrumbJsonLd = {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{articleHighlights.length > 0 && (
|
||||
<div class="space-y-3">
|
||||
<h3 class="text-sm font-semibold uppercase tracking-[0.22em] text-[var(--text-tertiary)]">
|
||||
{articleCopy.highlightsTitle}
|
||||
</h3>
|
||||
<div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
|
||||
{articleHighlights.map((item, index) => (
|
||||
<div class="rounded-2xl border border-[var(--border-color)]/80 bg-[var(--terminal-bg)]/80 p-4 shadow-[0_10px_30px_rgba(15,23,42,0.04)]">
|
||||
<div class="flex items-start gap-3">
|
||||
<span class="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-xl bg-[var(--primary)]/12 text-sm font-semibold text-[var(--primary)]">
|
||||
{String(index + 1).padStart(2, '0')}
|
||||
</span>
|
||||
<p class="text-sm leading-7 text-[var(--text-secondary)]">{item}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
@@ -867,7 +800,60 @@ const breadcrumbJsonLd = {
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<TableOfContents />
|
||||
<TableOfContents>
|
||||
<div slot="before-nav" class="terminal-panel-muted space-y-3">
|
||||
<span class="terminal-kicker">
|
||||
<i class="fas fa-bolt"></i>
|
||||
{articleCopy.floatingToolsTitle}
|
||||
</span>
|
||||
|
||||
<div
|
||||
class:list={[
|
||||
'grid gap-2',
|
||||
wechatShareQrEnabled && wechatShareQrSvg ? 'grid-cols-4' : 'grid-cols-3',
|
||||
]}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="flex h-11 w-full items-center justify-center rounded-2xl border border-[var(--border-color)]/70 bg-[var(--bg)]/75 text-[var(--text-secondary)] transition hover:border-[var(--primary)] hover:text-[var(--primary)]"
|
||||
data-article-floating-action="digest-copy"
|
||||
title={articleCopy.copySummary}
|
||||
aria-label={articleCopy.copySummary}
|
||||
>
|
||||
<i class="fas fa-copy text-sm"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="flex h-11 w-full items-center justify-center rounded-2xl border border-[var(--border-color)]/70 bg-[var(--bg)]/75 text-[var(--text-secondary)] transition hover:border-[var(--primary)] hover:text-[var(--primary)]"
|
||||
data-article-floating-action="digest-share"
|
||||
title={articleCopy.shareSummary}
|
||||
aria-label={articleCopy.shareSummary}
|
||||
>
|
||||
<i class="fas fa-share-nodes text-sm"></i>
|
||||
</button>
|
||||
{wechatShareQrEnabled && wechatShareQrSvg && (
|
||||
<button
|
||||
type="button"
|
||||
class="flex h-11 w-full items-center justify-center rounded-2xl border border-[var(--border-color)]/70 bg-[var(--bg)]/75 text-[var(--text-secondary)] transition hover:border-[var(--primary)] hover:text-[var(--primary)]"
|
||||
data-article-floating-action="wechat-qr"
|
||||
title={articleCopy.shareToWeChat}
|
||||
aria-label={articleCopy.shareToWeChat}
|
||||
>
|
||||
<i class="fab fa-weixin text-sm"></i>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
class="flex h-11 w-full items-center justify-center rounded-2xl border border-[var(--border-color)]/70 bg-[var(--bg)]/75 text-[var(--text-secondary)] transition hover:border-[var(--primary)] hover:text-[var(--primary)]"
|
||||
data-article-floating-action="permalink-copy"
|
||||
title={t('common.copyPermalink')}
|
||||
aria-label={t('common.copyPermalink')}
|
||||
>
|
||||
<i class="fas fa-link text-sm"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</TableOfContents>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -878,7 +864,7 @@ const breadcrumbJsonLd = {
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="flex min-h-screen items-center justify-center p-4">
|
||||
<div class="w-full max-w-3xl rounded-[30px] border border-[var(--border-color)] bg-[linear-gradient(180deg,rgba(var(--bg-rgb),0.98),rgba(var(--bg-rgb),0.92))] p-5 shadow-[0_24px_80px_rgba(15,23,42,0.28)] sm:p-6">
|
||||
<div class="w-full max-w-4xl rounded-[32px] border border-[var(--border-color)]/70 bg-[var(--terminal-bg)] p-5 shadow-[0_30px_90px_rgba(15,23,42,0.36)] sm:p-7">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="space-y-2">
|
||||
<span class="terminal-kicker">
|
||||
@@ -903,24 +889,25 @@ const breadcrumbJsonLd = {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 grid gap-6 lg:grid-cols-[240px_minmax(0,1fr)]">
|
||||
<div class="mx-auto w-full max-w-[240px] rounded-[28px] border border-[var(--border-color)]/80 bg-white p-4 shadow-[0_18px_45px_rgba(15,23,42,0.12)]">
|
||||
<div class="mt-6 grid gap-6 lg:grid-cols-[260px_minmax(0,1fr)]">
|
||||
<div class="mx-auto w-full max-w-[260px] rounded-[28px] border border-[var(--border-color)]/80 bg-white p-5 shadow-[0_18px_45px_rgba(15,23,42,0.12)]">
|
||||
<div class="overflow-hidden rounded-2xl" set:html={wechatShareQrSvg}></div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="rounded-2xl border border-[var(--border-color)]/70 bg-[var(--terminal-bg)]/82 p-4">
|
||||
<div class="rounded-2xl border border-[var(--border-color)]/70 bg-[var(--header-bg)] p-5">
|
||||
<div class="text-[11px] uppercase tracking-[0.2em] text-[var(--text-tertiary)]">
|
||||
{articleCopy.canonical}
|
||||
</div>
|
||||
<p class="mt-2 break-all font-mono text-xs leading-6 text-[var(--title-color)]">{canonicalUrl}</p>
|
||||
<p class="mt-3 break-all font-mono text-sm leading-7 text-[var(--title-color)]">{canonicalUrl}</p>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-[var(--border-color)]/70 bg-[var(--terminal-bg)]/72 p-4">
|
||||
<div class="rounded-2xl border border-[var(--border-color)]/70 bg-[var(--header-bg)] p-5">
|
||||
<div class="text-[11px] uppercase tracking-[0.2em] text-[var(--text-tertiary)]">
|
||||
{articleCopy.digestTitle}
|
||||
</div>
|
||||
<p class="mt-2 text-sm font-semibold leading-7 text-[var(--title-color)]">{post.title}</p>
|
||||
<p class="mt-3 text-base font-semibold leading-7 text-[var(--title-color)]">{post.title}</p>
|
||||
<p class="mt-3 text-sm leading-7 text-[var(--text-secondary)]">{articleSynopsis}</p>
|
||||
<p class="mt-3 text-sm leading-7 text-[var(--text-secondary)]">{articleCopy.qrModalHint}</p>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user