feat(sidebar): enhance document management UI and improve layout structure

- Updated the FolderTreeView component to clarify upload instructions.
- Introduced a new renderDocumentTree function in DocumentsSidebar for better organization and readability.
- Refactored the Sidebar component to utilize SidebarSection for improved layout consistency.
- Added contentClassName prop to SidebarSection for flexible styling options.
This commit is contained in:
Anish Sarkar 2026-07-06 11:11:53 +05:30
parent f11cc3868c
commit 62c0573b1c
4 changed files with 126 additions and 72 deletions

View file

@ -336,7 +336,7 @@ export function FolderTreeView({
<div className="flex flex-1 flex-col items-center justify-center gap-1 px-4 py-12 text-muted-foreground select-none">
<p className="text-sm font-medium">No documents found</p>
<p className="text-xs text-muted-foreground/70">
Use the upload button or connect a source above
Use the plus menu to upload files or manage connectors
</p>
</div>
);

View file

@ -1097,6 +1097,81 @@ function AuthenticatedDocumentsSidebarBase({
currentFilesystemTab === "cloud" &&
(zeroFoldersResult.type !== "complete" || zeroAllDocsResult.type !== "complete");
const renderDocumentTree = ({
documents = searchFilteredDocuments,
activeTypesForTree = activeTypes,
searchQuery = debouncedSearch.trim() || undefined,
}: {
documents?: DocumentNodeDoc[];
activeTypesForTree?: DocumentTypeEnum[];
searchQuery?: string;
} = {}) => (
<div className="relative flex-1 min-h-0 overflow-auto">
{deletableSelectedIds.length > 0 && (
<div className="absolute inset-x-0 top-0 z-10 flex items-center justify-center px-4 py-1.5 animate-in fade-in duration-150 pointer-events-none">
<Button
type="button"
variant="destructive"
size="sm"
onClick={() => setBulkDeleteConfirmOpen(true)}
className="pointer-events-auto h-auto gap-1.5 px-3 py-1 text-xs shadow-lg"
>
<Trash2 size={12} />
Delete {deletableSelectedIds.length}{" "}
{deletableSelectedIds.length === 1 ? "item" : "items"}
</Button>
</div>
)}
{showCloudSkeleton ? (
<CloudDocumentsSkeleton />
) : (
<FolderTreeView
folders={treeFolders}
documents={documents}
expandedIds={expandedIds}
onToggleExpand={toggleFolderExpand}
mentionedDocKeys={mentionedDocKeys}
onToggleChatMention={handleToggleChatMention}
onToggleFolderSelect={handleToggleFolderSelect}
onRenameFolder={handleRenameFolder}
onDeleteFolder={handleDeleteFolder}
onMoveFolder={handleMoveFolder}
onCreateFolder={handleCreateFolder}
searchQuery={searchQuery}
onPreviewDocument={(doc) => {
if (openMemoryDocument(doc)) return;
openEditorPanel({
documentId: doc.id,
searchSpaceId,
title: doc.title,
});
}}
onEditDocument={(doc) => {
if (openMemoryDocument(doc)) return;
openEditorPanel({
documentId: doc.id,
searchSpaceId,
title: doc.title,
});
}}
onDeleteDocument={(doc) => handleDeleteDocument(doc.id)}
onMoveDocument={handleMoveDocument}
onResetDocument={handleResetMemoryDocument}
onExportDocument={handleExportDocument}
onVersionHistory={(doc) => setVersionDocId(doc.id)}
activeTypes={activeTypesForTree}
onDropIntoFolder={handleDropIntoFolder}
onReorderFolder={handleReorderFolder}
watchedFolderIds={watchedFolderIds}
onRescanFolder={handleRescanFolder}
onStopWatchingFolder={handleStopWatching}
onExportFolder={handleExportFolder}
/>
)}
</div>
);
const cloudContent = (
<>
{isElectron && (
@ -1124,70 +1199,7 @@ function AuthenticatedDocumentsSidebarBase({
/>
</div>
<div className="relative flex-1 min-h-0 overflow-auto">
{deletableSelectedIds.length > 0 && (
<div className="absolute inset-x-0 top-0 z-10 flex items-center justify-center px-4 py-1.5 animate-in fade-in duration-150 pointer-events-none">
<Button
type="button"
variant="destructive"
size="sm"
onClick={() => setBulkDeleteConfirmOpen(true)}
className="pointer-events-auto h-auto gap-1.5 px-3 py-1 text-xs shadow-lg"
>
<Trash2 size={12} />
Delete {deletableSelectedIds.length}{" "}
{deletableSelectedIds.length === 1 ? "item" : "items"}
</Button>
</div>
)}
{showCloudSkeleton ? (
<CloudDocumentsSkeleton />
) : (
<FolderTreeView
folders={treeFolders}
documents={searchFilteredDocuments}
expandedIds={expandedIds}
onToggleExpand={toggleFolderExpand}
mentionedDocKeys={mentionedDocKeys}
onToggleChatMention={handleToggleChatMention}
onToggleFolderSelect={handleToggleFolderSelect}
onRenameFolder={handleRenameFolder}
onDeleteFolder={handleDeleteFolder}
onMoveFolder={handleMoveFolder}
onCreateFolder={handleCreateFolder}
searchQuery={debouncedSearch.trim() || undefined}
onPreviewDocument={(doc) => {
if (openMemoryDocument(doc)) return;
openEditorPanel({
documentId: doc.id,
searchSpaceId,
title: doc.title,
});
}}
onEditDocument={(doc) => {
if (openMemoryDocument(doc)) return;
openEditorPanel({
documentId: doc.id,
searchSpaceId,
title: doc.title,
});
}}
onDeleteDocument={(doc) => handleDeleteDocument(doc.id)}
onMoveDocument={handleMoveDocument}
onResetDocument={handleResetMemoryDocument}
onExportDocument={handleExportDocument}
onVersionHistory={(doc) => setVersionDocId(doc.id)}
activeTypes={activeTypes}
onDropIntoFolder={handleDropIntoFolder}
onReorderFolder={handleReorderFolder}
watchedFolderIds={watchedFolderIds}
onRescanFolder={handleRescanFolder}
onStopWatchingFolder={handleStopWatching}
onExportFolder={handleExportFolder}
/>
)}
</div>
{renderDocumentTree()}
</div>
</>
);
@ -1459,8 +1471,12 @@ function AuthenticatedDocumentsSidebarBase({
if (embedded) {
return (
<div className="flex h-full flex-col bg-sidebar text-sidebar-foreground">
{documentsContent}
<div className="flex h-full min-h-0 flex-col text-sidebar-foreground">
{renderDocumentTree({
documents: treeDocumentsWithMemory,
activeTypesForTree: [],
searchQuery: undefined,
})}
</div>
);
}
@ -1830,8 +1846,37 @@ function AnonymousDocumentsSidebar({
if (embedded) {
return (
<div className="flex h-full flex-col bg-sidebar text-sidebar-foreground">
{documentsContent}
<div className="flex h-full min-h-0 flex-col text-sidebar-foreground">
<FolderTreeView
folders={[]}
documents={treeDocuments}
expandedIds={new Set()}
onToggleExpand={() => {}}
mentionedDocKeys={mentionedDocKeys}
onToggleChatMention={handleToggleChatMention}
onToggleFolderSelect={() => {}}
onRenameFolder={() => gate("rename folders")}
onDeleteFolder={() => gate("delete folders")}
onMoveFolder={() => gate("organize folders")}
onCreateFolder={() => gate("create folders")}
onPreviewDocument={() => gate("preview documents")}
onEditDocument={() => gate("edit documents")}
onDeleteDocument={async () => {
handleRemoveDoc();
setSidebarDocs((prev) => prev.filter((d) => d.kind !== "doc" || d.id !== -1));
return true;
}}
onMoveDocument={() => gate("organize documents")}
onExportDocument={() => gate("export documents")}
onVersionHistory={() => gate("view version history")}
activeTypes={[]}
onDropIntoFolder={async () => gate("organize documents")}
onReorderFolder={async () => gate("organize folders")}
watchedFolderIds={new Set()}
onRescanFolder={() => gate("watch local folders")}
onStopWatchingFolder={() => gate("watch local folders")}
onExportFolder={() => gate("export folders")}
/>
</div>
);
}

View file

@ -336,13 +336,18 @@ export function Sidebar({
)}
</SidebarSection>
{documentsPanel?.open ? (
<div className="min-h-0 flex-1 border-t">
<SidebarSection
title={documentsItem?.title ?? "Documents"}
defaultOpen={true}
fillHeight
contentClassName="px-0"
>
<DocumentsSidebar
open={documentsPanel.open}
onOpenChange={documentsPanel.onOpenChange}
embedded
/>
</div>
</SidebarSection>
) : null}
</div>
)}

View file

@ -13,6 +13,7 @@ interface SidebarSectionProps {
alwaysShowAction?: boolean;
persistentAction?: React.ReactNode;
className?: string;
contentClassName?: string;
fillHeight?: boolean;
}
@ -24,6 +25,7 @@ export function SidebarSection({
alwaysShowAction = false,
persistentAction,
className,
contentClassName,
fillHeight = false,
}: SidebarSectionProps) {
const [isOpen, setIsOpen] = useState(defaultOpen);
@ -69,7 +71,9 @@ export function SidebarSection({
</div>
<CollapsibleContent className={cn("overflow-hidden flex-1 flex flex-col min-h-0")}>
<div className={cn("px-2 flex-1 flex flex-col min-h-0 overflow-hidden")}>{children}</div>
<div className={cn("px-2 flex-1 flex flex-col min-h-0 overflow-hidden", contentClassName)}>
{children}
</div>
</CollapsibleContent>
</Collapsible>
);