diff --git a/client/src/hooks/Nav/useSideNavLinks.ts b/client/src/hooks/Nav/useSideNavLinks.ts index 0f96c3709b..e25a7b952a 100644 --- a/client/src/hooks/Nav/useSideNavLinks.ts +++ b/client/src/hooks/Nav/useSideNavLinks.ts @@ -143,13 +143,15 @@ export default function useSideNavLinks({ }); } - links.push({ - title: 'com_sidepanel_attach_files', - label: '', - icon: AttachmentIcon, - id: 'files', - Component: FilesPanel, - }); + if (interfaceConfig.attachFiles !== false) { + links.push({ + title: 'com_sidepanel_attach_files', + label: '', + icon: AttachmentIcon, + id: 'files', + Component: FilesPanel, + }); + } if (hasAccessToBookmarks) { links.push({ @@ -195,6 +197,7 @@ export default function useSideNavLinks({ hasAccessToMemories, hasAccessToReadMemories, interfaceConfig.parameters, + interfaceConfig.attachFiles, endpointType, hasAccessToBookmarks, availableMCPServers, diff --git a/librechat.example.yaml b/librechat.example.yaml index 92206c4b6e..5b92d8d51e 100644 --- a/librechat.example.yaml +++ b/librechat.example.yaml @@ -83,6 +83,10 @@ interface: modelSelect: true parameters: true presets: true + # Enable/disable the Attach Files sidebar panel (default: true) + # When set to false, hides the sidebar's file management panel + # but users can still upload files directly in chats + attachFiles: true prompts: use: true create: true diff --git a/packages/api/src/app/AppService.interface.spec.ts b/packages/api/src/app/AppService.interface.spec.ts index e15d57e329..96ae1b24f4 100644 --- a/packages/api/src/app/AppService.interface.spec.ts +++ b/packages/api/src/app/AppService.interface.spec.ts @@ -154,4 +154,55 @@ describe('AppService interface configuration', () => { // Verify that peoplePicker is undefined when not provided expect(result.interfaceConfig?.peoplePicker).toBeUndefined(); }); + + it('should set attachFiles to true when config specifies attachFiles as true', async () => { + const config = { + interface: { + attachFiles: true, + }, + }; + + const result = await AppService({ config }); + + expect(result).toEqual( + expect.objectContaining({ + interfaceConfig: expect.objectContaining({ + attachFiles: true, + }), + }), + ); + }); + + it('should set attachFiles to false when config specifies attachFiles as false', async () => { + const config = { + interface: { + attachFiles: false, + }, + }; + + const result = await AppService({ config }); + + expect(result).toEqual( + expect.objectContaining({ + interfaceConfig: expect.objectContaining({ + attachFiles: false, + }), + }), + ); + }); + + it('should not set attachFiles when not provided in config', async () => { + const config = {}; + + const result = await AppService({ config }); + + expect(result).toEqual( + expect.objectContaining({ + interfaceConfig: expect.anything(), + }), + ); + + // Verify that attachFiles is undefined when not provided + expect(result.interfaceConfig?.attachFiles).toBeUndefined(); + }); }); diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index ca40ec2c8c..482218f402 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -696,6 +696,7 @@ export const interfaceSchema = z bookmarks: z.boolean().optional(), memories: z.boolean().optional(), presets: z.boolean().optional(), + attachFiles: z.boolean().optional(), prompts: z .union([ z.boolean(), @@ -752,6 +753,7 @@ export const interfaceSchema = z multiConvo: true, bookmarks: true, memories: true, + attachFiles: true, prompts: { use: true, create: true, diff --git a/packages/data-schemas/src/app/interface.ts b/packages/data-schemas/src/app/interface.ts index 3cd71cfb20..1cfea1e88d 100644 --- a/packages/data-schemas/src/app/interface.ts +++ b/packages/data-schemas/src/app/interface.ts @@ -40,6 +40,7 @@ export async function loadDefaultInterface({ customWelcome: interfaceConfig?.customWelcome ?? defaults.customWelcome, // Permissions - only include if explicitly configured + attachFiles: interfaceConfig?.attachFiles, bookmarks: interfaceConfig?.bookmarks, memories: shouldDisableMemories ? false : interfaceConfig?.memories, prompts: interfaceConfig?.prompts,