mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-29 19:35:20 +02:00
Merge pull request #738 from elammertsma/dev
Updated README.md, reorganized and fixed sidebar
This commit is contained in:
commit
46dbd75440
2 changed files with 47 additions and 15 deletions
|
|
@ -52,8 +52,10 @@ https://github.com/user-attachments/assets/a0a16566-6967-4374-ac51-9b3e07fbecd7
|
||||||
- Interact in Natural Language and get cited answers.
|
- Interact in Natural Language and get cited answers.
|
||||||
### 📄 **Cited Answers**
|
### 📄 **Cited Answers**
|
||||||
- Get Cited answers just like Perplexity.
|
- Get Cited answers just like Perplexity.
|
||||||
|
### 🧩 **Universal Compatibility**
|
||||||
|
- Connect virtually any inference provider via the OpenAI spec and LiteLLM.
|
||||||
### 🔔 **Privacy & Local LLM Support**
|
### 🔔 **Privacy & Local LLM Support**
|
||||||
- Works Flawlessly with Ollama local LLMs.
|
- Works Flawlessly with local LLMs like vLLM and Ollama.
|
||||||
### 🏠 **Self Hostable**
|
### 🏠 **Self Hostable**
|
||||||
- Open source and easy to deploy locally.
|
- Open source and easy to deploy locally.
|
||||||
### 👥 **Team Collaboration with RBAC**
|
### 👥 **Team Collaboration with RBAC**
|
||||||
|
|
@ -61,6 +63,7 @@ https://github.com/user-attachments/assets/a0a16566-6967-4374-ac51-9b3e07fbecd7
|
||||||
- Invite team members with customizable roles (Owner, Admin, Editor, Viewer)
|
- Invite team members with customizable roles (Owner, Admin, Editor, Viewer)
|
||||||
- Granular permissions for documents, chats, connectors, and settings
|
- Granular permissions for documents, chats, connectors, and settings
|
||||||
- Share knowledge bases securely within your organization
|
- Share knowledge bases securely within your organization
|
||||||
|
- Team chats update in real-time and "Chat about the chat" in comment threads
|
||||||
### 🎙️ Podcasts
|
### 🎙️ Podcasts
|
||||||
- Blazingly fast podcast generation agent. (Creates a 3-minute podcast in under 20 seconds.)
|
- Blazingly fast podcast generation agent. (Creates a 3-minute podcast in under 20 seconds.)
|
||||||
- Convert your chat conversations into engaging audio content
|
- Convert your chat conversations into engaging audio content
|
||||||
|
|
@ -237,6 +240,8 @@ Before self-hosting installation, make sure to complete the [prerequisite setup
|
||||||
|
|
||||||
### **BackEnd**
|
### **BackEnd**
|
||||||
|
|
||||||
|
- **LiteLLM**: Universal LLM integration supporting 100+ models (OpenAI, Anthropic, Ollama, etc.)
|
||||||
|
|
||||||
- **FastAPI**: Modern, fast web framework for building APIs with Python
|
- **FastAPI**: Modern, fast web framework for building APIs with Python
|
||||||
|
|
||||||
- **PostgreSQL with pgvector**: Database with vector search capabilities for similarity searches
|
- **PostgreSQL with pgvector**: Database with vector search capabilities for similarity searches
|
||||||
|
|
@ -253,8 +258,6 @@ Before self-hosting installation, make sure to complete the [prerequisite setup
|
||||||
|
|
||||||
- **LangChain**: Framework for developing AI-powered applications.
|
- **LangChain**: Framework for developing AI-powered applications.
|
||||||
|
|
||||||
- **LiteLLM**: Universal LLM integration supporting 100+ models (OpenAI, Anthropic, Ollama, etc.)
|
|
||||||
|
|
||||||
- **Rerankers**: Advanced result ranking for improved search relevance
|
- **Rerankers**: Advanced result ranking for improved search relevance
|
||||||
|
|
||||||
- **Hybrid Search**: Combines vector similarity and full-text search for optimal results using Reciprocal Rank Fusion (RRF)
|
- **Hybrid Search**: Combines vector similarity and full-text search for optimal results using Reciprocal Rank Fusion (RRF)
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue, useSetAtom } from "jotai";
|
||||||
import { Inbox, LogOut, SquareLibrary, Trash2 } from "lucide-react";
|
import { Inbox, LogOut, SquareLibrary, Trash2 } from "lucide-react";
|
||||||
import { useParams, usePathname, useRouter } from "next/navigation";
|
import { useParams, usePathname, useRouter } from "next/navigation";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { currentThreadAtom, resetCurrentThreadAtom } from "@/atoms/chat/current-thread.atom";
|
||||||
import { deleteSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms";
|
import { deleteSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms";
|
||||||
import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||||
import { currentUserAtom } from "@/atoms/user/user-query.atoms";
|
import { currentUserAtom } from "@/atoms/user/user-query.atoms";
|
||||||
|
|
@ -66,11 +67,16 @@ export function LayoutDataProvider({
|
||||||
const { data: user } = useAtomValue(currentUserAtom);
|
const { data: user } = useAtomValue(currentUserAtom);
|
||||||
const { data: searchSpacesData, refetch: refetchSearchSpaces } = useAtomValue(searchSpacesAtom);
|
const { data: searchSpacesData, refetch: refetchSearchSpaces } = useAtomValue(searchSpacesAtom);
|
||||||
const { mutateAsync: deleteSearchSpace } = useAtomValue(deleteSearchSpaceMutationAtom);
|
const { mutateAsync: deleteSearchSpace } = useAtomValue(deleteSearchSpaceMutationAtom);
|
||||||
|
const currentThreadState = useAtomValue(currentThreadAtom);
|
||||||
|
const resetCurrentThread = useSetAtom(resetCurrentThreadAtom);
|
||||||
|
|
||||||
// Current IDs from URL
|
// State for handling new chat navigation when router is out of sync
|
||||||
|
const [pendingNewChat, setPendingNewChat] = useState(false);
|
||||||
|
|
||||||
|
// Current IDs from URL, with fallback to atom for replaceState updates
|
||||||
const currentChatId = params?.chat_id
|
const currentChatId = params?.chat_id
|
||||||
? Number(Array.isArray(params.chat_id) ? params.chat_id[0] : params.chat_id)
|
? Number(Array.isArray(params.chat_id) ? params.chat_id[0] : params.chat_id)
|
||||||
: null;
|
: currentThreadState.id;
|
||||||
|
|
||||||
// Fetch current search space (for caching purposes)
|
// Fetch current search space (for caching purposes)
|
||||||
useQuery({
|
useQuery({
|
||||||
|
|
@ -122,6 +128,17 @@ export function LayoutDataProvider({
|
||||||
const [isDeletingSearchSpace, setIsDeletingSearchSpace] = useState(false);
|
const [isDeletingSearchSpace, setIsDeletingSearchSpace] = useState(false);
|
||||||
const [isLeavingSearchSpace, setIsLeavingSearchSpace] = useState(false);
|
const [isLeavingSearchSpace, setIsLeavingSearchSpace] = useState(false);
|
||||||
|
|
||||||
|
// Effect to complete new chat navigation after router syncs
|
||||||
|
// This runs when handleNewChat detected an out-of-sync state and triggered a sync
|
||||||
|
useEffect(() => {
|
||||||
|
if (pendingNewChat && params?.chat_id) {
|
||||||
|
// Router is now synced (chat_id is in params), complete navigation to new-chat
|
||||||
|
resetCurrentThread();
|
||||||
|
router.push(`/dashboard/${searchSpaceId}/new-chat`);
|
||||||
|
setPendingNewChat(false);
|
||||||
|
}
|
||||||
|
}, [pendingNewChat, params?.chat_id, router, searchSpaceId, resetCurrentThread]);
|
||||||
|
|
||||||
const searchSpaces: SearchSpace[] = useMemo(() => {
|
const searchSpaces: SearchSpace[] = useMemo(() => {
|
||||||
if (!searchSpacesData || !Array.isArray(searchSpacesData)) return [];
|
if (!searchSpacesData || !Array.isArray(searchSpacesData)) return [];
|
||||||
return searchSpacesData.map((space) => ({
|
return searchSpacesData.map((space) => ({
|
||||||
|
|
@ -172,12 +189,6 @@ export function LayoutDataProvider({
|
||||||
// Navigation items
|
// Navigation items
|
||||||
const navItems: NavItem[] = useMemo(
|
const navItems: NavItem[] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
|
||||||
title: "Documents",
|
|
||||||
url: `/dashboard/${searchSpaceId}/documents`,
|
|
||||||
icon: SquareLibrary,
|
|
||||||
isActive: pathname?.includes("/documents"),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: "Inbox",
|
title: "Inbox",
|
||||||
url: "#inbox", // Special URL to indicate this is handled differently
|
url: "#inbox", // Special URL to indicate this is handled differently
|
||||||
|
|
@ -185,6 +196,12 @@ export function LayoutDataProvider({
|
||||||
isActive: isInboxSidebarOpen,
|
isActive: isInboxSidebarOpen,
|
||||||
badge: unreadCount > 0 ? formatInboxCount(unreadCount) : undefined,
|
badge: unreadCount > 0 ? formatInboxCount(unreadCount) : undefined,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Documents",
|
||||||
|
url: `/dashboard/${searchSpaceId}/documents`,
|
||||||
|
icon: SquareLibrary,
|
||||||
|
isActive: pathname?.includes("/documents"),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
[searchSpaceId, pathname, isInboxSidebarOpen, unreadCount]
|
[searchSpaceId, pathname, isInboxSidebarOpen, unreadCount]
|
||||||
);
|
);
|
||||||
|
|
@ -289,8 +306,20 @@ export function LayoutDataProvider({
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleNewChat = useCallback(() => {
|
const handleNewChat = useCallback(() => {
|
||||||
router.push(`/dashboard/${searchSpaceId}/new-chat`);
|
// Check if router is out of sync (thread created via replaceState but params don't have chat_id)
|
||||||
}, [router, searchSpaceId]);
|
const isOutOfSync = currentThreadState.id !== null && !params?.chat_id;
|
||||||
|
|
||||||
|
if (isOutOfSync) {
|
||||||
|
// First sync Next.js router by navigating to the current chat's actual URL
|
||||||
|
// This updates the router's internal state to match the browser URL
|
||||||
|
router.replace(`/dashboard/${searchSpaceId}/new-chat/${currentThreadState.id}`);
|
||||||
|
// Set flag to trigger navigation to new-chat after params update
|
||||||
|
setPendingNewChat(true);
|
||||||
|
} else {
|
||||||
|
// Normal navigation - router is in sync
|
||||||
|
router.push(`/dashboard/${searchSpaceId}/new-chat`);
|
||||||
|
}
|
||||||
|
}, [router, searchSpaceId, currentThreadState.id, params?.chat_id]);
|
||||||
|
|
||||||
const handleChatSelect = useCallback(
|
const handleChatSelect = useCallback(
|
||||||
(chat: ChatItem) => {
|
(chat: ChatItem) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue