diff --git a/website/src/components/ui/FadeIn.tsx b/website/src/components/ui/FadeIn.tsx index d3ccdd31..ce1b4096 100644 --- a/website/src/components/ui/FadeIn.tsx +++ b/website/src/components/ui/FadeIn.tsx @@ -1,9 +1,24 @@ 'use client'; import { motion, useReducedMotion } from 'framer-motion'; -import { ReactNode } from 'react'; +import { ReactNode, useState, useEffect, memo } from 'react'; -export function FadeIn({ children, delay = 0, className = '' }: { children: ReactNode; delay?: number; className?: string }) { +export const FadeIn = memo(function FadeIn({ children, delay = 0, className = '' }: { children: ReactNode; delay?: number; className?: string }) { const shouldReduce = useReducedMotion(); + const [isMounted, setIsMounted] = useState(false); + + // Defer Framer Motion's initial state until after hydration. Without this, + // Framer Motion applies initial={{ opacity: 0 }} before React hydrates, causing + // the server-rendered HTML (opacity:1) to mismatch the client DOM (opacity:0). + useEffect(() => { + setIsMounted(true); + }, []); + + if (!isMounted) { + // Server + hydration pass: render without Framer Motion so the DOM matches + // the server HTML exactly. No opacity/transform styles applied. + return
{children}
; + } + return ( ); -} +}); diff --git a/website/src/instrumentation-client.ts b/website/src/instrumentation-client.ts index 4fe3437b..1a27bbab 100644 --- a/website/src/instrumentation-client.ts +++ b/website/src/instrumentation-client.ts @@ -12,6 +12,31 @@ Sentry.init({ integrations: [ Sentry.replayIntegration(), ], + beforeSend(event) { + const msg = event.exception?.values?.[0]?.value ?? ""; + + // Drop known browser-extension noise that cannot be fixed in app code: + // + // 1. "Cannot assign to read only property 'pushState'" — extensions + // (Vue/Redux DevTools, privacy tools) wrap history.pushState before our + // app loads, making it read-only. Not reproducible without the extension. + // + // 2. "Object Not Found Matching Id:N, MethodName:update" — Chrome DevTools + // Protocol messages from extensions interacting with CodeMirror/Monaco. + // The error originates outside our code and only affects a single session. + const isExtensionNoise = + msg.includes("Cannot assign to read only property 'pushState'") || + msg.includes("Object Not Found Matching Id:"); + + if (isExtensionNoise) { + if (process.env.NODE_ENV === "development") { + console.debug("[Sentry] Suppressed extension noise:", msg); + } + return null; + } + + return event; + }, }); export const onRouterTransitionStart = dsn