Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 101 additions & 19 deletions app/components/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
ArrowRight,
ChevronDown,
Download,
ExternalLink,
Link2,
Loader2,
Monitor,
Sparkles,
Expand All @@ -21,7 +23,7 @@ import { useTranslation } from "react-i18next";
import Image from "next/image";

import BackgroundLayer from "./hero/BackgroundLayer";
import { GITHUB_URLS } from "../constants";
import { FRIEND_LINKS, GITHUB_URLS } from "../constants";

// 定义平台和架构类型
type Platform = "win" | "macos" | "linux" | "mobile" | "unknown";
Expand Down Expand Up @@ -166,6 +168,7 @@ export default function Hero() {
const [currentPlatform, setCurrentPlatform] = useState<Platform>("unknown");
const [currentArch, setCurrentArch] = useState<Arch>("unknown");
const [loading, setLoading] = useState(true);
const [showFriendLinks, setShowFriendLinks] = useState(false);

// 获取最新 release 信息
const fetchReleaseInfo = useCallback(async () => {
Expand Down Expand Up @@ -426,25 +429,104 @@ export default function Hero() {
)}
</div>

{/* 第二行:Mirror酱 */}
<div className="relative w-full shrink-0 border-2 border-dashed border-[#008fa6]/40 p-0.5 md:w-auto md:max-w-[320px] dark:border-[#00F0FF]/40">
<Button
variant="outline"
className="group relative h-16 w-full overflow-hidden border-none bg-transparent px-5 text-left text-sm leading-tight font-semibold tracking-normal text-[#008fa6] normal-case hover:bg-[#008fa6]/10 dark:text-[#00F0FF] dark:hover:bg-[#00F0FF]/10"
style={{
clipPath:
"polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px)",
}}
onClick={() => window.open(mirrorDownloadUrl, "_blank")}
>
<span className="flex w-full flex-col items-start gap-1">
<span>{t("hero.mirrorDownloadLine1")}</span>
<span className="flex items-center gap-2">
{t("hero.mirrorDownloadLine2")}
<ArrowRight size={16} strokeWidth={2.5} />
{/* 第二行:Mirror酱 + 友链悬浮入口 */}
<div className="relative flex w-full flex-col gap-3 md:w-auto md:flex-row md:items-start">
<div className="relative w-full shrink-0 border-2 border-dashed border-[#008fa6]/40 p-0.5 md:w-auto md:max-w-[320px] dark:border-[#00F0FF]/40">
<Button
variant="outline"
className="group relative h-16 w-full overflow-hidden border-none bg-transparent px-5 text-left text-sm leading-tight font-semibold tracking-normal text-[#008fa6] normal-case hover:bg-[#008fa6]/10 dark:text-[#00F0FF] dark:hover:bg-[#00F0FF]/10"
style={{
clipPath:
"polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px)",
}}
onClick={() => window.open(mirrorDownloadUrl, "_blank")}
>
<span className="flex w-full flex-col items-start gap-1">
<span>{t("hero.mirrorDownloadLine1")}</span>
<span className="flex items-center gap-2">
{t("hero.mirrorDownloadLine2")}
<ArrowRight size={16} strokeWidth={2.5} />
</span>
</span>
</span>
</Button>
</Button>
</div>

<div
className="group/friend relative w-full md:w-auto"
onMouseEnter={() => setShowFriendLinks(true)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: 通过 hover + click 共同控制 showFriendLinks 会导致桌面端状态不一致,并在纯触摸设备上几乎无法使用。

当前可见性既受 hover(onMouseEnter/onMouseLeave)的控制,又受点击切换 showFriendLinks 的控制。在桌面端,这会导致在悬停时点击,面板可能立刻关闭;而在纯触摸设备上不会触发 hover,因此打开/关闭行为会变得不一致且更难使用。建议在每个断点下采用单一且一致的交互模式(例如:指针设备使用 hover,触摸设备仅使用点击),或者以 hover 作为基础,再为触摸设备提供点击兜底逻辑,并结合外部点击/失焦关闭的方式。

建议的实现方式:

                    <div
                      className="group/friend relative w-full md:w-auto"
                    >
  1. 确保在这个容器内部,showFriendLinks 只由点击(或另一种单一的交互模式)控制,并移除文件中其他位置基于 hover 的状态更新。
  2. 如果你希望桌面端使用 hover、移动端使用点击,可以改为:
    • 对有指针的桌面设备,使用纯 CSS 的 group-hover 来控制可见性;
    • 保留 showFriendLinks 作为触摸端的“仅点击”兜底逻辑,并通过指针能力检测(例如在 useEffect 或事件处理函数中使用 window.matchMedia('(hover: none)'))来控制是否启用。
  3. 如果仍然希望使用基于 hover 的显示面板,建议增加外部点击/失焦处理(例如通过 useEffectdocument.addEventListener('mousedown', ...)),在点击外部时关闭面板,并以 showFriendLinks 作为单一可信的状态来源。
Original comment in English

suggestion: Hover + click interaction for showFriendLinks can lead to inconsistent state on desktop and unusable behavior on pure touch devices.

Visibility is being driven both by hover (onMouseEnter/onMouseLeave) and by click toggling showFriendLinks. On desktop this can cause the panel to immediately close when clicking while hovered, and on touch-only devices hover never fires so the open/close behavior is inconsistent and harder to use. Consider using a single, consistent pattern per breakpoint (e.g., hover for pointer devices, click-only for touch) or deriving visibility from hover with a click fallback for touch, possibly combined with outside-click/blur to close.

Suggested implementation:

                    <div
                      className="group/friend relative w-full md:w-auto"
                    >
  1. Ensure that showFriendLinks is only controlled via click (or another single interaction pattern) inside this container, and remove any other hover-driven state updates elsewhere in the file.
  2. If you want desktop hover and mobile click behavior, you can instead:
    • Use CSS-only group-hover to control visibility for pointer/desktop.
    • Keep showFriendLinks as a click-only fallback for touch, and gate usage with a pointer-capability check (e.g., window.matchMedia('(hover: none)')) in a useEffect or handler.
  3. If a hover-based showing panel is still desired, consider adding an outside-click/blur handler (e.g., via useEffect and document.addEventListener('mousedown', ...)) to close the panel when clicking away, using showFriendLinks as the single source of truth.

onMouseLeave={() => setShowFriendLinks(false)}
>
<div className="relative w-full shrink-0 border-2 border-dashed border-[#008fa6]/40 p-0.5 md:w-auto dark:border-[#00F0FF]/40">
<Button
variant="outline"
className="group relative h-16 w-full overflow-hidden border-none bg-transparent px-4 text-[#008fa6] hover:bg-[#008fa6]/10 md:w-16 md:px-0 dark:text-[#00F0FF] dark:hover:bg-[#00F0FF]/10"
style={{
clipPath:
"polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px)",
}}
onClick={() => setShowFriendLinks((value) => !value)}
aria-expanded={showFriendLinks}
aria-label={t("hero.friendLinksTitle")}
>
<span className="flex w-full items-center justify-center gap-2 md:flex-col md:gap-1">
<Link2 size={17} />
<span className="font-mono text-[10px] tracking-[0.18em] uppercase md:tracking-[0.12em]">
{t("hero.friendLinks")}
</span>
</span>
</Button>
</div>

<AnimatePresence>
{showFriendLinks && (
<motion.div
initial={{ opacity: 0, scale: 0.98 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.98 }}
transition={{ duration: 0.18, ease: "easeOut" }}
className="absolute bottom-[calc(100%+0.6rem)] left-0 z-30 w-full origin-bottom border border-[#008fa6]/30 bg-[#F4F4F4]/95 p-1 shadow-[0_10px_30px_rgba(0,143,166,0.18)] backdrop-blur md:bottom-0 md:left-[calc(100%+0.6rem)] md:w-72 md:origin-bottom-left dark:border-[#00F0FF]/30 dark:bg-[#09090B]/95 dark:shadow-[0_10px_30px_rgba(0,240,255,0.18)]"
>
<div className="border border-black/10 bg-black/[0.02] p-3 dark:border-white/10 dark:bg-white/[0.02]">
<div className="mb-2 border-b border-[#008fa6]/15 pb-2 dark:border-[#00F0FF]/15">
<span className="font-mono text-xs tracking-[0.2em] text-[#008fa6] uppercase dark:text-[#00F0FF]">
{t("hero.friendLinksTitle")}
</span>
</div>
<ul className="space-y-2">
{FRIEND_LINKS.map((friend) => (
<li key={friend.id}>
<a
href={friend.href}
target="_blank"
rel="noopener noreferrer"
onClick={() => setShowFriendLinks(false)}
className="group/link flex items-center justify-between border border-black/10 bg-white/70 px-3 py-2 text-sm text-black/80 transition-all duration-200 hover:border-[#008fa6]/45 hover:bg-[#008fa6]/10 hover:text-[#008fa6] dark:border-white/10 dark:bg-white/[0.03] dark:text-white/80 dark:hover:border-[#00F0FF]/45 dark:hover:bg-[#00F0FF]/10 dark:hover:text-[#00F0FF]"
>
<span className="flex items-center gap-3">
<span className="rounded-sm border border-[#008fa6]/25 bg-white/85 p-1.5 dark:border-[#00F0FF]/25 dark:bg-white/10">
<Image
src={friend.iconSrc}
alt={friend.iconAlt}
width={20}
height={20}
className="h-5 w-5 object-contain"
/>
</span>
<span>{friend.name}</span>
</span>
<ExternalLink
size={14}
className="opacity-45 transition-opacity group-hover/link:opacity-100"
/>
</a>
</li>
))}
</ul>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
</motion.div>
) : (
Expand Down
40 changes: 40 additions & 0 deletions app/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,43 @@ export const QQ_GROUPS = {
DEV_GROUP: "1072587329",
DEV_GROUP_LINK: "https://qm.qq.com/q/EyirQpBiW4",
} as const;

export interface FriendLink {
id: string;
name: string;
href: string;
iconSrc: string;
iconAlt: string;
}

// 友情链接:图标来自 public/friend
export const FRIEND_LINKS: FriendLink[] = [
{
id: "MAA",
name: "MAA",
href: "https://maa.plus/",
iconSrc: "/friend/maa.png",
iconAlt: "MAA",
},
{
id: "MaaFramework",
name: "MaaFramework",
href: "https://maafw.com/",
iconSrc: "/friend/maa.png",
iconAlt: "MAA",
},
{
id: "ef-yituliu",
name: "终末地一图流",
href: "https://www.yituliu.cn/",
iconSrc: "/friend/ef-yituliu.png",
iconAlt: "终末地一图流",
},
{
id: "mirrorchyan",
name: "Mirror酱",
href: "https://mirrorchyan.com/zh/projects?rid=MaaEnd&os=windows&arch=x64&channel=stable",
iconSrc: "/friend/mirrorchyan.png",
iconAlt: "Mirror酱",
},
];
3 changes: 3 additions & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
"goToGitHub": "Go to GitHub",
"mirrorDownloadLine1": "Have a MirrorChyan CDK?",
"mirrorDownloadLine2": "Fast download on MirrorChyan",
"friendLinks": "Links",
"friendLinksTitle": "Friendly Links",
"friendLinksHint": "Hover to open",
"desktopOnlyLine1": "Desktop Only",
"desktopOnlyLine2": "Use PC to Download",
"windows": "Windows",
Expand Down
3 changes: 3 additions & 0 deletions locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
"goToGitHub": "前往 GitHub 下载",
"mirrorDownloadLine1": "已有 Mirror酱 CDK?",
"mirrorDownloadLine2": "前往 Mirror酱 高速下载",
"friendLinks": "友情链接",
"friendLinksTitle": "友情链接",
"friendLinksHint": "悬停展开",
"desktopOnlyLine1": "仅支持桌面端",
"desktopOnlyLine2": "请使用电脑下载",
"windows": "Windows",
Expand Down
Binary file added public/friend/ef-yituliu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/friend/maa.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/friend/mirrorchyan.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading