import React, { Suspense, useState, useEffect, useRef } from 'react';
import * as THREE from 'three';
import { Canvas } from '@react-three/fiber';
import { GenerativeSphere } from './components/GenerativeSphere';
import { AmbientBackground } from './components/AmbientBackground';
import { WorkSection } from './components/sections/WorkSection';
import { ResearchSection } from './components/sections/ResearchSection';
import { ServicesSection } from './components/sections/ServicesSection';
import { ContactSection } from './components/sections/ContactSection';
import { HeroOverlay } from './components/sections/HeroOverlay';
// Import Mobile Sections
import { MobileWorkSection } from './components/sections/mobile/MobileWorkSection';
import { MobileResearchSection } from './components/sections/mobile/MobileResearchSection';
import { MobileServicesSection } from './components/sections/mobile/MobileServicesSection';
import { MobileContactSection } from './components/sections/mobile/MobileContactSection';
// --- Helper Hooks ---
function useIsMobile() {
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
const check = () => setIsMobile(window.innerWidth < 768);
check();
window.addEventListener('resize', check);
return () => window.removeEventListener('resize', check);
}, []);
return isMobile;
}
// --- UI Components ---
// OPTIMIZED LOADER
const LoaderOverlay = ({ visible, progress }: { visible: boolean; progress: number }) => {
return (
{/* Title: BOLD WHITE - NO BLINKING */}
Team Triangle
{/* Timer: Bottom Left */}
2025 Team Triangle System
{progress}%
{/* Loading Bar Line */}
);
};
const Navbar = ({
visible,
isDimmed,
shapeMode,
toggleShape,
isTtsPlaying,
onSectionClick,
activeSection
}: {
visible: boolean;
isDimmed: boolean;
shapeMode: 'sphere' | 'triangle' | 'explode';
toggleShape: () => void;
isTtsPlaying: boolean;
onSectionClick: (section: string) => void;
activeSection: string | null;
}) => {
const isTriangle = shapeMode === 'triangle';
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
useEffect(() => {
if (activeSection) setMobileMenuOpen(false);
}, [activeSection]);
const hideClass = (isDimmed && !activeSection) ? 'opacity-0 -translate-y-4 pointer-events-none' : 'opacity-100 translate-y-0';
const bgClass = isDimmed ? 'opacity-0' : 'opacity-100';
const menuItems = ['work', 'research', 'services', 'contact'];
return (
<>
{menuItems.map((item, i) => (
))}
System Status: Online
>
);
};
const TypingConsole = ({ visible }: { visible: boolean }) => {
const [text, setText] = useState('');
const [isDeleting, setIsDeleting] = useState(false);
const [loopNum, setLoopNum] = useState(0);
const [typingSpeed, setTypingSpeed] = useState(150);
const phrases = ["training model...", "deploying gen-ai pipeline...", "optimizing inference...", "allocating neural buffers..."];
useEffect(() => {
let timer: any;
const handleTyping = () => {
const i = loopNum % phrases.length;
const fullText = phrases[i];
setText(isDeleting ? fullText.substring(0, text.length - 1) : fullText.substring(0, text.length + 1));
let typeSpeed = 50 + Math.random() * 60;
if (isDeleting) typeSpeed /= 2.5;
if (!isDeleting && text === fullText) { typeSpeed = 2500; setIsDeleting(true); }
else if (isDeleting && text === '') { setIsDeleting(false); setLoopNum(loopNum + 1); typeSpeed = 500; }
setTypingSpeed(typeSpeed);
};
if (visible) timer = setTimeout(handleTyping, typingSpeed);
return () => clearTimeout(timer);
}, [text, isDeleting, loopNum, phrases, typingSpeed, visible]);
return (
);
};
// --- Reusable Scroll Sections ---
const ContentTeaser = ({ title, sub, desc, btnText, onClick }: any) => {
return (
)
}
// --- Main App ---
const App: React.FC = () => {
const [showLoader, setShowLoader] = useState(true);
const [loadProgress, setLoadProgress] = useState(0);
const [showInterface, setShowInterface] = useState(false);
const [isNavDimmed, setIsNavDimmed] = useState(false);
const [isAudioMuted, setIsAudioMuted] = useState(true);
const audioRef = useRef(null);
const audioInitializedRef = useRef(false);
const [isTtsPlaying, setIsTtsPlaying] = useState(false);
const ttsAudioRef = useRef(null);
const [activeSection, setActiveSection] = useState(null);
const [isTriangle, setIsTriangle] = useState(false);
const scrollRef = useRef(0);
const smoothScrollRef = useRef(0);
const shiftRef = useRef(0);
const lastScrollTop = useRef(0);
const isMobile = useIsMobile();
const shapeMode = activeSection ? 'explode' : ((isTriangle || isTtsPlaying) ? 'triangle' : 'sphere');
useEffect(() => {
try {
const saved = window.localStorage.getItem('tt_audio_muted');
if (saved === '0') setIsAudioMuted(false);
if (saved === '1') setIsAudioMuted(true);
} catch {
// ignore
}
}, []);
useEffect(() => {
try {
window.localStorage.setItem('tt_audio_muted', isAudioMuted ? '1' : '0');
} catch {
// ignore
}
}, [isAudioMuted]);
useEffect(() => {
return () => {
if (audioRef.current) {
audioRef.current.pause();
audioRef.current.src = '';
audioRef.current.load();
audioRef.current = null;
}
};
}, []);
const ensureAudioInitialized = () => {
if (audioInitializedRef.current) return;
audioInitializedRef.current = true;
const a = new Audio('/audio/theme.mp3');
a.loop = true;
a.volume = 0.35;
a.preload = 'auto';
audioRef.current = a;
};
const applyAudioState = async (nextMuted: boolean) => {
ensureAudioInitialized();
const a = audioRef.current;
if (!a) return;
if (nextMuted) {
a.pause();
a.muted = true;
return;
}
a.muted = false;
try {
await a.play();
} catch {
// Autoplay can be blocked; user can click again.
}
};
const toggleAudioMuted = async () => {
const next = !isAudioMuted;
setIsAudioMuted(next);
await applyAudioState(next);
};
const stopTts = () => {
setIsTtsPlaying(false);
if (ttsAudioRef.current) {
ttsAudioRef.current.pause();
ttsAudioRef.current.src = '';
ttsAudioRef.current.load();
ttsAudioRef.current = null;
}
};
const playHomeTts = async () => {
if (activeSection) return;
if (isTtsPlaying) return;
const a = new Audio('/audio/tts.wav');
a.preload = 'auto';
a.volume = 1.0;
ttsAudioRef.current = a;
a.addEventListener('ended', stopTts, { once: true });
a.addEventListener('error', stopTts, { once: true });
setIsTtsPlaying(true);
try {
await a.play();
} catch {
stopTts();
}
};
const handleSectionClick = (section: string) => {
setActiveSection(section === activeSection ? null : section);
};
// --- FIXED LOADER LOGIC ---
useEffect(() => {
let interval: any;
interval = setInterval(() => {
setLoadProgress(prev => {
// When close to 100, ensure we reach it
if (prev >= 98) {
clearInterval(interval);
return 100;
}
// Random increment for organic feel
const increment = Math.random() * 3;
const next = prev + increment;
// Cap at 100
return Math.min(next, 100);
});
}, 25);
return () => clearInterval(interval);
}, []);
// Handle Transition from Loader to Interface
useEffect(() => {
if (loadProgress >= 100) {
// Small delay at 100% before fading out
const timeout = setTimeout(() => {
setShowLoader(false);
// Delay interface appearance slightly to ensure smooth fade
setTimeout(() => setShowInterface(true), 500);
}, 500);
return () => clearTimeout(timeout);
}
}, [loadProgress]);
// Scroll Loop
useEffect(() => {
let frameId: number;
const loop = () => {
smoothScrollRef.current = THREE.MathUtils.lerp(smoothScrollRef.current, scrollRef.current, 0.08);
frameId = requestAnimationFrame(loop);
};
loop();
return () => cancelAnimationFrame(frameId);
}, []);
const handleScroll = (e: React.UIEvent) => {
const s = e.currentTarget.scrollTop;
scrollRef.current = s;
if (s > lastScrollTop.current && s > 50) setIsNavDimmed(true);
else if (s < lastScrollTop.current || s <= 50) setIsNavDimmed(false);
lastScrollTop.current = s;
};
return (
{/*
Loader Overlay: Transparent BG, shows Sphere behind.
Mobile: Bottom aligned (pb-32)
Desktop: Center aligned (pb-0)
*/}
{/*
Main Interface Container:
Remains strictly hidden (opacity-0) until showInterface is true.
*/}
{/* Active Section Modals */}
{activeSection === 'work' && (isMobile ?
setActiveSection(null)} /> : setActiveSection(null)} />)}
{activeSection === 'research' && (isMobile ? setActiveSection(null)} /> : setActiveSection(null)} />)}
{activeSection === 'services' && (isMobile ? setActiveSection(null)} /> : setActiveSection(null)} />)}
{activeSection === 'contact' && (
isMobile ? (
setActiveSection(null)} />
) : (
setActiveSection(null)} isAudioMuted={isAudioMuted} onToggleAudioMuted={toggleAudioMuted} />
)
)}
{/* Main Scroll Container */}
{/* Spacer for Hero to be visible */}
{/* 1. Core Capabilities List */}
[01]
Machine Learning Systems
[02]
Generative AI Platforms
[03]
Fullstack Product Delivery
{/* 2. Content Teasers */}
handleSectionClick('work')}
/>
handleSectionClick('research')}
/>
handleSectionClick('services')}
/>
handleSectionClick('contact')}
/>
{/* 3. Massive Footer */}
);
};
export default App;