refactor: restructure instrumentation client initialization and enhance UI components for better performance and user experience

This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-03-08 17:48:09 -07:00
parent cba7923503
commit d70e292fbe
18 changed files with 227 additions and 264 deletions

View file

@ -1,10 +1,29 @@
"use client";
import { CTAHomepage } from "@/components/homepage/cta";
import { FeaturesBentoGrid } from "@/components/homepage/features-bento-grid";
import { FeaturesCards } from "@/components/homepage/features-card";
import dynamic from "next/dynamic";
import { HeroSection } from "@/components/homepage/hero-section";
import ExternalIntegrations from "@/components/homepage/integrations";
const FeaturesCards = dynamic(
() => import("@/components/homepage/features-card").then((m) => ({ default: m.FeaturesCards })),
{ ssr: false }
);
const FeaturesBentoGrid = dynamic(
() =>
import("@/components/homepage/features-bento-grid").then((m) => ({
default: m.FeaturesBentoGrid,
})),
{ ssr: false }
);
const ExternalIntegrations = dynamic(() => import("@/components/homepage/integrations"), {
ssr: false,
});
const CTAHomepage = dynamic(
() => import("@/components/homepage/cta").then((m) => ({ default: m.CTAHomepage })),
{ ssr: false }
);
export default function HomePage() {
return (

View file

@ -105,11 +105,19 @@ export function DocumentsFilters({
</div>
) : (
filteredTypes.map((value: DocumentTypeEnum, i) => (
<button
type="button"
<div
role="option"
aria-selected={activeTypes.includes(value)}
tabIndex={0}
key={value}
className="flex w-full items-center gap-2.5 py-2 px-3 rounded-md hover:bg-neutral-200 dark:hover:bg-neutral-700 transition-colors cursor-pointer text-left"
onClick={() => onToggleType(value, !activeTypes.includes(value))}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
onToggleType(value, !activeTypes.includes(value));
}
}}
>
{/* Icon */}
<div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-muted/50 text-foreground/80">
@ -132,7 +140,7 @@ export function DocumentsFilters({
onCheckedChange={(checked: boolean) => onToggleType(value, !!checked)}
className="h-4 w-4 shrink-0 rounded border-muted-foreground/30 data-[state=checked]:bg-primary data-[state=checked]:border-primary"
/>
</button>
</div>
))
)}
</div>

View file

@ -108,6 +108,9 @@ export default function RootLayout({
// Locale state is managed by LocaleContext and persisted in localStorage
return (
<html lang="en" suppressHydrationWarning>
<head>
<link rel="preconnect" href="https://api.github.com" />
</head>
<body className={cn(roboto.className, "bg-white dark:bg-black antialiased h-full w-full ")}>
<PostHogProvider>
<LocaleProvider>

View file

@ -101,7 +101,6 @@ const ThreadContent: FC = () => {
>
<ThreadPrimitive.Viewport
turnAnchor="top"
autoScroll
className={cn(
"aui-thread-viewport relative flex flex-1 min-h-0 flex-col overflow-y-auto px-4 pt-4 transition-[padding] duration-300 ease-out",
showGutter && "lg:pr-30"

View file

@ -1,16 +1,8 @@
"use client";
import { IconBrandGithub } from "@tabler/icons-react";
import { StarIcon } from "lucide-react";
import type { HTMLMotionProps, UseInViewOptions } from "motion/react";
import {
AnimatePresence,
motion,
useInView,
useMotionValue,
useSpring,
useTransform,
} from "motion/react";
import { motion, useInView, useMotionValue, useSpring } from "motion/react";
import * as React from "react";
import { cn } from "@/lib/utils";
@ -53,122 +45,6 @@ function useIsInView<T extends HTMLElement = HTMLElement>(
return { ref: localRef, isInView };
}
// ---------------------------------------------------------------------------
// Particles (for star burst effect on completion)
// ---------------------------------------------------------------------------
type ParticlesContextType = { animate: boolean; isInView: boolean };
const [ParticlesProvider, useParticles] =
getStrictContext<ParticlesContextType>("ParticlesContext");
function Particles({
ref,
animate = true,
inView = false,
inViewMargin = "0px",
inViewOnce = true,
children,
style,
...props
}: Omit<HTMLMotionProps<"div">, "children"> & {
animate?: boolean;
children: React.ReactNode;
} & UseIsInViewOptions) {
const { ref: localRef, isInView } = useIsInView(ref as React.Ref<HTMLDivElement>, {
inView,
inViewOnce,
inViewMargin,
});
return (
<ParticlesProvider value={{ animate, isInView }}>
<motion.div ref={localRef} style={{ position: "relative", ...style }} {...props}>
{children}
</motion.div>
</ParticlesProvider>
);
}
function ParticlesEffect({
side = "top",
align = "center",
count = 6,
radius = 30,
spread = 360,
duration = 0.8,
holdDelay = 0.05,
sideOffset = 0,
alignOffset = 0,
delay = 0,
transition,
style,
...props
}: Omit<HTMLMotionProps<"div">, "children"> & {
side?: "top" | "bottom" | "left" | "right";
align?: "start" | "center" | "end";
count?: number;
radius?: number;
spread?: number;
duration?: number;
holdDelay?: number;
sideOffset?: number;
alignOffset?: number;
delay?: number;
}) {
const { animate, isInView } = useParticles();
const isVertical = side === "top" || side === "bottom";
const alignPct = align === "start" ? "0%" : align === "end" ? "100%" : "50%";
const top = isVertical
? side === "top"
? `calc(0% - ${sideOffset}px)`
: `calc(100% + ${sideOffset}px)`
: `calc(${alignPct} + ${alignOffset}px)`;
const left = isVertical
? `calc(${alignPct} + ${alignOffset}px)`
: side === "left"
? `calc(0% - ${sideOffset}px)`
: `calc(100% + ${sideOffset}px)`;
const containerStyle: React.CSSProperties = {
position: "absolute",
top,
left,
transform: "translate(-50%, -50%)",
};
const angleStep = (spread * (Math.PI / 180)) / Math.max(1, count - 1);
return (
<AnimatePresence>
{animate &&
isInView &&
[...Array(count)].map((_, i) => {
const angle = i * angleStep;
const x = Math.cos(angle) * radius;
const y = Math.sin(angle) * radius;
return (
<motion.div
key={`particle-${angle}`}
style={{ ...containerStyle, ...style }}
initial={{ scale: 0, opacity: 0 }}
animate={{
x: `${x}px`,
y: `${y}px`,
scale: [0, 1, 0],
opacity: [0, 1, 0],
}}
transition={{
duration,
delay: delay + i * holdDelay,
ease: "easeOut",
...transition,
}}
{...props}
/>
);
})}
</AnimatePresence>
);
}
// ---------------------------------------------------------------------------
// Per-digit scrolling wheel
// ---------------------------------------------------------------------------
@ -317,18 +193,42 @@ function AnimatedStarCount({
value,
itemSize = 22,
isRolling = false,
animated = true,
className,
onComplete,
}: {
value: number;
itemSize?: number;
isRolling?: boolean;
animated?: boolean;
className?: string;
onComplete?: () => void;
}) {
const formatted = numberFormatter.format(value);
const chars = formatted.split("");
if (!animated) {
return (
<div className="flex items-center">
{chars.map((char, idx) => (
<div
key={`static-${idx}-${char}`}
className={className}
style={{
height: itemSize,
display: "flex",
alignItems: "center",
justifyContent: "center",
width: char >= "0" && char <= "9" ? undefined : "0.3em",
}}
>
{char}
</div>
))}
</div>
);
}
let totalDigits = 0;
for (const c of chars) {
if (c >= "0" && c <= "9") totalDigits++;
@ -407,13 +307,13 @@ function NavbarGitHubStars({
href = "https://github.com/MODSetter/SurfSense",
className,
}: NavbarGitHubStarsProps) {
const [hasMounted, setHasMounted] = React.useState(false);
const [stars, setStars] = React.useState(0);
const [isLoading, setIsLoading] = React.useState(true);
const [isCompleted, setIsCompleted] = React.useState(false);
const fillRaw = useMotionValue(0);
const fillSpring = useSpring(fillRaw, { stiffness: 12, damping: 14 });
const clipPath = useTransform(fillSpring, (v) => `inset(${100 - v * 100}% 0 0 0)`);
React.useEffect(() => {
setHasMounted(true);
}, []);
React.useEffect(() => {
const abortController = new AbortController();
@ -424,7 +324,6 @@ function NavbarGitHubStars({
.then((data) => {
if (data && typeof data.stargazers_count === "number") {
setStars(data.stargazers_count);
fillRaw.set(1);
}
})
.catch((err) => {
@ -434,7 +333,7 @@ function NavbarGitHubStars({
})
.finally(() => setIsLoading(false));
return () => abortController.abort();
}, [username, repo, fillRaw]);
}, [username, repo]);
return (
<a
@ -442,37 +341,20 @@ function NavbarGitHubStars({
target="_blank"
rel="noopener noreferrer"
className={cn(
"group flex items-center gap-2 rounded-full px-3 py-1.5 transition-colors",
"group inline-flex items-center rounded-full border border-neutral-200 bg-white/80 px-3 py-1.5 text-sm backdrop-blur-sm transition-colors dark:border-neutral-800 dark:bg-neutral-950/80",
"hover:bg-neutral-100 dark:hover:bg-neutral-900",
className
)}
>
<IconBrandGithub className="h-5 w-5 text-neutral-600 dark:text-neutral-300 shrink-0" />
<div className="flex items-center gap-1 rounded-md bg-neutral-100 dark:bg-neutral-800 group-hover:bg-neutral-200 dark:group-hover:bg-neutral-700 px-2 py-0.5 transition-colors">
<IconBrandGithub className="h-5 w-5 shrink-0 text-neutral-600 transition-colors dark:text-neutral-300 group-hover:text-neutral-800 dark:group-hover:text-neutral-100" />
<div className="ml-2 flex items-center text-neutral-500 transition-colors dark:text-neutral-400 group-hover:text-neutral-800 dark:group-hover:text-neutral-200">
<AnimatedStarCount
value={isLoading ? 10000 : stars}
itemSize={ITEM_SIZE}
isRolling={isLoading}
isRolling={hasMounted && isLoading}
animated={hasMounted}
className="text-sm font-semibold tabular-nums text-neutral-500 dark:text-neutral-400 group-hover:text-neutral-800 dark:group-hover:text-neutral-200 transition-colors"
onComplete={() => setIsCompleted(true)}
/>
<Particles animate={isCompleted}>
<div className="relative size-4">
<StarIcon
aria-hidden="true"
className="absolute inset-0 size-4 fill-neutral-400 stroke-neutral-400 dark:fill-neutral-700 dark:stroke-neutral-700 group-hover:fill-neutral-600 group-hover:stroke-neutral-600 dark:group-hover:fill-neutral-300 dark:group-hover:stroke-neutral-300 transition-colors"
/>
<motion.div className="absolute inset-0" style={{ clipPath }}>
<StarIcon
aria-hidden="true"
className="size-4 fill-neutral-300 stroke-neutral-300 dark:fill-neutral-400 dark:stroke-neutral-400 group-hover:fill-neutral-500 group-hover:stroke-neutral-500 dark:group-hover:fill-neutral-200 dark:group-hover:stroke-neutral-200 transition-colors"
/>
</motion.div>
</div>
<ParticlesEffect
delay={0.3}
className="size-1 rounded-full bg-neutral-300 dark:bg-neutral-400"
/>
</Particles>
</div>
</a>
);

View file

@ -32,11 +32,24 @@ const GoogleLogo = ({ className }: { className?: string }) => (
</svg>
);
function useIsDesktop(breakpoint = 1024) {
const [isDesktop, setIsDesktop] = useState(false);
useEffect(() => {
const mql = window.matchMedia(`(min-width: ${breakpoint}px)`);
setIsDesktop(mql.matches);
const handler = (e: MediaQueryListEvent) => setIsDesktop(e.matches);
mql.addEventListener("change", handler);
return () => mql.removeEventListener("change", handler);
}, [breakpoint]);
return isDesktop;
}
export function HeroSection() {
const containerRef = useRef<HTMLDivElement>(null);
const parentRef = useRef<HTMLDivElement>(null);
const heroVariant = useFeatureFlagVariantKey("notebooklm_superpowers_flag");
const isNotebookLMVariant = heroVariant === "superpowers";
const isDesktop = useIsDesktop();
return (
<div
@ -44,42 +57,46 @@ export function HeroSection() {
className="relative flex min-h-screen flex-col items-center justify-center overflow-hidden px-4 py-24 md:px-8 md:py-48"
>
<BackgroundGrids />
<CollisionMechanism
parentRef={parentRef}
beamOptions={{
initialX: -400,
translateX: 600,
duration: 7,
repeatDelay: 3,
}}
/>
<CollisionMechanism
parentRef={parentRef}
beamOptions={{
initialX: -200,
translateX: 800,
duration: 4,
repeatDelay: 3,
}}
/>
<CollisionMechanism
parentRef={parentRef}
beamOptions={{
initialX: 200,
translateX: 1200,
duration: 5,
repeatDelay: 3,
}}
/>
<CollisionMechanism
parentRef={parentRef}
beamOptions={{
initialX: 400,
translateX: 1400,
duration: 6,
repeatDelay: 3,
}}
/>
{isDesktop && (
<>
<CollisionMechanism
parentRef={parentRef}
beamOptions={{
initialX: -400,
translateX: 600,
duration: 7,
repeatDelay: 3,
}}
/>
<CollisionMechanism
parentRef={parentRef}
beamOptions={{
initialX: -200,
translateX: 800,
duration: 4,
repeatDelay: 3,
}}
/>
<CollisionMechanism
parentRef={parentRef}
beamOptions={{
initialX: 200,
translateX: 1200,
duration: 5,
repeatDelay: 3,
}}
/>
<CollisionMechanism
parentRef={parentRef}
beamOptions={{
initialX: 400,
translateX: 1400,
duration: 6,
repeatDelay: 3,
}}
/>
</>
)}
<h2 className="relative z-50 mx-auto mb-4 mt-8 max-w-4xl text-balance text-center text-3xl font-semibold tracking-tight text-gray-700 md:text-7xl dark:text-neutral-300">
{isNotebookLMVariant ? (

View file

@ -4,7 +4,11 @@ import { AnimatePresence, motion } from "motion/react";
import { useCallback, useEffect, useState } from "react";
import { createPortal } from "react-dom";
function ExpandedGifOverlay({
function isVideoSrc(src: string) {
return /\.(mp4|webm|ogg)(\?|$)/i.test(src);
}
function ExpandedMediaOverlay({
src,
alt,
onClose,
@ -21,6 +25,31 @@ function ExpandedGifOverlay({
return () => document.removeEventListener("keydown", handleKey);
}, [onClose]);
const mediaElement = isVideoSrc(src) ? (
<motion.video
initial={{ scale: 0.85, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.85, opacity: 0 }}
transition={{ duration: 0.25, ease: "easeOut" }}
src={src}
autoPlay
loop
muted
playsInline
className="max-h-[90vh] max-w-[90vw] cursor-pointer rounded-2xl shadow-2xl"
/>
) : (
<motion.img
initial={{ scale: 0.85, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.85, opacity: 0 }}
transition={{ duration: 0.25, ease: "easeOut" }}
src={src}
alt={alt}
className="max-h-[90vh] max-w-[90vw] cursor-pointer rounded-2xl shadow-2xl"
/>
);
return createPortal(
<motion.div
initial={{ opacity: 0 }}
@ -30,25 +59,22 @@ function ExpandedGifOverlay({
className="fixed inset-0 z-100 flex items-center justify-center bg-black/70 p-4 backdrop-blur-sm sm:p-8"
onClick={onClose}
>
<motion.img
initial={{ scale: 0.85, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.85, opacity: 0 }}
transition={{ duration: 0.25, ease: "easeOut" }}
src={src}
alt={alt}
className="max-h-[90vh] max-w-[90vw] cursor-pointer rounded-2xl shadow-2xl"
/>
{mediaElement}
</motion.div>,
document.body
);
}
function useExpandedGif() {
function useExpandedMedia() {
const [expanded, setExpanded] = useState(false);
const open = useCallback(() => setExpanded(true), []);
const close = useCallback(() => setExpanded(false), []);
return { expanded, open, close };
}
export { ExpandedGifOverlay, useExpandedGif };
/** @deprecated Use ExpandedMediaOverlay instead */
const ExpandedGifOverlay = ExpandedMediaOverlay;
/** @deprecated Use useExpandedMedia instead */
const useExpandedGif = useExpandedMedia;
export { ExpandedMediaOverlay, useExpandedMedia, ExpandedGifOverlay, useExpandedGif };

View file

@ -9,59 +9,57 @@ const carouselItems = [
title: "Connect & Sync",
description:
"Connect data sources like Notion, Drive and Gmail. Automatically sync to keep them updated.",
src: "/homepage/hero_tutorial/ConnectorFlowGif.gif",
src: "/homepage/hero_tutorial/ConnectorFlowGif.mp4",
},
{
title: "Upload Documents",
description: "Upload documents directly, from images to massive PDFs.",
src: "/homepage/hero_tutorial/DocUploadGif.gif",
src: "/homepage/hero_tutorial/DocUploadGif.mp4",
},
{
title: "Search & Citation",
description: "Ask questions and get cited responses from your knowledge base.",
src: "/homepage/hero_tutorial/BSNCGif.gif",
src: "/homepage/hero_tutorial/BSNCGif.mp4",
},
{
title: "Targeted Document Q&A",
description: "Mention specific documents in chat for targeted answers.",
src: "/homepage/hero_tutorial/BQnaGif_compressed.gif",
src: "/homepage/hero_tutorial/BQnaGif_compressed.mp4",
},
{
title: "Produce Reports Instantly",
description: "Generate reports from your sources in many formats.",
src: "/homepage/hero_tutorial/ReportGenGif_compressed.gif",
src: "/homepage/hero_tutorial/ReportGenGif_compressed.mp4",
},
{
title: "Create Podcasts",
description: "Turn anything into a podcast in under 20 seconds.",
src: "/homepage/hero_tutorial/PodcastGenGif.gif",
src: "/homepage/hero_tutorial/PodcastGenGif.mp4",
},
{
title: "Image Generation",
description: "Generate high-quality images easily from your conversations.",
src: "/homepage/hero_tutorial/ImageGenGif.gif",
src: "/homepage/hero_tutorial/ImageGenGif.mp4",
},
{
title: "Collaborative AI Chat",
description: "Collaborate on AI-powered conversations in realtime with your team.",
src: "/homepage/hero_realtime/RealTimeChatGif.gif",
src: "/homepage/hero_realtime/RealTimeChatGif.mp4",
},
{
title: "Realtime Comments",
description: "Add comments and tag teammates on any message.",
src: "/homepage/hero_realtime/RealTimeCommentsFlow.gif",
src: "/homepage/hero_realtime/RealTimeCommentsFlow.mp4",
},
];
function HeroCarouselCard({
index,
title,
description,
src,
isActive,
onExpandedChange,
}: {
index: number;
title: string;
description: string;
src: string;
@ -69,53 +67,50 @@ function HeroCarouselCard({
onExpandedChange?: (expanded: boolean) => void;
}) {
const { expanded, open, close } = useExpandedGif();
const videoRef = useRef<HTMLVideoElement>(null);
const [frozenFrame, setFrozenFrame] = useState<string | null>(null);
const [hasLoaded, setHasLoaded] = useState(false);
useEffect(() => {
onExpandedChange?.(expanded);
}, [expanded, onExpandedChange]);
const imgRef = useRef<HTMLImageElement>(null);
const [frozenFrame, setFrozenFrame] = useState<string | null>(null);
const [playKey, setPlayKey] = useState(0);
const captureFrame = useCallback((img: HTMLImageElement) => {
const captureFrame = useCallback((video: HTMLVideoElement) => {
try {
const canvas = document.createElement("canvas");
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
canvas.getContext("2d")?.drawImage(img, 0, 0);
setFrozenFrame(canvas.toDataURL());
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext("2d")?.drawImage(video, 0, 0);
setFrozenFrame(canvas.toDataURL("image/jpeg", 0.85));
} catch {
/* cross-origin or other issue */
/* tainted canvas */
}
}, []);
useEffect(() => {
const video = videoRef.current;
if (isActive) {
setPlayKey((k) => k + 1);
setFrozenFrame(null);
setHasLoaded(false);
if (video) {
video.currentTime = 0;
video.play().catch(() => {});
}
} else {
const img = imgRef.current;
if (img && img.complete && img.naturalWidth > 0) {
captureFrame(img);
if (video) {
if (video.readyState >= 2) captureFrame(video);
video.pause();
}
}
}, [isActive, captureFrame]);
useEffect(() => {
if (!isActive && !frozenFrame) {
const img = new Image();
img.onload = () => captureFrame(img);
img.src = src;
}
}, [isActive, frozenFrame, src, captureFrame]);
const handleCanPlay = useCallback(() => {
setHasLoaded(true);
}, []);
return (
<>
<div className="rounded-2xl border border-neutral-200/60 bg-white shadow-xl sm:rounded-3xl dark:border-neutral-700/60 dark:bg-neutral-900">
<div className="flex items-center gap-3 border-b border-neutral-200/60 px-4 py-3 sm:px-6 sm:py-4 dark:border-neutral-700/60">
{/* <span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-neutral-900 text-xs font-semibold text-white sm:h-8 sm:w-8 sm:text-sm dark:bg-white dark:text-neutral-900">
{index + 1}
</span> */}
<div className="min-w-0">
<h3 className="truncate text-base font-semibold text-neutral-900 sm:text-xl dark:text-white">
{title}
@ -130,13 +125,28 @@ function HeroCarouselCard({
onClick={isActive ? open : undefined}
>
{isActive ? (
<img
ref={imgRef}
key={`gif_${index}_${playKey}`}
src={src}
alt={title}
className="w-full rounded-lg sm:rounded-xl"
/>
<div className="relative">
<video
ref={videoRef}
src={src}
autoPlay
loop
muted
playsInline
onCanPlay={handleCanPlay}
className="w-full rounded-lg sm:rounded-xl"
/>
{!hasLoaded && frozenFrame && (
<img
src={frozenFrame}
alt={title}
className="absolute inset-0 w-full rounded-lg sm:rounded-xl"
/>
)}
{!hasLoaded && !frozenFrame && (
<div className="aspect-video w-full animate-pulse rounded-lg bg-neutral-100 sm:rounded-xl dark:bg-neutral-800" />
)}
</div>
) : frozenFrame ? (
<img src={frozenFrame} alt={title} className="w-full rounded-lg sm:rounded-xl" />
) : (
@ -284,7 +294,6 @@ function HeroCarousel() {
transition={{ duration: 0.7, ease: [0.32, 0.72, 0, 1] }}
>
<HeroCarouselCard
index={i}
title={item.title}
description={item.description}
src={item.src}

View file

@ -1,16 +1,13 @@
import posthog from "posthog-js";
if (process.env.NEXT_PUBLIC_POSTHOG_KEY) {
function initPostHog() {
if (!process.env.NEXT_PUBLIC_POSTHOG_KEY) return;
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
// Use reverse proxy to bypass ad blockers
api_host: "/ingest",
// Required for toolbar and other UI features to work correctly
ui_host: "https://us.posthog.com",
defaults: "2025-11-30",
// Disable automatic pageview capture, as we capture manually with PostHogProvider
// This ensures proper pageview tracking with Next.js client-side navigation
capture_pageview: "history_change",
// Enable session recording
capture_pageleave: true,
before_send: (event) => {
if (event.properties) {
@ -21,17 +18,20 @@ if (process.env.NEXT_PUBLIC_POSTHOG_KEY) {
}
return event;
},
loaded: (posthog) => {
// Expose PostHog to window for console access and toolbar
loaded: (ph) => {
if (typeof window !== "undefined") {
window.posthog = posthog;
window.posthog = ph;
}
},
});
}
// Always expose posthog to window for debugging/toolbar access
// This allows testing feature flags even without POSTHOG_KEY configured
if (typeof window !== "undefined") {
window.posthog = posthog;
if ("requestIdleCallback" in window) {
requestIdleCallback(initPostHog);
} else {
setTimeout(initPostHog, 3500);
}
}