+
);
}
diff --git a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx
index bbafa9703..0c0b0cbc2 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx
@@ -1,7 +1,6 @@
"use client";
import { useAtomValue, useSetAtom } from "jotai";
-import { Loader2 } from "lucide-react";
import { useParams, usePathname, useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
import type React from "react";
@@ -19,6 +18,7 @@ import { DashboardBreadcrumb } from "@/components/dashboard-breadcrumb";
import { LayoutDataProvider } from "@/components/layout";
import { OnboardingTour } from "@/components/onboarding-tour";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { UnifiedLoadingScreen } from "@/components/ui/unified-loading-screen";
export function DashboardClientLayout({
children,
@@ -153,23 +153,10 @@ export function DashboardClientLayout({
isAutoConfiguring
) {
return (
-
);
}
diff --git a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx
index 59e7878c4..4509a44a7 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx
@@ -9,6 +9,7 @@ import {
import { useQueryClient } from "@tanstack/react-query";
import { useAtomValue, useSetAtom } from "jotai";
import { useParams, useSearchParams } from "next/navigation";
+import { useTranslations } from "next-intl";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import { z } from "zod";
@@ -34,6 +35,7 @@ import { GeneratePodcastToolUI } from "@/components/tool-ui/generate-podcast";
import { LinkPreviewToolUI } from "@/components/tool-ui/link-preview";
import { ScrapeWebpageToolUI } from "@/components/tool-ui/scrape-webpage";
import { RecallMemoryToolUI, SaveMemoryToolUI } from "@/components/tool-ui/user-memory";
+import { Spinner } from "@/components/ui/spinner";
import { useChatSessionStateSync } from "@/hooks/use-chat-session-state";
import { useMessagesElectric } from "@/hooks/use-messages-electric";
// import { WriteTodosToolUI } from "@/components/tool-ui/write-todos";
@@ -236,6 +238,7 @@ interface ThinkingStepData {
}
export default function NewChatPage() {
+ const t = useTranslations("dashboard");
const params = useParams();
const queryClient = useQueryClient();
const [isInitializing, setIsInitializing] = useState(true);
@@ -1475,8 +1478,9 @@ export default function NewChatPage() {
// Show loading state only when loading an existing thread
if (isInitializing) {
return (
-
-
Loading chat...
+
);
}
diff --git a/surfsense_web/app/dashboard/layout.tsx b/surfsense_web/app/dashboard/layout.tsx
index 71cd6275f..8ffef229c 100644
--- a/surfsense_web/app/dashboard/layout.tsx
+++ b/surfsense_web/app/dashboard/layout.tsx
@@ -1,8 +1,8 @@
"use client";
-import { Loader2 } from "lucide-react";
import { useEffect, useState } from "react";
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { useTranslations } from "next-intl";
+import { UnifiedLoadingScreen } from "@/components/ui/unified-loading-screen";
import { getBearerToken, redirectToLogin } from "@/lib/auth-utils";
interface DashboardLayoutProps {
@@ -10,6 +10,7 @@ interface DashboardLayoutProps {
}
export default function DashboardLayout({ children }: DashboardLayoutProps) {
+ const t = useTranslations("dashboard");
const [isCheckingAuth, setIsCheckingAuth] = useState(true);
useEffect(() => {
@@ -25,19 +26,7 @@ export default function DashboardLayout({ children }: DashboardLayoutProps) {
// Show loading screen while checking authentication
if (isCheckingAuth) {
- return (
-
-
-
- Loading Dashboard
- Checking authentication...
-
-
-
-
-
-
- );
+ return
;
}
return (
diff --git a/surfsense_web/app/dashboard/loading.tsx b/surfsense_web/app/dashboard/loading.tsx
new file mode 100644
index 000000000..b18c5dd75
--- /dev/null
+++ b/surfsense_web/app/dashboard/loading.tsx
@@ -0,0 +1,21 @@
+"use client";
+
+import { useTranslations } from "next-intl";
+import { Spinner } from "@/components/ui/spinner";
+
+export default function DashboardLoading() {
+ const t = useTranslations("common");
+ return (
+
+
+
+
+
+
+ {t("loading")}
+
+
+
+ );
+}
+
diff --git a/surfsense_web/app/dashboard/page.tsx b/surfsense_web/app/dashboard/page.tsx
index 767ce5201..440f37123 100644
--- a/surfsense_web/app/dashboard/page.tsx
+++ b/surfsense_web/app/dashboard/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { useAtomValue } from "jotai";
-import { AlertCircle, Loader2, Plus, Search } from "lucide-react";
+import { AlertCircle, Plus, Search } from "lucide-react";
import { motion } from "motion/react";
import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
@@ -18,37 +18,7 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
-
-function LoadingScreen() {
- const t = useTranslations("dashboard");
- return (
-
-
-
-
- {t("loading")}
- {t("fetching_spaces")}
-
-
-
-
-
-
-
- {t("may_take_moment")}
-
-
-
-
- );
-}
+import { UnifiedLoadingScreen } from "@/components/ui/unified-loading-screen";
function ErrorScreen({ message }: { message: string }) {
const t = useTranslations("dashboard");
@@ -121,6 +91,7 @@ export default function DashboardPage() {
const router = useRouter();
const [showCreateDialog, setShowCreateDialog] = useState(false);
+ const t = useTranslations("dashboard");
const { data: searchSpaces = [], isLoading, error } = useAtomValue(searchSpacesAtom);
useEffect(() => {
@@ -131,11 +102,11 @@ export default function DashboardPage() {
}
}, [isLoading, searchSpaces, router]);
- if (isLoading) return
;
+ if (isLoading) return
;
if (error) return
;
if (searchSpaces.length > 0) {
- return
;
+ return
;
}
return (
diff --git a/surfsense_web/components/TokenHandler.tsx b/surfsense_web/components/TokenHandler.tsx
index b4ca36298..3f98451ef 100644
--- a/surfsense_web/components/TokenHandler.tsx
+++ b/surfsense_web/components/TokenHandler.tsx
@@ -1,7 +1,9 @@
"use client";
import { useSearchParams } from "next/navigation";
+import { useTranslations } from "next-intl";
import { useEffect } from "react";
+import { UnifiedLoadingScreen } from "@/components/ui/unified-loading-screen";
import { getAndClearRedirectPath, setBearerToken } from "@/lib/auth-utils";
import { trackLoginSuccess } from "@/lib/posthog/events";
@@ -25,6 +27,7 @@ const TokenHandler = ({
tokenParamName = "token",
storageKey = "surfsense_bearer_token",
}: TokenHandlerProps) => {
+ const t = useTranslations("auth");
const searchParams = useSearchParams();
useEffect(() => {
@@ -67,9 +70,7 @@ const TokenHandler = ({
}, [searchParams, tokenParamName, storageKey, redirectPath]);
return (
-
-
Processing authentication...
-
+
);
};
diff --git a/surfsense_web/components/new-chat/source-detail-panel.tsx b/surfsense_web/components/new-chat/source-detail-panel.tsx
index 08cff8380..1cac73a01 100644
--- a/surfsense_web/components/new-chat/source-detail-panel.tsx
+++ b/surfsense_web/components/new-chat/source-detail-panel.tsx
@@ -7,11 +7,11 @@ import {
ExternalLink,
FileText,
Hash,
- Loader2,
Sparkles,
X,
} from "lucide-react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
+import { useTranslations } from "next-intl";
import type React from "react";
import { forwardRef, type ReactNode, useCallback, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
@@ -20,6 +20,7 @@ import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { ScrollArea } from "@/components/ui/scroll-area";
+import { Spinner } from "@/components/ui/spinner";
import type {
GetDocumentByChunkResponse,
GetSurfsenseDocsByChunkResponse,
@@ -63,7 +64,7 @@ interface ChunkCardProps {
}
const ChunkCard = forwardRef
(
- ({ chunk, index, totalChunks, isCited, isActive, disableLayoutAnimation }, ref) => {
+ ({ chunk, index, totalChunks, isCited }, ref) => {
return (
(null);
const hasScrolledRef = useRef(false); // Use ref to avoid stale closures
const [summaryOpen, setSummaryOpen] = useState(false);
const [activeChunkIndex, setActiveChunkIndex] = useState
(null);
const [mounted, setMounted] = useState(false);
- const [hasScrolledToCited, setHasScrolledToCited] = useState(false);
+ const [_hasScrolledToCited, setHasScrolledToCited] = useState(false);
const shouldReduceMotion = useReducedMotion();
useEffect(() => {
@@ -382,11 +384,8 @@ export function SourceDetailPanel({
animate={{ opacity: 1, scale: 1 }}
className="flex flex-col items-center gap-4"
>
-
- Loading document
+
+ {t("loading_document")}
)}
diff --git a/surfsense_web/components/providers/ElectricProvider.tsx b/surfsense_web/components/providers/ElectricProvider.tsx
index f187d10c1..68263b036 100644
--- a/surfsense_web/components/providers/ElectricProvider.tsx
+++ b/surfsense_web/components/providers/ElectricProvider.tsx
@@ -1,8 +1,10 @@
"use client";
import { useAtomValue } from "jotai";
+import { useTranslations } from "next-intl";
import { useEffect, useRef, useState } from "react";
import { currentUserAtom } from "@/atoms/user/user-query.atoms";
+import { UnifiedLoadingScreen } from "@/components/ui/unified-loading-screen";
import { getBearerToken } from "@/lib/auth-utils";
import {
cleanupElectric,
@@ -28,6 +30,7 @@ interface ElectricProviderProps {
* 5. Provides client via context - hooks should use useElectricClient()
*/
export function ElectricProvider({ children }: ElectricProviderProps) {
+ const t = useTranslations("common");
const [electricClient, setElectricClient] = useState(null);
const [error, setError] = useState(null);
const {
@@ -120,9 +123,7 @@ export function ElectricProvider({ children }: ElectricProviderProps) {
if (!electricClient && !error) {
return (
-
+
);
}
diff --git a/surfsense_web/components/ui/unified-loading-screen.tsx b/surfsense_web/components/ui/unified-loading-screen.tsx
new file mode 100644
index 000000000..7ed6b83d9
--- /dev/null
+++ b/surfsense_web/components/ui/unified-loading-screen.tsx
@@ -0,0 +1,72 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { createPortal } from "react-dom";
+import { Logo } from "@/components/Logo";
+import { Spinner } from "@/components/ui/spinner";
+import { AmbientBackground } from "@/app/(home)/login/AmbientBackground";
+
+interface UnifiedLoadingScreenProps {
+ /** Optional message to display below the spinner */
+ message?: string;
+ /** Visual style variant */
+ variant?: "login" | "default";
+}
+
+export function UnifiedLoadingScreen({
+ message,
+ variant = "default",
+}: UnifiedLoadingScreenProps) {
+ const [mounted, setMounted] = useState(false);
+
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
+ // Fixed-size container to prevent layout shifts
+ const spinnerContainer = (
+
+
+
+ );
+
+ const content = variant === "login" ? (
+
+
+
+
+
+ {spinnerContainer}
+ {message && (
+
+ {message}
+
+ )}
+
+
+
+ ) : (
+
+
+ {spinnerContainer}
+ {message && (
+
+ {message}
+
+ )}
+
+
+ );
+
+ // Render inline during SSR, use portal after mounting
+ // This prevents the black flash during initial render
+ if (!mounted) {
+ return content;
+ }
+
+ return createPortal(content, document.body);
+}
+
diff --git a/surfsense_web/messages/en.json b/surfsense_web/messages/en.json
index 108d93262..59b9cf56a 100644
--- a/surfsense_web/messages/en.json
+++ b/surfsense_web/messages/en.json
@@ -2,7 +2,8 @@
"common": {
"app_name": "SurfSense",
"welcome": "Welcome",
- "loading": "Loading...",
+ "loading": "Loading",
+ "initializing": "Initializing",
"save": "Save",
"cancel": "Cancel",
"delete": "Delete",
@@ -76,9 +77,10 @@
"passwords_no_match": "Passwords do not match",
"password_mismatch": "Password Mismatch",
"passwords_no_match_desc": "The passwords you entered do not match",
- "creating_account": "Creating your account...",
- "creating_account_btn": "Creating account...",
- "redirecting_login": "Redirecting to login page..."
+ "creating_account": "Creating your account",
+ "creating_account_btn": "Creating account",
+ "redirecting_login": "Redirecting to login page",
+ "processing_authentication": "Processing authentication"
},
"searchSpace": {
"create_title": "Create Search Space",
@@ -143,12 +145,15 @@
"api_keys": "API Keys",
"profile": "Profile",
"loading_dashboard": "Loading Dashboard",
- "checking_auth": "Checking authentication...",
+ "checking_auth": "Checking authentication",
"loading_config": "Loading Configuration",
- "checking_llm_prefs": "Checking your LLM preferences...",
+ "checking_llm_prefs": "Checking your LLM preferences",
+ "setting_up_ai": "Setting up AI",
"config_error": "Configuration Error",
"failed_load_llm_config": "Failed to load your LLM configuration",
"error_loading_chats": "Error loading chats",
+ "loading_chat": "Loading chat",
+ "loading_document": "Loading document",
"no_recent_chats": "No recent chats",
"error_loading_space": "Error loading search space",
"unknown_search_space": "Unknown Search Space",
@@ -165,7 +170,7 @@
"create_search_space": "Create Search Space",
"add_new_search_space": "Add New Search Space",
"loading": "Loading",
- "fetching_spaces": "Fetching your search spaces...",
+ "fetching_spaces": "Fetching your search spaces",
"may_take_moment": "This may take a moment",
"error": "Error",
"something_wrong": "Something went wrong",
diff --git a/surfsense_web/messages/zh.json b/surfsense_web/messages/zh.json
index 51e378bb2..3c4b6cf34 100644
--- a/surfsense_web/messages/zh.json
+++ b/surfsense_web/messages/zh.json
@@ -3,6 +3,7 @@
"app_name": "SurfSense",
"welcome": "欢迎",
"loading": "加载中...",
+ "initializing": "正在初始化",
"save": "保存",
"cancel": "取消",
"delete": "删除",
@@ -76,9 +77,10 @@
"passwords_no_match": "密码不匹配",
"password_mismatch": "密码不匹配",
"passwords_no_match_desc": "您输入的密码不一致",
- "creating_account": "正在创建您的账户...",
- "creating_account_btn": "创建中...",
- "redirecting_login": "正在跳转到登录页面..."
+ "creating_account": "正在创建您的账户",
+ "creating_account_btn": "创建中",
+ "redirecting_login": "正在跳转到登录页面",
+ "processing_authentication": "正在处理身份验证"
},
"searchSpace": {
"create_title": "创建搜索空间",
@@ -128,12 +130,15 @@
"api_keys": "API 密钥",
"profile": "个人资料",
"loading_dashboard": "正在加载仪表盘",
- "checking_auth": "正在检查身份验证...",
+ "checking_auth": "正在检查身份验证",
"loading_config": "正在加载配置",
- "checking_llm_prefs": "正在检查您的 LLM 偏好设置...",
+ "checking_llm_prefs": "正在检查您的 LLM 偏好设置",
+ "setting_up_ai": "正在设置 AI",
"config_error": "配置错误",
"failed_load_llm_config": "无法加载您的 LLM 配置",
"error_loading_chats": "加载对话失败",
+ "loading_chat": "正在加载对话",
+ "loading_document": "正在加载文档",
"no_recent_chats": "暂无最近对话",
"error_loading_space": "加载搜索空间失败",
"unknown_search_space": "未知搜索空间",
@@ -150,7 +155,7 @@
"create_search_space": "创建搜索空间",
"add_new_search_space": "添加新的搜索空间",
"loading": "加载中",
- "fetching_spaces": "正在获取您的搜索空间...",
+ "fetching_spaces": "正在获取您的搜索空间",
"may_take_moment": "这可能需要一些时间",
"error": "错误",
"something_wrong": "出现错误",
From ce9e3b01b7957bbd34bf0907688ad7af1b509826 Mon Sep 17 00:00:00 2001
From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com>
Date: Sat, 24 Jan 2026 19:53:56 +0530
Subject: [PATCH 05/22] feat: expand onboarding tour with inbox step and update
tooltip positioning
- Added a new onboarding tour step for the inbox, guiding users to view mentions and notifications.
- Updated tooltip positioning logic to accommodate the new inbox step, ensuring proper alignment during the tour.
- Enhanced the check for required elements to include the inbox step, improving the tour initiation process.
---
.../layout/ui/sidebar/NavSection.tsx | 4 +++-
surfsense_web/components/onboarding-tour.tsx | 18 +++++++++++++-----
2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/surfsense_web/components/layout/ui/sidebar/NavSection.tsx b/surfsense_web/components/layout/ui/sidebar/NavSection.tsx
index 742a27bbc..dc730bc4a 100644
--- a/surfsense_web/components/layout/ui/sidebar/NavSection.tsx
+++ b/surfsense_web/components/layout/ui/sidebar/NavSection.tsx
@@ -20,7 +20,9 @@ export function NavSection({ items, onItemClick, isCollapsed = false }: NavSecti
const joyrideAttr =
item.title === "Documents" || item.title.toLowerCase().includes("documents")
? { "data-joyride": "documents-sidebar" }
- : {};
+ : item.title === "Inbox" || item.title.toLowerCase().includes("inbox")
+ ? { "data-joyride": "inbox-sidebar" }
+ : {};
if (isCollapsed) {
return (
diff --git a/surfsense_web/components/onboarding-tour.tsx b/surfsense_web/components/onboarding-tour.tsx
index 717a27607..12773c932 100644
--- a/surfsense_web/components/onboarding-tour.tsx
+++ b/surfsense_web/components/onboarding-tour.tsx
@@ -32,6 +32,12 @@ const TOUR_STEPS: TourStep[] = [
content: "Access and manage all your uploaded documents.",
placement: "right",
},
+ {
+ target: '[data-joyride="inbox-sidebar"]',
+ title: "Check your inbox",
+ content: "View mentions and notifications in one place.",
+ placement: "right",
+ },
];
interface TooltipPosition {
@@ -188,14 +194,15 @@ function TourTooltip({
const getPointerStyles = (): React.CSSProperties => {
const lineLength = 16;
const dotSize = 6;
- // Check if this is the documents step (stepIndex === 1)
+ // Check if this is the documents step (stepIndex === 1) or inbox step (stepIndex === 2)
const isDocumentsStep = stepIndex === 1;
+ const isInboxStep = stepIndex === 2;
if (position.pointerPosition === "left") {
return {
position: "absolute",
left: -lineLength - dotSize,
- top: isDocumentsStep ? "calc(50% - 8px)" : "50%",
+ top: isDocumentsStep || isInboxStep ? "calc(50% - 8px)" : "50%",
transform: "translateY(-50%)",
display: "flex",
alignItems: "center",
@@ -518,12 +525,13 @@ export function OnboardingTour() {
// User is new and hasn't seen tour - wait for DOM elements and start tour
const checkAndStartTour = () => {
- // Check if both required elements exist
+ // Check if all required elements exist
const connectorEl = document.querySelector(TOUR_STEPS[0].target);
const documentsEl = document.querySelector(TOUR_STEPS[1].target);
+ const inboxEl = document.querySelector(TOUR_STEPS[2].target);
- if (connectorEl && documentsEl) {
- // Both elements found, start tour
+ if (connectorEl && documentsEl && inboxEl) {
+ // All elements found, start tour
setIsActive(true);
setTargetEl(connectorEl);
setSpotlightTargetEl(connectorEl);
From eaecc091e331207ec2649dd3d913d9da8b302fe1 Mon Sep 17 00:00:00 2001
From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com>
Date: Sat, 24 Jan 2026 20:05:42 +0530
Subject: [PATCH 06/22] feat: add check icon to active items in sidebar
navigation and user profile
- Introduced a Check icon to visually indicate active items in the NavSection and SidebarUserProfile components.
- Updated styling to ensure the Check icon appears correctly without altering the background color for active items.
- Enhanced user experience by providing a clear visual cue for selected themes and languages in the sidebar.
---
.../layout/ui/sidebar/NavSection.tsx | 13 ++--
.../layout/ui/sidebar/SidebarUserProfile.tsx | 60 +++++++++++--------
2 files changed, 45 insertions(+), 28 deletions(-)
diff --git a/surfsense_web/components/layout/ui/sidebar/NavSection.tsx b/surfsense_web/components/layout/ui/sidebar/NavSection.tsx
index dc730bc4a..d58f52612 100644
--- a/surfsense_web/components/layout/ui/sidebar/NavSection.tsx
+++ b/surfsense_web/components/layout/ui/sidebar/NavSection.tsx
@@ -1,5 +1,6 @@
"use client";
+import { Check } from "lucide-react";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import type { NavItem } from "../../types/layout.types";
@@ -34,12 +35,14 @@ export function NavSection({ items, onItemClick, isCollapsed = false }: NavSecti
className={cn(
"relative flex h-10 w-10 items-center justify-center rounded-md transition-colors",
"hover:bg-accent hover:text-accent-foreground",
- "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
- item.isActive && "bg-accent text-accent-foreground"
+ "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
)}
{...joyrideAttr}
>
+ {item.isActive && (
+
+ )}
{item.badge && (
{item.badge}
@@ -64,13 +67,15 @@ export function NavSection({ items, onItemClick, isCollapsed = false }: NavSecti
className={cn(
"flex items-center gap-2 rounded-md mx-2 px-2 py-1.5 text-sm transition-colors text-left",
"hover:bg-accent hover:text-accent-foreground",
- "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
- item.isActive && "bg-accent text-accent-foreground"
+ "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
)}
{...joyrideAttr}
>
{item.title}
+ {item.isActive && (
+
+ )}
{item.badge && (
{item.badge}
diff --git a/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx b/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx
index 982d88e8b..66fe24565 100644
--- a/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx
+++ b/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx
@@ -1,6 +1,6 @@
"use client";
-import { ChevronUp, Languages, Laptop, LogOut, Moon, Settings, Sun } from "lucide-react";
+import { Check, ChevronUp, Languages, Laptop, LogOut, Moon, Settings, Sun } from "lucide-react";
import { useTranslations } from "next-intl";
import {
DropdownMenu,
@@ -197,11 +197,14 @@ export function SidebarUserProfile({
className={cn(
"mb-1 last:mb-0 transition-all",
"hover:bg-accent/50",
- isSelected && "bg-accent/80"
+ isSelected && "text-primary"
)}
>
{t(themeOption.value)}
+ {isSelected && (
+
+ )}
);
})}
@@ -226,11 +229,14 @@ export function SidebarUserProfile({
className={cn(
"mb-1 last:mb-0 transition-all",
"hover:bg-accent/50",
- isSelected && "bg-accent/80"
+ isSelected && "text-primary"
)}
>
{language.flag}
{language.name}
+ {isSelected && (
+
+ )}
);
})}
@@ -302,26 +308,29 @@ export function SidebarUserProfile({
{t("theme")}
-
- {THEMES.map((themeOption) => {
- const Icon = themeOption.icon;
- const isSelected = theme === themeOption.value;
- return (
- handleThemeChange(themeOption.value)}
- className={cn(
- "mb-1 last:mb-0 transition-all",
- "hover:bg-accent/50",
- isSelected && "bg-accent/80"
- )}
- >
-
- {t(themeOption.value)}
-
- );
- })}
-
+
+ {THEMES.map((themeOption) => {
+ const Icon = themeOption.icon;
+ const isSelected = theme === themeOption.value;
+ return (
+ handleThemeChange(themeOption.value)}
+ className={cn(
+ "mb-1 last:mb-0 transition-all",
+ "hover:bg-accent/50",
+ isSelected && "text-primary"
+ )}
+ >
+
+ {t(themeOption.value)}
+ {isSelected && (
+
+ )}
+
+ );
+ })}
+
)}
@@ -342,11 +351,14 @@ export function SidebarUserProfile({
className={cn(
"mb-1 last:mb-0 transition-all",
"hover:bg-accent/50",
- isSelected && "bg-accent/80"
+ isSelected && "text-primary"
)}
>
{language.flag}
{language.name}
+ {isSelected && (
+
+ )}
);
})}
From de08f0644fb52a8c8bd9d5e09feea3c39fb9626d Mon Sep 17 00:00:00 2001
From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com>
Date: Sat, 24 Jan 2026 20:51:46 +0530
Subject: [PATCH 07/22] feat: add ordering to search spaces retrieval for
consistent display
---
surfsense_backend/app/routes/search_spaces_routes.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/surfsense_backend/app/routes/search_spaces_routes.py b/surfsense_backend/app/routes/search_spaces_routes.py
index bc52a52b1..147f515b3 100644
--- a/surfsense_backend/app/routes/search_spaces_routes.py
+++ b/surfsense_backend/app/routes/search_spaces_routes.py
@@ -129,6 +129,7 @@ async def read_search_spaces(
result = await session.execute(
select(SearchSpace)
.filter(SearchSpace.user_id == user.id)
+ .order_by(SearchSpace.id.asc())
.offset(skip)
.limit(limit)
)
@@ -138,6 +139,7 @@ async def read_search_spaces(
select(SearchSpace)
.join(SearchSpaceMembership)
.filter(SearchSpaceMembership.user_id == user.id)
+ .order_by(SearchSpace.id.asc())
.offset(skip)
.limit(limit)
)
From 9215118bab94e21a6d025f5c71d2dad965128593 Mon Sep 17 00:00:00 2001
From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com>
Date: Sat, 24 Jan 2026 22:43:04 +0530
Subject: [PATCH 08/22] feat: enhance documentation and fixed bug in
`loading.tsx`
---
surfsense_web/app/auth/callback/loading.tsx | 2 +
.../components/bookstack-connect-form.tsx | 124 +--------------
.../components/luma-connect-form.tsx | 131 ----------------
.../components/obsidian-connect-form.tsx | 145 ------------------
.../content/docs/connectors/airtable.mdx | 12 +-
.../content/docs/connectors/bookstack.mdx | 68 +++++++-
.../content/docs/connectors/circleback.mdx | 129 +++++++++++++++-
.../content/docs/connectors/clickup.mdx | 4 +-
.../content/docs/connectors/confluence.mdx | 20 ++-
.../content/docs/connectors/discord.mdx | 14 +-
.../content/docs/connectors/elasticsearch.mdx | 113 +++++++++++++-
.../content/docs/connectors/github.mdx | 40 ++---
.../content/docs/connectors/gmail.mdx | 4 +-
.../docs/connectors/google-calendar.mdx | 4 +-
.../content/docs/connectors/google-drive.mdx | 4 +-
.../content/docs/connectors/jira.mdx | 16 +-
.../content/docs/connectors/linear.mdx | 6 +-
.../content/docs/connectors/luma.mdx | 66 +++++++-
.../content/docs/connectors/meta.json | 3 +-
.../docs/connectors/microsoft-teams.mdx | 8 +-
.../content/docs/connectors/notion.mdx | 13 +-
.../content/docs/connectors/obsidian.mdx | 143 +++++++++++++++++
.../content/docs/connectors/slack.mdx | 8 +-
23 files changed, 614 insertions(+), 463 deletions(-)
create mode 100644 surfsense_web/content/docs/connectors/obsidian.mdx
diff --git a/surfsense_web/app/auth/callback/loading.tsx b/surfsense_web/app/auth/callback/loading.tsx
index 24d1891b4..676ca3632 100644
--- a/surfsense_web/app/auth/callback/loading.tsx
+++ b/surfsense_web/app/auth/callback/loading.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { useTranslations } from "next-intl";
import { Spinner } from "@/components/ui/spinner";
diff --git a/surfsense_web/components/assistant-ui/connector-popup/connect-forms/components/bookstack-connect-form.tsx b/surfsense_web/components/assistant-ui/connector-popup/connect-forms/components/bookstack-connect-form.tsx
index 2b7123d78..789e23787 100644
--- a/surfsense_web/components/assistant-ui/connector-popup/connect-forms/components/bookstack-connect-form.tsx
+++ b/surfsense_web/components/assistant-ui/connector-popup/connect-forms/components/bookstack-connect-form.tsx
@@ -6,12 +6,6 @@ import type { FC } from "react";
import { useRef, useState } from "react";
import { useForm } from "react-hook-form";
import * as z from "zod";
-import {
- Accordion,
- AccordionContent,
- AccordionItem,
- AccordionTrigger,
-} from "@/components/ui/accordion";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import {
Form,
@@ -85,6 +79,7 @@ export const BookStackConnectForm: FC = ({ onSubmit, isSubmitt
BOOKSTACK_TOKEN_SECRET: values.token_secret,
},
is_indexable: true,
+ is_active: true,
last_indexed_at: null,
periodic_indexing_enabled: periodicEnabled,
indexing_frequency_minutes: periodicEnabled ? parseInt(frequencyMinutes, 10) : null,
@@ -302,123 +297,6 @@ export const BookStackConnectForm: FC = ({ onSubmit, isSubmitt