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 bfe8599f6..284c1ebc0 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx @@ -265,7 +265,7 @@ export function DashboardClientLayout({ -
{children}
+
{children}
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 00514facb..e18629b92 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 @@ -626,11 +626,11 @@ export default function NewChatPage() { -
- -
- -
+
+ } + />
); diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index 33d6a0cad..9596f4fe0 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -8,7 +8,6 @@ import { ThreadPrimitive, useAssistantState, useMessage, - useThreadViewport, } from "@assistant-ui/react"; import { useAtomValue } from "jotai"; import { @@ -69,6 +68,8 @@ import { cn } from "@/lib/utils"; */ interface ThreadProps { messageThinkingSteps?: Map; + /** Optional header component to render at the top of the viewport (sticky) */ + header?: React.ReactNode; } // Context to pass thinking steps to AssistantMessage @@ -212,71 +213,21 @@ const ThinkingStepsDisplay: FC<{ steps: ThinkingStep[]; isThreadRunning?: boolea ); }; -/** - * Component that handles auto-scroll when thinking steps update. - * Uses useThreadViewport to scroll to bottom when thinking steps change, - * ensuring the user always sees the latest content during streaming. - */ -const ThinkingStepsScrollHandler: FC = () => { - const thinkingStepsMap = useContext(ThinkingStepsContext); - const viewport = useThreadViewport(); - const isRunning = useAssistantState(({ thread }) => thread.isRunning); - // Track the serialized state to detect any changes - const prevStateRef = useRef(""); - - useEffect(() => { - // Only act during streaming - if (!isRunning) { - prevStateRef.current = ""; - return; - } - - // Serialize the thinking steps state to detect any changes - // This catches new steps, status changes, and item additions - let stateString = ""; - thinkingStepsMap.forEach((steps, msgId) => { - steps.forEach((step) => { - stateString += `${msgId}:${step.id}:${step.status}:${step.items?.length || 0};`; - }); - }); - - // If state changed at all during streaming, scroll - if (stateString !== prevStateRef.current && stateString !== "") { - prevStateRef.current = stateString; - - // Multiple attempts to ensure scroll happens after DOM updates - const scrollAttempt = () => { - try { - viewport.scrollToBottom(); - } catch (e) { - // Ignore errors - viewport might not be ready - } - }; - - // Delayed attempts to handle async DOM updates - requestAnimationFrame(scrollAttempt); - setTimeout(scrollAttempt, 100); - } - }, [thinkingStepsMap, viewport, isRunning]); - - return null; // This component doesn't render anything -}; - -export const Thread: FC = ({ messageThinkingSteps = new Map() }) => { +export const Thread: FC = ({ messageThinkingSteps = new Map(), header }) => { return ( - {/* Auto-scroll handler for thinking steps - must be inside Viewport */} - + {/* Optional sticky header for model selector etc. */} + {header &&
{header}
} thread.isEmpty}> diff --git a/surfsense_web/components/new-chat/chat-header.tsx b/surfsense_web/components/new-chat/chat-header.tsx index ef1533e23..34b2cc814 100644 --- a/surfsense_web/components/new-chat/chat-header.tsx +++ b/surfsense_web/components/new-chat/chat-header.tsx @@ -47,12 +47,7 @@ export function ChatHeader({ searchSpaceId }: ChatHeaderProps) { return ( <> - {/* Header Bar */} -
- -
- - {/* Config Sidebar */} + @@ -206,11 +207,14 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp - + {/* Switching overlay */} {isSwitching && (
@@ -221,8 +225,7 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp
)} -
- +
0 && filteredUserConfigs.length > 0 && ( - + )} {/* User Configs Section */} @@ -362,7 +365,7 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp )} {/* Add New Config Button */} -
+