refactor: enhance layout structure by introducing WorkspacePanel and updating component styles

This commit is contained in:
Anish Sarkar 2026-05-17 03:17:12 +05:30
parent bd1d1c42a7
commit a49ee05456
6 changed files with 107 additions and 62 deletions

View file

@ -18,35 +18,33 @@ export default function BuyMorePage() {
const [activeTab, setActiveTab] = useState<TabId>("pages"); const [activeTab, setActiveTab] = useState<TabId>("pages");
return ( return (
<div className="flex min-h-[calc(100vh-64px)] select-none items-center justify-center px-4 py-8"> <motion.div
<motion.div initial={{ opacity: 0, y: 20 }}
initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }}
animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3 }}
transition={{ duration: 0.3 }} className="w-full select-none space-y-6"
className="w-full max-w-md space-y-6" >
> <div className="flex items-center justify-center rounded-lg border bg-muted/30 p-1">
<div className="flex items-center justify-center rounded-lg border bg-muted/30 p-1"> {TABS.map((tab) => (
{TABS.map((tab) => ( <Button
<Button key={tab.id}
key={tab.id} type="button"
type="button" variant="ghost"
variant="ghost" size="sm"
size="sm" onClick={() => setActiveTab(tab.id)}
onClick={() => setActiveTab(tab.id)} className={cn(
className={cn( "h-auto flex-1 px-3 py-1.5 text-sm",
"h-auto flex-1 px-3 py-1.5 text-sm", activeTab === tab.id
activeTab === tab.id ? "bg-background text-foreground shadow-sm"
? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-accent-foreground"
: "text-muted-foreground hover:text-accent-foreground" )}
)} >
> {tab.label}
{tab.label} </Button>
</Button> ))}
))} </div>
</div>
{activeTab === "pages" ? <BuyPagesContent /> : <BuyTokensContent />} {activeTab === "pages" ? <BuyPagesContent /> : <BuyTokensContent />}
</motion.div> </motion.div>
</div>
); );
} }

View file

@ -5,15 +5,13 @@ import { MorePagesContent } from "@/components/settings/more-pages-content";
export default function MorePagesPage() { export default function MorePagesPage() {
return ( return (
<div className="flex min-h-[calc(100vh-64px)] select-none items-center justify-center px-4 py-8"> <motion.div
<motion.div initial={{ opacity: 0, y: 20 }}
initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }}
animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3 }}
transition={{ duration: 0.3 }} className="w-full select-none space-y-6"
className="w-full max-w-md space-y-6" >
> <MorePagesContent />
<MorePagesContent /> </motion.div>
</motion.div>
</div>
); );
} }

View file

