feat: implement macOS-specific title bar adjustments and enhance RightPanel with toggle functionality

This commit is contained in:
Anish Sarkar 2026-05-19 18:57:06 +05:30
parent ee3a6dc45f
commit cd4e5ae7f2
5 changed files with 322 additions and 200 deletions

View file

@ -3,7 +3,7 @@
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { PanelRight } from "lucide-react";
import dynamic from "next/dynamic";
import { startTransition, useEffect, type MouseEvent } from "react";
import { type MouseEvent, startTransition, useEffect } from "react";
import { closeReportPanelAtom, reportPanelAtom } from "@/atoms/chat/report-panel.atom";
import { citationPanelAtom, closeCitationPanelAtom } from "@/atoms/citation/citation-panel.atom";
import { documentsSidebarOpenAtom } from "@/atoms/documents/ui.atoms";
@ -12,6 +12,7 @@ import { rightPanelCollapsedAtom, rightPanelTabAtom } from "@/atoms/layout/right
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { closeHitlEditPanelAtom, hitlEditPanelAtom } from "@/features/chat-messages/hitl";
import { cn } from "@/lib/utils";
import { DocumentsSidebar } from "../sidebar";
const EditorPanelContent = dynamic(
@ -51,6 +52,8 @@ interface RightPanelProps {
open: boolean;
onOpenChange: (open: boolean) => void;
};
showCollapseButton?: boolean;
showTopBorder?: boolean;
}
function isKeyboardClick(event: MouseEvent) {
@ -80,13 +83,66 @@ function CollapseButton({ onClick }: { onClick: () => void }) {
);
}
interface RightPanelToggleButtonProps {
className?: string;
iconClassName?: string;
disabled?: boolean;
}
export function RightPanelToggleButton({
className,
iconClassName,
disabled = false,
}: RightPanelToggleButtonProps) {
const [collapsed, setCollapsed] = useAtom(rightPanelCollapsedAtom);
const documentsOpen = useAtomValue(documentsSidebarOpenAtom);
const reportState = useAtomValue(reportPanelAtom);
const editorState = useAtomValue(editorPanelAtom);
const hitlEditState = useAtomValue(hitlEditPanelAtom);
const citationState = useAtomValue(citationPanelAtom);
const reportOpen = reportState.isOpen && !!reportState.reportId;
const editorOpen =
editorState.isOpen &&
(editorState.kind === "document" ? !!editorState.documentId : !!editorState.localFilePath);
const hitlEditOpen = hitlEditState.isOpen && !!hitlEditState.onSave;
const citationOpen = citationState.isOpen && citationState.chunkId != null;
const hasContent = documentsOpen || reportOpen || editorOpen || hitlEditOpen || citationOpen;
const label = collapsed ? "Expand panel" : "Collapse panel";
if (!hasContent) return null;
return (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
disabled={disabled}
onClick={() => {
if (disabled) return;
startTransition(() => setCollapsed((value) => !value));
}}
className={cn(
"h-8 w-8 shrink-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground",
className
)}
>
<PanelRight className={cn("h-4 w-4", iconClassName)} />
<span className="sr-only">{label}</span>
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">{label}</TooltipContent>
</Tooltip>
);
}
/**
* Absolutely positioned expand button renders at top-right of the main
* container so it occupies the same screen position as the collapse button
* inside the Documents header.
*/
export function RightPanelExpandButton() {
const [collapsed, setCollapsed] = useAtom(rightPanelCollapsedAtom);
const [collapsed] = useAtom(rightPanelCollapsedAtom);
const documentsOpen = useAtomValue(documentsSidebarOpenAtom);
const reportState = useAtomValue(reportPanelAtom);
const editorState = useAtomValue(editorPanelAtom);
@ -104,24 +160,7 @@ export function RightPanelExpandButton() {
return (
<div className="flex shrink-0 items-center px-0.5">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
tabIndex={-1}
onClick={(event) => {
if (isKeyboardClick(event)) return;
startTransition(() => setCollapsed(false));
}}
className="h-8 w-8 shrink-0 -m-0.5 text-muted-foreground hover:bg-accent hover:text-accent-foreground"
>
<PanelRight className="h-4 w-4" />
<span className="sr-only">Expand panel</span>
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">Expand panel</TooltipContent>
</Tooltip>
<RightPanelToggleButton className="-m-0.5" />
</div>
);
}
@ -134,7 +173,11 @@ const PANEL_WIDTHS = {
citation: 560,
} as const;
export function RightPanel({ documentsPanel }: RightPanelProps) {
export function RightPanel({
documentsPanel,
showCollapseButton = true,
showTopBorder = false,
}: RightPanelProps) {
const [activeTab] = useAtom(rightPanelTabAtom);
const reportState = useAtomValue(reportPanelAtom);
const closeReport = useSetAtom(closeReportPanelAtom);
@ -208,14 +251,19 @@ export function RightPanel({ documentsPanel }: RightPanelProps) {
}
const targetWidth = PANEL_WIDTHS[effectiveTab];
const collapseButton = <CollapseButton onClick={() => setCollapsed(true)} />;
const collapseButton = showCollapseButton ? (
<CollapseButton onClick={() => setCollapsed(true)} />
) : null;
if (!isVisible) return null;
return (
<aside
style={{ width: targetWidth }}
className="flex h-full shrink-0 flex-col border-l bg-panel text-sidebar-foreground overflow-hidden transition-[width] duration-200 ease-out"
className={cn(
"flex h-full shrink-0 flex-col border-l bg-panel text-sidebar-foreground overflow-hidden transition-[width] duration-200 ease-out",
showTopBorder && "border-t"
)}
>
<div className="relative flex-1 min-h-0 overflow-hidden">
{effectiveTab === "sources" && documentsOpen && documentsPanel && (

View file

@ -9,6 +9,7 @@ import { Spinner } from "@/components/ui/spinner";
import { TooltipProvider } from "@/components/ui/tooltip";
import type { InboxItem } from "@/hooks/use-inbox";
import { useIsMobile } from "@/hooks/use-mobile";
import { useElectronAPI } from "@/hooks/use-platform";
import { cn } from "@/lib/utils";
import { SidebarProvider, useSidebarState } from "../../hooks";
import {
@ -19,7 +20,11 @@ import {
import type { ChatItem, NavItem, PageUsage, SearchSpace, User } from "../../types/layout.types";
import { Header } from "../header";
import { IconRail } from "../icon-rail";
import { RightPanel, RightPanelExpandButton } from "../right-panel/RightPanel";
import {
RightPanel,
RightPanelExpandButton,
RightPanelToggleButton,
} from "../right-panel/RightPanel";
import {
AllPrivateChatsSidebarContent,
AllSharedChatsSidebarContent,
@ -28,6 +33,7 @@ import {
MobileSidebar,
MobileSidebarTrigger,
Sidebar,
SidebarCollapseButton,
} from "../sidebar";
import { SidebarSlideOutPanel } from "../sidebar/SidebarSlideOutPanel";
import { TabBar } from "../tabs/TabBar";
@ -45,6 +51,36 @@ const DocumentTabContent = dynamic(
}
);
function MacDesktopTitleBar({
isSidebarCollapsed,
onToggleSidebar,
disableRightPanelToggle = false,
}: {
isSidebarCollapsed: boolean;
onToggleSidebar: () => void;
disableRightPanelToggle?: boolean;
}) {
return (
<div className="flex h-9 shrink-0 items-center bg-rail px-2 [app-region:drag] [-webkit-app-region:drag]">
<div className="ml-[72px] flex h-full items-center [app-region:no-drag] [-webkit-app-region:no-drag]">
<SidebarCollapseButton
isCollapsed={isSidebarCollapsed}
onToggle={onToggleSidebar}
className="h-6 w-6 rounded-md"
iconClassName="h-3.5 w-3.5"
/>
</div>
<div className="ml-auto flex h-full items-center [app-region:no-drag] [-webkit-app-region:no-drag]">
<RightPanelToggleButton
disabled={disableRightPanelToggle}
className="h-6 w-6 rounded-md"
iconClassName="h-3.5 w-3.5"
/>
</div>
</div>
);
}
// Per-tab data source
interface TabDataSource {
items: InboxItem[];
@ -130,22 +166,28 @@ function MainContentPanel({
isChatPage,
onTabSwitch,
onNewChat,
showRightPanelExpandButton = true,
showTopBorder = false,
children,
}: {
isChatPage: boolean;
onTabSwitch?: (tab: Tab) => void;
onNewChat?: () => void;
showRightPanelExpandButton?: boolean;
showTopBorder?: boolean;
children: React.ReactNode;
}) {
const activeTab = useAtomValue(activeTabAtom);
const isDocumentTab = activeTab?.type === "document";
return (
<div className="relative isolate flex flex-1 flex-col min-w-0">
<div
className={cn("relative isolate flex flex-1 flex-col min-w-0", showTopBorder && "border-t")}
>
<TabBar
onTabSwitch={onTabSwitch}
onNewChat={onNewChat}
rightActions={<RightPanelExpandButton />}
rightActions={showRightPanelExpandButton ? <RightPanelExpandButton /> : null}
className="min-w-0"
/>
<div className="relative flex flex-1 flex-col bg-panel overflow-hidden min-w-0">
@ -221,6 +263,8 @@ export function LayoutShell({
onTabSwitch,
}: LayoutShellProps) {
const isMobile = useIsMobile();
const electronAPI = useElectronAPI();
const isMacDesktop = electronAPI?.versions.platform === "darwin";
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const { isCollapsed, setIsCollapsed, toggleCollapsed } = useSidebarState(defaultCollapsed);
const {
@ -388,175 +432,190 @@ export function LayoutShell({
return (
<SidebarProvider value={sidebarContextValue}>
<TooltipProvider delayDuration={0}>
<div
className={cn(
"flex h-screen w-full gap-2 px-2 py-0 overflow-hidden bg-rail",
className
)}
>
<div className="hidden md:flex overflow-hidden border-r -mr-2 pr-2 bg-rail">
<IconRail
searchSpaces={searchSpaces}
activeSearchSpaceId={activeSearchSpaceId}
onSearchSpaceSelect={onSearchSpaceSelect}
onSearchSpaceDelete={onSearchSpaceDelete}
onSearchSpaceSettings={onSearchSpaceSettings}
onAddSearchSpace={onAddSearchSpace}
isSingleRailMode={false}
user={user}
onUserSettings={onUserSettings}
onAnnouncements={onAnnouncements}
announcementUnreadCount={announcementUnreadCount}
onLogout={onLogout}
theme={theme}
setTheme={setTheme}
<div className={cn("flex h-screen w-full flex-col overflow-hidden bg-rail", className)}>
{isMacDesktop ? (
<MacDesktopTitleBar
isSidebarCollapsed={isCollapsed}
onToggleSidebar={toggleCollapsed}
disableRightPanelToggle={useWorkspacePanel}
/>
</div>
{/* Sidebar + slide-out panels share one container; overflow visible so panels can overlay main content. Negative right margin closes the flex gap so the sidebar sits flush against the main panel, separated only by a border. */}
<div className="relative hidden md:flex shrink-0 z-20 -mr-2 border-r bg-panel">
<Sidebar
searchSpace={searchSpace}
isCollapsed={isCollapsed}
onToggleCollapse={toggleCollapsed}
navItems={navItems}
onNavItemClick={onNavItemClick}
chats={chats}
sharedChats={sharedChats}
activeChatId={activeChatId}
onNewChat={onNewChat}
onChatSelect={onChatSelect}
onChatRename={onChatRename}
onChatDelete={onChatDelete}
onChatArchive={onChatArchive}
onViewAllSharedChats={onViewAllSharedChats}
onViewAllPrivateChats={onViewAllPrivateChats}
isSharedChatsPanelOpen={activeSlideoutPanel === "shared"}
isPrivateChatsPanelOpen={activeSlideoutPanel === "private"}
user={user}
onSettings={onSettings}
onManageMembers={onManageMembers}
onUserSettings={onUserSettings}
onAnnouncements={onAnnouncements}
announcementUnreadCount={announcementUnreadCount}
onLogout={onLogout}
pageUsage={pageUsage}
theme={theme}
setTheme={setTheme}
renderUserProfile={false}
className="flex shrink-0"
isLoadingChats={isLoadingChats}
sidebarWidth={sidebarWidth}
isResizing={isResizing}
/>
{!isCollapsed && (
<hr
aria-orientation="vertical"
aria-label="Resize sidebar"
aria-valuemin={SIDEBAR_MIN_WIDTH}
aria-valuemax={SIDEBAR_MAX_WIDTH}
aria-valuenow={sidebarWidth}
tabIndex={0}
onPointerDown={onResizePointerDown}
style={{ touchAction: "none" }}
className={cn(
"absolute top-0 right-0 h-full w-4 translate-x-1/2 z-50 m-0 border-0 bg-transparent p-0 select-none cursor-col-resize",
"after:content-[''] after:absolute after:inset-y-0 after:left-1/2 after:w-px after:-translate-x-1/2 after:bg-transparent hover:after:bg-border/80 after:transition-colors",
isResizing && "after:bg-border"
)}
) : null}
<div className="flex min-h-0 flex-1 w-full gap-2 px-2 py-0 overflow-hidden">
<div className="hidden md:flex overflow-hidden -mr-2 pr-2 bg-rail">
<IconRail
searchSpaces={searchSpaces}
activeSearchSpaceId={activeSearchSpaceId}
onSearchSpaceSelect={onSearchSpaceSelect}
onSearchSpaceDelete={onSearchSpaceDelete}
onSearchSpaceSettings={onSearchSpaceSettings}
onAddSearchSpace={onAddSearchSpace}
isSingleRailMode={false}
user={user}
onUserSettings={onUserSettings}
onAnnouncements={onAnnouncements}
announcementUnreadCount={announcementUnreadCount}
onLogout={onLogout}
theme={theme}
setTheme={setTheme}
/>
)}
</div>
{/* Unified slide-out panel — shell stays open, content cross-fades */}
<SidebarSlideOutPanel
open={anySlideOutOpen}
onOpenChange={closeSlideout}
ariaLabel={panelAriaLabel}
{/* Sidebar + slide-out panels share one container; overflow visible so panels can overlay main content. Negative right margin closes the flex gap so the sidebar sits flush against the main panel, separated only by a border. */}
<div
className={cn(
"relative hidden md:flex shrink-0 z-20 -mr-2 bg-panel",
isMacDesktop ? "rounded-tl-xl border-t border-r border-l" : "border-r"
)}
>
<AnimatePresence mode="popLayout" initial={false}>
{activeSlideoutPanel === "inbox" && inbox && (
<motion.div
key="inbox"
className="h-full flex flex-col"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<InboxSidebarContent
onOpenChange={(open) => closeSlideout(open)}
comments={inbox.comments}
status={inbox.status}
totalUnreadCount={inbox.totalUnreadCount}
/>
</motion.div>
)}
{activeSlideoutPanel === "shared" && allSharedChatsPanel && (
<motion.div
key="shared"
className="h-full flex flex-col"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<AllSharedChatsSidebarContent
onOpenChange={(open) => closeSlideout(open)}
searchSpaceId={allSharedChatsPanel.searchSpaceId}
/>
</motion.div>
)}
{activeSlideoutPanel === "private" && allPrivateChatsPanel && (
<motion.div
key="private"
className="h-full flex flex-col"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<AllPrivateChatsSidebarContent
onOpenChange={(open) => closeSlideout(open)}
searchSpaceId={allPrivateChatsPanel.searchSpaceId}
/>
</motion.div>
)}
</AnimatePresence>
</SidebarSlideOutPanel>
</div>
<Sidebar
searchSpace={searchSpace}
isCollapsed={isCollapsed}
onToggleCollapse={toggleCollapsed}
navItems={navItems}
onNavItemClick={onNavItemClick}
chats={chats}
sharedChats={sharedChats}
activeChatId={activeChatId}
onNewChat={onNewChat}
onChatSelect={onChatSelect}
onChatRename={onChatRename}
onChatDelete={onChatDelete}
onChatArchive={onChatArchive}
onViewAllSharedChats={onViewAllSharedChats}
onViewAllPrivateChats={onViewAllPrivateChats}
isSharedChatsPanelOpen={activeSlideoutPanel === "shared"}
isPrivateChatsPanelOpen={activeSlideoutPanel === "private"}
user={user}
onSettings={onSettings}
onManageMembers={onManageMembers}
onUserSettings={onUserSettings}
onAnnouncements={onAnnouncements}
announcementUnreadCount={announcementUnreadCount}
onLogout={onLogout}
pageUsage={pageUsage}
theme={theme}
setTheme={setTheme}
renderUserProfile={false}
renderCollapseButton={!isMacDesktop}
className={cn("flex shrink-0", isMacDesktop && "rounded-tl-xl")}
isLoadingChats={isLoadingChats}
sidebarWidth={sidebarWidth}
isResizing={isResizing}
/>
<DesktopWorkspaceRegion>
{useWorkspacePanel ? (
<WorkspacePanel
viewportClassName={workspacePanelViewportClassName}
contentClassName={workspacePanelContentClassName}
{!isCollapsed && (
<hr
aria-orientation="vertical"
aria-label="Resize sidebar"
aria-valuemin={SIDEBAR_MIN_WIDTH}
aria-valuemax={SIDEBAR_MAX_WIDTH}
aria-valuenow={sidebarWidth}
tabIndex={0}
onPointerDown={onResizePointerDown}
style={{ touchAction: "none" }}
className={cn(
"absolute top-0 right-0 h-full w-4 translate-x-1/2 z-50 m-0 border-0 bg-transparent p-0 select-none cursor-col-resize",
"after:content-[''] after:absolute after:inset-y-0 after:left-1/2 after:w-px after:-translate-x-1/2 after:bg-transparent hover:after:bg-border/80 after:transition-colors",
isResizing && "after:bg-border"
)}
/>
)}
{/* Unified slide-out panel — shell stays open, content cross-fades */}
<SidebarSlideOutPanel
open={anySlideOutOpen}
onOpenChange={closeSlideout}
ariaLabel={panelAriaLabel}
>
{children}
</WorkspacePanel>
) : (
<>
{/* Main content panel */}
<MainContentPanel
isChatPage={isChatPage}
onTabSwitch={onTabSwitch}
onNewChat={onNewChat}
<AnimatePresence mode="popLayout" initial={false}>
{activeSlideoutPanel === "inbox" && inbox && (
<motion.div
key="inbox"
className="h-full flex flex-col"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<InboxSidebarContent
onOpenChange={(open) => closeSlideout(open)}
comments={inbox.comments}
status={inbox.status}
totalUnreadCount={inbox.totalUnreadCount}
/>
</motion.div>
)}
{activeSlideoutPanel === "shared" && allSharedChatsPanel && (
<motion.div
key="shared"
className="h-full flex flex-col"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<AllSharedChatsSidebarContent
onOpenChange={(open) => closeSlideout(open)}
searchSpaceId={allSharedChatsPanel.searchSpaceId}
/>
</motion.div>
)}
{activeSlideoutPanel === "private" && allPrivateChatsPanel && (
<motion.div
key="private"
className="h-full flex flex-col"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<AllPrivateChatsSidebarContent
onOpenChange={(open) => closeSlideout(open)}
searchSpaceId={allPrivateChatsPanel.searchSpaceId}
/>
</motion.div>
)}
</AnimatePresence>
</SidebarSlideOutPanel>
</div>
<DesktopWorkspaceRegion>
{useWorkspacePanel ? (
<WorkspacePanel
className={isMacDesktop ? "border-t" : undefined}
viewportClassName={workspacePanelViewportClassName}
contentClassName={workspacePanelContentClassName}
>
{children}
</MainContentPanel>
</WorkspacePanel>
) : (
<>
{/* Main content panel */}
<MainContentPanel
isChatPage={isChatPage}
onTabSwitch={onTabSwitch}
onNewChat={onNewChat}
showRightPanelExpandButton={!isMacDesktop}
showTopBorder={isMacDesktop}
>
{children}
</MainContentPanel>
{/* Right panel — tabbed Sources/Report (desktop only) */}
{documentsPanel ? (
<RightPanel
documentsPanel={{
open: documentsPanel.open,
onOpenChange: documentsPanel.onOpenChange,
}}
/>
) : null}
</>
)}
</DesktopWorkspaceRegion>
{/* Right panel — tabbed Sources/Report (desktop only) */}
{documentsPanel ? (
<RightPanel
documentsPanel={{
open: documentsPanel.open,
onOpenChange: documentsPanel.onOpenChange,
}}
showCollapseButton={!isMacDesktop}
showTopBorder={isMacDesktop}
/>
) : null}
</>
)}
</DesktopWorkspaceRegion>
</div>
</div>
</TooltipProvider>
</SidebarProvider>

View file

@ -95,6 +95,7 @@ interface SidebarProps {
sidebarWidth?: number;
isResizing?: boolean;
renderUserProfile?: boolean;
renderCollapseButton?: boolean;
}
export function Sidebar({
@ -132,6 +133,7 @@ export function Sidebar({
sidebarWidth = SIDEBAR_MIN_WIDTH,
isResizing = false,
renderUserProfile = true,
renderCollapseButton = true,
}: SidebarProps) {
const t = useTranslations("sidebar");
const [openDropdownChatId, setOpenDropdownChatId] = useState<number | null>(null);
@ -176,13 +178,15 @@ export function Sidebar({
onManageMembers={onManageMembers}
/>
</div>
<div className={cn("shrink-0", isCollapsed && "mx-auto")}>
<SidebarCollapseButton
isCollapsed={isCollapsed}
onToggle={onToggleCollapse ?? (() => {})}
disableTooltip={disableTooltips}
/>
</div>
{renderCollapseButton ? (
<div className={cn("shrink-0", isCollapsed && "mx-auto")}>
<SidebarCollapseButton
isCollapsed={isCollapsed}
onToggle={onToggleCollapse ?? (() => {})}
disableTooltip={disableTooltips}
/>
</div>
) : null}
</div>
<div className="flex flex-col gap-0.5 py-1.5">
@ -383,10 +387,7 @@ function SidebarUsageFooter({
if (isCollapsed) return null;
const containerClass = cn(
"px-3 py-3 space-y-3",
hasNavSectionAbove && "border-t"
);
const containerClass = cn("px-3 py-3 space-y-3", hasNavSectionAbove && "border-t");
if (isAnonymous) {
return (

View file

@ -6,17 +6,22 @@ import { Button } from "@/components/ui/button";
import { ShortcutKbd } from "@/components/ui/shortcut-kbd";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { usePlatformShortcut } from "@/hooks/use-platform-shortcut";
import { cn } from "@/lib/utils";
interface SidebarCollapseButtonProps {
isCollapsed: boolean;
onToggle: () => void;
disableTooltip?: boolean;
className?: string;
iconClassName?: string;
}
export function SidebarCollapseButton({
isCollapsed,
onToggle,
disableTooltip = false,
className,
iconClassName,
}: SidebarCollapseButtonProps) {
const t = useTranslations("sidebar");
const { shortcutKeys } = usePlatformShortcut();
@ -26,9 +31,12 @@ export function SidebarCollapseButton({
variant="ghost"
size="icon"
onClick={onToggle}
className="h-8 w-8 shrink-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground"
className={cn(
"h-8 w-8 shrink-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground",
className
)}
>
<PanelLeft className="h-4 w-4" />
<PanelLeft className={cn("h-4 w-4", iconClassName)} />
<span className="sr-only">{isCollapsed ? t("expand_sidebar") : t("collapse_sidebar")}</span>
</Button>
);