From b235ad2cbf640d53ea8c87b9b258d3e43baf6e7f Mon Sep 17 00:00:00 2001 From: Marco Beretta <81851188+berry-13@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:28:16 +0200 Subject: [PATCH] refactor(client): migrate dialogs, toggles and remaining controls to @librechat/client Swap behavioral controls for @librechat/client equivalents: HeadlessUI and legacy dialogs to OGDialog, native checkbox/switch to Checkbox/Switch (onCheckedChange), and remaining buttons/inputs across Chat, Skills, Tools, Sharing, Memories and Settings. Convert applicable native title= tooltips to TooltipAnchor and localize close/scroll aria-labels. Skip swaps that would break floating-label animations or bespoke select behavior. Update co-located test mocks to provide the newly-used Button and cn dependencies. --- .../components/Chat/Input/AudioRecorder.tsx | 13 ++-- client/src/components/Chat/Input/Mention.tsx | 6 +- .../Chat/Menus/Presets/PresetItems.tsx | 25 ++++--- .../Messages/Content/Parts/SubagentCall.tsx | 26 ++++--- .../Chat/Messages/Content/Parts/Summary.tsx | 21 +++--- .../Chat/Messages/Content/Parts/Thinking.tsx | 11 +-- .../Parts/__tests__/SubagentCall.test.tsx | 8 +++ .../Chat/Messages/Content/ProgressText.tsx | 10 +-- .../Chat/Messages/Content/ToolCallGroup.tsx | 10 +-- .../Messages/Content/UIResourceCarousel.tsx | 39 +++++----- .../ContentParts.integration.test.tsx | 11 ++- .../__tests__/UIResourceCarousel.test.tsx | 1 + .../src/components/Chat/Messages/Feedback.tsx | 66 +++++++++-------- client/src/components/Chat/Messages/Fork.tsx | 28 ++++---- .../Conversations/Conversations.tsx | 9 +-- .../components/Conversations/RenameForm.tsx | 28 +++++--- .../__tests__/Conversations.test.tsx | 8 +++ .../Endpoints/MessageEndpointIcon.tsx | 13 ++-- .../components/MCP/MCPServerStatusIcon.tsx | 49 +++++++------ .../SettingsTabs/Account/DeleteAccount.tsx | 22 +++--- .../Nav/SettingsTabs/Data/SharedLinks.tsx | 51 ++++++------- .../Sharing/PeoplePickerAdminSettings.tsx | 40 +++-------- .../SidePanel/Memories/MemoryCardActions.tsx | 13 ++-- .../components/Skills/buttons/SkillToggle.tsx | 29 ++------ .../Skills/forms/CreateSkillForm.tsx | 12 ++-- .../src/components/Skills/forms/SkillForm.tsx | 15 ++-- .../Skills/tree/SkillFileEditor.tsx | 7 +- .../components/Tools/AssistantToolsDialog.tsx | 72 ++++++++----------- .../components/ui/TermsAndConditionsModal.tsx | 18 ++--- .../client/src/components/ThemeSelector.tsx | 15 ++-- 30 files changed, 356 insertions(+), 320 deletions(-) diff --git a/client/src/components/Chat/Input/AudioRecorder.tsx b/client/src/components/Chat/Input/AudioRecorder.tsx index c8387f42cd..2a3a3e64b9 100644 --- a/client/src/components/Chat/Input/AudioRecorder.tsx +++ b/client/src/components/Chat/Input/AudioRecorder.tsx @@ -1,6 +1,6 @@ import { memo, useCallback, useRef } from 'react'; import { MicOff } from 'lucide-react'; -import { useToastContext, TooltipAnchor, ListeningIcon, Spinner } from '@librechat/client'; +import { Button, useToastContext, TooltipAnchor, ListeningIcon, Spinner } from '@librechat/client'; import { useLocalize, useSpeechToText, useGetAudioSettings } from '~/hooks'; import { globalAudioId, type TAskFunction } from '~/common'; import { useChatFormContext } from '~/Providers'; @@ -107,20 +107,19 @@ export default memo(function AudioRecorder({ {renderIcon()} - + } /> ); diff --git a/client/src/components/Chat/Input/Mention.tsx b/client/src/components/Chat/Input/Mention.tsx index c58a62ad93..3b3dadbd46 100644 --- a/client/src/components/Chat/Input/Mention.tsx +++ b/client/src/components/Chat/Input/Mention.tsx @@ -1,8 +1,8 @@ import { memo, useState, useRef, useEffect } from 'react'; import { AutoSizer, List } from 'react-virtualized'; -import { Spinner, useCombobox } from '@librechat/client'; import { EModelEndpoint } from 'librechat-data-provider'; import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { Input, Spinner, useCombobox } from '@librechat/client'; import type { RecoilState } from 'recoil'; import type { MentionOption, ConvoGenerator } from '~/common'; import { useGetConversation, useLocalize, TranslationKeys } from '~/hooks'; @@ -182,10 +182,10 @@ function MentionContent({ return (
- { diff --git a/client/src/components/Chat/Menus/Presets/PresetItems.tsx b/client/src/components/Chat/Menus/Presets/PresetItems.tsx index 24102db776..98b7d6ac31 100644 --- a/client/src/components/Chat/Menus/Presets/PresetItems.tsx +++ b/client/src/components/Chat/Menus/Presets/PresetItems.tsx @@ -5,6 +5,7 @@ import { getEndpointField } from 'librechat-data-provider'; import { Dialog, Label, + Button, PinIcon, EditIcon, TrashIcon, @@ -51,17 +52,18 @@ const PresetItems: FC<{ tabIndex={-1} >
- + - + - + } /> { e.preventDefault(); @@ -217,14 +221,15 @@ const PresetItems: FC<{ }} > - + } /> { e.preventDefault(); @@ -240,7 +245,7 @@ const PresetItems: FC<{ }} > - + } />
diff --git a/client/src/components/Chat/Messages/Content/Parts/SubagentCall.tsx b/client/src/components/Chat/Messages/Content/Parts/SubagentCall.tsx index 0c9de26447..11d3f24f43 100644 --- a/client/src/components/Chat/Messages/Content/Parts/SubagentCall.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/SubagentCall.tsx @@ -2,7 +2,13 @@ import { useCallback, useEffect, useId, useMemo, useReducer, useRef, useState } import { useRecoilValue } from 'recoil'; import { ContentTypes, EModelEndpoint } from 'librechat-data-provider'; import { ArrowDown, ChevronRight, Maximize2, Minimize2, Users } from 'lucide-react'; -import { OGDialog, OGDialogContent, OGDialogTitle, OGDialogDescription } from '@librechat/client'; +import { + Button, + OGDialog, + OGDialogTitle, + OGDialogContent, + OGDialogDescription, +} from '@librechat/client'; import type { Agents, TAttachment, TMessage, TMessageContentParts } from 'librechat-data-provider'; import type { PartWithIndex } from '~/components/Chat/Messages/Content/ParallelContent'; import type { SubagentTickerLine } from '~/utils/subagentContent'; @@ -539,14 +545,15 @@ export default function SubagentCall({
{!isAtBottom && ( - + )}
{localize('com_ui_prompt')} - +
- + {content && showCopyButton && ( - + )}
); diff --git a/client/src/components/Chat/Messages/Content/Parts/Thinking.tsx b/client/src/components/Chat/Messages/Content/Parts/Thinking.tsx index c103dc39de..2466828c28 100644 --- a/client/src/components/Chat/Messages/Content/Parts/Thinking.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/Thinking.tsx @@ -1,7 +1,7 @@ import { useState, useMemo, memo, useCallback, useRef, useId, type MouseEvent } from 'react'; import { useAtomValue } from 'jotai'; import { Lightbulb, ChevronDown, ChevronUp } from 'lucide-react'; -import { Clipboard, CheckMark, TooltipAnchor } from '@librechat/client'; +import { Button, Clipboard, CheckMark, TooltipAnchor } from '@librechat/client'; import type { FocusEvent, FC } from 'react'; import { useLocalize, useExpandCollapse } from '~/hooks'; import { showThinkingAtom } from '~/store/showThinking'; @@ -90,8 +90,9 @@ export const ThinkingButton = memo( {label} {content && showCopyButton && ( - + )}
); diff --git a/client/src/components/Chat/Messages/Content/Parts/__tests__/SubagentCall.test.tsx b/client/src/components/Chat/Messages/Content/Parts/__tests__/SubagentCall.test.tsx index 2b968071ad..6871ded4b6 100644 --- a/client/src/components/Chat/Messages/Content/Parts/__tests__/SubagentCall.test.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/__tests__/SubagentCall.test.tsx @@ -84,6 +84,14 @@ jest.mock('../Attachment', () => ({ jest.mock('@librechat/client', () => ({ __esModule: true, + Button: ({ + children, + variant: _variant, + size: _size, + ...props + }: React.ComponentProps<'button'> & { variant?: string; size?: string }) => ( + + ), OGDialog: ({ children }: { children: React.ReactNode }) => <>{children}, OGDialogContent: ({ children }: { children: React.ReactNode }) => (
{children}
diff --git a/client/src/components/Chat/Messages/Content/ProgressText.tsx b/client/src/components/Chat/Messages/Content/ProgressText.tsx index bfe7092574..d614f3b992 100644 --- a/client/src/components/Chat/Messages/Content/ProgressText.tsx +++ b/client/src/components/Chat/Messages/Content/ProgressText.tsx @@ -1,4 +1,5 @@ import { ChevronDown } from 'lucide-react'; +import { Button } from '@librechat/client'; import * as Popover from '@radix-ui/react-popover'; import CancelledIcon from './CancelledIcon'; import { cn } from '~/utils'; @@ -84,12 +85,13 @@ export default function ProgressText({ return ( - + ); } diff --git a/client/src/components/Chat/Messages/Content/ToolCallGroup.tsx b/client/src/components/Chat/Messages/Content/ToolCallGroup.tsx index cb074644ca..0835ab3f57 100644 --- a/client/src/components/Chat/Messages/Content/ToolCallGroup.tsx +++ b/client/src/components/Chat/Messages/Content/ToolCallGroup.tsx @@ -1,5 +1,6 @@ import { useState, useRef, useMemo, useEffect, useCallback } from 'react'; import { useRecoilValue } from 'recoil'; +import { Button } from '@librechat/client'; import { ChevronDown, Users } from 'lucide-react'; import { Tools, Constants, ContentTypes, ToolCallTypes } from 'librechat-data-provider'; import type { @@ -10,12 +11,12 @@ import type { } from 'librechat-data-provider'; import type { PartWithIndex } from './ParallelContent'; import { useLocalize, useExpandCollapse, scheduleMessageContentLayoutReconcile } from '~/hooks'; +import { isBashProgrammaticToolCall } from './routing'; import { cn, getToolDisplayLabel } from '~/utils'; import { StackedToolIcons } from './ToolOutput'; import { useMCPIconMap } from '~/hooks/MCP'; import { AttachmentGroup } from './Parts'; import store from '~/store'; -import { isBashProgrammaticToolCall } from './routing'; interface ToolMeta { name: string; @@ -239,9 +240,10 @@ export default function ToolCallGroup({ return (
- +
{shouldRenderBody && (
diff --git a/client/src/components/Chat/Messages/Content/UIResourceCarousel.tsx b/client/src/components/Chat/Messages/Content/UIResourceCarousel.tsx index 117bb9d2cc..e44d4ed9f0 100644 --- a/client/src/components/Chat/Messages/Content/UIResourceCarousel.tsx +++ b/client/src/components/Chat/Messages/Content/UIResourceCarousel.tsx @@ -1,14 +1,18 @@ import React, { useState } from 'react'; +import { Button } from '@librechat/client'; import { UIResourceRenderer } from '@mcp-ui/client'; +import { ChevronLeft, ChevronRight } from 'lucide-react'; import type { UIResource } from 'librechat-data-provider'; import { useOptionalMessagesOperations } from '~/Providers'; import { handleUIAction } from '~/utils'; +import { useLocalize } from '~/hooks'; interface UIResourceCarouselProps { uiResources: UIResource[]; } const UIResourceCarousel: React.FC = React.memo(({ uiResources }) => { + const localize = useLocalize(); const [showLeftArrow, setShowLeftArrow] = useState(false); const [showRightArrow, setShowRightArrow] = useState(true); const [isContainerHovered, setIsContainerHovered] = useState(false); @@ -70,23 +74,17 @@ const UIResourceCarousel: React.FC = React.memo(({ uiRe /> {showLeftArrow && ( - + + )}
= React.memo(({ uiRe
{showRightArrow && ( - + + )}
); diff --git a/client/src/components/Chat/Messages/Content/__tests__/ContentParts.integration.test.tsx b/client/src/components/Chat/Messages/Content/__tests__/ContentParts.integration.test.tsx index 555bf7c4f6..3c679fc7ca 100644 --- a/client/src/components/Chat/Messages/Content/__tests__/ContentParts.integration.test.tsx +++ b/client/src/components/Chat/Messages/Content/__tests__/ContentParts.integration.test.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { RecoilRoot } from 'recoil'; import { ContentTypes } from 'librechat-data-provider'; -import type { TAttachment, TMessageContentParts } from 'librechat-data-provider'; import { fireEvent, render, screen } from '@testing-library/react'; +import type { TAttachment, TMessageContentParts } from 'librechat-data-provider'; import ContentParts from '../ContentParts'; jest.mock('~/hooks', () => ({ @@ -53,7 +53,14 @@ jest.mock('lucide-react', () => ({ })); jest.mock('@librechat/client', () => ({ - Button: ({ children }: { children?: React.ReactNode }) => , + Button: ({ + children, + variant: _variant, + size: _size, + ...props + }: React.ComponentProps<'button'> & { variant?: string; size?: string }) => ( + + ), })); jest.mock('../Parts', () => ({ diff --git a/client/src/components/Chat/Messages/Content/__tests__/UIResourceCarousel.test.tsx b/client/src/components/Chat/Messages/Content/__tests__/UIResourceCarousel.test.tsx index 91d9499898..0f66cc65e4 100644 --- a/client/src/components/Chat/Messages/Content/__tests__/UIResourceCarousel.test.tsx +++ b/client/src/components/Chat/Messages/Content/__tests__/UIResourceCarousel.test.tsx @@ -24,6 +24,7 @@ jest.mock('~/Providers', () => ({ // Mock handleUIAction utility jest.mock('~/utils', () => ({ handleUIAction: jest.fn(), + cn: (...args: unknown[]) => args.filter(Boolean).join(' '), })); // Mock scrollTo diff --git a/client/src/components/Chat/Messages/Feedback.tsx b/client/src/components/Chat/Messages/Feedback.tsx index 1dfa2478a3..24b521283d 100644 --- a/client/src/components/Chat/Messages/Feedback.tsx +++ b/client/src/components/Chat/Messages/Feedback.tsx @@ -14,8 +14,10 @@ import { import { Button, OGDialog, + Textarea, OGDialogContent, OGDialogTitle, + TooltipAnchor, ThumbUpIcon, ThumbDownIcon, } from '@librechat/client'; @@ -55,19 +57,19 @@ function FeedbackOptionButton({ const label = localize(tag.label as Parameters[0]); return ( - + ); } @@ -146,16 +148,17 @@ function FeedbackButtons({ - + } /> - + } /> { - if (isThumbsUp) { - handleButtonFeedback(undefined); - } else { - setOpenDialog(true); - } - }} - type="button" - title={label} - aria-pressed="true" - > - - + { + if (isThumbsUp) { + handleButtonFeedback(undefined); + } else { + setOpenDialog(true); + } + }} + aria-label={label} + aria-pressed="true" + > + + + } + /> ); }; @@ -319,8 +329,8 @@ export default function Feedback({ {localize('com_ui_feedback_more_information')} -