"use client"; import { useEffect, useState } from "react"; import { SectionLabel } from "./section-label"; import { ZoomableImage } from "./zoomable-image"; import { ProjectIntroDialog } from "./project-intro-dialog"; import { personalProjects, type PersonalProject } from "@/lib/personal-projects"; const MD_COLUMNS_QUERY = "(min-width: 768px)"; /** 是否处于双列断点(与 md:columns-2 对齐) */ const useMdColumns = () => { const [isMd, setIsMd] = useState(false); useEffect(() => { const media = window.matchMedia(MD_COLUMNS_QUERY); const onChange = () => setIsMd(media.matches); onChange(); media.addEventListener("change", onChange); return () => media.removeEventListener("change", onChange); }, []); return isMd; }; /** 双列按 mdOrder 排;没填则回退到原数组位置 */ const sortByMdOrder = (items: PersonalProject[]) => { return items .map((item, index) => ({ item, index })) .sort((a, b) => { const aOrder = a.item.mdOrder ?? a.index; const bOrder = b.item.mdOrder ?? b.index; return aOrder - bOrder || a.index - b.index; }) .map(({ item }) => item); }; const ProjectLink = ({ href, variant, children }: { href: string; variant: "primary" | "outline"; children: React.ReactNode; }) => { return ( {children} ); }; const PersonalCard = ({ project }: { project: PersonalProject }) => { return (

{project.title}

{(project.meta?.company || project.meta?.period) && (

{[project.meta.company, project.meta.period] .filter(Boolean) .join(" · ")}

)}

{project.description}

{(project.live || project.gitea || project.showIntro || project.meta?.status) && (
{project.live && ( Live )} {project.gitea && ( Gitea )} {project.showIntro && ( )} {project.meta?.status && ( {project.meta.status} )}
)}
); }; /** 实习项目下方的个人作品:PC 两列瀑布流 */ export const PersonalProjects = ({ items = personalProjects }: { items?: PersonalProject[]; }) => { const isMdColumns = useMdColumns(); const visibleItems = isMdColumns ? sortByMdOrder(items) : items; return (
Project
{visibleItems.map((project) => ( ))}
); };