refactor(documents): remove unused document editing functionality and streamline components

- Eliminated references to document editing in DocumentNode, FolderTreeView, and DocumentsSidebar for a cleaner interface.
- Updated DocumentNode to enhance accessibility with keyboard interactions and improved click handling.
- Adjusted layout in AllChatsSidebar and MobileSidebar for better user experience and consistency across components.
This commit is contained in:
Anish Sarkar 2026-07-06 16:02:22 +05:30
parent 556f02c5be
commit 4088289f95
7 changed files with 66 additions and 83 deletions

View file

@ -1,5 +1,4 @@
const STORE_KEY = 'activeWorkspaceId';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let store: any = null;
async function getStore() {
@ -12,7 +11,6 @@ async function getStore() {
// One-time migration from the legacy `active-search-space` store so the
// user's last-selected workspace survives the rename.
if (store.get(STORE_KEY) == null) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const legacy: any = new Store({
name: 'active-search-space',
defaults: { activeSearchSpaceId: null as string | null },

View file

@ -6,8 +6,6 @@ export const globalDocumentsQueryParamsAtom = atom<GetDocumentsRequest["queryPar
page: 0,
});
export const documentsSidebarOpenAtom = atom(false);
export interface AgentCreatedDocument {
id: number;
title: string;

View file

@ -4,11 +4,9 @@ import {
AlertCircle,
Clock,
Download,
Eye,
History,
MoreHorizontal,
Move,
Pencil,
RotateCcw,
Trash2,
} from "lucide-react";
@ -44,8 +42,6 @@ import { SidebarListItem } from "../layout/ui/sidebar/SidebarListItem";
import { DND_TYPES } from "./FolderNode";
import { isVersionableType } from "./version-history";
const EDITABLE_DOCUMENT_TYPES = new Set(["FILE", "NOTE"]);
export interface DocumentNodeDoc {
id: number;
title: string;
@ -60,7 +56,6 @@ interface DocumentNodeProps {
isMentioned: boolean;
onToggleChatMention: (doc: DocumentNodeDoc, isMentioned: boolean) => void;
onPreview: (doc: DocumentNodeDoc) => void;
onEdit: (doc: DocumentNodeDoc) => void;
onDelete: (doc: DocumentNodeDoc) => void;
onMove: (doc: DocumentNodeDoc) => void;
onReset?: (doc: DocumentNodeDoc) => void;
@ -69,7 +64,6 @@ interface DocumentNodeProps {
canDelete?: boolean;
canMove?: boolean;
canMention?: boolean;
canEdit?: boolean;
contextMenuOpen?: boolean;
onContextMenuOpenChange?: (open: boolean) => void;
}
@ -80,7 +74,6 @@ export const DocumentNode = React.memo(function DocumentNode({
isMentioned,
onToggleChatMention,
onPreview,
onEdit,
onDelete,
onMove,
onReset,
@ -89,7 +82,6 @@ export const DocumentNode = React.memo(function DocumentNode({
canDelete = true,
canMove = true,
canMention = true,
canEdit = true,
contextMenuOpen,
onContextMenuOpenChange,
}: DocumentNodeProps) {
@ -100,10 +92,6 @@ export const DocumentNode = React.memo(function DocumentNode({
const isMemoryDocument =
doc.document_type === "USER_MEMORY" || doc.document_type === "TEAM_MEMORY";
const isSelectable = canMention && !isUnavailable;
const isEditable =
canEdit &&
(isMemoryDocument || EDITABLE_DOCUMENT_TYPES.has(doc.document_type)) &&
!isUnavailable;
const handleCheckChange = useCallback(() => {
if (isSelectable) {
@ -112,12 +100,20 @@ export const DocumentNode = React.memo(function DocumentNode({
}, [doc, isMentioned, isSelectable, onToggleChatMention]);
const handlePrimaryClick = useCallback(() => {
if (canMention) {
handleCheckChange();
return;
}
if (isUnavailable) return;
onPreview(doc);
}, [canMention, doc, handleCheckChange, onPreview]);
}, [doc, isUnavailable, onPreview]);
const handlePrimaryKeyDown = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.currentTarget !== event.target) return;
if (event.key !== "Enter" && event.key !== " ") return;
event.preventDefault();
handlePrimaryClick();
},
[handlePrimaryClick]
);
const [{ isDragging }, drag] = useDrag(
() => ({
@ -172,6 +168,11 @@ export const DocumentNode = React.memo(function DocumentNode({
dragging={isDragging}
className="gap-2.5 px-1"
style={{ paddingLeft: `${depth * 16 + 4}px` }}
role="button"
tabIndex={isUnavailable ? -1 : 0}
aria-disabled={isUnavailable}
onClick={handlePrimaryClick}
onKeyDown={handlePrimaryKeyDown}
>
{(() => {
if (statusState === "pending") {
@ -248,17 +249,11 @@ export const DocumentNode = React.memo(function DocumentNode({
onOpenChange={handleTitleTooltipOpenChange}
>
<TooltipTrigger asChild>
<Button
type="button"
variant="ghost"
aria-disabled={canMention ? !isSelectable : false}
onClick={handlePrimaryClick}
className="h-full min-w-0 flex-1 justify-start bg-transparent px-0 py-0 text-left font-normal text-inherit hover:bg-transparent hover:text-inherit"
>
<span className="flex h-full min-w-0 flex-1 items-center text-left font-normal text-inherit">
<span ref={titleRef} className="min-w-0 flex-1 truncate">
{doc.title}
</span>
</Button>
</span>
</TooltipTrigger>
<TooltipContent side="bottom" className="max-w-xs break-words">
{doc.title}
@ -304,16 +299,6 @@ export const DocumentNode = React.memo(function DocumentNode({
className="w-40"
onClick={(e) => e.stopPropagation()}
>
<DropdownMenuItem onClick={() => onPreview(doc)} disabled={isUnavailable}>
<Eye className="mr-2 h-4 w-4" />
Open
</DropdownMenuItem>
{isEditable && (
<DropdownMenuItem onClick={() => onEdit(doc)}>
<Pencil className="mr-2 h-4 w-4" />
Edit
</DropdownMenuItem>
)}
{canMove && (
<DropdownMenuItem onClick={() => onMove(doc)}>
<Move className="mr-2 h-4 w-4" />
@ -362,16 +347,6 @@ export const DocumentNode = React.memo(function DocumentNode({
{contextMenuOpen && (
<ContextMenuContent className="w-40" onClick={(e) => e.stopPropagation()}>
<ContextMenuItem onClick={() => onPreview(doc)} disabled={isUnavailable}>
<Eye className="mr-2 h-4 w-4" />
Open
</ContextMenuItem>
{isEditable && (
<ContextMenuItem onClick={() => onEdit(doc)}>
<Pencil className="mr-2 h-4 w-4" />
Edit
</ContextMenuItem>
)}
{canMove && (
<ContextMenuItem onClick={() => onMove(doc)}>
<Move className="mr-2 h-4 w-4" />

View file

@ -29,7 +29,6 @@ interface FolderTreeViewProps {
onMoveFolder: (folder: FolderDisplay) => void;
onCreateFolder: (parentId: number | null) => void;
onPreviewDocument: (doc: DocumentNodeDoc) => void;
onEditDocument: (doc: DocumentNodeDoc) => void;
onDeleteDocument: (doc: DocumentNodeDoc) => void;
onMoveDocument: (doc: DocumentNodeDoc) => void;
onResetDocument?: (doc: DocumentNodeDoc) => void;
@ -72,7 +71,6 @@ export function FolderTreeView({
onMoveFolder,
onCreateFolder,
onPreviewDocument,
onEditDocument,
onDeleteDocument,
onMoveDocument,
onResetDocument,
@ -215,7 +213,6 @@ export function FolderTreeView({
isMentioned={!isMemoryDocument && mentionedDocKeys.has(getMentionDocKey(d))}
onToggleChatMention={onToggleChatMention}
onPreview={onPreviewDocument}
onEdit={onEditDocument}
onDelete={onDeleteDocument}
onMove={onMoveDocument}
onReset={onResetDocument}
@ -224,7 +221,6 @@ export function FolderTreeView({
canDelete={!isMemoryDocument}
canMove={!isMemoryDocument}
canMention={!isMemoryDocument}
canEdit
contextMenuOpen={openContextMenuId === `doc-${d.id}`}
onContextMenuOpenChange={(open) => setOpenContextMenuId(open ? `doc-${d.id}` : null)}
/>
@ -233,7 +229,6 @@ export function FolderTreeView({
[
mentionedDocKeys,
onDeleteDocument,
onEditDocument,
onExportDocument,
onMoveDocument,
onPreviewDocument,

View file

@ -11,7 +11,6 @@ import {
RotateCcwIcon,
Search,
Trash2,
Users,
X,
} from "lucide-react";
import { useParams, useRouter } from "next/navigation";
@ -19,6 +18,7 @@ import { useTranslations } from "next-intl";
import { useCallback, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import { removeChatTabAtom } from "@/atoms/tabs/tabs.atom";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Dialog,
@ -301,7 +301,7 @@ function AllChatsContent({ workspaceId, className }: AllChatsContentProps) {
</div>
</div>
<div className="flex-1 overflow-y-auto overflow-x-hidden p-1.5">
<div className="flex-1 overflow-y-auto overflow-x-hidden px-3 py-1.5">
{isLoading ? (
<div className="space-y-1">
{[75, 90, 55, 80, 65, 85].map((titleWidth) => (
@ -319,15 +319,21 @@ function AllChatsContent({ workspaceId, className }: AllChatsContentProps) {
{t("error_loading_chats") || "Error loading chats"}
</div>
) : threads.length > 0 ? (
<div className="space-y-1">
{threads.map((thread) => {
<div>
{threads.map((thread, index) => {
const isDeleting = deletingThreadId === thread.id;
const isArchiving = archivingThreadId === thread.id;
const isBusy = isDeleting || isArchiving;
const isActive = currentChatId === thread.id;
return (
<div key={thread.id} className="group/item relative w-full">
<div
key={thread.id}
className={cn(
"group/item relative w-full hover:border-t-transparent [&:hover+div]:border-t-transparent",
index > 0 && "border-t border-border/60"
)}
>
{isMobile ? (
<Button
type="button"
@ -349,7 +355,7 @@ function AllChatsContent({ workspaceId, className }: AllChatsContentProps) {
"h-auto w-full justify-start gap-2.5 overflow-hidden px-3 py-2.5 text-left text-base font-normal",
"group-hover/item:bg-accent group-hover/item:text-accent-foreground",
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
thread.visibility === "SEARCH_SPACE" && "pr-10",
thread.visibility === "SEARCH_SPACE" && "pr-16",
isActive && "bg-accent text-accent-foreground",
isBusy && "opacity-50 pointer-events-none"
)}
@ -370,7 +376,7 @@ function AllChatsContent({ workspaceId, className }: AllChatsContentProps) {
"h-auto w-full justify-start gap-2.5 overflow-hidden px-3 py-2.5 text-left text-base font-normal",
"group-hover/item:bg-accent group-hover/item:text-accent-foreground",
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
thread.visibility === "SEARCH_SPACE" && "pr-10",
thread.visibility === "SEARCH_SPACE" && "pr-16",
isActive && "bg-accent text-accent-foreground",
isBusy && "opacity-50 pointer-events-none"
)}
@ -401,18 +407,20 @@ function AllChatsContent({ workspaceId, className }: AllChatsContentProps) {
: "opacity-0 group-hover/item:opacity-100"
)}
>
<div className="relative flex h-7 w-7 items-center justify-center">
<div className="relative flex h-7 w-14 items-center justify-end">
{thread.visibility === "SEARCH_SPACE" ? (
<Users
aria-label={t("shared_chat") || "Shared chat"}
<Badge
variant="secondary"
className={cn(
"absolute left-1/2 top-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 text-muted-foreground/50",
"absolute right-0 h-5 shrink-0 rounded-sm border-0 bg-popover-foreground/10 px-1.5 text-[11px] text-popover-foreground transition-opacity hover:bg-popover-foreground/10",
!isMobile &&
(openDropdownId === thread.id
? "opacity-0"
: "opacity-100 group-hover/item:opacity-0")
)}
/>
>
Shared
</Badge>
) : null}
<DropdownMenu
open={openDropdownId === thread.id}

View file

@ -8,16 +8,10 @@ import { useTranslations } from "next-intl";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import { makeFolderMention, mentionedDocumentsAtom } from "@/atoms/chat/mentioned-documents.atom";
import { connectorDialogOpenAtom } from "@/atoms/connector-dialog/connector-dialog.atoms";
import { deleteDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms";
import { expandedFolderIdsAtom } from "@/atoms/documents/folder.atoms";
import { agentCreatedDocumentsAtom } from "@/atoms/documents/ui.atoms";
import { openEditorPanelAtom } from "@/atoms/editor/editor-panel.atom";
import {
folderWatchDialogOpenAtom,
folderWatchInitialFolderAtom,
} from "@/atoms/folder-sync/folder-sync.atoms";
import { workspacesAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { CreateFolderDialog } from "@/components/documents/CreateFolderDialog";
import type { DocumentNodeDoc } from "@/components/documents/DocumentNode";
import { getDocumentTypeIcon } from "@/components/documents/DocumentTypeIcon";
@ -57,7 +51,6 @@ import type { DocumentTypeEnum } from "@/contracts/types/document.types";
import { useElectronAPI, usePlatform } from "@/hooks/use-platform";
import { documentsApiService } from "@/lib/apis/documents-api.service";
import { foldersApiService } from "@/lib/apis/folders-api.service";
import { workspacesApiService } from "@/lib/apis/workspaces-api.service";
import { authenticatedFetch } from "@/lib/auth-fetch";
import { getMentionDocKey } from "@/lib/chat/mention-doc-key";
import { getDocumentTypeLabel } from "@/lib/documents/document-type-labels";
@ -242,7 +235,6 @@ function AuthenticatedDocumentsSidebarBase({
const electronAPI = desktopFeaturesEnabled ? platformElectronAPI : null;
const { etlService } = useRuntimeConfig();
const workspaceId = getWorkspaceIdNumber(params) ?? 0;
const setConnectorDialogOpen = useSetAtom(connectorDialogOpenAtom);
const openEditorPanel = useSetAtom(openEditorPanelAtom);
const [activeTypes, setActiveTypes] = useState<DocumentTypeEnum[]>([]);
@ -1024,14 +1016,6 @@ function AuthenticatedDocumentsSidebarBase({
title: doc.title,
});
}}
onEditDocument={(doc) => {
if (openMemoryDocument(doc)) return;
openEditorPanel({
documentId: doc.id,
workspaceId: workspaceId,
title: doc.title,
});
}}
onDeleteDocument={(doc) => handleDeleteDocument(doc.id)}
onMoveDocument={handleMoveDocument}
onResetDocument={handleResetMemoryDocument}
@ -1251,7 +1235,6 @@ function AnonymousDocumentsSidebar({ embedded = false }: DocumentsSidebarProps)
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));

View file

@ -7,6 +7,7 @@ import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet";
import type { ChatItem, NavItem, PageUsage, User, Workspace } from "../../types/layout.types";
import { WorkspaceAvatar } from "../icon-rail/WorkspaceAvatar";
import { Sidebar } from "./Sidebar";
import { SidebarUserProfile } from "./SidebarUserProfile";
interface MobileSidebarProps {
isOpen: boolean;
@ -137,6 +138,30 @@ export function MobileSidebar({
</Button>
</div>
</ScrollArea>
<SidebarUserProfile
user={user}
onUserSettings={
onUserSettings
? () => {
onOpenChange(false);
onUserSettings();
}
: undefined
}
onAnnouncements={
onAnnouncements
? () => {
onOpenChange(false);
onAnnouncements();
}
: undefined
}
announcementUnreadCount={announcementUnreadCount}
onLogout={onLogout}
isCollapsed
theme={theme}
setTheme={setTheme}
/>
</div>
{/* Sidebar Content - right side */}
@ -209,6 +234,7 @@ export function MobileSidebar({
className="w-full border-none"
isLoadingChats={isLoadingChats}
disableTooltips
renderUserProfile={false}
/>
</div>
</SheetContent>