diff --git a/docs/app/[lang]/docs/layout.tsx b/docs/app/[lang]/docs/layout.tsx index 845bcb9b5f..c3afca3c17 100644 --- a/docs/app/[lang]/docs/layout.tsx +++ b/docs/app/[lang]/docs/layout.tsx @@ -2,6 +2,7 @@ import type { ReactNode } from "react"; import { DocsLayout } from "fumadocs-ui/layouts/docs"; import { RootProvider } from "fumadocs-ui/provider/next"; import { DocsSidebarFooter, DocsSidebarLanguageButton } from "@/components/DocsSidebarFooter"; +import { FloatingSponsorCard } from "@/components/FloatingSponsorCard"; import { StaticSearchDialog } from "@/components/StaticSearchDialog"; import { i18nUI, resolveLang } from "@/lib/i18n"; import { source } from "@/lib/source"; @@ -37,6 +38,7 @@ export default async function Layout({ params, children }: { params: Promise<{ l }} > {children} + ); diff --git a/docs/app/global.css b/docs/app/global.css index a024d85391..59e251e198 100644 --- a/docs/app/global.css +++ b/docs/app/global.css @@ -22,6 +22,23 @@ opacity 0.22s ease; } +/* 文档页右下角赞助悬浮卡片入场动画 */ + +@keyframes sponsor-card-in { + from { + opacity: 0; + transform: translateY(8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.sponsor-card-in { + animation: sponsor-card-in 0.3s ease-out both; +} + @theme { --color-landing-bg: #161616; --color-landing-ink: #ededf0; diff --git a/docs/components/FloatingSponsorCard.tsx b/docs/components/FloatingSponsorCard.tsx new file mode 100644 index 0000000000..d548f660df --- /dev/null +++ b/docs/components/FloatingSponsorCard.tsx @@ -0,0 +1,77 @@ +"use client"; + +import { X } from "lucide-react"; +import { useEffect, useState } from "react"; + +const DISMISS_STORAGE_KEY = "dbx.docs.sponsorCard.dismissedAt"; +// 关闭 7 天后重新出现 +const RESURFACE_MS = 7 * 24 * 60 * 60 * 1000; + +const RAINYUN_URL = "https://www.rainyun.com/MTE5Mjc4Ng==_"; + +const COPY = { + cn: { + badge: "赞助商", + tagline: "云服务器 · 游戏云 · 物理服务器", + closeLabel: "关闭赞助卡片", + }, + en: { + badge: "Sponsor", + tagline: "Cloud servers, game hosting & bare metal", + closeLabel: "Dismiss sponsor card", + }, +} as const; + +export function FloatingSponsorCard({ lang }: { lang: string }) { + const [visible, setVisible] = useState(false); + + useEffect(() => { + try { + const dismissedAt = Number(window.localStorage.getItem(DISMISS_STORAGE_KEY)); + if (!Number.isFinite(dismissedAt) || Date.now() - dismissedAt > RESURFACE_MS) { + setVisible(true); + } + } catch { + setVisible(true); + } + }, []); + + if (!visible) return null; + + const dismiss = () => { + setVisible(false); + try { + window.localStorage.setItem(DISMISS_STORAGE_KEY, String(Date.now())); + } catch { + // localStorage 不可用时按本次会话关闭处理 + } + }; + + const t = lang === "cn" ? COPY.cn : COPY.en; + + return ( + + ); +}