diff options
| -rw-r--r-- | CONTRIBUTE.md | 288 | ||||
| -rw-r--r-- | README.md | 114 | ||||
| -rw-r--r-- | apps/web/components/views/connections-tab-content.tsx | 664 | ||||
| -rw-r--r-- | apps/web/public/add-connections.png | bin | 0 -> 339198 bytes | |||
| -rw-r--r-- | apps/web/public/add-memory.png | bin | 0 -> 371710 bytes | |||
| -rw-r--r-- | apps/web/public/chat.png | bin | 0 -> 342407 bytes | |||
| -rw-r--r-- | apps/web/public/mcp.png | bin | 0 -> 421670 bytes |
7 files changed, 645 insertions, 421 deletions
diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md new file mode 100644 index 00000000..b3ea376f --- /dev/null +++ b/CONTRIBUTE.md @@ -0,0 +1,288 @@ +# Contributing to supermemory + +Thank you for your interest in contributing to supermemory! We welcome contributions from developers of all skill levels. This guide will help you get started with contributing to our AI-powered memory layer API. + +## π Quick Start + +### Prerequisites + +Before you begin, ensure you have the following installed: + +- **Bun** (>= 1.2.17) - Our preferred package manager +- **Git** for version control + +### Setting Up the Development Environment + +1. **Fork and Clone the Repository** + + ```bash + git clone https://github.com/your-username/supermemory-app.git + cd supermemory-app + ``` + +2. **Install Dependencies** + + ```bash + bun install + ``` + +3. **Set Up Environment Variables** + + ```bash + # Copy the example environment file + cp .env.example .env.local + + # Edit the file with your configuration + # You'll need to add your API keys and database URLs + ``` + +4. **Start the Development Server** + + ```bash + bun run dev + ``` + + This will start all applications in the monorepo. The web app will be available at `http://localhost:3000`. + +## π Project Structure + +supermemory is organized as a monorepo using Turbo: + +``` +supermemory-app/ +βββ apps/ +β βββ web/ # Next.js web application +βββ packages/ +β βββ ui/ # Shared UI components +β βββ lib/ # Shared utilities and logic +β βββ hooks/ # Shared React hooks +β βββ validation/ # Zod schemas and validation +β βββ eslint-config/ # ESLint configurations +β βββ typescript-config/ # TypeScript configurations +βββ turbo.json # Turbo configuration +``` + +## π οΈ Development Workflow + +### Available Scripts + +- `bun run dev` - Start development servers for all apps +- `bun run build` - Build all applications +- `bun run format-lint` - Format and lint code using Biome +- `bun run check-types` - Type check all packages + +### Code Quality + +We use several tools to maintain code quality: + +- **Biome** for linting and formatting +- **TypeScript** for type safety +- **Turbo** for build optimization + +Before submitting a PR, ensure your code passes all checks: + +```bash +bun run format-lint +bun run check-types +bun run build +``` + +### Tech Stack + +- **Frontend**: Next.js 15, React 19, TypeScript +- **Styling**: Tailwind CSS, Radix UI components +- **State Management**: Zustand, TanStack Query +- **Build Tool**: Turbo (monorepo) +- **Package Manager**: Bun +- **Deployment**: Cloudflare (OpenNext.js) + +## π― How to Contribute + +### Types of Contributions + +We welcome various types of contributions: + +- π **Bug fixes** +- β¨ **New features** +- π¨ **UI/UX enhancements** +- β‘ **Performance optimizations** + +### Finding Issues to Work On + +1. Check our [Issues](https://github.com/supermemoryai/supermemory/issues) page +2. Look for issues labeled `good first issue` for beginners +3. Issues labeled `help wanted` are great for contributors +4. Feel free to propose new features by opening an issue first + +### Making Changes + +1. **Create a Branch** + + ```bash + git checkout -b feature/your-feature-name + # or + git checkout -b fix/your-bug-fix + ``` + +2. **Make Your Changes** + - Follow our coding standards (see below) + - Write clear, concise commit messages + - Add tests if applicable + - Update documentation if needed + +3. **Test Your Changes** + ```bash + bun run dev # Test locally + bun run build # Ensure it builds + bun run format-lint # Check formatting + bun run check-types # Check types + ``` + +## π Coding Standards + +### General Guidelines + +- Use **TypeScript** for all new code +- Follow the existing code style and patterns +- Write self-documenting code with clear variable names +- Add JSDoc comments for complex functions +- Keep functions small and focused + +### Component Guidelines + +- Use functional components with hooks +- Prefer composition over inheritance +- Extract reusable logic into custom hooks +- Use proper TypeScript types for props + +### File Naming + +- Use `kebab-case` for file names +- Use `PascalCase` for component files +- Use `camelCase` for utility functions + +### Import Organization + +```typescript +// 1. React and Next.js imports +import React from 'react'; +import { NextPage } from 'next'; + +// 2. Third-party libraries +import { clsx } from 'clsx'; +import { motion } from 'motion'; + +// 3. Internal packages +import { Button } from '@repo/ui'; +import { useAuth } from '@repo/lib'; + +// 4. Relative imports +import { Header } from './header'; +import { Footer } from './footer'; +``` + +## π Pull Request Process + +### Before Submitting + +1. Ensure your branch is up to date with `main` +2. Run all quality checks +3. Test your changes thoroughly +4. Update documentation if needed + +### PR Guidelines + +1. **Title**: Use a clear, descriptive title + - β
`feat: add semantic search to memory graph` + - β
`fix: resolve authentication redirect loop` + - β `update stuff` + +2. **Description**: Include: + - What changes you made and why + - Screenshots for UI changes + - Any breaking changes + - Related issue numbers + +3. **Size**: Keep PRs focused and reasonably sized + - Prefer multiple small PRs over one large PR + - Each PR should address a single concern + +### Review Process + +1. All PRs require at least one review +2. Address feedback promptly and professionally +3. Be open to suggestions and improvements +4. Maintain a collaborative attitude + +## π Reporting Issues + +### Bug Reports + +When reporting bugs, please include: + +- **Environment**: OS, Node.js version, browser +- **Steps to reproduce** the issue +- **Expected behavior** +- **Actual behavior** +- **Screenshots** if applicable +- **Error messages** or console logs + +### Feature Requests + +For feature requests, please provide: + +- **Problem statement**: What problem does this solve? +- **Proposed solution**: How should it work? +- **Alternatives considered**: Other approaches you've thought of +- **Additional context**: Any relevant information + +## ποΈ Architecture Guidelines + +### State Management + +- Use **Zustand** for global state +- Use **TanStack Query** for server state +- Keep state as local as possible +- Use proper TypeScript types for state + +### API Integration + +- Use the existing API client patterns +- Handle loading and error states properly +- Implement proper error boundaries +- Use optimistic updates where appropriate + +### Performance + +- Use React.memo() for expensive components +- Implement proper loading states +- Optimize images and assets +- Use code splitting where beneficial + +## π€ Community Guidelines + +### Code of Conduct + +- Be respectful and inclusive +- Welcome newcomers and help them learn +- Focus on constructive feedback +- Maintain professionalism in all interactions + +### Getting Help + +- **Discord**: Join our community server +- **GitHub Discussions**: For questions and ideas +- **Issues**: For bug reports and feature requests +- **Email**: [[email protected]](mailto:[email protected]) + +## π License + +By contributing to supermemory, you agree that your contributions will be licensed under the same license as the project. + +## π Recognition + +All contributors will be recognized in our README and release notes. We appreciate every contribution, no matter how small! + +--- + +Thank you for contributing to supermemory! Together, we're building the future of AI-powered knowledge management. π @@ -1,4 +1,4 @@ -<div align="center" style="padding-bottom:10px;padding-top:10px"> +<div align="center" style="padding-bottom:20px;padding-top:20px"> <img src="logo.svg" alt="supermemory Logo" width="400" /> </div> @@ -6,94 +6,70 @@ <img src="apps/web/public/landing-page.jpeg" alt="supermemory" width="100%" /> </div> -## β¨ Features +## Features ### Core Functionality -- **Add Memories from Any Content**: Easily add memories from URLs, PDFs, and plain textβjust paste, upload, or link. -- **Chat with Your Memories**: Converse with your stored content using natural language chat. -- **Supermemory MCP Integration**: Seamlessly connect with all major AI tools (Claude, Cursor, etc.) via Supermemory MCP. -- **Graph View for All Memories**: Visualize and explore your memories and their connections in an interactive graph mode. - -## ποΈ Architecture -This is a **Turborepo monorepo** +- **[Add Memories from Any Content](#add-memory)**: Easily add memories from URLs, PDFs, and plain textβjust paste, upload, or link. +- **[Chat with Your Memories](#chat-memories)**: Converse with your stored content using natural language chat. +- **[Supermemory MCP Integration](#mcp-integration)**: Seamlessly connect with all major AI tools (Claude, Cursor, etc.) via Supermemory MCP. -### Technology Stack -- **Frontend**: Next.js 15 with React 19 -- **Backend**: Hono API framework on Cloudflare Workers -- **Database**: PostgreSQL with Drizzle ORM -- **Authentication**: Better Auth with organization support -- **Package Manager**: Bun -- **Monorepo**: Turbo for build optimization -- **Styling**: Tailwind CSS with Radix UI components -- **Monitoring**: Sentry for error tracking and performance monitoring +## How do i use this? -### Project Structure -``` -βββ apps/ -β βββ web/ # Next.js web application -βββ packages/ # Shared packages and utilities -βββ CLAUDE.md # Development guidelines for AI assistants -βββ turbo.json # Turborepo configuration -βββ package.json # Root package configuration -``` +Go to [app.supermemory.ai](https://app.supermemory.ai) and sign into with your account -## π Getting Started +1. <a id="add-memory"></a>Start Adding Memory with your choose of format (Note, Link, File) +<div align="center" style="padding-bottom:10px;padding-top:10px"> + <img src="apps/web/public/add-memory.png" alt="supermemory" width="100%" /> +</div> -### Prerequisites -- **Bun** package manager +2. You can also Connect to your favourite services (Notion, Google Drive, OneDrive) +<div align="center" style="padding-bottom:10px;padding-top:10px"> + <img src="apps/web/public/add-connections.png" alt="supermemory" width="100%" /> +</div> -### Installation +3. <a id="chat-memories"></a>Once Memories are added, you can chat with Supermemory by clicking on "Open Chat" and retrieve info from your saved memories +<div align="center" style="padding-bottom:10px;padding-top:10px"> + <img src="apps/web/public/chat.png" alt="supermemory" width="100%" /> +</div> -1. **Clone the repository** - ```bash - git clone https://github.com/supermemoryai/supermemory-app.git - cd supermemory - ``` +4. <a id="mcp-integration"></a>Add MCP to your AI Tools (by clicking on "Connect to your AI" and select the AI tool you are trying to integrate) +<div align="center" style="padding-bottom:10px;padding-top:10px"> + <img src="apps/web/public/mcp.png" alt="supermemory" width="100%" /> +</div> -2. **Install dependencies** - ```bash - bun install - ``` +## Support -3. **Environment Setup** - - Create environment files for each app: - ```bash - # Copy environment templates - cp apps/web/.env.example apps/web/.env.local - ``` +Have questions or feedback? We're here to help: -### Development +- Email: [[email protected]](mailto:[email protected]) +- Documentation: [docs.supermemory.ai](https://docs.supermemory.ai) -#### Start all applications in development mode: -```bash -bun run dev -``` +## Contributing -This will start: -- Web app at `http://localhost:3000` -- API endpoints available through the web app +We welcome contributions from developers of all skill levels! Whether you're fixing bugs, adding features, or improving documentation, your help makes supermemory better for everyone. +### Quick Start for Contributors -## π§ͺ Development Workflow +1. **Fork and clone** the repository +2. **Install dependencies** with `bun install` +3. **Set up your environment** by copying `.env.example` to `.env.local` +4. **Start developing** with `bun run dev` -### Code Quality -- **Linting & Formatting**: Uses Biome for consistent code style -- **Type Safety**: Strict TypeScript configuration across all packages +For detailed guidelines, development setup, coding standards, and the complete contribution workflow, please see our [**Contributing Guide**](CONTRIBUTE.md). -## π€ Contributing +### Ways to Contribute -### Development Guidelines -- Follow the code style enforced by Biome -- Write tests for new features -- Update documentation when adding new functionality -- Ensure all checks pass before submitting PRs +- π **Bug fixes** - Help us squash those pesky issues +- β¨ **New features** - Add functionality that users will love +- π¨ **UI/UX improvements** - Make the interface more intuitive +- β‘ **Performance optimizations** - Help us make supermemory faster +Check out our [Issues](https://github.com/supermemoryai/supermemory/issues) page for `good first issue` and `help wanted` labels to get started! -## π¬ Support & Community +## Updates & Roadmap -- **Issues**: [GitHub Issues](https://github.com/supermemoryai/supermemory-app/issues) -- **Email**: [[email protected]](mailto:[email protected]) -- **Twitter**: [@supermemoryai](https://x.com/supermemoryai) +Stay up to date with the latest improvements: +- [Changelog](https://docs.supermemory.ai/changelog/overview) +- [X](https://x.com/supermemoryai) diff --git a/apps/web/components/views/connections-tab-content.tsx b/apps/web/components/views/connections-tab-content.tsx index 177c2a4d..a94ed708 100644 --- a/apps/web/components/views/connections-tab-content.tsx +++ b/apps/web/components/views/connections-tab-content.tsx @@ -1,382 +1,342 @@ -"use client" +'use client'; -import { $fetch } from "@lib/api" +import { $fetch } from '@lib/api'; import { - fetchConnectionsFeature, - fetchConsumerProProduct, -} from "@repo/lib/queries" -import { Button } from "@repo/ui/components/button" + fetchConnectionsFeature, + fetchConsumerProProduct, +} from '@repo/lib/queries'; +import { Button } from '@repo/ui/components/button'; import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from "@repo/ui/components/dialog" -import { Skeleton } from "@repo/ui/components/skeleton" -import type { ConnectionResponseSchema } from "@repo/validation/api" -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" -import { GoogleDrive, Notion, OneDrive } from "@ui/assets/icons" -import { useCustomer } from "autumn-js/react" -import { Plus, Trash2 } from "lucide-react" -import { AnimatePresence, motion } from "motion/react" -import Link from "next/link" -import { useEffect, useState } from "react" -import { toast } from "sonner" -import type { z } from "zod" -import { useProject } from "@/stores" -import { analytics } from "@/lib/analytics" + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from '@repo/ui/components/dialog'; +import { Skeleton } from '@repo/ui/components/skeleton'; +import type { ConnectionResponseSchema } from '@repo/validation/api'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { GoogleDrive, Notion, OneDrive } from '@ui/assets/icons'; +import { useCustomer } from 'autumn-js/react'; +import { Trash2 } from 'lucide-react'; +import { AnimatePresence, motion } from 'motion/react'; +import Link from 'next/link'; +import { useEffect } from 'react'; +import { toast } from 'sonner'; +import type { z } from 'zod'; +import { useProject } from '@/stores'; +import { analytics } from '@/lib/analytics'; // Define types -type Connection = z.infer<typeof ConnectionResponseSchema> +type Connection = z.infer<typeof ConnectionResponseSchema>; // Connector configurations const CONNECTORS = { - "google-drive": { - title: "Google Drive", - description: "Connect your Google Docs, Sheets, and Slides", - icon: GoogleDrive, - }, - notion: { - title: "Notion", - description: "Import your Notion pages and databases", - icon: Notion, - }, - onedrive: { - title: "OneDrive", - description: "Access your Microsoft Office documents", - icon: OneDrive, - }, -} as const + 'google-drive': { + title: 'Google Drive', + description: 'Connect your Google Docs, Sheets, and Slides', + icon: GoogleDrive, + }, + notion: { + title: 'Notion', + description: 'Import your Notion pages and databases', + icon: Notion, + }, + onedrive: { + title: 'OneDrive', + description: 'Access your Microsoft Office documents', + icon: OneDrive, + }, +} as const; -type ConnectorProvider = keyof typeof CONNECTORS +type ConnectorProvider = keyof typeof CONNECTORS; export function ConnectionsTabContent() { - const queryClient = useQueryClient() - const [showAddDialog, setShowAddDialog] = useState(false) - const { selectedProject } = useProject() - const autumn = useCustomer() + const queryClient = useQueryClient(); + const { selectedProject } = useProject(); + const autumn = useCustomer(); - const handleUpgrade = async () => { - try { - await autumn.attach({ - productId: "consumer_pro", - successUrl: "https://app.supermemory.ai/", - }) - window.location.reload() - } catch (error) { - console.error(error) - } - } + const handleUpgrade = async () => { + try { + await autumn.attach({ + productId: 'consumer_pro', + successUrl: 'https://app.supermemory.ai/', + }); + window.location.reload(); + } catch (error) { + console.error(error); + } + }; - const { data: connectionsCheck } = fetchConnectionsFeature(autumn as any) - const connectionsUsed = connectionsCheck?.balance ?? 0 - const connectionsLimit = connectionsCheck?.included_usage ?? 0 + const { data: connectionsCheck } = fetchConnectionsFeature(autumn as any); + const connectionsUsed = connectionsCheck?.balance ?? 0; + const connectionsLimit = connectionsCheck?.included_usage ?? 0; - const { data: proCheck } = fetchConsumerProProduct(autumn as any) - const isProUser = proCheck?.allowed ?? false + const { data: proCheck } = fetchConsumerProProduct(autumn as any); + const isProUser = proCheck?.allowed ?? false; - const canAddConnection = connectionsUsed < connectionsLimit + const canAddConnection = connectionsUsed < connectionsLimit; - // Fetch connections - const { - data: connections = [], - isLoading, - error, - } = useQuery({ - queryKey: ["connections"], - queryFn: async () => { - const response = await $fetch("@post/connections/list", { - body: { - containerTags: [], - }, - }) + // Fetch connections + const { + data: connections = [], + isLoading, + error, + } = useQuery({ + queryKey: ['connections'], + queryFn: async () => { + const response = await $fetch('@post/connections/list', { + body: { + containerTags: [], + }, + }); - if (response.error) { - throw new Error(response.error?.message || "Failed to load connections") - } + if (response.error) { + throw new Error( + response.error?.message || 'Failed to load connections' + ); + } - return response.data as Connection[] - }, - staleTime: 30 * 1000, - refetchInterval: 60 * 1000, - }) + return response.data as Connection[]; + }, + staleTime: 30 * 1000, + refetchInterval: 60 * 1000, + }); - // Show error toast if connections fail to load - useEffect(() => { - if (error) { - toast.error("Failed to load connections", { - description: error instanceof Error ? error.message : "Unknown error", - }) - } - }, [error]) + // Show error toast if connections fail to load + useEffect(() => { + if (error) { + toast.error('Failed to load connections', { + description: error instanceof Error ? error.message : 'Unknown error', + }); + } + }, [error]); - // Add connection mutation - const addConnectionMutation = useMutation({ - mutationFn: async (provider: ConnectorProvider) => { - // Check if user can add connections - if (!canAddConnection && !isProUser) { - throw new Error( - "Free plan doesn't include connections. Upgrade to Pro for unlimited connections.", - ) - } + // Add connection mutation + const addConnectionMutation = useMutation({ + mutationFn: async (provider: ConnectorProvider) => { + // Check if user can add connections + if (!canAddConnection && !isProUser) { + throw new Error( + "Free plan doesn't include connections. Upgrade to Pro for unlimited connections." + ); + } - const response = await $fetch("@post/connections/:provider", { - params: { provider }, - body: { - redirectUrl: window.location.href, - containerTags: [selectedProject], - }, - }) + const response = await $fetch('@post/connections/:provider', { + params: { provider }, + body: { + redirectUrl: window.location.href, + containerTags: [selectedProject], + }, + }); - // biome-ignore lint/style/noNonNullAssertion: its fine - if ("data" in response && !("error" in response.data!)) { - return response.data - } + // biome-ignore lint/style/noNonNullAssertion: its fine + if ('data' in response && !('error' in response.data!)) { + return response.data; + } - throw new Error(response.error?.message || "Failed to connect") - }, - onSuccess: (data, provider) => { - analytics.connectionAdded(provider) - analytics.connectionAuthStarted() - if (data?.authLink) { - window.location.href = data.authLink - } - }, - onError: (error, provider) => { - analytics.connectionAuthFailed() - toast.error(`Failed to connect ${provider}`, { - description: error instanceof Error ? error.message : "Unknown error", - }) - }, - }) + throw new Error(response.error?.message || 'Failed to connect'); + }, + onSuccess: (data, provider) => { + analytics.connectionAdded(provider); + analytics.connectionAuthStarted(); + if (data?.authLink) { + window.location.href = data.authLink; + } + }, + onError: (error, provider) => { + analytics.connectionAuthFailed(); + toast.error(`Failed to connect ${provider}`, { + description: error instanceof Error ? error.message : 'Unknown error', + }); + }, + }); - // Delete connection mutation - const deleteConnectionMutation = useMutation({ - mutationFn: async (connectionId: string) => { - await $fetch(`@delete/connections/${connectionId}`) - }, - onSuccess: () => { - analytics.connectionDeleted() - toast.success( - "Connection removal has started. supermemory will permanently delete the documents in the next few minutes.", - ) - queryClient.invalidateQueries({ queryKey: ["connections"] }) - }, - onError: (error) => { - toast.error("Failed to remove connection", { - description: error instanceof Error ? error.message : "Unknown error", - }) - }, - }) + // Delete connection mutation + const deleteConnectionMutation = useMutation({ + mutationFn: async (connectionId: string) => { + await $fetch(`@delete/connections/${connectionId}`); + }, + onSuccess: () => { + analytics.connectionDeleted(); + toast.success( + 'Connection removal has started. supermemory will permanently delete the documents in the next few minutes.' + ); + queryClient.invalidateQueries({ queryKey: ['connections'] }); + }, + onError: (error) => { + toast.error('Failed to remove connection', { + description: error instanceof Error ? error.message : 'Unknown error', + }); + }, + }); - const getProviderIcon = (provider: string) => { - const connector = CONNECTORS[provider as ConnectorProvider] - if (connector) { - const Icon = connector.icon - return <Icon className="h-10 w-10" /> - } - return <span className="text-2xl">π</span> - } + const getProviderIcon = (provider: string) => { + const connector = CONNECTORS[provider as ConnectorProvider]; + if (connector) { + const Icon = connector.icon; + return <Icon className="h-10 w-10" />; + } + return <span className="text-2xl">π</span>; + }; - return ( - <div className="space-y-4"> - <div className="flex justify-between items-center mb-4"> - <div> - <p className="text-sm text-white/70"> - Connect your favorite services to import documents - </p> - {!isProUser && ( - <p className="text-xs text-white/50 mt-1"> - Connections require a Pro subscription - </p> - )} - </div> - <motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}> - <Button - className="bg-white/10 hover:bg-white/20 text-white border-white/20" - disabled={!canAddConnection && !isProUser} - onClick={() => setShowAddDialog(true)} - size="sm" - > - <Plus className="h-4 w-4 mr-2" /> - Add - </Button> - </motion.div> - </div> + return ( + <div className="space-y-4"> + <div className="mb-4"> + <p className="text-sm text-white/70"> + Connect your favorite services to import documents + </p> + {!isProUser && ( + <p className="text-xs text-white/50 mt-1"> + Connections require a Pro subscription + </p> + )} + </div> - {/* Show upgrade prompt for free users */} - {!isProUser && ( - <motion.div - animate={{ opacity: 1, y: 0 }} - className="p-4 bg-yellow-500/10 border border-yellow-500/20 rounded-lg" - initial={{ opacity: 0, y: -10 }} - > - <p className="text-sm text-yellow-400 mb-2"> - π Connections are a Pro feature - </p> - <p className="text-xs text-white/60 mb-3"> - Connect Google Drive, Notion, OneDrive and more to automatically - sync your documents. - </p> - <Button - asChild - className="bg-yellow-500/20 hover:bg-yellow-500/30 text-yellow-400 border-yellow-500/30" - onClick={handleUpgrade} - size="sm" - variant="secondary" - > - Upgrade to Pro - </Button> - </motion.div> - )} + {/* Show upgrade prompt for free users */} + {!isProUser && ( + <motion.div + animate={{ opacity: 1, y: 0 }} + className="p-4 bg-yellow-500/10 border border-yellow-500/20 rounded-lg" + initial={{ opacity: 0, y: -10 }} + > + <p className="text-sm text-yellow-400 mb-2"> + π Connections are a Pro feature + </p> + <p className="text-xs text-white/60 mb-3"> + Connect Google Drive, Notion, OneDrive and more to automatically + sync your documents. + </p> + <Button + asChild + className="bg-yellow-500/20 hover:bg-yellow-500/30 text-yellow-400 border-yellow-500/30" + onClick={handleUpgrade} + size="sm" + variant="secondary" + > + Upgrade to Pro + </Button> + </motion.div> + )} - {isLoading ? ( - <div className="space-y-3"> - {[...Array(2)].map((_, i) => ( - <motion.div - animate={{ opacity: 1 }} - className="p-4 bg-white/5 rounded-lg" - initial={{ opacity: 0 }} - key={`skeleton-${Date.now()}-${i}`} - transition={{ delay: i * 0.1 }} - > - <Skeleton className="h-12 w-full bg-white/10" /> - </motion.div> - ))} - </div> - ) : connections.length === 0 ? ( - <motion.div - animate={{ opacity: 1, scale: 1 }} - className="text-center py-8" - initial={{ opacity: 0, scale: 0.9 }} - transition={{ type: "spring", damping: 20 }} - > - <p className="text-white/50 mb-4">No connections yet</p> - <motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}> - <Button - className="bg-white/10 hover:bg-white/20 text-white border-white/20" - onClick={() => setShowAddDialog(true)} - size="sm" - variant="secondary" - > - Add Your First Connection - </Button> - </motion.div> - </motion.div> - ) : ( - <motion.div className="space-y-2"> - <AnimatePresence> - {connections.map((connection, index) => ( - <motion.div - animate={{ opacity: 1, x: 0 }} - className="flex items-center justify-between p-3 bg-white/5 rounded-lg hover:bg-white/10 transition-colors" - exit={{ opacity: 0, x: 20 }} - initial={{ opacity: 0, x: -20 }} - key={connection.id} - layout - transition={{ delay: index * 0.05 }} - > - <div className="flex items-center gap-3"> - <motion.div - animate={{ rotate: 0, opacity: 1 }} - initial={{ rotate: -180, opacity: 0 }} - transition={{ delay: index * 0.05 + 0.2 }} - > - {getProviderIcon(connection.provider)} - </motion.div> - <div> - <p className="font-medium text-white capitalize"> - {connection.provider.replace("-", " ")} - </p> - {connection.email && ( - <p className="text-sm text-white/60"> - {connection.email} - </p> - )} - </div> - </div> - <motion.div - whileHover={{ scale: 1.1 }} - whileTap={{ scale: 0.9 }} - > - <Button - className="text-white/50 hover:text-red-400" - disabled={deleteConnectionMutation.isPending} - onClick={() => - deleteConnectionMutation.mutate(connection.id) - } - size="icon" - variant="ghost" - > - <Trash2 className="h-4 w-4" /> - </Button> - </motion.div> - </motion.div> - ))} - </AnimatePresence> - </motion.div> - )} + {isLoading ? ( + <div className="space-y-3"> + {[...Array(2)].map((_, i) => ( + <motion.div + animate={{ opacity: 1 }} + className="p-4 bg-white/5 rounded-lg" + initial={{ opacity: 0 }} + key={`skeleton-${Date.now()}-${i}`} + transition={{ delay: i * 0.1 }} + > + <Skeleton className="h-12 w-full bg-white/10" /> + </motion.div> + ))} + </div> + ) : connections.length === 0 ? ( + <motion.div + animate={{ opacity: 1, scale: 1 }} + className="text-center py-4" + initial={{ opacity: 0, scale: 0.9 }} + transition={{ type: 'spring', damping: 20 }} + > + <p className="text-white/50 mb-2">No connections yet</p> + <p className="text-xs text-white/40"> + Choose a service below to connect + </p> + </motion.div> + ) : ( + <motion.div className="space-y-2"> + <AnimatePresence> + {connections.map((connection, index) => ( + <motion.div + animate={{ opacity: 1, x: 0 }} + className="flex items-center justify-between p-3 bg-white/5 rounded-lg hover:bg-white/10 transition-colors" + exit={{ opacity: 0, x: 20 }} + initial={{ opacity: 0, x: -20 }} + key={connection.id} + layout + transition={{ delay: index * 0.05 }} + > + <div className="flex items-center gap-3"> + <motion.div + animate={{ rotate: 0, opacity: 1 }} + initial={{ rotate: -180, opacity: 0 }} + transition={{ delay: index * 0.05 + 0.2 }} + > + {getProviderIcon(connection.provider)} + </motion.div> + <div> + <p className="font-medium text-white capitalize"> + {connection.provider.replace('-', ' ')} + </p> + {connection.email && ( + <p className="text-sm text-white/60"> + {connection.email} + </p> + )} + </div> + </div> + <motion.div + whileHover={{ scale: 1.1 }} + whileTap={{ scale: 0.9 }} + > + <Button + className="text-white/50 hover:text-red-400" + disabled={deleteConnectionMutation.isPending} + onClick={() => + deleteConnectionMutation.mutate(connection.id) + } + size="icon" + variant="ghost" + > + <Trash2 className="h-4 w-4" /> + </Button> + </motion.div> + </motion.div> + ))} + </AnimatePresence> + </motion.div> + )} - {/* Add Connection Dialog */} - <AnimatePresence> - {showAddDialog && ( - <Dialog onOpenChange={setShowAddDialog} open={showAddDialog}> - <DialogContent className="sm:max-w-2xl bg-black/90 backdrop-blur-xl border-white/10 text-white"> - <motion.div - animate={{ opacity: 1, scale: 1 }} - exit={{ opacity: 0, scale: 0.95 }} - initial={{ opacity: 0, scale: 0.95 }} - > - <DialogHeader> - <DialogTitle>Add a Connection</DialogTitle> - <DialogDescription className="text-white/60"> - Choose a service to connect and import your documents - </DialogDescription> - </DialogHeader> - <div className="grid gap-3 py-4"> - {Object.entries(CONNECTORS).map( - ([provider, config], index) => { - const Icon = config.icon - return ( - <motion.div - animate={{ opacity: 1, y: 0 }} - initial={{ opacity: 0, y: 20 }} - key={provider} - transition={{ delay: index * 0.05 }} - whileHover={{ scale: 1.02 }} - whileTap={{ scale: 0.98 }} - > - <Button - className="justify-start h-auto p-4 bg-white/5 hover:bg-white/10 border-white/10 text-white w-full" - disabled={addConnectionMutation.isPending} - onClick={() => { - addConnectionMutation.mutate( - provider as ConnectorProvider, - ) - setShowAddDialog(false) - // onClose?.() - }} - variant="outline" - > - <Icon className="h-8 w-8 mr-3" /> - <div className="text-left"> - <div className="font-medium">{config.title}</div> - <div className="text-sm text-white/60 mt-0.5"> - {config.description} - </div> - </div> - </Button> - </motion.div> - ) - }, - )} - </div> - </motion.div> - </DialogContent> - </Dialog> - )} - </AnimatePresence> - </div> - ) + {/* Available Connections Section */} + <div className="mt-6"> + <h3 className="text-lg font-medium text-white mb-4"> + Available Connections + </h3> + <div className="grid gap-3"> + {Object.entries(CONNECTORS).map(([provider, config], index) => { + const Icon = config.icon; + return ( + <motion.div + animate={{ opacity: 1, y: 0 }} + initial={{ opacity: 0, y: 20 }} + key={provider} + transition={{ delay: index * 0.05 }} + whileHover={{ scale: 1.02 }} + whileTap={{ scale: 0.98 }} + > + <Button + className="justify-start h-auto p-4 bg-white/5 hover:bg-white/10 border-white/10 text-white w-full" + disabled={addConnectionMutation.isPending} + onClick={() => { + addConnectionMutation.mutate(provider as ConnectorProvider); + }} + variant="outline" + > + <Icon className="h-8 w-8 mr-3" /> + <div className="text-left"> + <div className="font-medium">{config.title}</div> + <div className="text-sm text-white/60 mt-0.5"> + {config.description} + </div> + </div> + </Button> + </motion.div> + ); + })} + </div> + </div> + </div> + ); } diff --git a/apps/web/public/add-connections.png b/apps/web/public/add-connections.png Binary files differnew file mode 100644 index 00000000..1f49f4da --- /dev/null +++ b/apps/web/public/add-connections.png diff --git a/apps/web/public/add-memory.png b/apps/web/public/add-memory.png Binary files differnew file mode 100644 index 00000000..248ea3e3 --- /dev/null +++ b/apps/web/public/add-memory.png diff --git a/apps/web/public/chat.png b/apps/web/public/chat.png Binary files differnew file mode 100644 index 00000000..869c27ec --- /dev/null +++ b/apps/web/public/chat.png diff --git a/apps/web/public/mcp.png b/apps/web/public/mcp.png Binary files differnew file mode 100644 index 00000000..05ceb619 --- /dev/null +++ b/apps/web/public/mcp.png |