diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx
index 38426a47b..d016efb24 100644
--- a/surfsense_web/components/assistant-ui/thread.tsx
+++ b/surfsense_web/components/assistant-ui/thread.tsx
@@ -220,14 +220,12 @@ const ThreadWelcome: FC = () => {
return (
- {/* Greeting positioned above the composer - fixed position */}
+ {/* Greeting positioned above the composer */}
-
- {greeting}
-
+ {greeting}
{/* Composer - top edge fixed, expands downward only */}
-
diff --git a/surfsense_web/components/homepage/github-stars-badge.tsx b/surfsense_web/components/homepage/github-stars-badge.tsx
index 8aaeea65b..38a22f7ac 100644
--- a/surfsense_web/components/homepage/github-stars-badge.tsx
+++ b/surfsense_web/components/homepage/github-stars-badge.tsx
@@ -1,174 +1,10 @@
"use client";
import { IconBrandGithub } from "@tabler/icons-react";
-import { StarIcon } from "lucide-react";
-import type { HTMLMotionProps, UseInViewOptions } from "motion/react";
-import {
- AnimatePresence,
- motion,
- useInView,
- useMotionValue,
- useSpring,
- useTransform,
-} from "motion/react";
+import { motion, useMotionValue, useSpring } from "motion/react";
import * as React from "react";
import { cn } from "@/lib/utils";
-// ---------------------------------------------------------------------------
-// Utilities
-// ---------------------------------------------------------------------------
-function getStrictContext
(name?: string) {
- const Context = React.createContext(undefined);
- const Provider = ({ value, children }: { value: T; children?: React.ReactNode }) => (
- {children}
- );
- const useSafeContext = () => {
- const ctx = React.useContext(Context);
- if (ctx === undefined) {
- throw new Error(`useContext must be used within ${name ?? "a Provider"}`);
- }
- return ctx;
- };
- return [Provider, useSafeContext] as const;
-}
-
-interface UseIsInViewOptions {
- inView?: boolean;
- inViewOnce?: boolean;
- inViewMargin?: UseInViewOptions["margin"];
-}
-
-function useIsInView(
- ref: React.Ref,
- options: UseIsInViewOptions = {}
-) {
- const { inView, inViewOnce = false, inViewMargin = "0px" } = options;
- const localRef = React.useRef(null);
- React.useImperativeHandle(ref, () => localRef.current as T);
- const inViewResult = useInView(localRef, {
- once: inViewOnce,
- margin: inViewMargin,
- });
- const isInView = !inView || inViewResult;
- return { ref: localRef, isInView };
-}
-
-// ---------------------------------------------------------------------------
-// Particles (for star burst effect on completion)
-// ---------------------------------------------------------------------------
-type ParticlesContextType = { animate: boolean; isInView: boolean };
-const [ParticlesProvider, useParticles] =
- getStrictContext("ParticlesContext");
-
-function Particles({
- ref,
- animate = true,
- inView = false,
- inViewMargin = "0px",
- inViewOnce = true,
- children,
- style,
- ...props
-}: Omit, "children"> & {
- animate?: boolean;
- children: React.ReactNode;
-} & UseIsInViewOptions) {
- const { ref: localRef, isInView } = useIsInView(ref as React.Ref, {
- inView,
- inViewOnce,
- inViewMargin,
- });
- return (
-
-
- {children}
-
-
- );
-}
-
-function ParticlesEffect({
- side = "top",
- align = "center",
- count = 6,
- radius = 30,
- spread = 360,
- duration = 0.8,
- holdDelay = 0.05,
- sideOffset = 0,
- alignOffset = 0,
- delay = 0,
- transition,
- style,
- ...props
-}: Omit, "children"> & {
- side?: "top" | "bottom" | "left" | "right";
- align?: "start" | "center" | "end";
- count?: number;
- radius?: number;
- spread?: number;
- duration?: number;
- holdDelay?: number;
- sideOffset?: number;
- alignOffset?: number;
- delay?: number;
-}) {
- const { animate, isInView } = useParticles();
- const isVertical = side === "top" || side === "bottom";
- const alignPct = align === "start" ? "0%" : align === "end" ? "100%" : "50%";
-
- const top = isVertical
- ? side === "top"
- ? `calc(0% - ${sideOffset}px)`
- : `calc(100% + ${sideOffset}px)`
- : `calc(${alignPct} + ${alignOffset}px)`;
- const left = isVertical
- ? `calc(${alignPct} + ${alignOffset}px)`
- : side === "left"
- ? `calc(0% - ${sideOffset}px)`
- : `calc(100% + ${sideOffset}px)`;
-
- const containerStyle: React.CSSProperties = {
- position: "absolute",
- top,
- left,
- transform: "translate(-50%, -50%)",
- };
- const angleStep = (spread * (Math.PI / 180)) / Math.max(1, count - 1);
-
- return (
-
- {animate &&
- isInView &&
- [...Array(count)].map((_, i) => {
- const angle = i * angleStep;
- const x = Math.cos(angle) * radius;
- const y = Math.sin(angle) * radius;
- return (
-
- );
- })}
-
- );
-}
-
// ---------------------------------------------------------------------------
// Per-digit scrolling wheel
// ---------------------------------------------------------------------------
@@ -409,11 +245,6 @@ function NavbarGitHubStars({
}: NavbarGitHubStarsProps) {
const [stars, setStars] = React.useState(0);
const [isLoading, setIsLoading] = React.useState(true);
- const [isCompleted, setIsCompleted] = React.useState(false);
-
- const fillRaw = useMotionValue(0);
- const fillSpring = useSpring(fillRaw, { stiffness: 12, damping: 14 });
- const clipPath = useTransform(fillSpring, (v) => `inset(${100 - v * 100}% 0 0 0)`);
React.useEffect(() => {
const abortController = new AbortController();
@@ -424,7 +255,6 @@ function NavbarGitHubStars({
.then((data) => {
if (data && typeof data.stargazers_count === "number") {
setStars(data.stargazers_count);
- fillRaw.set(1);
}
})
.catch((err) => {
@@ -434,7 +264,7 @@ function NavbarGitHubStars({
})
.finally(() => setIsLoading(false));
return () => abortController.abort();
- }, [username, repo, fillRaw]);
+ }, [username, repo]);
return (
-
-
setIsCompleted(true)}
- />
-
-
-
-
-
-
-
-
-
-
+
);
}
diff --git a/surfsense_web/components/layout/providers/LayoutDataProvider.tsx b/surfsense_web/components/layout/providers/LayoutDataProvider.tsx
index 4e167b2e4..3de1b7ac5 100644
--- a/surfsense_web/components/layout/providers/LayoutDataProvider.tsx
+++ b/surfsense_web/components/layout/providers/LayoutDataProvider.tsx
@@ -262,6 +262,15 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
}
}, [pendingNewChat, params?.chat_id, router, searchSpaceId, resetCurrentThread]);
+ // Reset transient slide-out panels when switching search spaces.
+ // Some browsers can retain overlay/backdrop state across route transitions.
+ useEffect(() => {
+ setIsInboxSidebarOpen(false);
+ setIsAllSharedChatsSidebarOpen(false);
+ setIsAllPrivateChatsSidebarOpen(false);
+ setIsAnnouncementsSidebarOpen(false);
+ }, [searchSpaceId]);
+
const searchSpaces: SearchSpace[] = useMemo(() => {
if (!searchSpacesData || !Array.isArray(searchSpacesData)) return [];
return searchSpacesData.map((space) => ({
diff --git a/surfsense_web/components/layout/ui/right-panel/RightPanel.tsx b/surfsense_web/components/layout/ui/right-panel/RightPanel.tsx
index 0c812d88f..1e48f6f74 100644
--- a/surfsense_web/components/layout/ui/right-panel/RightPanel.tsx
+++ b/surfsense_web/components/layout/ui/right-panel/RightPanel.tsx
@@ -2,7 +2,6 @@
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { PanelRight, PanelRightClose } from "lucide-react";
-import { AnimatePresence, motion } from "motion/react";
import { startTransition, useEffect } from "react";
import { closeReportPanelAtom, reportPanelAtom } from "@/atoms/chat/report-panel.atom";
import { documentsSidebarOpenAtom } from "@/atoms/documents/ui.atoms";
@@ -99,70 +98,35 @@ export function RightPanel({ documentsPanel }: RightPanelProps) {
const targetWidth = PANEL_WIDTHS[effectiveTab];
const collapseButton = setCollapsed(true)} />;
- const contentKey =
- effectiveTab === "sources" && documentsOpen
- ? "sources"
- : effectiveTab === "report" && reportOpen
- ? "report"
- : null;
+ if (!isVisible) return null;
return (
-
- {isVisible && (
-
-
-
- {contentKey === "sources" && documentsPanel && (
-
-
-
- )}
- {contentKey === "report" && (
-
-
-
-
-
- )}
-
+
);
}
diff --git a/surfsense_web/components/layout/ui/shell/LayoutShell.tsx b/surfsense_web/components/layout/ui/shell/LayoutShell.tsx
index ab2945f61..286d65adc 100644
--- a/surfsense_web/components/layout/ui/shell/LayoutShell.tsx
+++ b/surfsense_web/components/layout/ui/shell/LayoutShell.tsx
@@ -1,6 +1,5 @@
"use client";
-import { motion } from "motion/react";
import { useMemo, useState } from "react";
import { TooltipProvider } from "@/components/ui/tooltip";
import type { InboxItem } from "@/hooks/use-inbox";
@@ -308,17 +307,13 @@ export function LayoutShell({
isResizing={isResizing}
/>
-
+
{children}
-
+
{/* Right panel — tabbed Sources/Report (desktop only) */}
{documentsPanel && (