Merge pull request #38 from MODSetter/dev

Updated Readme & Some UI Fixes
This commit is contained in:
Rohan Verma 2025-04-14 19:35:56 -07:00 committed by GitHub
commit 0013fe4f61
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 207 additions and 100 deletions

View file

@ -1,11 +1,12 @@
![headnew](https://github.com/user-attachments/assets/a44fd1e7-1861-46d0-aff7-19cf33e86baa)
![new_header](https://github.com/user-attachments/assets/e236b764-0ddc-42ff-a1f1-8fbb3d2e0e65)
# SurfSense
While tools like NotebookLM and Perplexity are impressive and highly effective for conducting research on any topic, SurfSense elevates this capability by integrating with your personal knowledge base. It is a highly customizable AI research agent, connected to external sources such as search engines (Tavily), Slack, Notion, and more to come.
While tools like NotebookLM and Perplexity are impressive and highly effective for conducting research on any topic/query, SurfSense elevates this capability by integrating with your personal knowledge base. It is a highly customizable AI research agent, connected to external sources such as search engines (Tavily), Slack, Notion, YouTube, GitHub and more to come.
# Video
@ -45,6 +46,7 @@ Open source and easy to deploy locally.
- Slack
- Notion
- Youtube Videos
- GitHub
- and more to come.....
#### 🔖 Cross Browser Extension
@ -150,6 +152,20 @@ For local frontend setup just fill out the `.env` file of frontend.
You should see your Next.js frontend running at `localhost:3000`
#### Some FrontEnd Screens
**Search Spaces**
![search_spaces](https://github.com/user-attachments/assets/e254c38c-f937-44b6-9e9d-770db583d099)
**Research Agent**
![researcher](https://github.com/user-attachments/assets/fda3e61f-f936-4b66-b565-d84edde44a67)
**Agent Chat**
![chat](https://github.com/user-attachments/assets/bb352d52-1c6d-4020-926b-722d0b98b491)
---
@ -193,12 +209,15 @@ After filling in your SurfSense API key you should be able to use extension now.
### **BackEnd**
- **FastAPI**: Modern, fast web framework for building APIs with Python
- **PostgreSQL with pgvector**: Database with vector search capabilities for similarity searches
- **SQLAlchemy**: SQL toolkit and ORM (Object-Relational Mapping) for database interactions
- **Alembic**: A database migrations tool for SQLAlchemy.
- **FastAPI Users**: Authentication and user management with JWT and OAuth support
- **LangChain**: Framework for developing AI-powered applications
- **GPT Integration**: Integration with LLM models through LiteLLM
@ -214,10 +233,8 @@ After filling in your SurfSense API key you should be able to use extension now.
- **pgvector**: PostgreSQL extension for efficient vector similarity operations
- **Chonkie**: Advanced document chunking and embedding library
- Uses `AutoEmbeddings` for flexible embedding model selection
- `LateChunker` for optimized document chunking based on embedding model's max sequence length
- Uses `AutoEmbeddings` for flexible embedding model selection
- `LateChunker` for optimized document chunking based on embedding model's max sequence length

View file

@ -1,29 +1,24 @@
"use client";
import { cn } from "@/lib/utils";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import {
IconBrandGoogle,
IconBrandSlack,
IconBrandWindows,
IconBrandDiscord,
IconSearch,
IconMessages,
IconDatabase,
IconCloud,
IconBrandGithub,
IconBrandNotion,
IconMail,
IconBrandSlack,
IconBrandWindows,
IconBrandZoom,
IconChevronDown,
IconChevronRight,
IconMail,
IconWorldWww,
} from "@tabler/icons-react";
import { motion, AnimatePresence } from "framer-motion";
import { useState } from "react";
import { useParams } from "next/navigation";
import { AnimatePresence, motion } from "framer-motion";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { useForm } from "react-hook-form";
import { useParams } from "next/navigation";
import { useState } from "react";
// Define the Connector type
interface Connector {
@ -31,7 +26,7 @@ interface Connector {
title: string;
description: string;
icon: React.ReactNode;
status: "available" | "coming-soon" | "connected"; // Added connected status example
status: "available" | "coming-soon" | "connected";
}
interface ConnectorCategory {
@ -47,12 +42,11 @@ const connectorCategories: ConnectorCategory[] = [
title: "Search Engines",
connectors: [
{
id: "web-search",
title: "Web Search",
description: "Enable web search capabilities for broader context.",
id: "tavily-api",
title: "Tavily API",
description: "Search the web using the Tavily API",
icon: <IconWorldWww className="h-6 w-6" />,
status: "available", // Example status
// Potentially add config form here if needed (e.g., choosing provider)
status: "available",
},
// Add other search engine connectors like Tavily, Serper if they have UI config
],
@ -94,10 +88,9 @@ const connectorCategories: ConnectorCategory[] = [
description: "Connect to your Notion workspace to access pages and databases.",
icon: <IconBrandNotion className="h-6 w-6" />,
status: "available",
// No form here, assumes it links to its own page
},
{
id: "github-connector", // Keep the id simple
id: "github-connector",
title: "GitHub",
description: "Connect a GitHub PAT to index code and docs from accessible repositories.",
icon: <IconBrandGithub className="h-6 w-6" />,
@ -127,6 +120,44 @@ const connectorCategories: ConnectorCategory[] = [
},
];
// Animation variants
const fadeIn = {
hidden: { opacity: 0 },
visible: { opacity: 1, transition: { duration: 0.4 } }
};
const staggerContainer = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1
}
}
};
const cardVariants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: {
type: "spring",
stiffness: 260,
damping: 20
}
},
hover: {
scale: 1.02,
boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
transition: {
type: "spring",
stiffness: 400,
damping: 10
}
}
};
export default function ConnectorsPage() {
const params = useParams();
const searchSpaceId = params.search_space_id as string;
@ -141,85 +172,142 @@ export default function ConnectorsPage() {
};
return (
<div className="container mx-auto py-8 max-w-6xl">
<div className="container mx-auto py-12 max-w-6xl">
<motion.div
initial={{ opacity: 0, y: 20 }}
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="mb-8 text-center"
transition={{
duration: 0.6,
ease: [0.22, 1, 0.36, 1]
}}
className="mb-12 text-center"
>
<h1 className="text-3xl font-bold tracking-tight">Connect Your Tools</h1>
<p className="text-muted-foreground mt-2">
<h1 className="text-4xl font-bold tracking-tight bg-gradient-to-r from-indigo-500 to-purple-500 bg-clip-text text-transparent">
Connect Your Tools
</h1>
<p className="text-muted-foreground mt-3 text-lg max-w-2xl mx-auto">
Integrate with your favorite services to enhance your research capabilities.
</p>
</motion.div>
<div className="space-y-6">
<motion.div
className="space-y-8"
initial="hidden"
animate="visible"
variants={staggerContainer}
>
{connectorCategories.map((category) => (
<Collapsible
<motion.div
key={category.id}
open={expandedCategories.includes(category.id)}
onOpenChange={() => toggleCategory(category.id)}
className="space-y-2"
variants={fadeIn}
className="rounded-lg border bg-card text-card-foreground shadow-sm"
>
<div className="flex items-center justify-between space-x-4 px-1">
<h3 className="text-lg font-semibold dark:text-gray-200">{category.title}</h3>
<CollapsibleTrigger asChild>
{/* Replace with your preferred expand/collapse icon/button */}
<button className="text-sm text-indigo-600 hover:underline dark:text-indigo-400">
{expandedCategories.includes(category.id) ? "Collapse" : "Expand"}
</button>
</CollapsibleTrigger>
</div>
<CollapsibleContent>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 p-1">
{category.connectors.map((connector) => (
<div key={connector.id} className="col-span-1 flex flex-col divide-y divide-gray-200 dark:divide-gray-700 rounded-lg bg-white dark:bg-gray-800 shadow">
<div className="flex w-full items-center justify-between space-x-6 p-6 flex-grow">
<div className="flex-1 truncate">
<div className="flex items-center space-x-3">
<span className="text-gray-900 dark:text-gray-100">{connector.icon}</span>
<h3 className="truncate text-sm font-medium text-gray-900 dark:text-gray-100">
{connector.title}
</h3>
{connector.status === "coming-soon" && (
<span className="inline-block flex-shrink-0 rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200">
Coming soon
</span>
)}
{/* TODO: Add 'Connected' badge based on actual state */}
</div>
<p className="mt-1 truncate text-sm text-gray-500 dark:text-gray-400">
{connector.description}
</p>
</div>
</div>
{/* Always render Link button if available */}
{connector.status === 'available' && (
<div className="px-6 py-4 border-t border-gray-200 dark:border-gray-700">
<Link href={`/dashboard/${searchSpaceId}/connectors/add/${connector.id}`}>
<Button variant="default" className="w-full">
Connect
</Button>
</Link>
</div>
)}
{connector.status === 'coming-soon' && (
<div className="px-6 py-4 border-t border-gray-200 dark:border-gray-700">
<Button variant="outline" disabled className="w-full">
Coming Soon
</Button>
</div>
)}
{/* TODO: Add logic for 'connected' status */}
</div>
))}
<Collapsible
open={expandedCategories.includes(category.id)}
onOpenChange={() => toggleCategory(category.id)}
className="w-full"
>
<div className="flex items-center justify-between space-x-4 p-4">
<h3 className="text-xl font-semibold">{category.title}</h3>
<CollapsibleTrigger asChild>
<Button variant="ghost" size="sm" className="w-9 p-0 hover:bg-muted">
<motion.div
animate={{ rotate: expandedCategories.includes(category.id) ? 180 : 0 }}
transition={{ duration: 0.3, ease: "easeInOut" }}
>
<IconChevronDown className="h-5 w-5" />
</motion.div>
<span className="sr-only">Toggle</span>
</Button>
</CollapsibleTrigger>
</div>
</CollapsibleContent>
<Separator className="my-4" />
</Collapsible>
<CollapsibleContent>
<AnimatePresence>
<motion.div
className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 p-4"
variants={staggerContainer}
initial="hidden"
animate="visible"
exit="hidden"
>
{category.connectors.map((connector) => (
<motion.div
key={connector.id}
variants={cardVariants}
whileHover="hover"
className="col-span-1"
>
<Card className="h-full flex flex-col overflow-hidden border-transparent transition-all duration-200 hover:border-primary/50">
<CardHeader className="flex-row items-center gap-4 pb-2">
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 dark:bg-primary/20">
<motion.div
whileHover={{ rotate: 5, scale: 1.1 }}
className="text-primary"
>
{connector.icon}
</motion.div>
</div>
<div>
<div className="flex items-center gap-2">
<h3 className="font-medium">{connector.title}</h3>
{connector.status === "coming-soon" && (
<Badge variant="outline" className="text-xs bg-amber-100 dark:bg-amber-950 text-amber-800 dark:text-amber-300 border-amber-200 dark:border-amber-800">
Coming soon
</Badge>
)}
{connector.status === "connected" && (
<Badge variant="outline" className="text-xs bg-green-100 dark:bg-green-950 text-green-800 dark:text-green-300 border-green-200 dark:border-green-800">
Connected
</Badge>
)}
</div>
</div>
</CardHeader>
<CardContent className="pb-4">
<p className="text-sm text-muted-foreground">
{connector.description}
</p>
</CardContent>
<CardFooter className="mt-auto pt-2">
{connector.status === 'available' && (
<Link href={`/dashboard/${searchSpaceId}/connectors/add/${connector.id}`} className="w-full">
<Button variant="default" className="w-full group">
<span>Connect</span>
<motion.div
className="ml-1"
initial={{ x: 0 }}
whileHover={{ x: 3 }}
transition={{ type: "spring", stiffness: 400, damping: 10 }}
>
<IconChevronRight className="h-4 w-4" />
</motion.div>
</Button>
</Link>
)}
{connector.status === 'coming-soon' && (
<Button variant="outline" disabled className="w-full opacity-70">
Coming Soon
</Button>
)}
{connector.status === 'connected' && (
<Button variant="outline" className="w-full border-green-500 text-green-600 hover:bg-green-50 dark:hover:bg-green-950">
Manage
</Button>
)}
</CardFooter>
</Card>
</motion.div>
))}
</motion.div>
</AnimatePresence>
</CollapsibleContent>
</Collapsible>
</motion.div>
))}
</div>
</motion.div>
</div>
);
}

View file

@ -36,7 +36,7 @@ export function ModernHeroWithGradients() {
</h1>
</div>
<p className="mx-auto max-w-3xl py-6 text-center text-base text-gray-600 dark:text-neutral-300 md:text-lg lg:text-xl">
A Customizable AI Research Agent just like NotebookLM or Perplexity, but connected to external sources such as search engines (Tavily), Slack, Notion, and more.
A Customizable AI Research Agent just like NotebookLM or Perplexity, but connected to external sources such as search engines (Tavily), Slack, Notion, YouTube, GitHub and more.
</p>
<div className="flex flex-col items-center gap-6 py-6 sm:flex-row">
<Link

View file

@ -11,7 +11,7 @@ import {
Link,
Webhook,
} from 'lucide-react';
import { IconBrandNotion, IconBrandSlack, IconBrandYoutube } from "@tabler/icons-react";
import { IconBrandNotion, IconBrandSlack, IconBrandYoutube, IconBrandGithub } from "@tabler/icons-react";
import { Button } from '@/components/ui/button';
import { Connector, ResearchMode } from './types';
@ -20,6 +20,8 @@ export const getConnectorIcon = (connectorType: string) => {
const iconProps = { className: "h-4 w-4" };
switch(connectorType) {
case 'GITHUB_CONNECTOR':
return <IconBrandGithub {...iconProps} />;
case 'YOUTUBE_VIDEO':
return <IconBrandYoutube {...iconProps} />;
case 'CRAWLED_URL':