refactor(right-panel): simplify toggle button logic and update layout for document handling

This commit is contained in:
Anish Sarkar 2026-08-05 02:49:58 +05:30
parent 13cae846fe
commit 997feeb901
2 changed files with 42 additions and 85 deletions

View file

@ -4,7 +4,7 @@ import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { PanelRight } from "lucide-react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import dynamic from "next/dynamic";
import { type MouseEvent, startTransition, useEffect } from "react";
import { 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";
@ -74,49 +74,25 @@ interface RightPanelProps {
open: boolean;
onOpenChange: (open: boolean) => void;
};
showCollapseButton?: boolean;
reserveDocumentToggleSpace?: boolean;
showTopBorder?: boolean;
}
function isKeyboardClick(event: MouseEvent) {
return event.detail === 0;
}
function CollapseButton({ onClick }: { onClick: () => void }) {
return (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
tabIndex={-1}
onClick={(event) => {
if (isKeyboardClick(event)) return;
onClick();
}}
className="h-8 w-8 shrink-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground"
>
<PanelRight className="h-4 w-4" />
<span className="sr-only">Collapse panel</span>
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">Collapse panel</TooltipContent>
</Tooltip>
);
}
interface RightPanelToggleButtonProps {
className?: string;
iconClassName?: string;
disabled?: boolean;
documentsOnly?: boolean;
}
export function RightPanelToggleButton({
className,
iconClassName,
disabled = false,
documentsOnly = false,
}: RightPanelToggleButtonProps) {
const [collapsed, setCollapsed] = useAtom(rightPanelCollapsedAtom);
const activeTab = useAtomValue(rightPanelTabAtom);
const documentsOpen = useAtomValue(documentsSidebarOpenAtom);
const reportState = useAtomValue(reportPanelAtom);
const editorState = useAtomValue(editorPanelAtom);
@ -135,9 +111,17 @@ export function RightPanelToggleButton({
const citationOpen = citationState.isOpen && citationState.target != null;
const hasContent =
documentsOpen || reportOpen || editorOpen || hitlEditOpen || citationOpen || artifactsOpen;
const effectiveTab = resolveEffectiveTab(activeTab, {
sources: documentsOpen,
report: reportOpen,
editor: editorOpen,
"hitl-edit": hitlEditOpen,
citation: citationOpen,
artifacts: artifactsOpen,
});
const label = collapsed ? "Expand panel" : "Collapse panel";
if (!hasContent) return null;
if (!hasContent || (documentsOnly && effectiveTab !== "sources")) return null;
return (
<Tooltip>
@ -146,6 +130,7 @@ export function RightPanelToggleButton({
variant="ghost"
size="icon"
disabled={disabled}
aria-expanded={!collapsed}
onClick={() => {
if (disabled) return;
startTransition(() => setCollapsed((value) => !value));
@ -164,41 +149,6 @@ export function RightPanelToggleButton({
);
}
/**
* 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] = useAtom(rightPanelCollapsedAtom);
const documentsOpen = useAtomValue(documentsSidebarOpenAtom);
const reportState = useAtomValue(reportPanelAtom);
const editorState = useAtomValue(editorPanelAtom);
const hitlEditState = useAtomValue(hitlEditPanelAtom);
const citationState = useAtomValue(citationPanelAtom);
const artifactsOpen = useAtomValue(artifactsPanelOpenAtom);
const reportOpen = reportState.isOpen && !!reportState.reportId;
const editorOpen =
editorState.isOpen &&
(editorState.kind === "document"
? !!editorState.documentId
: editorState.kind === "memory"
? !!editorState.memoryScope
: !!editorState.localFilePath);
const hitlEditOpen = hitlEditState.isOpen && !!hitlEditState.onSave;
const citationOpen = citationState.isOpen && citationState.target != null;
const hasContent =
documentsOpen || reportOpen || editorOpen || hitlEditOpen || citationOpen || artifactsOpen;
if (!collapsed || !hasContent) return null;
return (
<div className="flex shrink-0 items-center px-0.5">
<RightPanelToggleButton className="-m-0.5" />
</div>
);
}
const PANEL_WIDTHS = {
sources: 420,
report: 640,
@ -242,7 +192,7 @@ function resolveEffectiveTab(
export function RightPanel({
documentsPanel,
showCollapseButton = true,
reserveDocumentToggleSpace = true,
showTopBorder = false,
}: RightPanelProps) {
const [activeTab] = useAtom(rightPanelTabAtom);
@ -256,7 +206,7 @@ export function RightPanel({
const closeCitation = useSetAtom(closeCitationPanelAtom);
const artifactsOpen = useAtomValue(artifactsPanelOpenAtom);
const closeArtifacts = useSetAtom(closeArtifactsPanelAtom);
const [collapsed, setCollapsed] = useAtom(rightPanelCollapsedAtom);
const collapsed = useAtomValue(rightPanelCollapsedAtom);
const reduceMotion = useReducedMotion();
const documentsOpen = documentsPanel?.open ?? false;
@ -311,9 +261,6 @@ export function RightPanel({
});
const targetWidth = PANEL_WIDTHS[effectiveTab];
const collapseButton = showCollapseButton ? (
<CollapseButton onClick={() => setCollapsed(true)} />
) : null;
return (
<AnimatePresence initial={false}>
@ -338,7 +285,11 @@ export function RightPanel({
open={documentsPanel.open}
onOpenChange={documentsPanel.onOpenChange}
embedded
headerAction={collapseButton}
headerAction={
reserveDocumentToggleSpace ? (
<div aria-hidden="true" className="h-8 w-8 shrink-0" />
) : null
}
/>
</div>
)}

View file

@ -21,11 +21,7 @@ import type { ChatItem, NavItem, PageUsage, User, Workspace } from "../../types/
import { Header } from "../header";
import { IconRail } from "../icon-rail";
import { MobileDocumentsWorkspaceView } from "../right-panel/MobileDocumentsWorkspaceView";
import {
RightPanel,
RightPanelExpandButton,
RightPanelToggleButton,
} from "../right-panel/RightPanel";
import { RightPanel, RightPanelToggleButton } from "../right-panel/RightPanel";
import { MobileSidebar, MobileSidebarTrigger, Sidebar, SidebarCollapseButton } from "../sidebar";
import type { NotificationsDropdownData } from "../sidebar/NotificationsDropdown";
import { TabBar } from "../tabs/TabBar";
@ -82,6 +78,7 @@ function MacDesktopTitleBar({
<div className="ml-auto flex h-full items-center [app-region:no-drag] [-webkit-app-region:no-drag]">
<RightPanelToggleButton
disabled={disableRightPanelToggle}
documentsOnly
className="h-6 w-6 rounded-md"
iconClassName="h-3.5 w-3.5"
/>
@ -151,7 +148,7 @@ function MainContentPanel({
onTabPrefetch,
onNewChat,
showTabs = true,
showRightPanelExpandButton = true,
reserveRightPanelToggleSpace = true,
showTopBorder = false,
children,
}: {
@ -160,7 +157,7 @@ function MainContentPanel({
onTabPrefetch?: (tab: ResolvedTab) => void;
onNewChat?: () => void;
showTabs?: boolean;
showRightPanelExpandButton?: boolean;
reserveRightPanelToggleSpace?: boolean;
showTopBorder?: boolean;
children: React.ReactNode;
}) {
@ -178,7 +175,7 @@ function MainContentPanel({
onTabSwitch={onTabSwitch}
onTabPrefetch={onTabPrefetch}
onNewChat={onNewChat}
showRightPanelExpandButton={showRightPanelExpandButton}
reserveRightPanelToggleSpace={reserveRightPanelToggleSpace}
showTopBorder={showTopBorder}
>
{children}
@ -214,7 +211,7 @@ function TabbedMainContentPanel({
onTabSwitch,
onTabPrefetch,
onNewChat,
showRightPanelExpandButton,
reserveRightPanelToggleSpace,
showTopBorder,
children,
}: {
@ -222,7 +219,7 @@ function TabbedMainContentPanel({
onTabSwitch?: (tab: ResolvedTab) => void;
onTabPrefetch?: (tab: ResolvedTab) => void;
onNewChat?: () => void;
showRightPanelExpandButton: boolean;
reserveRightPanelToggleSpace: boolean;
showTopBorder: boolean;
children: React.ReactNode;
}) {
@ -239,7 +236,11 @@ function TabbedMainContentPanel({
onTabSwitch={onTabSwitch}
onTabPrefetch={onTabPrefetch}
onNewChat={onNewChat}
rightActions={showRightPanelExpandButton ? <RightPanelExpandButton /> : null}
rightActions={
reserveRightPanelToggleSpace ? (
<div aria-hidden="true" className="h-8 w-8 shrink-0" />
) : null
}
className="min-w-0"
/>
<div className="relative flex flex-1 flex-col bg-panel overflow-hidden min-w-0">
@ -265,7 +266,7 @@ function TabbedMainContentPanel({
}
function DesktopWorkspaceRegion({ children }: { children: React.ReactNode }) {
return <div className="flex h-full min-w-0 flex-1 -mr-2">{children}</div>;
return <div className="relative flex h-full min-w-0 flex-1 -mr-2">{children}</div>;
}
export function LayoutShell({
@ -591,7 +592,7 @@ export function LayoutShell({
onTabPrefetch={onTabPrefetch}
onNewChat={onNewChat}
showTabs={showTabs}
showRightPanelExpandButton={!isMacDesktop}
reserveRightPanelToggleSpace={!isMacDesktop}
showTopBorder={isMacDesktop}
>
{children}
@ -600,9 +601,14 @@ export function LayoutShell({
{/* Right panel — tabbed Documents/Report/Editor/Citations/Artifacts (desktop only) */}
<RightPanel
documentsPanel={documentsPanel}
showCollapseButton={!isMacDesktop}
reserveDocumentToggleSpace={!isMacDesktop}
showTopBorder={isMacDesktop}
/>
{!isMacDesktop && (
<div className="absolute right-2 top-2 z-30">
<RightPanelToggleButton documentsOnly />
</div>
)}
</>
)}
</DesktopWorkspaceRegion>