@ -136,7 +136,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
// Documents sidebar state (shared atom so Composer can toggle it) // Documents sidebar state (shared atom so Composer can toggle it)
const [isDocumentsSidebarOpen, setIsDocumentsSidebarOpen] = useAtom(documentsSidebarOpenAtom); const [isDocumentsSidebarOpen, setIsDocumentsSidebarOpen] = useAtom(documentsSidebarOpenAtom);
const [isDocumentsDocked, setIsDocumentsDocked] = useState(true); const [isDocumentsDocked, setIsDocumentsDocked] = useState(true);
const [isRightPanelCollapsed, setIsRightPanelCollapsed] = useAtom(rightPanelCollapsedAtom); const setIsRightPanelCollapsed = useSetAtom(rightPanelCollapsedAtom);
// Open documents sidebar by default on desktop (docked mode) // Open documents sidebar by default on desktop (docked mode)
const documentsInitialized = useRef(false); const documentsInitialized = useRef(false);
@ -668,6 +668,8 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
// Detect if we're on the chat page (needs overflow-hidden for chat's own scroll) // Detect if we're on the chat page (needs overflow-hidden for chat's own scroll)
const isChatPage = pathname?.includes("/new-chat") ?? false; const isChatPage = pathname?.includes("/new-chat") ?? false;
const useWorkspacePanel =
pathname?.endsWith("/buy-more") === true || pathname?.endsWith("/more-pages") === true;
return ( return (
<> <>
@ -705,6 +707,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
theme={theme} theme={theme}
setTheme={setTheme} setTheme={setTheme}
isChatPage={isChatPage} isChatPage={isChatPage}
useWorkspacePanel={useWorkspacePanel}
isLoadingChats={isLoadingThreads} isLoadingChats={isLoadingThreads}
activeSlideoutPanel={activeSlideoutPanel} activeSlideoutPanel={activeSlideoutPanel}
onSlideoutPanelChange={setActiveSlideoutPanel} onSlideoutPanelChange={setActiveSlideoutPanel}

View file

@ -31,6 +31,7 @@ import {
} from "../sidebar"; } from "../sidebar";
import { SidebarSlideOutPanel } from "../sidebar/SidebarSlideOutPanel"; import { SidebarSlideOutPanel } from "../sidebar/SidebarSlideOutPanel";
import { TabBar } from "../tabs/TabBar"; import { TabBar } from "../tabs/TabBar";
import { WorkspacePanel } from "./WorkspacePanel";
const DocumentTabContent = dynamic( const DocumentTabContent = dynamic(
() => import("../tabs/DocumentTabContent").then((m) => ({ default: m.DocumentTabContent })), () => import("../tabs/DocumentTabContent").then((m) => ({ default: m.DocumentTabContent })),
@ -98,6 +99,7 @@ interface LayoutShellProps {
setTheme?: (theme: "light" | "dark" | "system") => void; setTheme?: (theme: "light" | "dark" | "system") => void;
defaultCollapsed?: boolean; defaultCollapsed?: boolean;
isChatPage?: boolean; isChatPage?: boolean;
useWorkspacePanel?: boolean;
children: React.ReactNode; children: React.ReactNode;
className?: string; className?: string;
// Unified slide-out panel state // Unified slide-out panel state
@ -166,6 +168,10 @@ function MainContentPanel({
); );
} }
function DesktopWorkspaceRegion({ children }: { children: React.ReactNode }) {
return <div className="flex h-full min-w-0 flex-1 -mr-2">{children}</div>;
}
export function LayoutShell({ export function LayoutShell({
searchSpaces, searchSpaces,
activeSearchSpaceId, activeSearchSpaceId,
@ -198,6 +204,7 @@ export function LayoutShell({
setTheme, setTheme,
defaultCollapsed = false, defaultCollapsed = false,
isChatPage = false, isChatPage = false,
useWorkspacePanel = false,
children, children,
className, className,
activeSlideoutPanel = null, activeSlideoutPanel = null,
@ -287,9 +294,13 @@ export function LayoutShell({
isLoadingChats={isLoadingChats} isLoadingChats={isLoadingChats}
/> />
<main className={cn("flex-1", isChatPage ? "overflow-hidden" : "overflow-auto")}> {useWorkspacePanel ? (
{children} <WorkspacePanel>{children}</WorkspacePanel>
</main> ) : (
<main className={cn("flex-1", isChatPage ? "overflow-hidden" : "overflow-auto")}>
{children}
</main>
)}
{/* Mobile unified slide-out panel */} {/* Mobile unified slide-out panel */}
<SidebarSlideOutPanel <SidebarSlideOutPanel
@ -506,26 +517,32 @@ export function LayoutShell({
</SidebarSlideOutPanel> </SidebarSlideOutPanel>
</div> </div>
{/* Main content panel */} <DesktopWorkspaceRegion>
<MainContentPanel {useWorkspacePanel ? (
isChatPage={isChatPage} <WorkspacePanel>{children}</WorkspacePanel>
onTabSwitch={onTabSwitch} ) : (
onNewChat={onNewChat} <>
> {/* Main content panel */}
{children} <MainContentPanel
</MainContentPanel> isChatPage={isChatPage}
onTabSwitch={onTabSwitch}
onNewChat={onNewChat}
>
{children}
</MainContentPanel>
{/* Right panel — tabbed Sources/Report (desktop only) */} {/* Right panel — tabbed Sources/Report (desktop only) */}
{documentsPanel && ( {documentsPanel ? (
<div className="-ml-2 -mr-2"> <RightPanel
<RightPanel documentsPanel={{
documentsPanel={{ open: documentsPanel.open,
open: documentsPanel.open, onOpenChange: documentsPanel.onOpenChange,
onOpenChange: documentsPanel.onOpenChange, }}
}} />
/> ) : null}
</div> </>
)} )}
</DesktopWorkspaceRegion>
</div> </div>
</TooltipProvider> </TooltipProvider>
</SidebarProvider> </SidebarProvider>

View file

@ -0,0 +1,28 @@
import type { ReactNode } from "react";
import { cn } from "@/lib/utils";
interface WorkspacePanelProps {
children: ReactNode;
className?: string;
contentClassName?: string;
}
/**
* Full workspace area to the right of the left rail/sidebar.
* Use this when a route should own the whole workspace instead of rendering
* inside the normal TabBar/Header/main/right-panel chrome.
*/
export function WorkspacePanel({ children, className, contentClassName }: WorkspacePanelProps) {
return (
<main
className={cn(
"relative isolate flex min-w-0 flex-1 flex-col overflow-hidden bg-panel",
className
)}
>
<div className="flex min-h-0 flex-1 items-center justify-center overflow-auto px-4 py-8">
<div className={cn("w-full max-w-md", contentClassName)}>{children}</div>
</div>
</main>
);
}

View file

@ -1 +1,2 @@
export { LayoutShell } from "./LayoutShell"; export { LayoutShell } from "./LayoutShell";
export { WorkspacePanel } from "./WorkspacePanel";