import { Toaster } from "@/components/ui/toaster"; import { Toaster as Sonner } from "@/components/ui/sonner"; import { TooltipProvider } from "@/components/ui/tooltip"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { useEffect } from "react"; import { BrowserRouter, useLocation } from "react-router-dom"; import { ThemeProvider } from "@/components/ThemeProvider"; import { AnalyticsProvider } from "@/components/AnalyticsProvider"; import { AppRoutes } from "./routes"; const queryClient = new QueryClient(); /** * Scrollt bei einem Seitenwechsel nach oben. Mit Anker (URL mit #) springt es zum Abschnitt, * sobald er gerendert ist, z. B. beim Footer-Link „/#services“ von einer Unterseite aus. */ export const ScrollToTop = () => { const { pathname, hash } = useLocation(); useEffect(() => { if (!hash) { window.scrollTo(0, 0); return; } const id = decodeURIComponent(hash.slice(1)); let versuche = 0; let frame = 0; let abgebrochen = false; const springe = () => { const ziel = document.getElementById(id); if (!ziel) { if (versuche++ < 60) frame = requestAnimationFrame(springe); // höchstens ca. 1 s warten return; } // Erst springen, wenn die Schriften geladen sind: Vorher ist der Text darüber höher, // und der Abschnitt landet nach dem Nachladen ein Stück zu weit oben. const schriften = document.fonts?.ready ?? Promise.resolve(); schriften.then(() => { if (!abgebrochen) ziel.scrollIntoView(); }); }; springe(); return () => { abgebrochen = true; cancelAnimationFrame(frame); }; }, [pathname, hash]); return null; }; const App = () => ( ); export default App;