diff --git a/surfsense_web/app/(home)/contact/page.tsx b/surfsense_web/app/(home)/contact/page.tsx new file mode 100644 index 000000000..44a6e6d74 --- /dev/null +++ b/surfsense_web/app/(home)/contact/page.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import { ContactFormGridWithDetails } from "@/components/contact/contact-form"; + +const page = () => { + return ( +
+ +
+ ); +}; + +export default page; diff --git a/surfsense_web/app/(home)/layout.tsx b/surfsense_web/app/(home)/layout.tsx new file mode 100644 index 000000000..06832cfc4 --- /dev/null +++ b/surfsense_web/app/(home)/layout.tsx @@ -0,0 +1,14 @@ +"use client"; + +import { Footer } from "@/components/homepage/footer"; +import { Navbar } from "@/components/homepage/navbar"; + +export default function HomePageLayout({ children }: { children: React.ReactNode }) { + return ( +
+ + {children} +
+ ); +} diff --git a/surfsense_web/app/login/AmbientBackground.tsx b/surfsense_web/app/(home)/login/AmbientBackground.tsx similarity index 100% rename from surfsense_web/app/login/AmbientBackground.tsx rename to surfsense_web/app/(home)/login/AmbientBackground.tsx diff --git a/surfsense_web/app/login/GoogleLoginButton.tsx b/surfsense_web/app/(home)/login/GoogleLoginButton.tsx similarity index 100% rename from surfsense_web/app/login/GoogleLoginButton.tsx rename to surfsense_web/app/(home)/login/GoogleLoginButton.tsx diff --git a/surfsense_web/app/login/LocalLoginForm.tsx b/surfsense_web/app/(home)/login/LocalLoginForm.tsx similarity index 100% rename from surfsense_web/app/login/LocalLoginForm.tsx rename to surfsense_web/app/(home)/login/LocalLoginForm.tsx diff --git a/surfsense_web/app/login/page.tsx b/surfsense_web/app/(home)/login/page.tsx similarity index 100% rename from surfsense_web/app/login/page.tsx rename to surfsense_web/app/(home)/login/page.tsx index 59ff16ee3..c3f5501a6 100644 --- a/surfsense_web/app/login/page.tsx +++ b/surfsense_web/app/(home)/login/page.tsx @@ -1,7 +1,7 @@ "use client"; -import { AnimatePresence, motion } from "motion/react"; import { Loader2 } from "lucide-react"; +import { AnimatePresence, motion } from "motion/react"; import { useSearchParams } from "next/navigation"; import { Suspense, useEffect, useState } from "react"; import { toast } from "sonner"; diff --git a/surfsense_web/app/page.tsx b/surfsense_web/app/(home)/page.tsx similarity index 50% rename from surfsense_web/app/page.tsx rename to surfsense_web/app/(home)/page.tsx index dfa0e7587..8f85774ac 100644 --- a/surfsense_web/app/page.tsx +++ b/surfsense_web/app/(home)/page.tsx @@ -1,19 +1,21 @@ "use client"; -import { Footer } from "@/components/Footer"; import { CTAHomepage } from "@/components/homepage/cta"; import { FeaturesBentoGrid } from "@/components/homepage/features-bento-grid"; -import { ModernHeroWithGradients } from "@/components/homepage/ModernHeroWithGradients"; -import { Navbar } from "@/components/Navbar"; +import { FeaturesCards } from "@/components/homepage/features-card"; +import { Footer } from "@/components/homepage/footer"; +import { HeroSection } from "@/components/homepage/hero-section"; +import ExternalIntegrations from "@/components/homepage/integrations"; +import { Navbar } from "@/components/homepage/navbar"; export default function HomePage() { return (
- - + + + -
); } diff --git a/surfsense_web/app/(home)/pricing/page.tsx b/surfsense_web/app/(home)/pricing/page.tsx new file mode 100644 index 000000000..04837a578 --- /dev/null +++ b/surfsense_web/app/(home)/pricing/page.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import PricingBasic from "@/components/pricing/pricing-section"; + +const page = () => { + return ( +
+ +
+ ); +}; + +export default page; diff --git a/surfsense_web/app/privacy/page.tsx b/surfsense_web/app/(home)/privacy/page.tsx similarity index 100% rename from surfsense_web/app/privacy/page.tsx rename to surfsense_web/app/(home)/privacy/page.tsx diff --git a/surfsense_web/app/register/page.tsx b/surfsense_web/app/(home)/register/page.tsx similarity index 100% rename from surfsense_web/app/register/page.tsx rename to surfsense_web/app/(home)/register/page.tsx diff --git a/surfsense_web/app/terms/page.tsx b/surfsense_web/app/(home)/terms/page.tsx similarity index 100% rename from surfsense_web/app/terms/page.tsx rename to surfsense_web/app/(home)/terms/page.tsx diff --git a/surfsense_web/app/contact/page.tsx b/surfsense_web/app/contact/page.tsx deleted file mode 100644 index cb722ff77..000000000 --- a/surfsense_web/app/contact/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react' -import { ContactFormGridWithDetails } from '@/components/contact/contact-form' - -const page = () => { - return ( -
- -
- ) -} - -export default page \ No newline at end of file diff --git a/surfsense_web/app/pricing/page.tsx b/surfsense_web/app/pricing/page.tsx deleted file mode 100644 index 6d71d6ff4..000000000 --- a/surfsense_web/app/pricing/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react' -import PricingBasic from '@/components/pricing/pricing-section' - -const page = () => { - return ( -
- -
- ) -} - -export default page \ No newline at end of file diff --git a/surfsense_web/components/Navbar.tsx b/surfsense_web/components/Navbar.tsx deleted file mode 100644 index eb67831c5..000000000 --- a/surfsense_web/components/Navbar.tsx +++ /dev/null @@ -1,305 +0,0 @@ -"use client"; -import { IconMail, IconMenu2, IconUser, IconUserPlus, IconX } from "@tabler/icons-react"; -import { AnimatePresence, motion, useMotionValueEvent, useScroll } from "motion/react"; -import Link from "next/link"; -import { useRef, useState } from "react"; -import { cn } from "@/lib/utils"; -import { Logo } from "./Logo"; -import { ThemeTogglerComponent } from "./theme/theme-toggle"; -import { Button } from "./ui/button"; - -interface NavbarProps { - navItems: { - name: string; - link: string; - }[]; - visible: boolean; -} - -export const Navbar = () => { - const navItems = [ - { - name: "Docs", - link: "/docs", - }, - // { - // name: "Product", - // link: "/#product", - // }, - // { - // name: "Pricing", - // link: "/#pricing", - // }, - ]; - - const ref = useRef(null); - const { scrollY } = useScroll({ - target: ref, - offset: ["start start", "end start"], - }); - const [visible, setVisible] = useState(false); - - useMotionValueEvent(scrollY, "change", (latest) => { - if (latest > 100) { - setVisible(true); - } else { - setVisible(false); - } - }); - - return ( - - - - - ); -}; - -const DesktopNav = ({ navItems, visible }: NavbarProps) => { - const [hoveredIndex, setHoveredIndex] = useState(null); - - const handleGoogleLogin = () => { - // Redirect to the login page - window.location.href = "/login"; - }; - - return ( - setHoveredIndex(null)} - animate={{ - backdropFilter: "blur(16px)", - background: visible - ? "rgba(var(--background-rgb), 0.8)" - : "rgba(var(--background-rgb), 0.6)", - width: visible ? "38%" : "80%", - height: visible ? "48px" : "64px", - y: visible ? 8 : 0, - }} - initial={{ - width: "80%", - height: "64px", - background: "rgba(var(--background-rgb), 0.6)", - }} - transition={{ - type: "spring", - stiffness: 400, - damping: 30, - }} - className={cn( - "hidden lg:flex flex-row self-center items-center justify-between py-2 mx-auto px-6 rounded-full relative z-[60] backdrop-saturate-[1.8]", - visible ? "border dark:border-white/10 border-gray-300/30" : "border-0" - )} - style={ - { - "--background-rgb": "var(--tw-dark) ? '0, 0, 0' : '255, 255, 255'", - } as React.CSSProperties - } - > -
- - SurfSense -
-
- - {navItems.map((navItem, idx) => ( - setHoveredIndex(idx)} - className="relative" - > - - {navItem.name} - {hoveredIndex === idx && ( - - )} - - - ))} - - - - {!visible && ( - - - - - - - )} - -
-
- ); -}; - -const MobileNav = ({ navItems, visible }: NavbarProps) => { - const [open, setOpen] = useState(false); - - const handleGoogleLogin = () => { - // Redirect to the login page - window.location.href = "./login"; - }; - - return ( - -
- -
- - {open ? ( - setOpen(!open)} /> - ) : ( - setOpen(!open)} - /> - )} -
-
- - - {open && ( - - {navItems.map((navItem: { link: string; name: string }) => ( - setOpen(false)} - className="relative dark:text-white/90 text-gray-800 hover:text-gray-900 dark:hover:text-white transition-colors" - > - {navItem.name} - - ))} - setOpen(false)}> - - - - - )} - -
- ); -}; diff --git a/surfsense_web/components/homepage/ModernHeroWithGradients.tsx b/surfsense_web/components/homepage/ModernHeroWithGradients.tsx deleted file mode 100644 index 0f95c09d0..000000000 --- a/surfsense_web/components/homepage/ModernHeroWithGradients.tsx +++ /dev/null @@ -1,549 +0,0 @@ -"use client"; -import { IconBrandDiscord, IconBrandGithub, IconFileTypeDoc, IconMail, IconUserPlus } from "@tabler/icons-react"; -import Image from "next/image"; -import Link from "next/link"; -import { cn } from "@/lib/utils"; -import { Logo } from "../Logo"; -import { Features } from "./features-card"; - -export function ModernHeroWithGradients() { - return ( -
-
-
- - - - - - - -
-
- - MODSetter%2FSurfSense | Trendshift - -
- - - Documentation - - {/* Import the Logo component or define it in this file */} -
-
- -
-

- SurfSense -

-
-

- Your all-in-one AI research workspace. -

-
- - - Sign Up - - - - Discord - - - - GitHub - -
-
- -
-
-
- ); -} - -const TopLines = () => { - return ( - - Top Lines - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -const BottomLines = () => { - return ( - - Bottom Lines - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -const SideLines = () => { - return ( - - Side Lines - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -const BottomGradient = ({ className }: { className?: string }) => { - return ( - - Bottom Gradient - - - - - - - - - - - ); -}; - -const TopGradient = ({ className }: { className?: string }) => { - return ( - - Top Gradient - - - - - - - - - - - - - - - - - - ); -}; - -const DarkModeGradient = () => { - return ( -
-
-
-
-
- ); -}; diff --git a/surfsense_web/components/homepage/cta.tsx b/surfsense_web/components/homepage/cta.tsx index 1176c69df..a09d7354c 100644 --- a/surfsense_web/components/homepage/cta.tsx +++ b/surfsense_web/components/homepage/cta.tsx @@ -6,7 +6,7 @@ import { cn } from "@/lib/utils"; export function CTAHomepage() { return ( -
+
diff --git a/surfsense_web/components/homepage/features-card.tsx b/surfsense_web/components/homepage/features-card.tsx index 78c4cccc1..79326198f 100644 --- a/surfsense_web/components/homepage/features-card.tsx +++ b/surfsense_web/components/homepage/features-card.tsx @@ -2,42 +2,33 @@ import { Sliders, Users, Workflow } from "lucide-react"; import type { ReactNode } from "react"; import { Card, CardContent, CardHeader } from "@/components/ui/card"; -export function Features() { +export function FeaturesCards() { return (
-
- {/*
-

Built to cover your needs

-

Libero sapiente aliquam quibusdam aspernatur, praesentium iusto repellendus.

-
*/} +
+
+

+ Everything Your Team Needs to Succeed +

+

+ Powerful features designed to enhance collaboration, boost productivity, and streamline + your workflow. +

+
- - - - - - -

Customizable

-
- - -

Customize your research agent to your specific needs.

-
-
- -

Streamline your workflow

+

Streamlined Workflow

- Pull all your knowledge into one place, so you can find what matters and get things - done faster. + Centralize all your knowledge and resources in one intelligent workspace. Find what + you need instantly and accelerate decision-making.

@@ -52,7 +43,26 @@ export function Features() { -

Make your company and personal content collaborative.

+

+ Work together effortlessly with real-time collaboration tools that keep your entire + team aligned. +

+
+ + + + + + + + +

Fully Customizable

+
+ + +

+ Choose from 100+ leading LLMs and seamlessly call any model on demand. +

diff --git a/surfsense_web/components/Footer.tsx b/surfsense_web/components/homepage/footer.tsx similarity index 97% rename from surfsense_web/components/Footer.tsx rename to surfsense_web/components/homepage/footer.tsx index 074e90686..88e640e81 100644 --- a/surfsense_web/components/Footer.tsx +++ b/surfsense_web/components/homepage/footer.tsx @@ -22,7 +22,7 @@ export function Footer() { ]; return ( -
+
diff --git a/surfsense_web/components/homepage/hero-section.tsx b/surfsense_web/components/homepage/hero-section.tsx new file mode 100644 index 000000000..147502afe --- /dev/null +++ b/surfsense_web/components/homepage/hero-section.tsx @@ -0,0 +1,318 @@ +"use client"; +import { AnimatePresence, motion } from "motion/react"; +import Image from "next/image"; +import Link from "next/link"; +import React, { useEffect, useRef, useState } from "react"; +import Balancer from "react-wrap-balancer"; +import { cn } from "@/lib/utils"; + +export function HeroSection() { + const containerRef = useRef(null); + const parentRef = useRef(null); + + return ( +
+ + + + + + +

+ + The AI Workspace{" "} +
+
+ Built for Teams +
+
+
+

+ {/* // TODO:aCTUAL DESCRITION */} +

+ Your multi-collaborative AI workspace, connected to external sources. +

+
+ + Start Free Trial + + + Explore + +
+
+
+ {/* Light mode image */} + header + {/* Dark mode image */} + header +
+
+
+ ); +} + +const BackgroundGrids = () => { + return ( +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ ); +}; + +const CollisionMechanism = React.forwardRef< + HTMLDivElement, + { + containerRef: React.RefObject; + parentRef: React.RefObject; + beamOptions?: { + initialX?: number; + translateX?: number; + initialY?: number; + translateY?: number; + rotate?: number; + className?: string; + duration?: number; + delay?: number; + repeatDelay?: number; + }; + } +>(({ parentRef, containerRef, beamOptions = {} }, ref) => { + const beamRef = useRef(null); + const [collision, setCollision] = useState<{ + detected: boolean; + coordinates: { x: number; y: number } | null; + }>({ detected: false, coordinates: null }); + const [beamKey, setBeamKey] = useState(0); + const [cycleCollisionDetected, setCycleCollisionDetected] = useState(false); + + useEffect(() => { + const checkCollision = () => { + if (beamRef.current && containerRef.current && parentRef.current && !cycleCollisionDetected) { + const beamRect = beamRef.current.getBoundingClientRect(); + const containerRect = containerRef.current.getBoundingClientRect(); + const parentRect = parentRef.current.getBoundingClientRect(); + + if (beamRect.bottom >= containerRect.top) { + const relativeX = beamRect.left - parentRect.left + beamRect.width / 2; + const relativeY = beamRect.bottom - parentRect.top; + + setCollision({ + detected: true, + coordinates: { x: relativeX, y: relativeY }, + }); + setCycleCollisionDetected(true); + if (beamRef.current) { + beamRef.current.style.opacity = "0"; + } + } + } + }; + + const animationInterval = setInterval(checkCollision, 50); + + return () => clearInterval(animationInterval); + }, [cycleCollisionDetected, containerRef]); + + useEffect(() => { + if (collision.detected && collision.coordinates) { + setTimeout(() => { + setCollision({ detected: false, coordinates: null }); + setCycleCollisionDetected(false); + // Set beam opacity to 0 + if (beamRef.current) { + beamRef.current.style.opacity = "1"; + } + }, 2000); + + // Reset the beam animation after a delay + setTimeout(() => { + setBeamKey((prevKey) => prevKey + 1); + }, 2000); + } + }, [collision]); + + return ( + <> + + + {collision.detected && collision.coordinates && ( + + )} + + + ); +}); + +CollisionMechanism.displayName = "CollisionMechanism"; + +const Explosion = ({ ...props }: React.HTMLProps) => { + const spans = Array.from({ length: 20 }, (_, index) => ({ + id: index, + initialX: 0, + initialY: 0, + directionX: Math.floor(Math.random() * 80 - 40), + directionY: Math.floor(Math.random() * -50 - 10), + })); + + return ( +
+ + {spans.map((span) => ( + + ))} +
+ ); +}; + +const GridLineVertical = ({ className, offset }: { className?: string; offset?: string }) => { + return ( +
+ ); +}; diff --git a/surfsense_web/components/homepage/integrations.tsx b/surfsense_web/components/homepage/integrations.tsx new file mode 100644 index 000000000..47d4e7325 --- /dev/null +++ b/surfsense_web/components/homepage/integrations.tsx @@ -0,0 +1,185 @@ +"use client"; +import React, { useEffect, useState } from "react"; + +interface Integration { + name: string; + icon: string; +} + +const INTEGRATIONS: Integration[] = [ + // Search + { name: "Tavily", icon: "https://www.tavily.com/images/logo.svg" }, + { + name: "LinkUp", + icon: "https://framerusercontent.com/images/7zeIm6t3f1HaSltkw8upEvsD80.png?scale-down-to=512", + }, + + // Communication + { name: "Slack", icon: "https://cdn.simpleicons.org/slack/4A154B" }, + { name: "Discord", icon: "https://cdn.simpleicons.org/discord/5865F2" }, + { name: "Gmail", icon: "https://cdn.simpleicons.org/gmail/EA4335" }, + + // Project Management + { name: "Linear", icon: "https://cdn.simpleicons.org/linear/5E6AD2" }, + { name: "Jira", icon: "https://cdn.simpleicons.org/jira/0052CC" }, + { name: "ClickUp", icon: "https://cdn.simpleicons.org/clickup/7B68EE" }, + { name: "Airtable", icon: "https://cdn.simpleicons.org/airtable/18BFFF" }, + + // Documentation & Knowledge + { name: "Confluence", icon: "https://cdn.simpleicons.org/confluence/172B4D" }, + { name: "Notion", icon: "https://cdn.simpleicons.org/notion/000000/ffffff" }, + + // Cloud Storage + { name: "Google Drive", icon: "https://cdn.simpleicons.org/googledrive/4285F4" }, + { name: "Dropbox", icon: "https://cdn.simpleicons.org/dropbox/0061FF" }, + { + name: "Amazon S3", + icon: "https://upload.wikimedia.org/wikipedia/commons/b/bc/Amazon-S3-Logo.svg", + }, + + // Development + { name: "GitHub", icon: "https://cdn.simpleicons.org/github/181717/ffffff" }, + + // Productivity + { name: "Google Calendar", icon: "https://cdn.simpleicons.org/googlecalendar/4285F4" }, + { name: "Luma", icon: "https://images.lumacdn.com/social-images/default-social-202407.png" }, + + // Media + { name: "YouTube", icon: "https://cdn.simpleicons.org/youtube/FF0000" }, +]; + +function SemiCircleOrbit({ radius, centerX, centerY, count, iconSize, startIndex }: any) { + return ( + <> + {/* Semi-circle glow background */} +
+
+
+ + {/* Orbit icons */} + {Array.from({ length: count }).map((_, index) => { + const actualIndex = startIndex + index; + // Skip if we've run out of integrations + if (actualIndex >= INTEGRATIONS.length) return null; + + const angle = (index / (count - 1)) * 180; + const x = radius * Math.cos((angle * Math.PI) / 180); + const y = radius * Math.sin((angle * Math.PI) / 180); + const integration = INTEGRATIONS[actualIndex]; + + // Tooltip positioning — above or below based on angle + const tooltipAbove = angle > 90; + + return ( +
+ {integration.name} + + {/* Tooltip */} + +
+ ); + })} + + ); +} + +export default function ExternalIntegrations() { + const [size, setSize] = useState({ width: 0, height: 0 }); + + useEffect(() => { + const updateSize = () => setSize({ width: window.innerWidth, height: window.innerHeight }); + updateSize(); + window.addEventListener("resize", updateSize); + return () => window.removeEventListener("resize", updateSize); + }, []); + + const baseWidth = Math.min(size.width * 0.8, 700); + const centerX = baseWidth / 2; + const centerY = baseWidth * 0.5; + + const iconSize = + size.width < 480 + ? Math.max(24, baseWidth * 0.05) + : size.width < 768 + ? Math.max(28, baseWidth * 0.06) + : Math.max(32, baseWidth * 0.07); + + return ( +
+
+

Integrations

+

+ Connect your favourite apps to your workflow. +

+ +
+ + + +
+
+
+ ); +} diff --git a/surfsense_web/components/homepage/navbar.tsx b/surfsense_web/components/homepage/navbar.tsx new file mode 100644 index 000000000..731db4469 --- /dev/null +++ b/surfsense_web/components/homepage/navbar.tsx @@ -0,0 +1,177 @@ +"use client"; +import { IconBrandDiscord, IconBrandGithub, IconMenu2, IconX } from "@tabler/icons-react"; +import { AnimatePresence, motion } from "motion/react"; +import Link from "next/link"; +import React, { useEffect, useState } from "react"; +import { Logo } from "@/components/Logo"; +import { ThemeTogglerComponent } from "@/components/theme/theme-toggle"; +import { cn } from "@/lib/utils"; + +export const Navbar = () => { + const [isScrolled, setIsScrolled] = useState(false); + + const navItems = [ + { name: "Home", link: "/" }, + { name: "Pricing", link: "/pricing" }, + { name: "Sign In", link: "/login" }, + { name: "Docs", link: "/docs" }, + ]; + + useEffect(() => { + const handleScroll = () => { + setIsScrolled(window.scrollY > 20); + }; + + window.addEventListener("scroll", handleScroll); + return () => window.removeEventListener("scroll", handleScroll); + }, []); + + return ( +
+ + +
+ ); +}; + +const DesktopNav = ({ navItems, isScrolled }: any) => { + const [hovered, setHovered] = useState(null); + return ( + { + setHovered(null); + }} + className={cn( + "mx-auto hidden w-full max-w-7xl flex-row items-center justify-between self-start rounded-full px-4 py-2 lg:flex transition-all duration-300", + isScrolled + ? "bg-white/80 backdrop-blur-md border border-white/20 shadow-lg dark:bg-neutral-950/80 dark:border-neutral-800/50" + : "bg-transparent border border-transparent" + )} + > +
+ + SurfSense +
+
+ {navItems.map((navItem: any, idx: number) => ( + setHovered(idx)} + className="relative px-4 py-2 text-neutral-600 dark:text-neutral-300" + key={`link=${idx}`} + href={navItem.link} + > + {hovered === idx && ( + + )} + {navItem.name} + + ))} +
+
+ + + + + + 8.3k + + + + Book a call + +
+
+ ); +}; + +const MobileNav = ({ navItems, isScrolled }: any) => { + const [open, setOpen] = useState(false); + + return ( + <> + +
+
+ + SurfSense +
+ {open ? ( + setOpen(!open)} /> + ) : ( + setOpen(!open)} /> + )} +
+ + + {open && ( + + {navItems.map((navItem: any, idx: number) => ( + + {navItem.name} + + ))} +
+ + + + + + + 8.3k + + +
+ +
+ )} +
+
+ + ); +}; diff --git a/surfsense_web/components/pricing.tsx b/surfsense_web/components/pricing.tsx index b666c8982..083ad0d8e 100644 --- a/surfsense_web/components/pricing.tsx +++ b/surfsense_web/components/pricing.tsx @@ -91,42 +91,53 @@ export function Pricing({
-
- {plans.map((plan, index) => ( - + {plans.map((plan, index) => ( + + plans.length === 3 && index === 0 && "origin-right", + plans.length === 3 && index === 2 && "origin-left" + )} + > {plan.isPopular && (
@@ -136,9 +147,12 @@ export function Pricing({
)}
-

{plan.name}

-
- +

{plan.name}

+
+ + {isNaN(Number(plan.price)) ? ( + {isMonthly ? plan.price : plan.yearlyPrice} + ) : ( - - {plan.period !== "Next 3 months" && ( - - / {plan.period} - )} -
+
+ {plan.period && plan.period !== "Next 3 months" && ( + + / {plan.period} + + )} +
-

- {isMonthly ? "billed monthly" : "billed annually"} -

+

+ {isNaN(Number(plan.price)) ? "" : isMonthly ? "billed monthly" : "billed annually"} +

    {plan.features.map((feature, idx) => ( diff --git a/surfsense_web/components/pricing/pricing-section.tsx b/surfsense_web/components/pricing/pricing-section.tsx index c017eb1e7..5a0341359 100644 --- a/surfsense_web/components/pricing/pricing-section.tsx +++ b/surfsense_web/components/pricing/pricing-section.tsx @@ -4,55 +4,38 @@ import { Pricing } from "@/components/pricing"; const demoPlans = [ { - name: "STARTER", - price: "50", - yearlyPrice: "40", - period: "per month", + name: "COMMUNITY", + price: "0", + yearlyPrice: "0", + period: "forever", features: [ - "Up to 10 projects", - "Basic analytics", - "48-hour support response time", - "Limited API access", - "Community support", + "Supports 100+ LLMs", + "Supports local Ollama or vLLM setups", + "6000+ Embedding Models", + "50+ File extensions supported.", + "Podcasts support with local TTS providers.", + "Connects with 15+ external sources.", + "Cross-Browser Extension for dynamic webpages including authenticated content", + "Upcoming: Mergeable MindMaps", + "Upcoming: Note Management", ], - description: "Perfect for individuals and small projects", - buttonText: "Start Free Trial", - href: "/sign-up", - isPopular: false, - }, - { - name: "PROFESSIONAL", - price: "99", - yearlyPrice: "79", - period: "per month", - features: [ - "Unlimited projects", - "Advanced analytics", - "24-hour support response time", - "Full API access", - "Priority support", - "Team collaboration", - "Custom integrations", - ], - description: "Ideal for growing teams and businesses", + description: "Open source version with powerful features", buttonText: "Get Started", - href: "/sign-up", + href: "/docs", isPopular: true, }, { name: "ENTERPRISE", - price: "299", - yearlyPrice: "239", - period: "per month", + price: "Contact Us", + yearlyPrice: "Contact Us", + period: "", features: [ - "Everything in Professional", - "Custom solutions", - "Dedicated account manager", - "1-hour support response time", - "SSO Authentication", - "Advanced security", - "Custom contracts", - "SLA agreement", + "Everything in Community", + "Priority Support", + "Access Controls", + "Multicollaborative and multiplayer features", + "Video generation", + "Advanced security features", ], description: "For large organizations with specific needs", buttonText: "Contact Sales", @@ -63,11 +46,7 @@ const demoPlans = [ function PricingBasic() { return ( - + ); } diff --git a/surfsense_web/package.json b/surfsense_web/package.json index 517e7690d..f65904784 100644 --- a/surfsense_web/package.json +++ b/surfsense_web/package.json @@ -74,6 +74,7 @@ "react-markdown": "^10.1.0", "react-rough-notation": "^1.0.5", "react-syntax-highlighter": "^15.6.1", + "react-wrap-balancer": "^1.1.1", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", diff --git a/surfsense_web/pnpm-lock.yaml b/surfsense_web/pnpm-lock.yaml index bfda2d29d..3463929ec 100644 --- a/surfsense_web/pnpm-lock.yaml +++ b/surfsense_web/pnpm-lock.yaml @@ -173,6 +173,9 @@ importers: react-syntax-highlighter: specifier: ^15.6.1 version: 15.6.1(react@19.1.0) + react-wrap-balancer: + specifier: ^1.1.1 + version: 1.1.1(react@19.1.0) rehype-raw: specifier: ^7.0.0 version: 7.0.0 @@ -4797,6 +4800,11 @@ packages: react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-wrap-balancer@1.1.1: + resolution: {integrity: sha512-AB+l7FPRWl6uZ28VcJ8skkwLn2+UC62bjiw8tQUrZPlEWDVnR9MG0lghyn7EyxuJSsFEpht4G+yh2WikEqQ/5Q==} + peerDependencies: + react: '>=16.8.0 || ^17.0.0 || ^18' + react@19.1.0: resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} engines: {node: '>=0.10.0'} @@ -11108,6 +11116,10 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + react-wrap-balancer@1.1.1(react@19.1.0): + dependencies: + react: 19.1.0 + react@19.1.0: {} readable-stream@3.6.2: diff --git a/surfsense_web/public/homepage/temp_hero_dark.png b/surfsense_web/public/homepage/temp_hero_dark.png new file mode 100644 index 000000000..92f4284a7 Binary files /dev/null and b/surfsense_web/public/homepage/temp_hero_dark.png differ diff --git a/surfsense_web/public/homepage/temp_hero_light.png b/surfsense_web/public/homepage/temp_hero_light.png new file mode 100644 index 000000000..d7a0734cf Binary files /dev/null and b/surfsense_web/public/homepage/temp_hero_light.png differ