"use client";
import { ChevronDown, Download, Monitor } from "lucide-react";
import { AnimatePresence, motion } from "motion/react";
import Link from "next/link";
import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import Balancer from "react-wrap-balancer";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { ExpandedMediaOverlay, useExpandedMedia } from "@/components/ui/expanded-gif-overlay";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { AUTH_TYPE, BACKEND_URL } from "@/lib/env-config";
import { trackLoginAttempt } from "@/lib/posthog/events";
import { cn } from "@/lib/utils";
const GoogleLogo = ({ className }: { className?: string }) => (
);
const TAB_ITEMS = [
{
title: "General Assist",
description: "Launch SurfSense instantly from any application.",
src: "/homepage/hero_tutorial/general_assist.mp4",
featured: true,
},
{
title: "Quick Assist",
description: "Select text anywhere, then ask AI to explain, rewrite, or act on it.",
src: "/homepage/hero_tutorial/quick_assist.mp4",
featured: true,
},
{
title: "Extreme Assist",
description: "Get inline writing suggestions powered by your knowledge base as you type in any app.",
src: "/homepage/hero_tutorial/extreme_assist.mp4",
featured: true,
},
{
title: "Watch Local Folder",
description: "Watch a local folder and automatically sync file changes to your knowledge base. Works great with Obsidian vaults.",
src: "/homepage/hero_tutorial/folder_watch.mp4",
featured: true,
},
// {
// title: "Connect & Sync",
// description:
// "Connect data sources like Notion, Drive and Gmail. Automatically sync to keep them updated.",
// src: "/homepage/hero_tutorial/ConnectorFlowGif.mp4",
// featured: true,
// },
// {
// title: "Upload Documents",
// description: "Upload documents directly, from images to massive PDFs.",
// src: "/homepage/hero_tutorial/DocUploadGif.mp4",
// featured: true,
// },
{
title: "Video & Presentations",
description: "Create short videos and editable presentations with AI-generated visuals and narration from your sources.",
src: "/homepage/hero_tutorial/video_gen_surf.mp4",
featured: false,
},
{
title: "Search & Citation",
description: "Ask questions and get cited responses from your knowledge base.",
src: "/homepage/hero_tutorial/BSNCGif.mp4",
featured: false,
},
{
title: "Document Q&A",
description: "Mention specific documents in chat for targeted answers.",
src: "/homepage/hero_tutorial/BQnaGif_compressed.mp4",
featured: false,
},
{
title: "Reports",
description: "Generate reports from your sources in many formats.",
src: "/homepage/hero_tutorial/ReportGenGif_compressed.mp4",
featured: false,
},
{
title: "Podcasts",
description: "Turn anything into a podcast in under 20 seconds.",
src: "/homepage/hero_tutorial/PodcastGenGif.mp4",
featured: false,
},
{
title: "Image Generation",
description: "Generate high-quality images easily from your conversations.",
src: "/homepage/hero_tutorial/ImageGenGif.mp4",
featured: false,
},
{
title: "Collaborative Chat",
description: "Collaborate on AI-powered conversations in realtime with your team.",
src: "/homepage/hero_realtime/RealTimeChatGif.mp4",
featured: false,
},
{
title: "Comments",
description: "Add comments and tag teammates on any message.",
src: "/homepage/hero_realtime/RealTimeCommentsFlow.mp4",
featured: false,
},
] as const;
export function HeroSection() {
return (
NotebookLM for Teams
An open source, privacy focused alternative to NotebookLM for teams with no data
limits.
);
}
function GetStartedButton() {
const isGoogleAuth = AUTH_TYPE === "GOOGLE";
const handleGoogleLogin = () => {
trackLoginAttempt("google");
window.location.href = `${BACKEND_URL}/auth/google/authorize-redirect`;
};
if (isGoogleAuth) {
return (
);
}
return (
Get Started
);
}
type OSInfo = {
os: "macOS" | "Windows" | "Linux";
arch: "arm64" | "x64";
};
function useUserOS(): OSInfo {
const [info, setInfo] = useState({ os: "macOS", arch: "arm64" });
useEffect(() => {
const ua = navigator.userAgent;
let os: OSInfo["os"] = "macOS";
let arch: OSInfo["arch"] = "x64";
if (/Windows/i.test(ua)) {
os = "Windows";
arch = "x64";
} else if (/Linux/i.test(ua)) {
os = "Linux";
arch = "x64";
} else {
os = "macOS";
arch = /Mac/.test(ua) && !/Intel/.test(ua) ? "arm64" : "arm64";
}
const uaData = (navigator as Navigator & { userAgentData?: { architecture?: string } })
.userAgentData;
if (uaData?.architecture === "arm") arch = "arm64";
else if (uaData?.architecture === "x86") arch = "x64";
setInfo({ os, arch });
}, []);
return info;
}
interface ReleaseAsset {
name: string;
url: string;
}
function useLatestRelease() {
const [assets, setAssets] = useState([]);
useEffect(() => {
const controller = new AbortController();
fetch("https://api.github.com/repos/MODSetter/SurfSense/releases/latest", {
signal: controller.signal,
})
.then((r) => r.json())
.then((data) => {
if (data?.assets) {
setAssets(
data.assets
.filter((a: { name: string }) => /\.(exe|dmg|AppImage|deb)$/.test(a.name))
.map((a: { name: string; browser_download_url: string }) => ({
name: a.name,
url: a.browser_download_url,
}))
);
}
})
.catch(() => {});
return () => controller.abort();
}, []);
return assets;
}
const ASSET_LABELS: Record = {
".exe": "Windows (exe)",
"-arm64.dmg": "macOS Apple Silicon (dmg)",
"-x64.dmg": "macOS Intel (dmg)",
"-arm64.zip": "macOS Apple Silicon (zip)",
"-x64.zip": "macOS Intel (zip)",
".AppImage": "Linux (AppImage)",
".deb": "Linux (deb)",
};
function getAssetLabel(name: string): string {
for (const [suffix, label] of Object.entries(ASSET_LABELS)) {
if (name.endsWith(suffix)) return label;
}
return name;
}
function DownloadButton() {
const { os, arch } = useUserOS();
const assets = useLatestRelease();
const { primary, alternatives } = useMemo(() => {
if (assets.length === 0) return { primary: null, alternatives: [] };
const matchers: Record boolean> = {
Windows: (n) => n.endsWith(".exe"),
macOS: (n) => n.endsWith(`-${arch}.dmg`),
Linux: (n) => n.endsWith(".AppImage"),
};
const match = matchers[os];
const primary = assets.find((a) => match(a.name)) ?? null;
const alternatives = assets.filter((a) => a !== primary);
return { primary, alternatives };
}, [assets, os, arch]);
const fallbackUrl = GITHUB_RELEASES_URL;
if (!primary) {
return (
Download for {os}
);
}
return (
);
}
const BrowserWindow = () => {
const [selectedIndex, setSelectedIndex] = useState(0);
const selectedItem = TAB_ITEMS[selectedIndex];
const { expanded, open, close } = useExpandedMedia();
return (
<>
{TAB_ITEMS.map((item, index) => (
{index !== TAB_ITEMS.length - 1 && (
)}
))}
{selectedItem.title}
{selectedItem.description}
{expanded && (
)}
>
);
};
const TabVideo = memo(function TabVideo({ src }: { src: string }) {
const videoRef = useRef(null);
const [hasLoaded, setHasLoaded] = useState(false);
useEffect(() => {
setHasLoaded(false);
const video = videoRef.current;
if (!video) return;
video.currentTime = 0;
video.play().catch(() => {});
}, [src]);
const handleCanPlay = useCallback(() => {
setHasLoaded(true);
}, []);
return (
);
});
const GITHUB_RELEASES_URL = "https://github.com/MODSetter/SurfSense/releases/latest";