diff --git a/src/components/GlassCard.tsx b/src/components/GlassCard.tsx new file mode 100644 index 0000000..5793e55 --- /dev/null +++ b/src/components/GlassCard.tsx @@ -0,0 +1,38 @@ +import { useRef, useState, useCallback, ReactNode } from 'react'; +import { cn } from '@/lib/utils'; + +interface GlassCardProps { + children: ReactNode; + className?: string; + interactive?: boolean; +} + +export const GlassCard = ({ children, className, interactive = true }: GlassCardProps) => { + const cardRef = useRef(null); + const [glowPos, setGlowPos] = useState({ x: 0, y: 0 }); + const [isHovered, setIsHovered] = useState(false); + + const handleMouseMove = useCallback((e: React.MouseEvent) => { + if (!cardRef.current || !interactive) return; + const rect = cardRef.current.getBoundingClientRect(); + setGlowPos({ x: e.clientX - rect.left, y: e.clientY - rect.top }); + }, [interactive]); + + return ( +
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + {interactive && isHovered && ( +
+ )} +
{children}
+
+ ); +}; diff --git a/src/hooks/useMousePosition.ts b/src/hooks/useMousePosition.ts new file mode 100644 index 0000000..86aaf0b --- /dev/null +++ b/src/hooks/useMousePosition.ts @@ -0,0 +1,22 @@ +import { useState, useEffect, useCallback, RefObject } from 'react'; + +export function useMousePosition(containerRef?: RefObject) { + const [position, setPosition] = useState({ x: 0, y: 0 }); + + const handleMouseMove = useCallback((e: MouseEvent) => { + if (containerRef?.current) { + const rect = containerRef.current.getBoundingClientRect(); + setPosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }); + } else { + setPosition({ x: e.clientX, y: e.clientY }); + } + }, [containerRef]); + + useEffect(() => { + const target = containerRef?.current || window; + target.addEventListener('mousemove', handleMouseMove as EventListener); + return () => target.removeEventListener('mousemove', handleMouseMove as EventListener); + }, [handleMouseMove, containerRef]); + + return position; +} diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index 7130b54..4632c1a 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -1,11 +1,111 @@ -// Update this page (the content is just a fallback if you fail to update the page) +import { useRef } from 'react'; +import { GlassCard } from '@/components/GlassCard'; +import { useMousePosition } from '@/hooks/useMousePosition'; +import avatar from '@/assets/avatar.png'; +import { Home, MessageCircle, Rss, GitBranch, Search, ExternalLink } from 'lucide-react'; + +const links = [ + { icon: Home, label: 'Home', href: 'https://damaj.tech/', desc: 'Main page' }, + { icon: MessageCircle, label: 'Who Am I?', href: 'https://damaj.tech/site/whoami', desc: 'About me' }, + { icon: Rss, label: 'Blog', href: 'https://damaj.tech/site/blog', desc: 'My writings' }, +]; + +const services = [ + { icon: GitBranch, label: 'Git Server', href: 'https://git.damaj.tech/', desc: 'Self-hosted Forgejo' }, + { icon: Search, label: 'SearXNG', href: 'https://searxng.damaj.tech/', desc: 'Private search engine' }, +]; const Index = () => { + const containerRef = useRef(null); + const mouse = useMousePosition(containerRef); + return ( -
-
-

Welcome to Your Blank App

-

Start building your amazing project here!

+
+ {/* Global mouse glow */} +
+ +
+ {/* Avatar + Intro */} + +
+ Mohamad's avatar +
+

+ 👋 Hey There! +

+

+ My name is Mohamad +

+

+ Welcome to my space. Feel free to look around. +

+
+
+
+ + {/* Website Content */} +
+

+ Website Content +

+
+ {links.map((link) => ( + + +
+ + +
+

{link.label}

+

{link.desc}

+
+
+ ))} +
+
+ + {/* Self-hosted Services */} +
+

+ Self-hosted Services +

+
+ {services.map((svc) => ( + + +
+ + +
+

{svc.label}

+

{svc.desc}

+
+
+ ))} +
+
+ + {/* Footer */} +

+ damaj.tech +

);