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 0f0eaed96..8e877b99c 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 @@ -276,7 +276,7 @@ export default function NewChatPage() { const initializeThread = useCallback(async () => { setIsInitializing(true); - // Reset all state when switching between chats to prevent stale data + // Reset all state when switching between chats/search spaces to prevent stale data setMessages([]); setThreadId(null); setCurrentThread(null); @@ -284,8 +284,8 @@ export default function NewChatPage() { setMentionedDocuments([]); setSidebarDocuments([]); setMessageDocumentsMap({}); - clearPlanOwnerRegistry(); // Reset plan ownership for new chat - closeReportPanel(); // Close report panel when switching chats + clearPlanOwnerRegistry(); + closeReportPanel(); try { if (urlChatId > 0) { @@ -346,6 +346,7 @@ export default function NewChatPage() { } }, [ urlChatId, + searchSpaceId, setMessageDocumentsMap, setMentionedDocuments, setSidebarDocuments, @@ -1671,7 +1672,7 @@ export default function NewChatPage() { {/* Disabled for now */} -
+
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 && (