mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-08 22:22:17 +02:00
feat(assistant-ui): add "Manage Connectors" option to ComposerAction and clean up DocumentsFilters component
- Introduced a new dropdown menu item for managing connectors in the ComposerAction component. - Removed unused upload button and related props from the DocumentsFilters component for improved clarity and simplicity. - Updated LayoutDataProvider and RightPanel components to streamline state management and enhance layout consistency.
This commit is contained in:
parent
cda232101f
commit
f11cc3868c
7 changed files with 44 additions and 248 deletions
|
|
@ -1359,6 +1359,10 @@ const ComposerAction: FC<ComposerActionProps> = ({
|
|||
<Camera className="h-4 w-4" />
|
||||
Take a screenshot
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => setConnectorDialogOpen(true)}>
|
||||
<Unplug className="h-4 w-4" />
|
||||
Manage Connectors
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSub
|
||||
open={toolsPopoverOpen}
|
||||
onOpenChange={(open) => {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
"use client";
|
||||
|
||||
import { FolderPlus, ListFilter, Search, Upload, X } from "lucide-react";
|
||||
import { FolderPlus, ListFilter, Search, X } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import React, { useCallback, useMemo, useRef, useState } from "react";
|
||||
import { useDocumentUploadDialog } from "@/components/assistant-ui/document-upload-popup";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
|
@ -21,7 +20,6 @@ export function DocumentsFilters({
|
|||
onToggleType,
|
||||
activeTypes,
|
||||
onCreateFolder,
|
||||
onUploadClick,
|
||||
}: {
|
||||
typeCounts: Partial<Record<DocumentTypeEnum, number>>;
|
||||
onSearch: (v: string) => void;
|
||||
|
|
@ -29,15 +27,11 @@ export function DocumentsFilters({
|
|||
onToggleType: (type: DocumentTypeEnum, checked: boolean) => void;
|
||||
activeTypes: DocumentTypeEnum[];
|
||||
onCreateFolder?: () => void;
|
||||
onUploadClick?: () => void;
|
||||
}) {
|
||||
const t = useTranslations("documents");
|
||||
const id = React.useId();
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const { openDialog: openUploadDialog } = useDocumentUploadDialog();
|
||||
const handleUpload = onUploadClick ?? openUploadDialog;
|
||||
|
||||
const [typeSearchQuery, setTypeSearchQuery] = useState("");
|
||||
const [scrollPos, setScrollPos] = useState<"top" | "middle" | "bottom">("top");
|
||||
const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
|
||||
|
|
@ -207,18 +201,6 @@ export function DocumentsFilters({
|
|||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Upload Button */}
|
||||
<Button
|
||||
data-joyride="upload-button"
|
||||
onClick={handleUpload}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 shrink-0 gap-1.5 border-0 bg-white text-gray-700 shadow-none hover:bg-accent hover:text-accent-foreground dark:bg-white dark:text-gray-800"
|
||||
>
|
||||
<Upload size={13} />
|
||||
<span>Upload</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import { currentThreadAtom, resetCurrentThreadAtom } from "@/atoms/chat/current-
|
|||
import { documentsSidebarOpenAtom } from "@/atoms/documents/ui.atoms";
|
||||
import { statusInboxItemsAtom } from "@/atoms/inbox/status-inbox.atom";
|
||||
import { announcementsDialogAtom } from "@/atoms/layout/dialogs.atom";
|
||||
import { rightPanelCollapsedAtom } from "@/atoms/layout/right-panel.atom";
|
||||
import { deleteSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms";
|
||||
import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { removeChatTabAtom, syncChatTabAtom, type Tab } from "@/atoms/tabs/tabs.atom";
|
||||
|
|
@ -133,7 +132,6 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
|
|||
|
||||
// Documents sidebar state (shared atom so Composer can toggle it)
|
||||
const [isDocumentsSidebarOpen, setIsDocumentsSidebarOpen] = useAtom(documentsSidebarOpenAtom);
|
||||
const setIsRightPanelCollapsed = useSetAtom(rightPanelCollapsedAtom);
|
||||
|
||||
// Open documents sidebar by default on desktop (docked mode)
|
||||
const documentsInitialized = useRef(false);
|
||||
|
|
@ -335,9 +333,9 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
|
|||
}, [threadsData, searchSpaceId]);
|
||||
|
||||
// Navigation items
|
||||
// Inbox, Automations, and Documents are rendered explicitly below "New chat"
|
||||
// Inbox, Automations, and Artifacts are rendered explicitly below "New chat"
|
||||
// in the sidebar (also surfaced in the icon rail's collapsed mode via this
|
||||
// list). Announcements has been moved to the avatar dropdown.
|
||||
// list). Documents is embedded below Recents; announcements live in the avatar dropdown.
|
||||
const isAutomationsActive = pathname?.includes("/automations") === true;
|
||||
const isArtifactsActive = pathname?.endsWith("/artifacts") === true;
|
||||
const navItems: NavItem[] = useMemo(
|
||||
|
|
@ -523,27 +521,17 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
|
|||
return;
|
||||
}
|
||||
if (item.url === "#documents") {
|
||||
if (!isMobile) {
|
||||
if (!isDocumentsSidebarOpen) {
|
||||
setIsDocumentsSidebarOpen(true);
|
||||
setIsRightPanelCollapsed(false);
|
||||
setIsDocumentsSidebarOpen((prev) => {
|
||||
if (!prev) {
|
||||
setActiveSlideoutPanel(null);
|
||||
} else {
|
||||
setIsRightPanelCollapsed((prev) => !prev);
|
||||
}
|
||||
} else {
|
||||
setIsDocumentsSidebarOpen((prev) => {
|
||||
if (!prev) {
|
||||
setActiveSlideoutPanel(null);
|
||||
}
|
||||
return !prev;
|
||||
});
|
||||
}
|
||||
return !prev;
|
||||
});
|
||||
return;
|
||||
}
|
||||
router.push(item.url);
|
||||
},
|
||||
[router, isMobile, isDocumentsSidebarOpen, setIsDocumentsSidebarOpen, setIsRightPanelCollapsed]
|
||||
[router, setIsDocumentsSidebarOpen]
|
||||
);
|
||||
|
||||
const handleNewChat = useCallback(() => {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@
|
|||
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||
import { PanelRight } from "lucide-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";
|
||||
import { closeEditorPanelAtom, editorPanelAtom } from "@/atoms/editor/editor-panel.atom";
|
||||
import {
|
||||
type RightPanelTab,
|
||||
|
|
@ -18,7 +17,6 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip
|
|||
import { artifactsPanelOpenAtom, closeArtifactsPanelAtom } from "@/features/chat-artifacts";
|
||||
import { closeHitlEditPanelAtom, hitlEditPanelAtom } from "@/features/chat-messages/hitl";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { DocumentsSidebar } from "../sidebar";
|
||||
|
||||
const EditorPanelContent = dynamic(
|
||||
() =>
|
||||
|
|
@ -61,41 +59,9 @@ const ArtifactsPanelContent = dynamic(
|
|||
);
|
||||
|
||||
interface RightPanelProps {
|
||||
documentsPanel?: {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
};
|
||||
showCollapseButton?: 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;
|
||||
|
|
@ -108,7 +74,6 @@ export function RightPanelToggleButton({
|
|||
disabled = false,
|
||||
}: RightPanelToggleButtonProps) {
|
||||
const [collapsed, setCollapsed] = useAtom(rightPanelCollapsedAtom);
|
||||
const documentsOpen = useAtomValue(documentsSidebarOpenAtom);
|
||||
const reportState = useAtomValue(reportPanelAtom);
|
||||
const editorState = useAtomValue(editorPanelAtom);
|
||||
const hitlEditState = useAtomValue(hitlEditPanelAtom);
|
||||
|
|
@ -124,8 +89,7 @@ export function RightPanelToggleButton({
|
|||
: !!editorState.localFilePath);
|
||||
const hitlEditOpen = hitlEditState.isOpen && !!hitlEditState.onSave;
|
||||
const citationOpen = citationState.isOpen && citationState.chunkId != null;
|
||||
const hasContent =
|
||||
documentsOpen || reportOpen || editorOpen || hitlEditOpen || citationOpen || artifactsOpen;
|
||||
const hasContent = reportOpen || editorOpen || hitlEditOpen || citationOpen || artifactsOpen;
|
||||
const label = collapsed ? "Expand panel" : "Collapse panel";
|
||||
|
||||
if (!hasContent) return null;
|
||||
|
|
@ -162,7 +126,6 @@ export function RightPanelToggleButton({
|
|||
*/
|
||||
export function RightPanelExpandButton() {
|
||||
const [collapsed] = useAtom(rightPanelCollapsedAtom);
|
||||
const documentsOpen = useAtomValue(documentsSidebarOpenAtom);
|
||||
const reportState = useAtomValue(reportPanelAtom);
|
||||
const editorState = useAtomValue(editorPanelAtom);
|
||||
const hitlEditState = useAtomValue(hitlEditPanelAtom);
|
||||
|
|
@ -178,8 +141,7 @@ export function RightPanelExpandButton() {
|
|||
: !!editorState.localFilePath);
|
||||
const hitlEditOpen = hitlEditState.isOpen && !!hitlEditState.onSave;
|
||||
const citationOpen = citationState.isOpen && citationState.chunkId != null;
|
||||
const hasContent =
|
||||
documentsOpen || reportOpen || editorOpen || hitlEditOpen || citationOpen || artifactsOpen;
|
||||
const hasContent = reportOpen || editorOpen || hitlEditOpen || citationOpen || artifactsOpen;
|
||||
|
||||
if (!collapsed || !hasContent) return null;
|
||||
|
||||
|
|
@ -201,8 +163,7 @@ const PANEL_WIDTHS = {
|
|||
|
||||
/**
|
||||
* Priority order used to fall back to another open surface when the active
|
||||
* tab's content closes. Artifacts sit just above the always-available sources
|
||||
* tab.
|
||||
* tab's content closes. The neutral "sources" tab is kept as the closed state.
|
||||
*/
|
||||
const TAB_FALLBACK_ORDER: RightPanelTab[] = [
|
||||
"hitl-edit",
|
||||
|
|
@ -221,11 +182,7 @@ function resolveEffectiveTab(
|
|||
return TAB_FALLBACK_ORDER.find((tab) => openByTab[tab]) ?? "sources";
|
||||
}
|
||||
|
||||
export function RightPanel({
|
||||
documentsPanel,
|
||||
showCollapseButton = true,
|
||||
showTopBorder = false,
|
||||
}: RightPanelProps) {
|
||||
export function RightPanel({ showTopBorder = false }: RightPanelProps) {
|
||||
const [activeTab] = useAtom(rightPanelTabAtom);
|
||||
const reportState = useAtomValue(reportPanelAtom);
|
||||
const closeReport = useSetAtom(closeReportPanelAtom);
|
||||
|
|
@ -237,9 +194,8 @@ export function RightPanel({
|
|||
const closeCitation = useSetAtom(closeCitationPanelAtom);
|
||||
const artifactsOpen = useAtomValue(artifactsPanelOpenAtom);
|
||||
const closeArtifacts = useSetAtom(closeArtifactsPanelAtom);
|
||||
const [collapsed, setCollapsed] = useAtom(rightPanelCollapsedAtom);
|
||||
const [collapsed] = useAtom(rightPanelCollapsedAtom);
|
||||
|
||||
const documentsOpen = documentsPanel?.open ?? false;
|
||||
const reportOpen = reportState.isOpen && !!reportState.reportId;
|
||||
const editorOpen =
|
||||
editorState.isOpen &&
|
||||
|
|
@ -278,11 +234,10 @@ export function RightPanel({
|
|||
]);
|
||||
|
||||
const isVisible =
|
||||
(documentsOpen || reportOpen || editorOpen || hitlEditOpen || citationOpen || artifactsOpen) &&
|
||||
!collapsed;
|
||||
(reportOpen || editorOpen || hitlEditOpen || citationOpen || artifactsOpen) && !collapsed;
|
||||
|
||||
const effectiveTab = resolveEffectiveTab(activeTab, {
|
||||
sources: documentsOpen,
|
||||
sources: false,
|
||||
report: reportOpen,
|
||||
editor: editorOpen,
|
||||
"hitl-edit": hitlEditOpen,
|
||||
|
|
@ -291,10 +246,6 @@ export function RightPanel({
|
|||
});
|
||||
|
||||
const targetWidth = PANEL_WIDTHS[effectiveTab];
|
||||
const collapseButton = showCollapseButton ? (
|
||||
<CollapseButton onClick={() => setCollapsed(true)} />
|
||||
) : null;
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
|
|
@ -306,16 +257,6 @@ export function RightPanel({
|
|||
)}
|
||||
>
|
||||
<div className="relative flex-1 min-h-0 overflow-hidden">
|
||||
{effectiveTab === "sources" && documentsOpen && documentsPanel && (
|
||||
<div className="h-full">
|
||||
<DocumentsSidebar
|
||||
open={documentsPanel.open}
|
||||
onOpenChange={documentsPanel.onOpenChange}
|
||||
embedded
|
||||
headerAction={collapseButton}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{effectiveTab === "report" && reportOpen && (
|
||||
<div className="h-full flex flex-col">
|
||||
<ReportPanelContent
|
||||
|
|
|
|||
|
|
@ -440,6 +440,7 @@ export function LayoutShell({
|
|||
onChatArchive={onChatArchive}
|
||||
onViewAllChats={onViewAllChats}
|
||||
isAllChatsActive={isAllChatsPage}
|
||||
documentsPanel={documentsPanel}
|
||||
user={user}
|
||||
onSettings={onSettings}
|
||||
onManageMembers={onManageMembers}
|
||||
|
|
@ -532,17 +533,8 @@ export function LayoutShell({
|
|||
{children}
|
||||
</MainContentPanel>
|
||||
|
||||
{/* Right panel — tabbed Sources/Report (desktop only) */}
|
||||
{documentsPanel ? (
|
||||
<RightPanel
|
||||
documentsPanel={{
|
||||
open: documentsPanel.open,
|
||||
onOpenChange: documentsPanel.onOpenChange,
|
||||
}}
|
||||
showCollapseButton={!isMacDesktop}
|
||||
showTopBorder={isMacDesktop}
|
||||
/>
|
||||
) : null}
|
||||
{/* Right panel — Report/Editor/Citations/Artifacts (desktop only) */}
|
||||
<RightPanel showTopBorder={isMacDesktop} />
|
||||
</>
|
||||
)}
|
||||
</DesktopWorkspaceRegion>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import {
|
|||
Paperclip,
|
||||
Server,
|
||||
Trash2,
|
||||
Unplug,
|
||||
Upload,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
|
|
@ -25,8 +24,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|||
import { toast } from "sonner";
|
||||
import { agentFlagsAtom } from "@/atoms/agent/agent-flags-query.atom";
|
||||
import { makeFolderMention, mentionedDocumentsAtom } from "@/atoms/chat/mentioned-documents.atom";
|
||||
import { connectorDialogOpenAtom } from "@/atoms/connector-dialog/connector-dialog.atoms";
|
||||
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
|
||||
import { deleteDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms";
|
||||
import { expandedFolderIdsAtom } from "@/atoms/documents/folder.atoms";
|
||||
import { agentCreatedDocumentsAtom } from "@/atoms/documents/ui.atoms";
|
||||
|
|
@ -58,7 +55,6 @@ import {
|
|||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { Avatar, AvatarFallback, AvatarGroup } from "@/components/ui/avatar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Drawer, DrawerContent, DrawerHandle, DrawerTitle } from "@/components/ui/drawer";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
|
|
@ -67,7 +63,6 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { useAnonymousMode, useIsAnonymous } from "@/contexts/anonymous-mode";
|
||||
import { useLoginGate } from "@/contexts/login-gate";
|
||||
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
|
||||
import type { DocumentTypeEnum } from "@/contracts/types/document.types";
|
||||
import { useDebouncedValue } from "@/hooks/use-debounced-value";
|
||||
import { useMediaQuery } from "@/hooks/use-media-query";
|
||||
|
|
@ -167,18 +162,6 @@ interface WatchedFolderEntry {
|
|||
active: boolean;
|
||||
}
|
||||
|
||||
const SHOWCASE_CONNECTORS = [
|
||||
{ type: "GOOGLE_DRIVE_CONNECTOR", label: "Google Drive" },
|
||||
{ type: "GOOGLE_GMAIL_CONNECTOR", label: "Gmail" },
|
||||
{ type: "NOTION_CONNECTOR", label: "Notion" },
|
||||
{ type: "YOUTUBE_CONNECTOR", label: "YouTube" },
|
||||
{ type: "GOOGLE_CALENDAR_CONNECTOR", label: "Google Calendar" },
|
||||
{ type: "SLACK_CONNECTOR", label: "Slack" },
|
||||
{ type: "LINEAR_CONNECTOR", label: "Linear" },
|
||||
{ type: "JIRA_CONNECTOR", label: "Jira" },
|
||||
{ type: "GITHUB_CONNECTOR", label: "GitHub" },
|
||||
] as const;
|
||||
|
||||
interface DocumentsSidebarProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
|
|
@ -228,11 +211,8 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
const electronAPI = desktopFeaturesEnabled ? platformElectronAPI : null;
|
||||
const { etlService } = useRuntimeConfig();
|
||||
const searchSpaceId = getWorkspaceIdNumber(params) ?? 0;
|
||||
const setConnectorDialogOpen = useSetAtom(connectorDialogOpenAtom);
|
||||
const openEditorPanel = useSetAtom(openEditorPanelAtom);
|
||||
const { data: agentFlags } = useAtomValue(agentFlagsAtom);
|
||||
const { data: connectors } = useAtomValue(connectorsAtom);
|
||||
const connectorCount = connectors?.length ?? 0;
|
||||
|
||||
const [search, setSearch] = useState("");
|
||||
const debouncedSearch = useDebouncedValue(search, 250);
|
||||
|
|
@ -1116,76 +1096,9 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
const showCloudSkeleton =
|
||||
currentFilesystemTab === "cloud" &&
|
||||
(zeroFoldersResult.type !== "complete" || zeroAllDocsResult.type !== "complete");
|
||||
const connectorButtonLabel = connectorCount > 0 ? "Manage connectors" : "Connect your connectors";
|
||||
|
||||
const cloudContent = (
|
||||
<>
|
||||
{/* Connected tools strip */}
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setConnectorDialogOpen(true)}
|
||||
className="shrink-0 mx-4 mt-6 mb-2.5 h-auto select-none justify-start gap-2 bg-muted px-3 py-1.5 text-xs text-muted-foreground"
|
||||
>
|
||||
<Unplug className="size-4 shrink-0" />
|
||||
<span className="truncate">{connectorButtonLabel}</span>
|
||||
{connectorCount > 0 && (
|
||||
<span className="shrink-0 rounded-full bg-muted-foreground/15 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
|
||||
{connectorCount}
|
||||
</span>
|
||||
)}
|
||||
<AvatarGroup className="ml-auto shrink-0">
|
||||
{connectorCount > 0 && connectors
|
||||
? connectors.slice(0, isMobile ? 5 : 9).map((connector, i) => {
|
||||
const avatar = (
|
||||
<Avatar
|
||||
key={connector.id}
|
||||
className="size-6"
|
||||
style={{ zIndex: Math.max(9 - i, 1) }}
|
||||
>
|
||||
<AvatarFallback className="bg-muted text-[10px] text-muted-foreground">
|
||||
{getConnectorIcon(connector.connector_type, "size-3.5")}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
);
|
||||
if (isMobile) return avatar;
|
||||
return (
|
||||
<Tooltip key={connector.id}>
|
||||
<TooltipTrigger asChild>{avatar}</TooltipTrigger>
|
||||
<TooltipContent side="top" className="text-xs">
|
||||
{connector.name}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
})
|
||||
: (isMobile ? SHOWCASE_CONNECTORS.slice(0, 5) : SHOWCASE_CONNECTORS).map(
|
||||
({ type, label }, i) => {
|
||||
const avatar = (
|
||||
<Avatar
|
||||
key={type}
|
||||
className="size-6"
|
||||
style={{ zIndex: SHOWCASE_CONNECTORS.length - i }}
|
||||
>
|
||||
<AvatarFallback className="bg-muted text-[10px] text-muted-foreground">
|
||||
{getConnectorIcon(type, "size-3.5")}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
);
|
||||
if (isMobile) return avatar;
|
||||
return (
|
||||
<Tooltip key={type}>
|
||||
<TooltipTrigger asChild>{avatar}</TooltipTrigger>
|
||||
<TooltipContent side="top" className="text-xs">
|
||||
{label}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</AvatarGroup>
|
||||
</Button>
|
||||
|
||||
{isElectron && (
|
||||
<Button
|
||||
type="button"
|
||||
|
|
@ -1541,7 +1454,6 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
</>
|
||||
);
|
||||
|
||||
|
|
@ -1825,44 +1737,6 @@ function AnonymousDocumentsSidebar({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Connectors strip (gated) */}
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => gate("connect your data sources")}
|
||||
className="shrink-0 mx-4 mt-6 mb-2.5 h-auto select-none justify-start gap-2 border bg-muted/50 px-3 py-1.5 text-xs text-muted-foreground"
|
||||
>
|
||||
<Unplug className="size-4 shrink-0" />
|
||||
<span className="truncate">Connect your connectors</span>
|
||||
<AvatarGroup className="ml-auto shrink-0">
|
||||
{(isMobile ? SHOWCASE_CONNECTORS.slice(0, 5) : SHOWCASE_CONNECTORS).map(
|
||||
({ type, label }, i) => {
|
||||
const avatar = (
|
||||
<Avatar
|
||||
key={type}
|
||||
className="size-6"
|
||||
style={{ zIndex: SHOWCASE_CONNECTORS.length - i }}
|
||||
>
|
||||
<AvatarFallback className="bg-muted text-[10px] text-muted-foreground">
|
||||
{getConnectorIcon(type, "size-3.5")}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
);
|
||||
if (isMobile) return avatar;
|
||||
return (
|
||||
<Tooltip key={type}>
|
||||
<TooltipTrigger asChild>{avatar}</TooltipTrigger>
|
||||
<TooltipContent side="top" className="text-xs">
|
||||
{label}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</AvatarGroup>
|
||||
</Button>
|
||||
|
||||
{/* Filters & upload */}
|
||||
<div className="flex-1 min-h-0 pt-0 flex flex-col">
|
||||
<div className="px-4 pb-1.5">
|
||||
|
|
@ -1873,7 +1747,6 @@ function AnonymousDocumentsSidebar({
|
|||
onToggleType={() => {}}
|
||||
activeTypes={[]}
|
||||
onCreateFolder={() => gate("create folders")}
|
||||
onUploadClick={handleAnonUploadClick}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { SIDEBAR_MIN_WIDTH } from "../../hooks/useSidebarResize";
|
|||
import type { ChatItem, NavItem, PageUsage, SearchSpace, User } from "../../types/layout.types";
|
||||
import { ChatListItem } from "./ChatListItem";
|
||||
import { CreditBalanceDisplay } from "./CreditBalanceDisplay";
|
||||
import { DocumentsSidebar } from "./DocumentsSidebar";
|
||||
import { NavSection } from "./NavSection";
|
||||
import { SidebarButton } from "./SidebarButton";
|
||||
import { SidebarCollapseButton } from "./SidebarCollapseButton";
|
||||
|
|
@ -76,6 +77,10 @@ interface SidebarProps {
|
|||
onChatArchive?: (chat: ChatItem) => void;
|
||||
onViewAllChats?: () => void;
|
||||
isAllChatsActive?: boolean;
|
||||
documentsPanel?: {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
};
|
||||
user: User;
|
||||
onSettings?: () => void;
|
||||
onManageMembers?: () => void;
|
||||
|
|
@ -113,6 +118,7 @@ export function Sidebar({
|
|||
onChatArchive,
|
||||
onViewAllChats,
|
||||
isAllChatsActive = false,
|
||||
documentsPanel,
|
||||
user,
|
||||
onSettings,
|
||||
onManageMembers,
|
||||
|
|
@ -136,11 +142,9 @@ export function Sidebar({
|
|||
const t = useTranslations("sidebar");
|
||||
const [openDropdownChatId, setOpenDropdownChatId] = useState<number | null>(null);
|
||||
|
||||
// Inbox, Automations, and Documents are rendered explicitly right below
|
||||
// Inbox, Automations, and Artifacts are rendered explicitly right below
|
||||
// New Chat. Pull them out of the nav items list so they don't also appear
|
||||
// in the bottom NavSection. Documents is only present in navItems on
|
||||
// mobile; Automations is identified by URL suffix so the same code path
|
||||
// works across search spaces.
|
||||
// in the bottom NavSection. Documents is embedded below Recents.
|
||||
const inboxItem = useMemo(() => navItems.find((item) => item.url === "#inbox"), [navItems]);
|
||||
const automationsItem = useMemo(
|
||||
() => navItems.find((item) => item.url.endsWith("/automations")),
|
||||
|
|
@ -280,7 +284,10 @@ export function Sidebar({
|
|||
<SidebarSection
|
||||
title={t("recents")}
|
||||
defaultOpen={true}
|
||||
fillHeight={true}
|
||||
fillHeight={!documentsPanel?.open}
|
||||
className={
|
||||
documentsPanel?.open ? "flex shrink-0 flex-col min-h-0 max-h-[35%]" : undefined
|
||||
}
|
||||
alwaysShowAction={!disableTooltips && isAllChatsActive}
|
||||
action={
|
||||
onViewAllChats ? (
|
||||
|
|
@ -328,6 +335,15 @@ export function Sidebar({
|
|||
<p className="px-2 py-1 text-sm text-muted-foreground/60">{t("no_chats")}</p>
|
||||
)}
|
||||
</SidebarSection>
|
||||
{documentsPanel?.open ? (
|
||||
<div className="min-h-0 flex-1 border-t">
|
||||
<DocumentsSidebar
|
||||
open={documentsPanel.open}
|
||||
onOpenChange={documentsPanel.onOpenChange}
|
||||
embedded
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue