diff --git a/surfsense_web/atoms/chat/report-panel.atom.ts b/surfsense_web/atoms/chat/report-panel.atom.ts index 8092e623b..edae8979d 100644 --- a/surfsense_web/atoms/chat/report-panel.atom.ts +++ b/surfsense_web/atoms/chat/report-panel.atom.ts @@ -1,5 +1,4 @@ import { atom } from "jotai"; -import { documentsSidebarOpenAtom } from "@/atoms/documents/ui.atoms"; import { rightPanelCollapsedAtom, rightPanelTabAtom } from "@/atoms/layout/right-panel.atom"; interface ReportPanelState { @@ -25,11 +24,14 @@ export const reportPanelAtom = atom(initialState); /** Derived read-only atom for checking if panel is open */ export const reportPanelOpenAtom = atom((get) => get(reportPanelAtom).isOpen); +/** Snapshot of `rightPanelCollapsedAtom` taken before the report opens */ +const preReportCollapsedAtom = atom(null); + /** Action atom to open the report panel with a specific report */ export const openReportPanelAtom = atom( null, ( - _get, + get, set, { reportId, @@ -38,6 +40,9 @@ export const openReportPanelAtom = atom( shareToken, }: { reportId: number; title: string; wordCount?: number; shareToken?: string | null } ) => { + if (!get(reportPanelAtom).isOpen) { + set(preReportCollapsedAtom, get(rightPanelCollapsedAtom)); + } set(reportPanelAtom, { isOpen: true, reportId, @@ -47,12 +52,16 @@ export const openReportPanelAtom = atom( }); set(rightPanelTabAtom, "report"); set(rightPanelCollapsedAtom, false); - set(documentsSidebarOpenAtom, true); } ); /** Action atom to close the report panel */ -export const closeReportPanelAtom = atom(null, (_get, set) => { +export const closeReportPanelAtom = atom(null, (get, set) => { set(reportPanelAtom, initialState); set(rightPanelTabAtom, "sources"); + const prev = get(preReportCollapsedAtom); + if (prev !== null) { + set(rightPanelCollapsedAtom, prev); + set(preReportCollapsedAtom, null); + } }); diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index 93b296000..b77edb6b7 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -627,7 +627,7 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false side="bottom" align="start" sideOffset={12} - className="w-[calc(100vw-2rem)] max-w-56 sm:max-w-72 sm:w-72 p-0" + className="w-[calc(100vw-2rem)] max-w-56 sm:max-w-72 sm:w-72 p-0 select-none" onOpenAutoFocus={(e) => e.preventDefault()} >
diff --git a/surfsense_web/components/layout/ui/sidebar/ChatListItem.tsx b/surfsense_web/components/layout/ui/sidebar/ChatListItem.tsx index 902c11b44..fecb85175 100644 --- a/surfsense_web/components/layout/ui/sidebar/ChatListItem.tsx +++ b/surfsense_web/components/layout/ui/sidebar/ChatListItem.tsx @@ -8,7 +8,6 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, - DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { useLongPress } from "@/hooks/use-long-press"; @@ -20,6 +19,8 @@ interface ChatListItemProps { name: string; isActive?: boolean; archived?: boolean; + dropdownOpen?: boolean; + onDropdownOpenChange?: (open: boolean) => void; onClick?: () => void; onRename?: () => void; onArchive?: () => void; @@ -30,6 +31,8 @@ export function ChatListItem({ name, isActive, archived, + dropdownOpen: controlledOpen, + onDropdownOpenChange, onClick, onRename, onArchive, @@ -37,11 +40,13 @@ export function ChatListItem({ }: ChatListItemProps) { const t = useTranslations("sidebar"); const isMobile = useIsMobile(); - const [dropdownOpen, setDropdownOpen] = useState(false); + const [internalOpen, setInternalOpen] = useState(false); + const dropdownOpen = controlledOpen ?? internalOpen; + const setDropdownOpen = onDropdownOpenChange ?? setInternalOpen; const animatedName = useTypewriter(name); const { handlers: longPressHandlers, wasLongPress } = useLongPress( - useCallback(() => setDropdownOpen(true), []) + useCallback(() => setDropdownOpen(true), [setDropdownOpen]) ); const handleClick = useCallback(() => { @@ -68,12 +73,12 @@ export function ChatListItem({ {/* Actions dropdown - trigger hidden on mobile, long-press opens it instead */}
- @@ -118,8 +123,7 @@ export function ChatListItem({ )} )} - {onArchive && onDelete && } - {onDelete && ( + {onDelete && ( { e.stopPropagation(); diff --git a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx index 4e95c381f..d13da7e47 100644 --- a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx @@ -2,6 +2,7 @@ import { FolderOpen, PenSquare } from "lucide-react"; import { useTranslations } from "next-intl"; +import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; @@ -89,6 +90,7 @@ export function Sidebar({ isResizing = false, }: SidebarProps) { const t = useTranslations("sidebar"); + const [openDropdownChatId, setOpenDropdownChatId] = useState(null); return (
@@ -209,18 +217,20 @@ export function Sidebar({
4 ? "pb-8" : ""}`} > - {sharedChats.slice(0, 20).map((chat) => ( - onChatSelect(chat)} - onRename={() => onChatRename?.(chat)} - onArchive={() => onChatArchive?.(chat)} - onDelete={() => onChatDelete?.(chat)} - /> - ))} + {sharedChats.slice(0, 20).map((chat) => ( + setOpenDropdownChatId(open ? chat.id : null)} + onClick={() => onChatSelect(chat)} + onRename={() => onChatRename?.(chat)} + onArchive={() => onChatArchive?.(chat)} + onDelete={() => onChatDelete?.(chat)} + /> + ))}
{/* Gradient fade indicator when more than 4 items */} {sharedChats.length > 4 && ( @@ -281,18 +291,20 @@ export function Sidebar({
4 ? "pb-8" : ""}`} > - {chats.slice(0, 20).map((chat) => ( - onChatSelect(chat)} - onRename={() => onChatRename?.(chat)} - onArchive={() => onChatArchive?.(chat)} - onDelete={() => onChatDelete?.(chat)} - /> - ))} + {chats.slice(0, 20).map((chat) => ( + setOpenDropdownChatId(open ? chat.id : null)} + onClick={() => onChatSelect(chat)} + onRename={() => onChatRename?.(chat)} + onArchive={() => onChatArchive?.(chat)} + onDelete={() => onChatDelete?.(chat)} + /> + ))}
{/* Gradient fade indicator when more than 4 items */} {chats.length > 4 && (