Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions frontend/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export function Sidebar({
const searchInputRef = useRef<HTMLInputElement>(null);

async function addChat() {
// If sidebar is open, close it
if (isOpen) {
// If sidebar is open on mobile, close it
if (isOpen && isMobile) {
onToggle();
}

Expand Down Expand Up @@ -74,10 +74,14 @@ export function Sidebar({

const sidebarRef = useRef<HTMLDivElement>(null);

// Use the centralized hook for mobile detection
const isMobile = useIsMobile();

// Modified click outside handler to ignore clicks in dropdowns and dialogs
// Only applies on mobile - desktop users use the toggle button
const handleClickOutside = useCallback(
(event: MouseEvent | TouchEvent) => {
if (isOpen) {
if (isOpen && isMobile) {
// Check if the click was inside a dropdown or dialog
const target = event.target as HTMLElement;
const isInDropdown = target.closest('[role="menu"]');
Expand All @@ -89,14 +93,11 @@ export function Sidebar({
}
}
},
[isOpen, onToggle]
[isOpen, onToggle, isMobile]
);

useClickOutside(sidebarRef, handleClickOutside);

// Use the centralized hook for mobile detection
const isMobile = useIsMobile();

// Track if component is mounted to prevent state updates after unmount
const isMountedRef = useRef(true);
useLayoutEffect(() => {
Expand Down Expand Up @@ -135,20 +136,20 @@ export function Sidebar({
ref={sidebarRef}
className={cn([
"fixed md:static z-10 h-full overflow-y-hidden",
isOpen ? "block w-[280px]" : "hidden md:block md:w-[280px]"
isOpen ? "block w-[280px]" : "hidden"
])}
>
<div className="h-full border-r border-input dark:bg-background bg-[hsl(var(--footer-bg))] backdrop-blur-lg flex flex-col items-stretch w-[280px]">
{/* Header section matching UnifiedChat's h-14 */}
<div className="h-14 flex items-center px-4 md:py-2">
<div className="flex justify-between items-center gap-2 w-full">
<Button variant="outline" size="icon" className="md:hidden h-9 w-9" onClick={onToggle}>
<Button variant="outline" size="icon" className="h-9 w-9" onClick={onToggle}>
<PanelRightOpen className="h-4 w-4" />
</Button>
<Button
variant="outline"
size="icon"
className="md:w-full gap-2 h-9 md:h-10"
className="flex-1 gap-2 h-9 md:h-10"
onClick={addChat}
>
<SquarePenIcon className="h-4 w-4" />
Expand Down Expand Up @@ -204,7 +205,7 @@ export function Sidebar({

export function SidebarToggle({ onToggle }: { onToggle: () => void }) {
return (
<Button variant="outline" size="icon" className="md:hidden h-9 w-9" onClick={onToggle}>
<Button variant="outline" size="icon" className="h-9 w-9" onClick={onToggle}>
<PanelRightClose className="h-4 w-4" />
</Button>
);
Expand Down
72 changes: 38 additions & 34 deletions frontend/src/components/UnifiedChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2255,7 +2255,9 @@ export function UnifiedChat() {
};

return (
<div className="grid h-dvh min-h-0 w-full grid-cols-1 md:grid-cols-[280px_1fr] overflow-hidden">
<div
className={`grid h-dvh min-h-0 w-full grid-cols-1 overflow-hidden ${isSidebarOpen ? "md:grid-cols-[280px_1fr]" : ""}`}
>
{/* Use the existing Sidebar component */}
<Sidebar chatId={chatId} isOpen={isSidebarOpen} onToggle={toggleSidebar} />

Expand All @@ -2271,9 +2273,9 @@ export function UnifiedChat() {
</div>
)}

{/* Mobile sidebar toggle */}
{/* Sidebar toggle - visible when sidebar is closed */}
{!isSidebarOpen && (
<div className="fixed top-[9.5px] left-4 z-20 md:hidden">
<div className="fixed top-[9.5px] left-4 z-20">
<SidebarToggle onToggle={toggleSidebar} />
</div>
)}
Expand All @@ -2289,37 +2291,39 @@ export function UnifiedChat() {
>
{conversation?.metadata?.title || "Chat"}
</h1>
{/* Mobile new chat button - positioned on the right */}
<Button
variant="outline"
size="icon"
className="md:hidden absolute right-0 h-9 w-9"
onClick={() => {
// Clear conversation and start new chat
const usp = new URLSearchParams(window.location.search);
usp.delete("conversation_id");
const newUrl = usp.toString()
? `${window.location.pathname}?${usp.toString()}`
: window.location.pathname;
window.history.replaceState(null, "", newUrl);
window.dispatchEvent(new Event("newchat"));
setChatId(undefined);
setConversation(null);
setMessages([]);
setLastSeenItemId(undefined);
// Clear pagination state
setOldestItemId(undefined);
setHasMoreOlderMessages(false);
setIsLoadingOlderMessages(false);
// Close sidebar if open
if (isSidebarOpen) {
toggleSidebar();
}
}}
aria-label="New chat"
>
<SquarePen className="h-4 w-4" />
</Button>
{/* New chat button - visible when sidebar is closed */}
{!isSidebarOpen && (
<Button
variant="outline"
size="icon"
className="absolute right-0 h-9 w-9"
onClick={() => {
// Clear conversation and start new chat
const usp = new URLSearchParams(window.location.search);
usp.delete("conversation_id");
const newUrl = usp.toString()
? `${window.location.pathname}?${usp.toString()}`
: window.location.pathname;
window.history.replaceState(null, "", newUrl);
window.dispatchEvent(new Event("newchat"));
setChatId(undefined);
setConversation(null);
setMessages([]);
setLastSeenItemId(undefined);
// Clear pagination state
setOldestItemId(undefined);
setHasMoreOlderMessages(false);
setIsLoadingOlderMessages(false);
// Close sidebar if open
if (isSidebarOpen) {
toggleSidebar();
}
}}
aria-label="New chat"
>
<SquarePen className="h-4 w-4" />
</Button>
)}
</div>
</div>
)}
Expand Down
Loading