This commit is contained in:
Mackie 2026-05-29 10:39:52 +08:00
parent 83395216bf
commit 6a754448bf
3 changed files with 98 additions and 102 deletions

View file

@ -2,6 +2,7 @@
import React, { useEffect, useRef, useState } from 'react'
import { cn } from '@/utilities/ui'
import { InfiniteSlider } from '@/components/ui/infinite-slider'
import { ArrowRightIcon, PhoneCallIcon } from 'lucide-react'
import { createPortal } from 'react-dom'
import { MenuToggleIcon } from '@/components/ui/menu-toggle-icon'
@ -236,7 +237,6 @@ function HeroSection({ isDark, richText, links }: HeroProps & { isDark: boolean
// ─── Logo cloud ───────────────────────────────────────────────────────────────
const LOGOS = [
{ src: 'https://portfolio-media.s3web.bymackie.com/Skill_logo/aftereffects-plain.svg', alt: 'aftereffects' },
{ src: 'https://portfolio-media.s3web.bymackie.com/Skill_logo/almalinux-original.svg', alt: 'almalinux' },
{ src: 'https://portfolio-media.s3web.bymackie.com/Skill_logo/amazonwebservices-original-wordmark.svg', alt: 'amazonwebservices' },
@ -291,51 +291,36 @@ const LOGOS = [
{ src: 'https://portfolio-media.s3web.bymackie.com/Skill_logo/wordpress-original.svg', alt: 'wordpress' },
{ src: 'https://portfolio-media.s3web.bymackie.com/Skill_logo/xcode-original.svg', alt: 'xcode' },
{ src: 'https://portfolio-media.s3web.bymackie.com/Skill_logo/zend-original-wordmark.svg', alt: 'zend' },
]
function LogosSection({ isDark }: { isDark: boolean }) {
const text = isDark ? 'text-white/40' : 'text-black/40'
const border = isDark ? 'border-white/10' : 'border-black/10'
const logoFilter = isDark ? 'brightness-0 invert opacity-30' : 'brightness-0 opacity-25'
return (
<section className={cn('relative border-t py-8 overflow-hidden', border)}>
<section className={cn('relative border-t py-8', border)}>
<p className={cn('mb-6 text-center text-xs tracking-widest uppercase px-6', text)}>
Skills
</p>
{/* Marquee track */}
<div style={{ overflow: 'hidden', width: '100%' }}>
<div
style={{
display: 'flex',
width: 'max-content',
animation: 'marquee 35s linear infinite',
}}
>
{/* Only 2 copies — animation goes to -50% which = exactly 1 copy width */}
{[...LOGOS, ...LOGOS].map((l, i) => (
<div className="[mask-image:linear-gradient(to_right,transparent,black,transparent)]">
<InfiniteSlider gap={42} duration={35}>
{LOGOS.map((l) => (
<img
key={i}
key={l.alt}
src={l.src}
alt={l.alt}
className={cn('h-5 w-auto flex-shrink-0 transition-all', logoFilter)}
style={{ marginRight: 64 }}
className={cn(
'h-5 w-auto flex-shrink-0 pointer-events-none select-none',
isDark ? 'brightness-0 invert opacity-30' : 'brightness-0 opacity-25'
)}
/>
))}
</InfiniteSlider>
</div>
</div>
<style>{`
@keyframes marquee {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
`}</style>
</section>
)
}
// ─── Main export ──────────────────────────────────────────────────────────────
interface HeroPageProps {

View file

@ -0,0 +1,17 @@
import clsx from 'clsx'
import React from 'react'
interface Props {
className?: string
}
export const Icon = ({ className }: Props) => (
<img
alt="Icon"
width={34}
height={34}
decoding="async"
className={clsx('w-[34px] h-[34px]', className)}
src="/favicon.svg"
/>
)

View file

@ -3,6 +3,8 @@ import sharp from 'sharp'
import path from 'path'
import { buildConfig, PayloadRequest } from 'payload'
import { fileURLToPath } from 'url'
import { Logo } from '@/components/Logo'
import { Icon } from '@/components/Icon'
import { Categories } from './collections/Categories'
import { Media } from './collections/Media'
@ -21,14 +23,11 @@ const dirname = path.dirname(filename)
export default buildConfig({
admin: {
components: {
// The `BeforeLogin` component renders a message that you see while logging into your admin panel.
// Feel free to delete this at any time. Simply remove the line below.
beforeLogin: ['@/components/BeforeLogin'],
// The `BeforeDashboard` component renders the 'welcome' block that you see after logging into your admin panel.
// Feel free to delete this at any time. Simply remove the line below.
beforeDashboard: ['@/components/BeforeDashboard'],
graphics: {
Logo: '@/components/Logo#Logo', // ✅ string-based import
Logo,
Icon,
},
},
importMap: {
@ -64,7 +63,6 @@ export default buildConfig({
},
},
// This config helps us configure global or default features that the other editors can inherit
editor: defaultLexical,
db: mongooseAdapter({
url: process.env.DATABASE_URI || '',
@ -81,15 +79,11 @@ export default buildConfig({
jobs: {
access: {
run: ({ req }: { req: PayloadRequest }): boolean => {
// Allow logged in users to execute this endpoint (default)
if (req.user) return true
const secret = process.env.CRON_SECRET
if (!secret) return false
// If there is no logged in user, then check
// for the Vercel Cron secret to be present as an
// Authorization header:
const authHeader = req.headers.get('authorization')
return authHeader === `Bearer ${secret}`
},