- Added 'use client' to not-found.tsx - Refactored RelatedProducts to Server Component to fix 'fs' import error - Created RelatedProductLink for client-side analytics - Fixed lint syntax issues in RecordModeVisuals.tsx - Fixed rule-of-hooks violation in WebsiteVideo.tsx
262 lines
11 KiB
TypeScript
262 lines
11 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
import { useRecordMode } from './RecordModeContext';
|
|
|
|
export function RecordModeVisuals({ children }: { children: React.ReactNode }) {
|
|
const { isActive, isPlaying, zoomLevel, cursorPosition, isBlurry } = useRecordMode();
|
|
const [mounted, setMounted] = React.useState(false);
|
|
const [isEmbedded, setIsEmbedded] = React.useState(false);
|
|
const [iframeUrl, setIframeUrl] = React.useState<string | null>(null);
|
|
|
|
React.useEffect(() => {
|
|
setMounted(true);
|
|
// Explicit non-magical detection
|
|
const embedded =
|
|
window.location.search.includes('embedded=true') || window.name === 'record-mode-iframe';
|
|
setIsEmbedded(embedded);
|
|
|
|
if (!embedded) {
|
|
const url = new URL(window.location.href);
|
|
url.searchParams.set('embedded', 'true');
|
|
setIframeUrl(url.toString());
|
|
}
|
|
}, [isEmbedded]);
|
|
|
|
// Hydration Guard: Match server on first render
|
|
if (!mounted) return <>{children}</>;
|
|
|
|
// Recursion Guard: If we are already in an embedded iframe,
|
|
// strictly return just the children to prevent Inception.
|
|
if (isEmbedded) {
|
|
return (
|
|
<>
|
|
<style
|
|
dangerouslySetInnerHTML={{
|
|
__html: `
|
|
/* Harder Isolation: Hide ALL potentially duplicate overlays and DEV TOOLS */
|
|
#nextjs-portal,
|
|
#nextjs-portal-root,
|
|
[data-nextjs-toast-wrapper],
|
|
.nextjs-static-indicator,
|
|
[data-nextjs-indicator],
|
|
[class*="nextjs-"],
|
|
[id*="nextjs-"],
|
|
nextjs-portal,
|
|
#feedback-overlay,
|
|
.feedback-ui-root,
|
|
.feedback-ui-ignore,
|
|
[class*="z-[9999]"],
|
|
[class*="z-[10000]"],
|
|
[style*="z-index: 9999"],
|
|
[style*="z-index: 10000"],
|
|
.fixed.bottom-6.left-6,
|
|
.fixed.bottom-6.left-1/2,
|
|
.feedback-ui-overlay,
|
|
[id^="feedback-"],
|
|
[class^="feedback-"] {
|
|
display: none !important;
|
|
opacity: 0 !important;
|
|
visibility: hidden !important;
|
|
pointer-events: none !important;
|
|
z-index: -10000 !important;
|
|
}
|
|
|
|
/* Nuclear Option 2.0: Kill ALL scrollbars on ALL elements */
|
|
* {
|
|
scrollbar-width: none !important;
|
|
-ms-overflow-style: none !important;
|
|
}
|
|
*::-webkit-scrollbar {
|
|
display: none !important;
|
|
width: 0 !important;
|
|
height: 0 !important;
|
|
}
|
|
|
|
html, body {
|
|
border-radius: 3rem;
|
|
background: #050505 !important;
|
|
color: white !important;
|
|
overflow-x: hidden !important;
|
|
overflow-y: auto !important;
|
|
}
|
|
`,
|
|
}}
|
|
/>
|
|
{children}
|
|
</>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{/* Global Style for Body Lock */}
|
|
{isActive && (
|
|
<style
|
|
dangerouslySetInnerHTML={{
|
|
__html: `
|
|
html, body {
|
|
overflow: hidden !important;
|
|
height: 100vh !important;
|
|
position: fixed !important;
|
|
width: 100vw !important;
|
|
}
|
|
/* Kill Next.js Dev tools on host while Studio is active */
|
|
#nextjs-portal,
|
|
[data-nextjs-toast-wrapper],
|
|
.nextjs-static-indicator {
|
|
display: none !important;
|
|
}
|
|
`,
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
<div
|
|
className={`transition-all duration-1000 ${isActive ? 'fixed inset-0 z-[9997] bg-[#020202] flex items-center justify-center p-6 md:p-12 lg:p-20' : 'relative w-full'}`}
|
|
>
|
|
{/* Studio Background - Only visible when active */}
|
|
{isActive && (
|
|
<div className="absolute inset-0 z-0 pointer-events-none overflow-hidden">
|
|
<div className="absolute inset-0 bg-gradient-to-br from-[#03110a] via-[#020202] to-[#030a11] animate-pulse duration-[10s]" />
|
|
<div
|
|
className="absolute -top-[60%] -left-[50%] w-[140%] h-[140%] rounded-full opacity-[0.7]"
|
|
style={{
|
|
background: 'radial-gradient(circle, #10b981 0%, transparent 70%)',
|
|
filter: 'blur(160px)',
|
|
animation: 'mesh-float-1 18s ease-in-out infinite',
|
|
}}
|
|
/>
|
|
<div
|
|
className="absolute -bottom-[60%] -right-[50%] w-[130%] h-[130%] rounded-full opacity-[0.55]"
|
|
style={{
|
|
background: 'radial-gradient(circle, #06b6d4 0%, transparent 70%)',
|
|
filter: 'blur(150px)',
|
|
animation: 'mesh-float-2 22s ease-in-out infinite',
|
|
}}
|
|
/>
|
|
<div
|
|
className="absolute -top-[30%] -right-[40%] w-[100%] h-[100%] rounded-full opacity-[0.5]"
|
|
style={{
|
|
background: 'radial-gradient(circle, #82ed20 0%, transparent 70%)',
|
|
filter: 'blur(130px)',
|
|
animation: 'mesh-float-3 14s ease-in-out infinite',
|
|
}}
|
|
/>
|
|
<div
|
|
className="absolute -bottom-[50%] -left-[40%] w-[110%] h-[110%] rounded-full opacity-[0.45]"
|
|
style={{
|
|
background: 'radial-gradient(circle, #2563eb 0%, transparent 70%)',
|
|
filter: 'blur(140px)',
|
|
animation: 'mesh-float-4 20s ease-in-out infinite',
|
|
}}
|
|
/>
|
|
<div
|
|
className="absolute inset-0 opacity-[0.12] mix-blend-overlay"
|
|
style={{
|
|
backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E")`,
|
|
backgroundSize: '128px 128px',
|
|
}}
|
|
/>
|
|
<div
|
|
className="absolute inset-0 opacity-[0.06]"
|
|
style={{
|
|
backgroundImage:
|
|
'repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(255,255,255,0.03) 2px, rgba(255,255,255,0.03) 4px)',
|
|
}}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
<div
|
|
className={`transition-all duration-700 ease-in-out relative z-10 w-full ${isActive ? 'h-full max-h-[1000px] max-w-[1600px] drop-shadow-[0_60px_150px_rgba(0,0,0,1)] scale-in' : 'h-full'}`}
|
|
style={{
|
|
transform: isPlaying ? `scale(${zoomLevel})` : undefined,
|
|
transformOrigin: isPlaying ? `${cursorPosition.x}px ${cursorPosition.y}px` : 'center',
|
|
filter: isBlurry ? 'blur(4px)' : 'none',
|
|
willChange: 'transform, filter',
|
|
WebkitBackfaceVisibility: 'hidden',
|
|
backfaceVisibility: 'hidden',
|
|
}}
|
|
>
|
|
<div
|
|
className={
|
|
isActive
|
|
? 'relative h-full w-full rounded-[3rem] overflow-hidden bg-[#050505] isolate'
|
|
: 'w-full h-full'
|
|
}
|
|
style={{ transform: isActive ? 'translateZ(0)' : 'none' }}
|
|
>
|
|
{isActive && (
|
|
<>
|
|
<div className="absolute inset-0 rounded-[3rem] border border-white/[0.08] pointer-events-none z-50" />
|
|
<div
|
|
className="absolute inset-[-2px] rounded-[3rem] pointer-events-none z-20"
|
|
style={{
|
|
background:
|
|
'linear-gradient(135deg, rgba(16,185,129,0.15), rgba(130,237,32,0.15))',
|
|
animation: 'pulse-ring 4s ease-in-out infinite',
|
|
}}
|
|
/>
|
|
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-[#82ed20]/[0.05] to-transparent h-[15%] w-full top-[-15%] animate-scan-slow z-50 pointer-events-none opacity-20" />
|
|
</>
|
|
)}
|
|
|
|
<div
|
|
className={
|
|
isActive
|
|
? 'w-full h-full rounded-[3rem] overflow-hidden relative'
|
|
: 'w-full h-full relative'
|
|
}
|
|
style={{
|
|
WebkitMaskImage: isActive ? '-webkit-radial-gradient(white, black)' : 'none',
|
|
transform: isActive ? 'translateZ(0)' : 'none',
|
|
}}
|
|
>
|
|
{isActive && iframeUrl ? (
|
|
<iframe
|
|
src={iframeUrl}
|
|
name="record-mode-iframe"
|
|
className="w-full h-full border-0 block"
|
|
style={{
|
|
backgroundColor: '#050505',
|
|
scrollbarWidth: 'none',
|
|
msOverflowStyle: 'none',
|
|
height: '100%',
|
|
width: '100%',
|
|
}}
|
|
/>
|
|
) : (
|
|
<div
|
|
className={
|
|
isActive
|
|
? 'blur-2xl opacity-20 pointer-events-none scale-95 transition-all duration-700'
|
|
: 'transition-all duration-700'
|
|
}
|
|
>
|
|
{children}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style
|
|
dangerouslySetInnerHTML={{
|
|
__html: `
|
|
@keyframes mesh-float-1 { 0%, 100% { transform: translate(0, 0) scale(1) rotate(0deg); } 33% { transform: translate(15%, 10%) scale(1.1) rotate(5deg); } 66% { transform: translate(-10%, 20%) scale(0.9) rotate(-3deg); } }
|
|
@keyframes mesh-float-2 { 0%, 100% { transform: translate(0, 0) scale(1) rotate(0deg); } 33% { transform: translate(-20%, -15%) scale(1.2) rotate(-8deg); } 66% { transform: translate(15%, -10%) scale(0.8) rotate(4deg); } }
|
|
@keyframes mesh-float-3 { 0%, 100% { transform: translate(0, 0) scale(1.2); } 50% { transform: translate(20%, -25%) scale(0.7); } }
|
|
@keyframes mesh-float-4 { 0%, 100% { transform: translate(0, 0) scale(1); } 50% { transform: translate(-15%, 25%) scale(1.1); } }
|
|
@keyframes pulse-ring { 0%, 100% { opacity: 0.15; transform: scale(1); } 50% { opacity: 0.4; transform: scale(1.005); } }
|
|
@keyframes scan-slow { 0% { transform: translateY(-100%); opacity: 0; } 5% { opacity: 0.2; } 95% { opacity: 0.2; } 100% { transform: translateY(800%); opacity: 0; } }
|
|
@keyframes scale-in { 0% { transform: scale(0.95); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }
|
|
.scale-in { animation: scale-in 0.7s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
|
|
`,
|
|
}}
|
|
/>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|