From cb7daa77db1f40ad34849a895567136b1542c151 Mon Sep 17 00:00:00 2001 From: Xubin Ren <52506698+Re-bin@users.noreply.github.com> Date: Fri, 22 May 2026 00:31:55 +0800 Subject: [PATCH] feat(webui): refine collapsible sidebar --- webui/src/App.tsx | 25 +- webui/src/components/Sidebar.tsx | 259 +++++++++++++------ webui/src/components/thread/ThreadHeader.tsx | 25 +- webui/src/tests/app-layout.test.tsx | 10 +- 4 files changed, 225 insertions(+), 94 deletions(-) diff --git a/webui/src/App.tsx b/webui/src/App.tsx index c303446e2..aa3cf0cf8 100644 --- a/webui/src/App.tsx +++ b/webui/src/App.tsx @@ -43,6 +43,7 @@ const SIDEBAR_STORAGE_KEY = "nanobot-webui.sidebar"; const COMPLETED_RUNS_STORAGE_KEY = "nanobot-webui.sidebar.completed-runs.v1"; const RESTART_STARTED_KEY = "nanobot-webui.restartStartedAt"; const SIDEBAR_WIDTH = 272; +const SIDEBAR_RAIL_WIDTH = 56; const TOKEN_REFRESH_MARGIN_MS = 30_000; const TOKEN_REFRESH_MIN_DELAY_MS = 5_000; type ShellView = "chat" | "settings"; @@ -411,6 +412,10 @@ function Shell({ setDesktopSidebarOpen(false); }, []); + const openDesktopSidebar = useCallback(() => { + setDesktopSidebarOpen(true); + }, []); + const closeMobileSidebar = useCallback(() => { setMobileSidebarOpen(false); }, []); @@ -732,17 +737,19 @@ function Shell({ "relative z-20 hidden shrink-0 overflow-hidden lg:block", "transition-[width] duration-300 ease-out", )} - style={{ width: desktopSidebarOpen ? SIDEBAR_WIDTH : 0 }} + style={{ + width: desktopSidebarOpen ? SIDEBAR_WIDTH : SIDEBAR_RAIL_WIDTH, + }} >
- +
) : null} @@ -797,7 +804,7 @@ function Shell({ onTurnEnd={onTurnEnd} theme={theme} onToggleTheme={toggle} - hideSidebarToggleOnDesktop={desktopSidebarOpen} + hideSidebarToggleOnDesktop /> {view === "settings" && ( diff --git a/webui/src/components/Sidebar.tsx b/webui/src/components/Sidebar.tsx index 53acfc3a9..1040c9696 100644 --- a/webui/src/components/Sidebar.tsx +++ b/webui/src/components/Sidebar.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, type ReactNode } from "react"; import { Archive, ListFilter, @@ -28,6 +28,7 @@ import type { SidebarSortMode, SidebarViewState, } from "@/lib/types"; +import { cn } from "@/lib/utils"; interface SidebarProps { sessions: ChatSummary[]; @@ -44,7 +45,9 @@ interface SidebarProps { onToggleArchived: () => void; onUpdateView: (view: Partial) => void; onCollapse: () => void; + onExpand?: () => void; containActionMenus?: boolean; + collapsed?: boolean; pinnedKeys?: string[]; archivedKeys?: string[]; titleOverrides?: Record; @@ -59,6 +62,8 @@ export function Sidebar(props: SidebarProps) { const { t } = useTranslation(); const [menuPortalContainer, setMenuPortalContainer] = useState(null); + const collapsed = Boolean(props.collapsed); + const toggleLabel = t("thread.header.toggleSidebar"); return ( ); } +function SidebarActionButton({ + collapsed, + label, + icon, + onClick, + className, +}: { + collapsed: boolean; + label: string; + icon: ReactNode; + onClick: () => void; + className?: string; +}) { + return ( + + ); +} + function SidebarViewMenu({ + compact = false, view, onUpdateView, }: { + compact?: boolean; view?: SidebarViewState; onUpdateView: (view: Partial) => void; }) { @@ -182,11 +268,28 @@ function SidebarViewMenu({ diff --git a/webui/src/components/thread/ThreadHeader.tsx b/webui/src/components/thread/ThreadHeader.tsx index 72136b10f..f929e7e04 100644 --- a/webui/src/components/thread/ThreadHeader.tsx +++ b/webui/src/components/thread/ThreadHeader.tsx @@ -32,12 +32,17 @@ export function ThreadHeader({ onClick={onToggleSidebar} className={cn( "h-7 w-7 rounded-md text-muted-foreground hover:bg-accent/35 hover:text-foreground", - hideSidebarToggleOnDesktop && "lg:pointer-events-none lg:opacity-0", + hideSidebarToggleOnDesktop && "lg:hidden", )} > - + ); } @@ -52,7 +57,7 @@ export function ThreadHeader({ onClick={onToggleSidebar} className={cn( "h-7 w-7 rounded-md text-muted-foreground hover:bg-accent/35 hover:text-foreground", - hideSidebarToggleOnDesktop && "lg:pointer-events-none lg:opacity-0", + hideSidebarToggleOnDesktop && "lg:hidden", )} > @@ -62,7 +67,12 @@ export function ThreadHeader({ - +
@@ -73,10 +83,12 @@ function ThemeButton({ theme, onToggleTheme, label, + className, }: { theme: "light" | "dark"; onToggleTheme: () => void; label: string; + className?: string; }) { return (