'use client'; import { cn } from '@/lib/utils'; import { Canvas, useFrame, useThree } from '@react-three/fiber'; import { AnimatePresence, motion } from 'framer-motion'; import Link from 'next/link'; import React, { useState, useMemo, useRef, useEffect } from 'react'; import * as THREE from 'three'; type Uniforms = { [key: string]: { value: number[] | number[][] | number; type: string; }; }; interface ShaderProps { source: string; uniforms: { [key: string]: { value: number[] | number[][] | number; type: string; }; }; maxFps?: number; } interface SignInPageProps { className?: string; } export const CanvasRevealEffect = ({ animationSpeed = 10, opacities = [0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.8, 0.8, 0.8, 1], colors = [[0, 255, 255]], containerClassName, dotSize, showGradient = true, reverse = false, // This controls the direction }: { animationSpeed?: number; opacities?: number[]; colors?: number[][]; containerClassName?: string; dotSize?: number; showGradient?: boolean; reverse?: boolean; // This prop determines the direction }) => { return (
{' '} {/* Removed bg-white */}
{showGradient && ( // Adjust gradient colors if needed based on background (was bg-white, now likely uses containerClassName bg) // Example assuming a dark background like the SignInPage uses:
)}
); }; interface DotMatrixProps { colors?: number[][]; opacities?: number[]; totalSize?: number; dotSize?: number; shader?: string; center?: ('x' | 'y')[]; } const DotMatrix: React.FC = ({ colors = [[0, 0, 0]], opacities = [0.04, 0.04, 0.04, 0.04, 0.04, 0.08, 0.08, 0.08, 0.08, 0.14], totalSize = 20, dotSize = 2, shader = '', // This shader string will now contain the animation logic center = ['x', 'y'], }) => { // ... uniforms calculation remains the same for colors, opacities, etc. const uniforms = React.useMemo(() => { let colorsArray = [colors[0], colors[0], colors[0], colors[0], colors[0], colors[0]]; if (colors.length === 2) { colorsArray = [colors[0], colors[0], colors[0], colors[1], colors[1], colors[1]]; } else if (colors.length === 3) { colorsArray = [colors[0], colors[0], colors[1], colors[1], colors[2], colors[2]]; } return { u_colors: { value: colorsArray.map((color) => [ (color?.[0] ?? 0) / 255, (color?.[1] ?? 0) / 255, (color?.[2] ?? 0) / 255, ]), type: 'uniform3fv', }, u_opacities: { value: opacities, type: 'uniform1fv', }, u_total_size: { value: totalSize, type: 'uniform1f', }, u_dot_size: { value: dotSize, type: 'uniform1f', }, u_reverse: { value: shader.includes('u_reverse_active') ? 1 : 0, // Convert boolean to number (1 or 0) type: 'uniform1i', // Use 1i for bool in WebGL1/GLSL100, or just bool for GLSL300+ if supported }, }; }, [colors, opacities, totalSize, dotSize, shader]); // Add shader to dependencies return ( ); }; const ShaderMaterial = ({ source, uniforms, }: { source: string; hovered?: boolean; maxFps?: number; uniforms: Uniforms; }) => { const { size } = useThree(); const ref = useRef(null); useFrame(({ clock }) => { if (!ref.current) return; const timestamp = clock.getElapsedTime(); const material: any = ref.current.material; const timeLocation = material.uniforms.u_time; timeLocation.value = timestamp; }); const getUniforms = () => { const preparedUniforms: any = {}; for (const uniformName in uniforms) { const uniform: any = uniforms[uniformName]; switch (uniform.type) { case 'uniform1f': preparedUniforms[uniformName] = { value: uniform.value, type: '1f' }; break; case 'uniform1i': preparedUniforms[uniformName] = { value: uniform.value, type: '1i' }; break; case 'uniform3f': preparedUniforms[uniformName] = { value: new THREE.Vector3().fromArray(uniform.value), type: '3f', }; break; case 'uniform1fv': preparedUniforms[uniformName] = { value: uniform.value, type: '1fv' }; break; case 'uniform3fv': preparedUniforms[uniformName] = { value: uniform.value.map((v: number[]) => new THREE.Vector3().fromArray(v)), type: '3fv', }; break; case 'uniform2f': preparedUniforms[uniformName] = { value: new THREE.Vector2().fromArray(uniform.value), type: '2f', }; break; default: console.error(`Invalid uniform type for '${uniformName}'.`); break; } } preparedUniforms.u_time = { value: 0, type: '1f' }; preparedUniforms.u_resolution = { value: new THREE.Vector2(size.width * 2, size.height * 2), }; // Initialize u_resolution return preparedUniforms; }; // Shader material const material = useMemo(() => { const materialObject = new THREE.ShaderMaterial({ vertexShader: ` precision mediump float; in vec2 coordinates; uniform vec2 u_resolution; out vec2 fragCoord; void main(){ float x = position.x; float y = position.y; gl_Position = vec4(x, y, 0.0, 1.0); fragCoord = (position.xy + vec2(1.0)) * 0.5 * u_resolution; fragCoord.y = u_resolution.y - fragCoord.y; } `, fragmentShader: source, uniforms: getUniforms(), glslVersion: THREE.GLSL3, blending: THREE.CustomBlending, blendSrc: THREE.SrcAlphaFactor, blendDst: THREE.OneFactor, }); return materialObject; }, [size.width, size.height, source]); return ( ); }; const Shader: React.FC = ({ source, uniforms, maxFps = 60 }) => { return ( ); }; const AnimatedNavLink = ({ href, children }: { href: string; children: React.ReactNode }) => { const defaultTextColor = 'text-gray-300'; const hoverTextColor = 'text-white'; const textSizeClass = 'text-sm'; return (
{children} {children}
); }; function MiniNavbar() { const [isOpen, setIsOpen] = useState(false); const [headerShapeClass, setHeaderShapeClass] = useState('rounded-full'); const shapeTimeoutRef = useRef(null); const toggleMenu = () => { setIsOpen(!isOpen); }; useEffect(() => { if (shapeTimeoutRef.current) { clearTimeout(shapeTimeoutRef.current); } if (isOpen) { setHeaderShapeClass('rounded-xl'); } else { shapeTimeoutRef.current = setTimeout(() => { setHeaderShapeClass('rounded-full'); }, 300); } return () => { if (shapeTimeoutRef.current) { clearTimeout(shapeTimeoutRef.current); } }; }, [isOpen]); const logoElement = (
); const navLinksData = [ { label: 'Manifesto', href: '#1' }, { label: 'Careers', href: '#2' }, { label: 'Discover', href: '#3' }, ]; const loginButtonElement = ( ); const signupButtonElement = (
); return (
{logoElement}
{loginButtonElement} {signupButtonElement}
{loginButtonElement} {signupButtonElement}
); } export const SignInPage = ({ className }: SignInPageProps) => { const [email, setEmail] = useState(''); const [step, setStep] = useState<'email' | 'code' | 'success'>('email'); const [code, setCode] = useState(['', '', '', '', '', '']); const codeInputRefs = useRef<(HTMLInputElement | null)[]>([]); const [initialCanvasVisible, setInitialCanvasVisible] = useState(true); const [reverseCanvasVisible, setReverseCanvasVisible] = useState(false); const handleEmailSubmit = (e: React.FormEvent) => { e.preventDefault(); if (email) { setStep('code'); } }; // Focus first input when code screen appears useEffect(() => { if (step === 'code') { setTimeout(() => { codeInputRefs.current[0]?.focus(); }, 500); } }, [step]); const handleCodeChange = (index: number, value: string) => { if (value.length <= 1) { const newCode = [...code]; newCode[index] = value; setCode(newCode); // Focus next input if value is entered if (value && index < 5) { codeInputRefs.current[index + 1]?.focus(); } // Check if code is complete if (index === 5 && value) { const isComplete = newCode.every((digit) => digit.length === 1); if (isComplete) { // First show the new reverse canvas setReverseCanvasVisible(true); // Then hide the original canvas after a small delay setTimeout(() => { setInitialCanvasVisible(false); }, 50); // Transition to success screen after animation setTimeout(() => { setStep('success'); }, 2000); } } } }; const handleKeyDown = (index: number, e: React.KeyboardEvent) => { if (e.key === 'Backspace' && !code[index] && index > 0) { codeInputRefs.current[index - 1]?.focus(); } }; const handleBackClick = () => { setStep('email'); setCode(['', '', '', '', '', '']); // Reset animations if going back setReverseCanvasVisible(false); setInitialCanvasVisible(true); }; return (
{/* Initial canvas (forward animation) */} {initialCanvasVisible && (
)} {/* Reverse canvas (appears when code is complete) */} {reverseCanvasVisible && (
)}
{/* Content Layer */}
{/* Top navigation */} {/* Main content container */}
{/* Left side (form) */}
{step === 'email' ? (

Welcome Developer

Your sign in component

or
setEmail(e.target.value)} className="w-full backdrop-blur-[1px] text-white border-1 border-white/10 rounded-full py-3 px-4 focus:outline-none focus:border focus:border-white/30 text-center" required />

By signing up, you agree to the{' '} MSA ,{' '} Product Terms ,{' '} Policies ,{' '} Privacy Notice , and{' '} Cookie Notice .

) : step === 'code' ? (

We sent you a code

Please enter it

{code.map((digit, i) => (
{ codeInputRefs.current[i] = el; }} type="text" inputMode="numeric" pattern="[0-9]*" maxLength={1} value={digit} onChange={(e) => handleCodeChange(i, e.target.value)} onKeyDown={(e) => handleKeyDown(i, e)} className="w-8 text-center text-xl bg-transparent text-white border-none focus:outline-none focus:ring-0 appearance-none" style={{ caretColor: 'transparent' }} /> {!digit && (
0
)}
{i < 5 && |}
))}
Resend code
Back d !== '') ? 'bg-white text-black border-transparent hover:bg-white/90 cursor-pointer' : 'bg-[#111] text-white/50 border-white/10 cursor-not-allowed' }`} disabled={!code.every((d) => d !== '')} > Continue

By signing up, you agree to the{' '} MSA ,{' '} Product Terms ,{' '} Policies ,{' '} Privacy Notice , and{' '} Cookie Notice .

) : (

You're in!

Welcome

Continue to Dashboard
)}
); };