From ffcca3254edda7e2c8cf81f5e8ee67a7fdf8d75c Mon Sep 17 00:00:00 2001 From: Daniel Lew Date: Tue, 25 Nov 2025 12:56:32 -0600 Subject: [PATCH 1/6] =?UTF-8?q?=F0=9F=93=A2=20fix:=20Remove=20Side=20Panel?= =?UTF-8?q?=20Elements=20from=20Screen=20Reader=20when=20Hidden=20(#10648)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: remove side panel elements from screen reader when hidden There's both left & right side panels; elements of both of them are hidden when dismissed. However, currently they are being hidden by using classes to hide their UI (such as making the sidebar zero width). That works for visually dismissing these elements, but they can still be viewed by a screen reader (using the tab key to jump between interactable elements). That can be a rather confusing experience for anyone visually impaired (such as duplicate buttons, or buttons that do nothing). -------- I've changed it so hidden elements are fully removed from the render. This prevents them from being interactable via keyboard. I leveraged Motion to duplicate the animations as they happened before. I subtly cleaned up the animations while I was at it. * Implemented reasonable suggestions from Copilot review --- client/src/components/Chat/Header.tsx | 37 +++++++++--------- client/src/components/Nav/MobileNav.tsx | 4 +- client/src/components/Nav/Nav.tsx | 38 +++++++++---------- .../components/SidePanel/SidePanelGroup.tsx | 12 +++--- client/src/routes/Root.tsx | 2 +- 5 files changed, 49 insertions(+), 44 deletions(-) diff --git a/client/src/components/Chat/Header.tsx b/client/src/components/Chat/Header.tsx index bf04a2e4f3..5025307020 100644 --- a/client/src/components/Chat/Header.tsx +++ b/client/src/components/Chat/Header.tsx @@ -11,6 +11,7 @@ import BookmarkMenu from './Menus/BookmarkMenu'; import { TemporaryChat } from './TemporaryChat'; import AddMultiConvo from './AddMultiConvo'; import { useHasAccess } from '~/hooks'; +import { AnimatePresence, motion } from 'framer-motion'; const defaultInterface = getConfigDefaults().interface; @@ -38,24 +39,24 @@ export default function Header() { return (
-
-
- - -
-
+
+ + {!navVisible && ( + + + + + )} + + +
{interfaceConfig.presets === true && interfaceConfig.modelSelect && } {hasAccessToBookmarks === true && } diff --git a/client/src/components/Nav/MobileNav.tsx b/client/src/components/Nav/MobileNav.tsx index 6f11b327ce..7a508a28eb 100644 --- a/client/src/components/Nav/MobileNav.tsx +++ b/client/src/components/Nav/MobileNav.tsx @@ -9,8 +9,10 @@ import { clearMessagesCache } from '~/utils'; import store from '~/store'; export default function MobileNav({ + navVisible, setNavVisible, }: { + navVisible: boolean; setNavVisible: Dispatch>; }) { const localize = useLocalize(); @@ -25,7 +27,7 @@ export default function MobileNav({ type="button" data-testid="mobile-header-new-chat-button" aria-label={localize('com_nav_open_sidebar')} - className="m-1 inline-flex size-10 items-center justify-center rounded-full hover:bg-surface-hover" + className={`m-1 inline-flex size-10 items-center justify-center rounded-full hover:bg-surface-hover ${navVisible ? 'invisible' : ''}`} onClick={() => setNavVisible((prev) => { localStorage.setItem('navVisible', JSON.stringify(!prev)); diff --git a/client/src/components/Nav/Nav.tsx b/client/src/components/Nav/Nav.tsx index ecea9c3d14..e449904143 100644 --- a/client/src/components/Nav/Nav.tsx +++ b/client/src/components/Nav/Nav.tsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useState, useMemo, memo, lazy, Suspense, useRef } from 'react'; import { useRecoilValue } from 'recoil'; +import { AnimatePresence, motion } from 'framer-motion'; import { useMediaQuery } from '@librechat/client'; import { PermissionTypes, Permissions } from 'librechat-data-provider'; import type { ConversationListResponse } from 'librechat-data-provider'; @@ -190,22 +191,21 @@ const Nav = memo( return ( <> -
-
-
-
+ + {navVisible && ( + +
-
-
-
+ + )} + {isSmallScreen && } ); diff --git a/client/src/components/SidePanel/SidePanelGroup.tsx b/client/src/components/SidePanel/SidePanelGroup.tsx index 5a81f088df..14473127b5 100644 --- a/client/src/components/SidePanel/SidePanelGroup.tsx +++ b/client/src/components/SidePanel/SidePanelGroup.tsx @@ -147,11 +147,13 @@ const SidePanelGroup = memo( {artifacts != null && isSmallScreen && (
{artifacts}
)} - ); From 30df16f5b52645631c4d2b8242d2dabea172d105 Mon Sep 17 00:00:00 2001 From: Dustin Healy <54083382+dustinhealy@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:02:01 -0800 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=8D=9E=20feat:=20Add=20Toasts=20for?= =?UTF-8?q?=20Successful=20Conversation=20Deletion=20(#10661)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add toasts for succesful conversation deletion * chore: address copilot comments --- .../components/Conversations/ConvoOptions/DeleteButton.tsx | 5 +++++ .../Nav/SettingsTabs/General/ArchivedChatsTable.tsx | 5 +++++ client/src/locales/en/translation.json | 1 + 3 files changed, 11 insertions(+) diff --git a/client/src/components/Conversations/ConvoOptions/DeleteButton.tsx b/client/src/components/Conversations/ConvoOptions/DeleteButton.tsx index 185556ab72..d0de38175e 100644 --- a/client/src/components/Conversations/ConvoOptions/DeleteButton.tsx +++ b/client/src/components/Conversations/ConvoOptions/DeleteButton.tsx @@ -55,6 +55,11 @@ export function DeleteConversationDialog({ } setMenuOpen?.(false); retainView(); + showToast({ + message: localize('com_ui_convo_delete_success'), + severity: NotificationSeverity.SUCCESS, + showIcon: true, + }); }, onError: () => { showToast({ diff --git a/client/src/components/Nav/SettingsTabs/General/ArchivedChatsTable.tsx b/client/src/components/Nav/SettingsTabs/General/ArchivedChatsTable.tsx index ad79aed383..ebea2ea0bf 100644 --- a/client/src/components/Nav/SettingsTabs/General/ArchivedChatsTable.tsx +++ b/client/src/components/Nav/SettingsTabs/General/ArchivedChatsTable.tsx @@ -93,6 +93,11 @@ export default function ArchivedChatsTable({ onSuccess: async () => { setIsDeleteOpen(false); await refetch(); + showToast({ + message: localize('com_ui_convo_delete_success'), + severity: NotificationSeverity.SUCCESS, + showIcon: true, + }); }, onError: (error: unknown) => { showToast({ diff --git a/client/src/locales/en/translation.json b/client/src/locales/en/translation.json index f7d1152334..a82e931072 100644 --- a/client/src/locales/en/translation.json +++ b/client/src/locales/en/translation.json @@ -799,6 +799,7 @@ "com_ui_continue_oauth": "Continue with OAuth", "com_ui_controls": "Controls", "com_ui_convo_delete_error": "Failed to delete conversation", + "com_ui_convo_delete_success": "Conversation successfully deleted", "com_ui_copied": "Copied!", "com_ui_copied_to_clipboard": "Copied to clipboard", "com_ui_copy_code": "Copy code", From 8b7af65265b5773fd7ab5f87a265a28f380b4d8f Mon Sep 17 00:00:00 2001 From: Dustin Healy <54083382+dustinhealy@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:02:52 -0800 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=AA=84=20style:=20Improved=20Input=20?= =?UTF-8?q?Collapse=20UI=20(#10659)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: shift collapse chevron inside ChatForm input area * feat: add soft gradient on bottom of collapsed text input so there isn't a hard cut off when text overflows * feat: add better scroll bar behavior for main chat input * fix: smooth out purple gradient for temporary chats * feat: better colors for gradient * feat: use blur instead of colors * chore: address copilot comments --- client/src/components/Chat/Input/ChatForm.tsx | 73 +++++++++++-------- client/src/style.css | 20 +++++ 2 files changed, 63 insertions(+), 30 deletions(-) diff --git a/client/src/components/Chat/Input/ChatForm.tsx b/client/src/components/Chat/Input/ChatForm.tsx index 022cddd156..edd12bd3ac 100644 --- a/client/src/components/Chat/Input/ChatForm.tsx +++ b/client/src/components/Chat/Input/ChatForm.tsx @@ -260,37 +260,50 @@ const ChatForm = memo(({ index = 0 }: { index?: number }) => { {endpoint && (
- { - ref(e); - (textAreaRef as React.MutableRefObject).current = e; - }} - disabled={disableInputs || isNotAppendable} - onPaste={handlePaste} - onKeyDown={handleKeyDown} - onKeyUp={handleKeyUp} - onCompositionStart={handleCompositionStart} - onCompositionEnd={handleCompositionEnd} - id={mainTextareaId} - tabIndex={0} - data-testid="text-input" - rows={1} - onFocus={() => { - handleFocusOrClick(); - setIsTextAreaFocused(true); - }} - onBlur={setIsTextAreaFocused.bind(null, false)} - aria-label={localize('com_ui_message_input')} - onClick={handleFocusOrClick} - style={{ height: 44, overflowY: 'auto' }} - className={cn( - baseClasses, - removeFocusRings, - 'transition-[max-height] duration-200 disabled:cursor-not-allowed', +
+ { + ref(e); + (textAreaRef as React.MutableRefObject).current = + e; + }} + disabled={disableInputs || isNotAppendable} + onPaste={handlePaste} + onKeyDown={handleKeyDown} + onKeyUp={handleKeyUp} + onCompositionStart={handleCompositionStart} + onCompositionEnd={handleCompositionEnd} + id={mainTextareaId} + tabIndex={0} + data-testid="text-input" + rows={1} + onFocus={() => { + handleFocusOrClick(); + setIsTextAreaFocused(true); + }} + onBlur={setIsTextAreaFocused.bind(null, false)} + aria-label={localize('com_ui_message_input')} + onClick={handleFocusOrClick} + style={{ height: 44, overflowY: 'auto' }} + className={cn( + baseClasses, + removeFocusRings, + 'scrollbar-hover transition-[max-height] duration-200 disabled:cursor-not-allowed', + )} + /> + {isCollapsed && ( +
)} - /> -
+
+
Date: Tue, 25 Nov 2025 14:07:37 -0800 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=93=84=20refactor:=20Add=20Provider?= =?UTF-8?q?=20Fallback=20for=20Media=20Encoding=20using=20Client=20Endpoin?= =?UTF-8?q?t=20(#10656)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using direct endpoints (e.g., Google) instead of Agents, `this.options.agent` is undefined, causing provider detection to fail. This resulted in "Unknown content type document" errors for Google/Gemini PDF uploads. Added `?? this.options.endpoint` fallback in addDocuments(), addVideos(), and addAudios() methods to ensure correct provider detection for all endpoint types. --- api/app/clients/BaseClient.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/api/app/clients/BaseClient.js b/api/app/clients/BaseClient.js index 149d331df1..c0d9169b51 100644 --- a/api/app/clients/BaseClient.js +++ b/api/app/clients/BaseClient.js @@ -1213,8 +1213,8 @@ class BaseClient { this.options.req, attachments, { - provider: this.options.agent?.provider, - endpoint: this.options.agent?.endpoint, + provider: this.options.agent?.provider ?? this.options.endpoint, + endpoint: this.options.agent?.endpoint ?? this.options.endpoint, useResponsesApi: this.options.agent?.model_parameters?.useResponsesApi, }, getStrategyFunctions, @@ -1231,8 +1231,8 @@ class BaseClient { this.options.req, attachments, { - provider: this.options.agent?.provider, - endpoint: this.options.agent?.endpoint, + provider: this.options.agent?.provider ?? this.options.endpoint, + endpoint: this.options.agent?.endpoint ?? this.options.endpoint, }, getStrategyFunctions, ); @@ -1246,8 +1246,8 @@ class BaseClient { this.options.req, attachments, { - provider: this.options.agent?.provider, - endpoint: this.options.agent?.endpoint, + provider: this.options.agent?.provider ?? this.options.endpoint, + endpoint: this.options.agent?.endpoint ?? this.options.endpoint, }, getStrategyFunctions, );