diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index 33d6a0cad..8a7eca58e 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -262,6 +262,55 @@ const ThinkingStepsScrollHandler: FC = () => { return null; // This component doesn't render anything }; +/** + * Component that handles auto-scroll when a new user query is submitted. + * Scrolls to position the new user message at the top of the viewport, + * similar to Cursor's behavior where the current query stays at the top. + */ +const NewQueryScrollHandler: FC = () => { + const messages = useAssistantState(({ thread }) => thread.messages); + const prevMessageCountRef = useRef(0); + const prevLastUserMsgIdRef = useRef(""); + + useEffect(() => { + const currentCount = messages.length; + + // Find the last user message + const lastUserMessage = [...messages].reverse().find((m) => m.role === "user"); + const lastUserMsgId = lastUserMessage?.id || ""; + + // Check if a new user message was added (not on initial load) + if ( + prevMessageCountRef.current > 0 && // Not initial load + currentCount > prevMessageCountRef.current && + lastUserMsgId !== prevLastUserMsgIdRef.current && + lastUserMsgId + ) { + // New user message added - scroll to make it visible at the top + // Use multiple attempts to ensure the DOM has updated + const scrollToNewQuery = () => { + // Find the last user message element + const userMessages = document.querySelectorAll('[data-role="user"]'); + const lastUserMsgElement = userMessages[userMessages.length - 1]; + if (lastUserMsgElement) { + // Scroll so the user message is at the top of the viewport + lastUserMsgElement.scrollIntoView({ behavior: "smooth", block: "start" }); + } + }; + + // Delayed attempts to handle async DOM updates + requestAnimationFrame(scrollToNewQuery); + setTimeout(scrollToNewQuery, 50); + setTimeout(scrollToNewQuery, 150); + } + + prevMessageCountRef.current = currentCount; + prevLastUserMsgIdRef.current = lastUserMsgId; + }, [messages]); + + return null; // This component doesn't render anything +}; + export const Thread: FC = ({ messageThinkingSteps = new Map() }) => { return ( @@ -275,6 +324,8 @@ export const Thread: FC = ({ messageThinkingSteps = new Map() }) => turnAnchor="top" className="aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth px-4 pt-4" > + {/* Auto-scroll handler for new queries - positions new user messages at the top */} + {/* Auto-scroll handler for thinking steps - must be inside Viewport */} diff --git a/surfsense_web/components/new-chat/chat-header.tsx b/surfsense_web/components/new-chat/chat-header.tsx index ef1533e23..1e0259447 100644 --- a/surfsense_web/components/new-chat/chat-header.tsx +++ b/surfsense_web/components/new-chat/chat-header.tsx @@ -48,7 +48,7 @@ export function ChatHeader({ searchSpaceId }: ChatHeaderProps) { return ( <> {/* Header Bar */} -
+
diff --git a/surfsense_web/components/new-chat/model-selector.tsx b/surfsense_web/components/new-chat/model-selector.tsx index 89390f957..2af476e3b 100644 --- a/surfsense_web/components/new-chat/model-selector.tsx +++ b/surfsense_web/components/new-chat/model-selector.tsx @@ -175,9 +175,10 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp role="combobox" aria-expanded={open} className={cn( - "h-9 gap-2 px-3 rounded-xl border border-border/50 bg-background/50 backdrop-blur-sm", - "hover:bg-muted/80 hover:border-border transition-all duration-200", + "h-9 gap-2 px-3 rounded-xl border border-border/30 bg-background/50 backdrop-blur-sm", + "hover:bg-muted/80 hover:border-border/30 transition-all duration-200", "text-sm font-medium text-foreground", + "focus-visible:ring-0 focus-visible:ring-offset-0", className )} > @@ -206,11 +207,11 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp - + {/* Switching overlay */} {isSwitching && (
@@ -221,8 +222,7 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp
)} -
- +
0 && filteredUserConfigs.length > 0 && ( - + )} {/* User Configs Section */} @@ -362,7 +362,7 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp )} {/* Add New Config Button */} -
+