"use client"; import * as React from "react"; import { cn } from "../utils/cn"; import { ShieldCheck, ArrowLeft, ArrowRight, RefreshCw } from "lucide-react"; import { MonoLabel, Label } from "./Typography"; import { motion, AnimatePresence } from "framer-motion"; interface IframeSectionProps { src: string; title?: string; description?: string; height?: string; className?: string; zoom?: number; offsetY?: number; clipHeight?: number; browserFrame?: boolean; allowScroll?: boolean; desktopWidth?: number; minimal?: boolean; perspective?: boolean; rotate?: number; delay?: number; noScale?: boolean; dynamicGlow?: boolean; minHeight?: number; mobileWidth?: number; mobileHeight?: string; desktopHeight?: string; } /** * Reusable Browser UI components to maintain consistency */ const BrowserChromeComponent: React.FC<{ url: string; minimal?: boolean }> = ({ url, minimal, }) => { if (minimal) return null; return (
{/* 3D Rim Highlight */}
{/* Status Indicators (Traffic Lights) - Enhanced with subtle depth */}
{[ "bg-slate-300 from-slate-200 to-slate-400", "bg-slate-300 from-slate-200 to-slate-400", "bg-slate-300 from-slate-200 to-slate-400", ].map((cls, i) => (
{/* Subtle glint, soft blur */}
))}
{/* Navigation Controls - Hidden on mobile */}
{/* URL Bar - Solid high-fidelity layer instead of expensive backdrop-blur */}
{url.replace("varnish-cache://", "")} VARNISH_TUNNEL // ALPHA_NODE_04
{/* Industrial Accent / Technical ID - Hidden on mobile */}
SECURE_LINK // ID: VARNISH_4X
); }; const BrowserChrome = React.memo(BrowserChromeComponent); BrowserChrome.displayName = "BrowserChrome"; export const IframeSection: React.FC = ({ src, title, description, height = "500px", className, zoom, offsetY = 0, clipHeight, browserFrame = false, allowScroll = false, desktopWidth = 1200, minimal = false, perspective = false, rotate: _rotate = 0, delay: _delay = 0, noScale = false, dynamicGlow = true, minHeight = 400, mobileWidth = 390, mobileHeight, desktopHeight, }) => { const containerRef = React.useRef(null); const iframeRef = React.useRef(null); const canvasRef = React.useRef(null); const [scale, setScale] = React.useState(1); const [activeInternalWidth, setActiveInternalWidth] = React.useState(desktopWidth); const [isLoading, setIsLoading] = React.useState(true); const [isIframeLoaded, setIsIframeLoaded] = React.useState(false); const [glowColors, setGlowColors] = React.useState([ "rgba(148, 163, 184, 0.1)", "rgba(148, 163, 184, 0.1)", "rgba(148, 163, 184, 0.1)", "rgba(148, 163, 184, 0.1)", ]); const [scrollState, setScrollState] = React.useState({ atTop: true, atBottom: false, isScrollable: false, }); const [headerHeightPx, setHeaderHeightPx] = React.useState(0); // Responsive Header Height Calculation React.useEffect(() => { if (browserFrame && !minimal) { const updateHeaderHeight = () => { setHeaderHeightPx(window.innerWidth < 768 ? 48 : 56); }; updateHeaderHeight(); window.addEventListener("resize", updateHeaderHeight); return () => window.removeEventListener("resize", updateHeaderHeight); } else { setHeaderHeightPx(0); } }, [browserFrame, minimal]); // Scaling & Adaptive Viewport Logic React.useEffect(() => { if (!containerRef.current || noScale) { setScale(1); setActiveInternalWidth(desktopWidth); return; } const updateDimensions = () => { if (containerRef.current) { const containerWidth = containerRef.current.offsetWidth; if (containerWidth > 0) { // Adaptive threshold: Switch to mobile width if container is small const useMobile = containerWidth < 500; const internalWidth = useMobile ? mobileWidth : desktopWidth; setActiveInternalWidth(internalWidth); // Calculate scale based on the active target width const newScale = zoom || containerWidth / internalWidth; setScale(newScale); } } }; updateDimensions(); const observer = new ResizeObserver(updateDimensions); observer.observe(containerRef.current); return () => observer.disconnect(); }, [desktopWidth, mobileWidth, zoom, noScale]); const updateScrollState = React.useCallback(() => { try { const doc = iframeRef.current?.contentDocument?.documentElement; if (doc) { const atTop = doc.scrollTop <= 5; const atBottom = doc.scrollTop + doc.clientHeight >= doc.scrollHeight - 5; const isScrollable = doc.scrollHeight > doc.clientHeight + 10; setScrollState({ atTop, atBottom, isScrollable }); } } catch (_e) {} }, []); // Ambilight effect (sampled from iframe if same-origin) const updateAmbilight = React.useCallback(() => { if (!dynamicGlow || !iframeRef.current || !canvasRef.current) return; try { const iframe = iframeRef.current; const doc = iframe.contentDocument; if (!doc) return; const canvas = canvasRef.current; const ctx = canvas.getContext("2d", { willReadFrequently: true }); if (!ctx) return; canvas.width = 100; canvas.height = 100; const body = doc.body; const computedStyle = window.getComputedStyle(body); const bgColor = computedStyle.backgroundColor || "rgba(255,255,255,1)"; const sampleX = (x: number, y: number) => { const el = doc.elementFromPoint(x, y); if (el) return window.getComputedStyle(el).backgroundColor; return bgColor; }; const w = doc.documentElement.scrollWidth || iframe.offsetWidth; const h = doc.documentElement.scrollHeight || iframe.offsetHeight; const sampleMargin = 20; const colors = [ sampleX(w / 2, sampleMargin + offsetY), sampleX(w - sampleMargin, h / 2 + offsetY), sampleX(w / 2, h - sampleMargin + offsetY), sampleX(sampleMargin, h / 2 + offsetY), ]; setGlowColors( colors.map((c) => { if (!c || c === "transparent") return "rgba(148, 163, 184, 0.1)"; return c.replace("rgb(", "rgba(").replace(")", ", 0.5)"); }), ); updateScrollState(); } catch (_e) {} }, [dynamicGlow, offsetY, updateScrollState]); // Height parse helper const parseNumericHeight = (h: string | number) => { if (typeof h === "number") return h; const match = h.match(/^(\d+(?:\.\d+)?)(px)$/); return match ? parseFloat(match[1]) : null; }; const [isMobile, setIsMobile] = React.useState(false); React.useEffect(() => { const checkMobile = () => setIsMobile(window.innerWidth < 768); checkMobile(); window.addEventListener("resize", checkMobile); return () => window.removeEventListener("resize", checkMobile); }, []); const activeHeight = isMobile ? mobileHeight || height : desktopHeight || height; const baseNumericHeight = parseNumericHeight(activeHeight); const finalScaledHeight = clipHeight ? clipHeight * scale : baseNumericHeight ? baseNumericHeight * scale : null; const chassisStyle = { height: activeHeight === "100%" ? "100%" : finalScaledHeight ? `${finalScaledHeight + headerHeightPx}px` : `calc(${activeHeight} + ${headerHeightPx}px)`, minHeight: minHeight ? `${minHeight}px` : undefined, }; const [loadingPhase, setLoadingPhase] = React.useState(0); const loadingPhases = [ "DETERMINING ROUTE", "INITIALIZING HANDSHAKE", "ESTABLISHING ENCRYPTED LINK", "SYNCING ASSETS", "FINALIZING...", ]; // 1. Safety Trigger: Force-stop loading after 2.5s no matter what React.useEffect(() => { if (!isLoading) return; const timer = setTimeout(() => { setIsLoading(false); }, 2500); return () => clearTimeout(timer); }, [isLoading]); // 2. Sync Trigger: Cleanup when BOTH phases and iframe load complete React.useEffect(() => { if (!isLoading) return; // Early exit: if iframe is already loaded, we only need the first 2 "handshake" phases const phasesRequired = isIframeLoaded ? loadingPhases.length - 2 : loadingPhases.length - 1; if (loadingPhase >= phasesRequired) { const timer = setTimeout(() => { setIsLoading(false); }, 300); return () => clearTimeout(timer); } }, [isLoading, isIframeLoaded, loadingPhase, loadingPhases.length]); // 3. Phase Incrementer (Faster: 400ms) React.useEffect(() => { if (!isLoading) return; const interval = setInterval(() => { setLoadingPhase((prev) => { if (prev < loadingPhases.length - 1) return prev + 1; clearInterval(interval); return prev; }); }, 400); return () => clearInterval(interval); }, [isLoading, loadingPhases.length]); return (