mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-23 19:05:16 +02:00
refactor: enhance layout structure by introducing WorkspacePanel and updating component styles
This commit is contained in:
parent
bd1d1c42a7
commit
a49ee05456
6 changed files with 107 additions and 62 deletions
|
|
@ -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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
28
surfsense_web/components/layout/ui/shell/WorkspacePanel.tsx
Normal file
28
surfsense_web/components/layout/ui/shell/WorkspacePanel.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
export { LayoutShell } from "./LayoutShell";
|
export { LayoutShell } from "./LayoutShell";
|
||||||
|
export { WorkspacePanel } from "./WorkspacePanel";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue