mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 00:36:31 +02:00
feat: frontpage
This commit is contained in:
parent
9895a20176
commit
3adabe7a16
7 changed files with 696 additions and 3 deletions
|
|
@ -1,7 +1,9 @@
|
|||
"use client";
|
||||
|
||||
import { Footer } from "@/components/Footer";
|
||||
import { ModernHeroWithGradients } from "@/components/ModernHeroWithGradients";
|
||||
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";
|
||||
|
||||
export default function HomePage() {
|
||||
|
|
@ -9,6 +11,8 @@ export default function HomePage() {
|
|||
<main className="min-h-screen bg-gradient-to-b from-gray-50 to-gray-100 text-gray-900 dark:from-black dark:to-gray-900 dark:text-white">
|
||||
<Navbar />
|
||||
<ModernHeroWithGradients />
|
||||
<FeaturesBentoGrid />
|
||||
<CTAHomepage />
|
||||
<Footer />
|
||||
</main>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
"use client";
|
||||
import { IconBrandDiscord, IconBrandGithub, IconFileTypeDoc, IconMail } from "@tabler/icons-react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Logo } from "./Logo";
|
||||
import { Logo } from "../Logo";
|
||||
import { Features } from "./features-card";
|
||||
|
||||
export function ModernHeroWithGradients() {
|
||||
return (
|
||||
<div className="relative h-full min-h-[50rem] w-full bg-gray-50 dark:bg-black">
|
||||
<div className="relative h-full min-h-[50rem] w-full">
|
||||
<div className="relative z-20 mx-auto w-full px-4 py-6 md:px-8 lg:px-4">
|
||||
<div className="relative my-12 overflow-hidden rounded-3xl bg-white py-16 shadow-sm dark:bg-gray-900/80 dark:shadow-lg dark:shadow-purple-900/10 md:py-48 mx-auto w-full max-w-[95%] xl:max-w-[98%]">
|
||||
<TopLines />
|
||||
|
|
@ -77,6 +79,7 @@ export function ModernHeroWithGradients() {
|
|||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<Features />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
110
surfsense_web/components/homepage/cta.tsx
Normal file
110
surfsense_web/components/homepage/cta.tsx
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
"use client";
|
||||
import { IconMessageCircleQuestion } from "@tabler/icons-react";
|
||||
import Link from "next/link";
|
||||
import type React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function CTAHomepage() {
|
||||
return (
|
||||
<section className="w-full grid grid-cols-1 md:grid-cols-3 my-20 md:my-40 justify-start relative z-20 max-w-7xl mx-auto bg-gradient-to-br from-gray-100 to-white dark:from-neutral-900 dark:to-neutral-950">
|
||||
<GridLineHorizontal className="top-0" offset="200px" />
|
||||
<GridLineHorizontal className="bottom-0 top-auto" offset="200px" />
|
||||
<GridLineVertical className="left-0" offset="80px" />
|
||||
<GridLineVertical className="left-auto right-0" offset="80px" />
|
||||
<div className="md:col-span-2 p-8 md:p-14">
|
||||
<h2 className="text-left text-neutral-500 dark:text-neutral-200 text-xl md:text-3xl tracking-tight font-medium">
|
||||
Transform how your team{" "}
|
||||
<span className="font-bold text-black dark:text-white">discovers and collaborates</span>
|
||||
</h2>
|
||||
<p className="text-left text-neutral-500 mt-4 max-w-lg dark:text-neutral-200 text-xl md:text-3xl tracking-tight font-medium">
|
||||
Unite your <span className="text-sky-700">team's knowledge</span> in one collaborative
|
||||
space with <span className="text-indigo-700">intelligent search</span>.
|
||||
</p>
|
||||
|
||||
<div className="flex items-start sm:items-center flex-col sm:flex-row sm:gap-4">
|
||||
<Link href="/contact">
|
||||
<button
|
||||
type="button"
|
||||
className="mt-8 flex space-x-2 items-center group text-base px-4 py-2 rounded-lg text-black dark:text-white border border-neutral-200 dark:border-neutral-800 shadow-[0px_2px_0px_0px_rgba(255,255,255,0.3)_inset]"
|
||||
>
|
||||
<span>Talk to us</span>
|
||||
<IconMessageCircleQuestion className="text-black dark:text-white group-hover:translate-x-1 stroke-[1px] h-3 w-3 mt-0.5 transition-transform duration-200" />
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="border-t md:border-t-0 md:border-l border-dashed p-8 md:p-14">
|
||||
<p className="text-base text-neutral-700 dark:text-neutral-200">
|
||||
"SurfSense has revolutionized how our team shares and discovers knowledge.
|
||||
Everyone can contribute and find what they need instantly. True collaboration at scale."
|
||||
</p>
|
||||
<div className="flex flex-col text-sm items-start mt-4 gap-1">
|
||||
<p className="font-bold text-neutral-800 dark:text-neutral-200">
|
||||
Sarah Chen
|
||||
</p>
|
||||
<p className="text-neutral-500 dark:text-neutral-400">
|
||||
Research Lead
|
||||
</p>
|
||||
</div>
|
||||
</div> */}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const GridLineHorizontal = ({ className, offset }: { className?: string; offset?: string }) => {
|
||||
return (
|
||||
<div
|
||||
style={
|
||||
{
|
||||
"--background": "#ffffff",
|
||||
"--color": "rgba(0, 0, 0, 0.2)",
|
||||
"--height": "1px",
|
||||
"--width": "5px",
|
||||
"--fade-stop": "90%",
|
||||
"--offset": offset || "200px", //-100px if you want to keep the line inside
|
||||
"--color-dark": "rgba(255, 255, 255, 0.2)",
|
||||
maskComposite: "exclude",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={cn(
|
||||
"absolute w-[calc(100%+var(--offset))] h-[var(--height)] left-[calc(var(--offset)/2*-1)]",
|
||||
"bg-[linear-gradient(to_right,var(--color),var(--color)_50%,transparent_0,transparent)]",
|
||||
"[background-size:var(--width)_var(--height)]",
|
||||
"[mask:linear-gradient(to_left,var(--background)_var(--fade-stop),transparent),_linear-gradient(to_right,var(--background)_var(--fade-stop),transparent),_linear-gradient(black,black)]",
|
||||
"[mask-composite:exclude]",
|
||||
"z-30",
|
||||
"dark:bg-[linear-gradient(to_right,var(--color-dark),var(--color-dark)_50%,transparent_0,transparent)]",
|
||||
className
|
||||
)}
|
||||
></div>
|
||||
);
|
||||
};
|
||||
|
||||
const GridLineVertical = ({ className, offset }: { className?: string; offset?: string }) => {
|
||||
return (
|
||||
<div
|
||||
style={
|
||||
{
|
||||
"--background": "#ffffff",
|
||||
"--color": "rgba(0, 0, 0, 0.2)",
|
||||
"--height": "5px",
|
||||
"--width": "1px",
|
||||
"--fade-stop": "90%",
|
||||
"--offset": offset || "150px", //-100px if you want to keep the line inside
|
||||
"--color-dark": "rgba(255, 255, 255, 0.2)",
|
||||
maskComposite: "exclude",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={cn(
|
||||
"absolute h-[calc(100%+var(--offset))] w-[var(--width)] top-[calc(var(--offset)/2*-1)]",
|
||||
"bg-[linear-gradient(to_bottom,var(--color),var(--color)_50%,transparent_0,transparent)]",
|
||||
"[background-size:var(--width)_var(--height)]",
|
||||
"[mask:linear-gradient(to_top,var(--background)_var(--fade-stop),transparent),_linear-gradient(to_bottom,var(--background)_var(--fade-stop),transparent),_linear-gradient(black,black)]",
|
||||
"[mask-composite:exclude]",
|
||||
"z-30",
|
||||
"dark:bg-[linear-gradient(to_bottom,var(--color-dark),var(--color-dark)_50%,transparent_0,transparent)]",
|
||||
className
|
||||
)}
|
||||
></div>
|
||||
);
|
||||
};
|
||||
448
surfsense_web/components/homepage/features-bento-grid.tsx
Normal file
448
surfsense_web/components/homepage/features-bento-grid.tsx
Normal file
|
|
@ -0,0 +1,448 @@
|
|||
import { IconFileBroken, IconSparkles, IconTableColumn, IconUsers } from "@tabler/icons-react";
|
||||
import Image from "next/image";
|
||||
import React from "react";
|
||||
import { BentoGrid, BentoGridItem } from "@/components/ui/bento-grid";
|
||||
|
||||
export function FeaturesBentoGrid() {
|
||||
return (
|
||||
<BentoGrid className="max-w-7xl my-8 mx-auto md:auto-rows-[20rem]">
|
||||
{items.map((item, i) => (
|
||||
<BentoGridItem
|
||||
key={i}
|
||||
title={item.title}
|
||||
description={item.description}
|
||||
header={item.header}
|
||||
className={item.className}
|
||||
icon={item.icon}
|
||||
/>
|
||||
))}
|
||||
</BentoGrid>
|
||||
);
|
||||
}
|
||||
|
||||
const CitationIllustration = () => (
|
||||
<div className="relative flex w-full h-full min-h-[6rem] items-center justify-center overflow-hidden rounded-xl bg-gradient-to-br from-blue-50 via-purple-50 to-pink-50 dark:from-blue-950/20 dark:via-purple-950/20 dark:to-pink-950/20 p-4">
|
||||
<svg viewBox="0 0 400 200" className="w-full h-full" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>Citation feature illustration showing clickable source reference</title>
|
||||
{/* Background chat message */}
|
||||
<g>
|
||||
{/* Chat bubble */}
|
||||
<rect
|
||||
x="20"
|
||||
y="30"
|
||||
width="200"
|
||||
height="60"
|
||||
rx="12"
|
||||
className="fill-white dark:fill-neutral-800"
|
||||
opacity="0.9"
|
||||
/>
|
||||
{/* Text lines */}
|
||||
<line
|
||||
x1="35"
|
||||
y1="50"
|
||||
x2="150"
|
||||
y2="50"
|
||||
className="stroke-neutral-400 dark:stroke-neutral-600"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<line
|
||||
x1="35"
|
||||
y1="65"
|
||||
x2="180"
|
||||
y2="65"
|
||||
className="stroke-neutral-400 dark:stroke-neutral-600"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
|
||||
{/* Citation badge with glow */}
|
||||
<defs>
|
||||
<filter id="glow">
|
||||
<feGaussianBlur stdDeviation="2" result="coloredBlur" />
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
{/* Clickable citation */}
|
||||
<g className="cursor-pointer" filter="url(#glow)">
|
||||
<rect
|
||||
x="185"
|
||||
y="57"
|
||||
width="28"
|
||||
height="20"
|
||||
rx="6"
|
||||
className="fill-blue-500 dark:fill-blue-600"
|
||||
/>
|
||||
<text
|
||||
x="199"
|
||||
y="70"
|
||||
fontSize="12"
|
||||
fontWeight="bold"
|
||||
className="fill-white"
|
||||
textAnchor="middle"
|
||||
>
|
||||
[1]
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
{/* Connecting line with animation effect */}
|
||||
<g>
|
||||
<path
|
||||
d="M 199 77 Q 240 90, 260 110"
|
||||
className="stroke-blue-500 dark:stroke-blue-400"
|
||||
strokeWidth="2"
|
||||
strokeDasharray="4,4"
|
||||
fill="none"
|
||||
opacity="0.6"
|
||||
>
|
||||
<animate
|
||||
attributeName="stroke-dashoffset"
|
||||
from="8"
|
||||
to="0"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</path>
|
||||
|
||||
{/* Arrow head */}
|
||||
<polygon
|
||||
points="258,113 262,110 260,106"
|
||||
className="fill-blue-500 dark:fill-blue-400"
|
||||
opacity="0.6"
|
||||
/>
|
||||
</g>
|
||||
|
||||
{/* Citation popup card */}
|
||||
<g>
|
||||
{/* Card shadow */}
|
||||
<rect
|
||||
x="245"
|
||||
y="113"
|
||||
width="145"
|
||||
height="75"
|
||||
rx="10"
|
||||
className="fill-black"
|
||||
opacity="0.1"
|
||||
transform="translate(2, 2)"
|
||||
/>
|
||||
|
||||
{/* Main card */}
|
||||
<rect
|
||||
x="245"
|
||||
y="113"
|
||||
width="145"
|
||||
height="75"
|
||||
rx="10"
|
||||
className="fill-white dark:fill-neutral-800 stroke-blue-500 dark:stroke-blue-400"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
|
||||
{/* Card header */}
|
||||
<rect
|
||||
x="245"
|
||||
y="113"
|
||||
width="145"
|
||||
height="25"
|
||||
rx="10"
|
||||
className="fill-blue-100 dark:fill-blue-900/50"
|
||||
/>
|
||||
<line
|
||||
x1="245"
|
||||
y1="138"
|
||||
x2="390"
|
||||
y2="138"
|
||||
className="stroke-blue-200 dark:stroke-blue-800"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
|
||||
{/* Header text */}
|
||||
<text
|
||||
x="317.5"
|
||||
y="128"
|
||||
fontSize="9"
|
||||
fontWeight="600"
|
||||
className="fill-blue-700 dark:fill-blue-300"
|
||||
textAnchor="middle"
|
||||
>
|
||||
Referenced Chunk
|
||||
</text>
|
||||
|
||||
{/* Content lines */}
|
||||
<line
|
||||
x1="255"
|
||||
y1="150"
|
||||
x2="365"
|
||||
y2="150"
|
||||
className="stroke-neutral-600 dark:stroke-neutral-400"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<line
|
||||
x1="255"
|
||||
y1="162"
|
||||
x2="340"
|
||||
y2="162"
|
||||
className="stroke-neutral-500 dark:stroke-neutral-500"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<line
|
||||
x1="255"
|
||||
y1="174"
|
||||
x2="380"
|
||||
y2="174"
|
||||
className="stroke-neutral-400 dark:stroke-neutral-600"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</g>
|
||||
|
||||
{/* Sparkle effects */}
|
||||
<g className="opacity-60">
|
||||
{/* Sparkle 1 */}
|
||||
<circle cx="195" cy="45" r="2" className="fill-yellow-400">
|
||||
<animate attributeName="opacity" values="0;1;0" dur="2s" repeatCount="indefinite" />
|
||||
</circle>
|
||||
<circle cx="195" cy="45" r="1" className="fill-white">
|
||||
<animate attributeName="opacity" values="0;1;0" dur="2s" repeatCount="indefinite" />
|
||||
</circle>
|
||||
|
||||
{/* Sparkle 2 */}
|
||||
<circle cx="370" cy="125" r="2" className="fill-purple-400">
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="0;1;0"
|
||||
dur="2.5s"
|
||||
begin="0.5s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</circle>
|
||||
<circle cx="370" cy="125" r="1" className="fill-white">
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="0;1;0"
|
||||
dur="2.5s"
|
||||
begin="0.5s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</circle>
|
||||
|
||||
{/* Sparkle 3 */}
|
||||
<circle cx="250" cy="95" r="1.5" className="fill-blue-400">
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="0;1;0"
|
||||
dur="3s"
|
||||
begin="1s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</circle>
|
||||
</g>
|
||||
|
||||
{/* AI Sparkle icon in corner */}
|
||||
<g transform="translate(25, 100)">
|
||||
<path
|
||||
d="M 0,0 L 3,-8 L 6,0 L 14,3 L 6,6 L 3,14 L 0,6 L -8,3 Z"
|
||||
className="fill-purple-500 dark:fill-purple-400"
|
||||
opacity="0.7"
|
||||
>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
type="rotate"
|
||||
from="0 3 3"
|
||||
to="360 3 3"
|
||||
dur="8s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
|
||||
const CollaborationIllustration = () => (
|
||||
<div className="relative flex w-full h-full min-h-44 flex-1 flex-col items-center justify-center overflow-hidden pointer-events-none select-none">
|
||||
<div
|
||||
role="img"
|
||||
aria-label="Illustration of a realtime collaboration in a text editor."
|
||||
className="pointer-events-none absolute inset-0 flex flex-col items-start justify-center pl-4 select-none"
|
||||
>
|
||||
<div className="relative flex h-fit w-fit flex-col items-start">
|
||||
<div className="w-full text-2xl sm:text-3xl lg:text-4xl leading-tight text-neutral-700 dark:text-neutral-300">
|
||||
<span className="flex items-stretch flex-wrap">
|
||||
{/* <span>Real-time </span> */}
|
||||
<span className="relative bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-300 px-1">
|
||||
Real-time
|
||||
</span>
|
||||
<span className="relative z-10 inline-flex items-stretch justify-start">
|
||||
<span className="absolute h-full w-0.5 rounded-b-sm bg-blue-500"></span>
|
||||
<span className="absolute inline-flex h-6 sm:h-7 -translate-y-full items-center rounded-t-sm rounded-r-sm px-2 py-0.5 text-xs sm:text-sm font-medium text-white bg-blue-500">
|
||||
Rich
|
||||
</span>
|
||||
</span>
|
||||
<span>collabo</span>
|
||||
<span>orat</span>
|
||||
<span className="relative z-10 inline-flex items-stretch justify-start">
|
||||
<span className="absolute h-full w-0.5 rounded-b-sm bg-purple-600 dark:bg-purple-500"></span>
|
||||
<span className="absolute inline-flex h-6 sm:h-7 -translate-y-full items-center rounded-t-sm rounded-r-sm px-2 py-0.5 text-xs sm:text-sm font-medium text-white bg-purple-600 dark:bg-purple-500">
|
||||
Alex
|
||||
</span>
|
||||
</span>
|
||||
<span>ion</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* Bottom gradient fade */}
|
||||
<div className="absolute -right-4 bottom-0 -left-4 h-24 bg-gradient-to-t from-white dark:from-black to-transparent"></div>
|
||||
{/* Right gradient fade */}
|
||||
<div className="absolute top-0 -right-4 bottom-0 w-20 bg-gradient-to-l from-white dark:from-black to-transparent"></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const AnnotationIllustration = () => (
|
||||
<div className="relative flex w-full h-full min-h-44 flex-1 flex-col items-center justify-center overflow-hidden pointer-events-none select-none">
|
||||
<div
|
||||
role="img"
|
||||
aria-label="Illustration of a text editor with annotation comments."
|
||||
className="pointer-events-none absolute inset-0 flex flex-col items-start justify-center pl-4 select-none md:left-1/2"
|
||||
>
|
||||
<div className="relative flex h-fit w-fit flex-col items-start justify-center gap-3.5">
|
||||
{/* Text above the comment box */}
|
||||
<div className="absolute left-0 h-fit -translate-x-full pr-7 text-3xl sm:text-4xl lg:text-5xl tracking-tight whitespace-nowrap text-neutral-400 dark:text-neutral-600">
|
||||
<span className="relative">
|
||||
Add context with
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-white dark:from-black via-white dark:via-black to-transparent"></div>
|
||||
</span>{" "}
|
||||
<span className="bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-300">
|
||||
comments
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Comment card */}
|
||||
<div className="flex flex-col items-start gap-4 rounded-xl bg-neutral-100 dark:bg-neutral-900/50 px-6 py-5 text-xl sm:text-2xl lg:text-3xl max-w-md">
|
||||
<div className="truncate leading-normal text-neutral-600 dark:text-neutral-400">
|
||||
<span>Let's discuss this tomorrow!</span>
|
||||
</div>
|
||||
|
||||
{/* Reaction icons */}
|
||||
<div className="flex items-center gap-3 opacity-30">
|
||||
{/* @ icon */}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
className="w-6 h-6 sm:w-7 sm:h-7 lg:w-8 lg:h-8"
|
||||
>
|
||||
<title>Mention icon</title>
|
||||
<g
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.8"
|
||||
>
|
||||
<path d="M11.998 15.6a3.6 3.6 0 1 0 0-7.2 3.6 3.6 0 0 0 0 7.2Z" />
|
||||
<path d="M15.602 8.4v4.44c0 1.326 1.026 2.52 2.28 2.52a2.544 2.544 0 0 0 2.52-2.52V12a8.4 8.4 0 1 0-3.36 6.72" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
{/* Emoji icon */}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
className="w-6 h-6 sm:w-7 sm:h-7 lg:w-8 lg:h-8"
|
||||
>
|
||||
<title>Emoji icon</title>
|
||||
<g
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.8"
|
||||
>
|
||||
<path d="M12.002 20.4a8.4 8.4 0 1 0 0-16.8 8.4 8.4 0 0 0 0 16.8Z" />
|
||||
<path d="M9 13.8s.9 1.8 3 1.8 3-1.8 3-1.8M9.6 9.6h.008M14.398 9.6h.009M9.597 9.9a.3.3 0 1 0 0-.6.3.3 0 0 0 0 .6ZM14.402 9.9a.3.3 0 1 0 0-.6.3.3 0 0 0 0 .6Z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
{/* Attachment icon */}
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="w-6 h-6 sm:w-7 sm:h-7 lg:w-8 lg:h-8"
|
||||
>
|
||||
<title>Attachment icon</title>
|
||||
<path
|
||||
d="M16.8926 14.0829L12.425 18.4269C10.565 20.2353 7.47136 20.2353 5.61136 18.4269C3.75976 16.6269 3.75976 13.6029 5.61136 11.8029L12.4886 5.11529C13.7294 3.90809 15.7934 3.90689 17.0354 5.11169C18.2714 6.31169 18.2738 8.33009 17.039 9.53249L10.1462 16.2189C9.83929 16.5093 9.43285 16.6711 9.01036 16.6711C8.58786 16.6711 8.18142 16.5093 7.87456 16.2189C7.72623 16.0757 7.60817 15.9042 7.52737 15.7146C7.44656 15.525 7.40466 15.321 7.40416 15.1149C7.40416 14.7009 7.57216 14.3037 7.87456 14.0109L12.4178 9.59849"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.8"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom gradient fade */}
|
||||
<div className="absolute -right-4 bottom-0 -left-4 h-20 bg-gradient-to-t from-white dark:from-black to-transparent"></div>
|
||||
{/* Right gradient fade */}
|
||||
<div className="absolute top-0 -right-4 bottom-0 w-20 bg-gradient-to-l from-white dark:from-black to-transparent"></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const AudioCommentIllustration = () => (
|
||||
<div className="relative flex w-full h-full min-h-[6rem] overflow-hidden rounded-xl">
|
||||
<Image
|
||||
src="/homepage/comments-audio.webp"
|
||||
alt="Audio Comment Illustration"
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const items = [
|
||||
{
|
||||
title: "Find, Ask, Act",
|
||||
description:
|
||||
"Get instant information, detailed updates, and cited answers across company and personal knowledge.",
|
||||
header: <CitationIllustration />,
|
||||
className: "md:col-span-2",
|
||||
icon: <IconSparkles className="h-4 w-4 text-neutral-500" />,
|
||||
},
|
||||
{
|
||||
title: "Work Together in Real Time",
|
||||
description:
|
||||
"Transform your company docs into multiplayer spaces with live edits, synced content, and presence.",
|
||||
header: <CollaborationIllustration />,
|
||||
className: "md:col-span-1",
|
||||
icon: <IconUsers className="h-4 w-4 text-neutral-500" />,
|
||||
},
|
||||
{
|
||||
title: "Collaborate Beyond Text",
|
||||
description:
|
||||
"Create podcasts and multimedia your team can comment on, share, and refine together.",
|
||||
header: <AudioCommentIllustration />,
|
||||
className: "md:col-span-1",
|
||||
icon: <IconTableColumn className="h-4 w-4 text-neutral-500" />,
|
||||
},
|
||||
{
|
||||
title: "Context Where It Counts",
|
||||
description: "Add comments directly to your chats and docs for clear, in-the-moment feedback.",
|
||||
header: <AnnotationIllustration />,
|
||||
className: "md:col-span-2",
|
||||
icon: <IconFileBroken className="h-4 w-4 text-neutral-500" />,
|
||||
},
|
||||
];
|
||||
74
surfsense_web/components/homepage/features-card.tsx
Normal file
74
surfsense_web/components/homepage/features-card.tsx
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { Sliders, Users, Workflow } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
|
||||
export function Features() {
|
||||
return (
|
||||
<section className="py-2 md:py-8 dark:bg-transparent">
|
||||
<div className="@container mx-auto max-w-5xl">
|
||||
{/* <div className="text-center">
|
||||
<h2 className="text-balance text-4xl font-semibold lg:text-5xl">Built to cover your needs</h2>
|
||||
<p className="mt-4">Libero sapiente aliquam quibusdam aspernatur, praesentium iusto repellendus.</p>
|
||||
</div> */}
|
||||
<div className="@min-4xl:max-w-full @min-4xl:grid-cols-3 mx-auto mt-8 grid max-w-sm gap-6 *:text-center md:mt-16">
|
||||
<Card className="group shadow-black-950/5">
|
||||
<CardHeader className="pb-3">
|
||||
<CardDecorator>
|
||||
<Sliders className="size-6" aria-hidden />
|
||||
</CardDecorator>
|
||||
|
||||
<h3 className="mt-6 font-medium">Customizable</h3>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
<p className="text-sm">Customize your research agent to your specific needs.</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="group shadow-black-950/5">
|
||||
<CardHeader className="pb-3">
|
||||
<CardDecorator>
|
||||
<Workflow className="size-6" aria-hidden />
|
||||
</CardDecorator>
|
||||
|
||||
<h3 className="mt-6 font-medium">Streamline your workflow</h3>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
<p className="text-sm">
|
||||
Pull all your knowledge into one place, so you can find what matters and get things
|
||||
done faster.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="group shadow-black-950/5">
|
||||
<CardHeader className="pb-3">
|
||||
<CardDecorator>
|
||||
<Users className="size-6" aria-hidden />
|
||||
</CardDecorator>
|
||||
|
||||
<h3 className="mt-6 font-medium">Seamless Collaboration</h3>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
<p className="text-sm">Make your company and personal content collaborative.</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const CardDecorator = ({ children }: { children: ReactNode }) => (
|
||||
<div
|
||||
aria-hidden
|
||||
className="relative mx-auto size-36 [mask-image:radial-gradient(ellipse_50%_50%_at_50%_50%,#000_70%,transparent_100%)]"
|
||||
>
|
||||
<div className="absolute inset-0 [--border:black] dark:[--border:white] bg-[linear-gradient(to_right,var(--border)_1px,transparent_1px),linear-gradient(to_bottom,var(--border)_1px,transparent_1px)] bg-[size:24px_24px] opacity-10" />
|
||||
<div className="bg-background absolute inset-0 m-auto flex size-12 items-center justify-center border-t border-l">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
54
surfsense_web/components/ui/bento-grid.tsx
Normal file
54
surfsense_web/components/ui/bento-grid.tsx
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import { cn } from "@/lib/utils";
|
||||
|
||||
export const BentoGrid = ({
|
||||
className,
|
||||
children,
|
||||
}: {
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"mx-auto grid max-w-7xl grid-cols-1 gap-4 md:auto-rows-[18rem] md:grid-cols-3",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const BentoGridItem = ({
|
||||
className,
|
||||
title,
|
||||
description,
|
||||
header,
|
||||
icon,
|
||||
}: {
|
||||
className?: string;
|
||||
title?: string | React.ReactNode;
|
||||
description?: string | React.ReactNode;
|
||||
header?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"group/bento shadow-input row-span-1 flex flex-col justify-between space-y-4 rounded-xl border border-neutral-200 bg-white p-4 transition duration-200 hover:shadow-xl dark:border-white/[0.2] dark:bg-black dark:shadow-none",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{header}
|
||||
<div className="transition duration-200 group-hover/bento:translate-x-2">
|
||||
{icon}
|
||||
<div className="mt-2 mb-2 font-sans font-bold text-neutral-600 dark:text-neutral-200">
|
||||
{title}
|
||||
</div>
|
||||
<div className="font-sans text-xs font-normal text-neutral-600 dark:text-neutral-300">
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
BIN
surfsense_web/public/homepage/comments-audio.webp
Normal file
BIN
surfsense_web/public/homepage/comments-audio.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Loading…
Add table
Add a link
Reference in a new issue