From 042b42a8a0c68094dc08d14926ab478e2f5e0b99 Mon Sep 17 00:00:00 2001 From: "DESKTOP-RTLN3BA\\$punk" Date: Wed, 11 Mar 2026 16:37:56 -0700 Subject: [PATCH 1/3] refactor: fixed forefox rendering issues - Updated NewChatPage to reset state when switching between chats and search spaces. - Enhanced LayoutDataProvider to reset transient slide-out panels on search space changes. - Refactored RightPanel to simplify rendering logic and remove unnecessary animations. - Cleaned up LayoutShell by removing motion components for improved performance. --- .../new-chat/[[...chat_id]]/page.tsx | 9 +- .../components/assistant-ui/thread.tsx | 6 +- .../layout/providers/LayoutDataProvider.tsx | 9 ++ .../layout/ui/right-panel/RightPanel.tsx | 90 ++++++------------- .../layout/ui/shell/LayoutShell.tsx | 27 +++--- 5 files changed, 55 insertions(+), 86 deletions(-) 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..94a3618e9 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -220,14 +220,14 @@ const ThreadWelcome: FC = () => { return (
- {/* Greeting positioned above the composer - fixed position */} + {/* Greeting positioned above the composer */}
-

+

{greeting}

{/* Composer - top edge fixed, expands downward only */} -
+
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..f03283192 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,27 +307,23 @@ export function LayoutShell({ isResizing={isResizing} /> - +
{children}
- +
- {/* Right panel — tabbed Sources/Report (desktop only) */} - {documentsPanel && ( - - )} + {/* Right panel — tabbed Sources/Report (desktop only) */} + {documentsPanel && ( + + )} {/* Inbox Sidebar - slide-out panel */} {inbox && ( From b9f877946181319bb717c504ac3ec5bc14638f15 Mon Sep 17 00:00:00 2001 From: "DESKTOP-RTLN3BA\\$punk" Date: Wed, 11 Mar 2026 16:42:52 -0700 Subject: [PATCH 2/3] refactor: simplify GitHub stars badge component - Removed unused imports and utility functions to streamline the code. - Eliminated unnecessary state management and animations for improved performance. - Updated styling for better visual consistency and user experience. --- .../homepage/github-stars-badge.tsx | 203 +----------------- 1 file changed, 8 insertions(+), 195 deletions(-) diff --git a/surfsense_web/components/homepage/github-stars-badge.tsx b/surfsense_web/components/homepage/github-stars-badge.tsx index 8aaeea65b..8f313db58 100644 --- a/surfsense_web/components/homepage/github-stars-badge.tsx +++ b/surfsense_web/components/homepage/github-stars-badge.tsx @@ -1,174 +1,14 @@ "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 * 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 +249,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 +259,6 @@ function NavbarGitHubStars({ .then((data) => { if (data && typeof data.stargazers_count === "number") { setStars(data.stargazers_count); - fillRaw.set(1); } }) .catch((err) => { @@ -434,7 +268,7 @@ function NavbarGitHubStars({ }) .finally(() => setIsLoading(false)); return () => abortController.abort(); - }, [username, repo, fillRaw]); + }, [username, repo]); return ( -
- setIsCompleted(true)} - /> - -
-
- -
-
+
); } From b81619da7e2e45508a52546a51f709f1019a9943 Mon Sep 17 00:00:00 2001 From: "DESKTOP-RTLN3BA\\$punk" Date: Wed, 11 Mar 2026 16:44:25 -0700 Subject: [PATCH 3/3] chore: linting --- .../components/assistant-ui/thread.tsx | 4 +--- .../components/homepage/github-stars-badge.tsx | 6 +----- .../components/layout/ui/shell/LayoutShell.tsx | 18 +++++++++--------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index 94a3618e9..d016efb24 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -222,9 +222,7 @@ const ThreadWelcome: FC = () => {
{/* 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 8f313db58..38a22f7ac 100644 --- a/surfsense_web/components/homepage/github-stars-badge.tsx +++ b/surfsense_web/components/homepage/github-stars-badge.tsx @@ -1,11 +1,7 @@ "use client"; import { IconBrandGithub } from "@tabler/icons-react"; -import { - motion, - useMotionValue, - useSpring, -} from "motion/react"; +import { motion, useMotionValue, useSpring } from "motion/react"; import * as React from "react"; import { cn } from "@/lib/utils"; diff --git a/surfsense_web/components/layout/ui/shell/LayoutShell.tsx b/surfsense_web/components/layout/ui/shell/LayoutShell.tsx index f03283192..286d65adc 100644 --- a/surfsense_web/components/layout/ui/shell/LayoutShell.tsx +++ b/surfsense_web/components/layout/ui/shell/LayoutShell.tsx @@ -315,15 +315,15 @@ export function LayoutShell({
- {/* Right panel — tabbed Sources/Report (desktop only) */} - {documentsPanel && ( - - )} + {/* Right panel — tabbed Sources/Report (desktop only) */} + {documentsPanel && ( + + )} {/* Inbox Sidebar - slide-out panel */} {inbox && (