🔨 style: Improve MCP UI (#8745)

* refactor: Enhance MCP components with improved UI elements and localization updates

* refactor: Clean up MCP components by removing unused imports and improving layout

* refactor: Update server status badge styling for improved UI consistency

* refactor: Move group up a level so 'X' and background highlight occur at same time for cancellation button

* refactor: Remove unused translation keys from the localization file

---------

Co-authored-by: Dustin Healy <dustinhealy1@gmail.com>
This commit is contained in:
Marco Beretta 2025-07-30 20:56:22 +02:00 committed by GitHub
parent 19a8f5c545
commit 09659c1040
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 113 additions and 173 deletions

View file

@ -1,6 +1,6 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { Input, Label, Button } from '@librechat/client'; import { Input, Label, Button, TooltipAnchor, CircleHelpIcon } from '@librechat/client';
import { useMCPAuthValuesQuery } from '~/data-provider/Tools/queries'; import { useMCPAuthValuesQuery } from '~/data-provider/Tools/queries';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
@ -31,16 +31,24 @@ function AuthField({ name, config, hasValue, control, errors }: AuthFieldProps)
return ( return (
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<TooltipAnchor
description={config.description || ''}
render={
<div className="flex items-center gap-2">
<Label htmlFor={name} className="text-sm font-medium"> <Label htmlFor={name} className="text-sm font-medium">
{config.title} {config.title}
</Label> </Label>
<CircleHelpIcon className="h-5 w-5 text-text-tertiary" />
</div>
}
/>
{hasValue ? ( {hasValue ? (
<div className="flex min-w-fit items-center gap-2 whitespace-nowrap rounded-full border border-border-medium px-2 py-0.5 text-xs font-medium text-text-secondary"> <div className="flex min-w-fit items-center gap-2 whitespace-nowrap rounded-full border border-border-light px-2 py-0.5 text-xs font-medium text-text-secondary">
<div className="h-1.5 w-1.5 rounded-full bg-green-500" /> <div className="h-1.5 w-1.5 rounded-full bg-green-500" />
<span>{localize('com_ui_set')}</span> <span>{localize('com_ui_set')}</span>
</div> </div>
) : ( ) : (
<div className="flex min-w-fit items-center gap-2 whitespace-nowrap rounded-full border border-border-medium px-2 py-0.5 text-xs font-medium text-text-secondary"> <div className="flex min-w-fit items-center gap-2 whitespace-nowrap rounded-full border border-border-light px-2 py-0.5 text-xs font-medium text-text-secondary">
<div className="h-1.5 w-1.5 rounded-full border border-border-medium" /> <div className="h-1.5 w-1.5 rounded-full border border-border-medium" />
<span>{localize('com_ui_unset')}</span> <span>{localize('com_ui_unset')}</span>
</div> </div>
@ -60,16 +68,10 @@ function AuthField({ name, config, hasValue, control, errors }: AuthFieldProps)
? localize('com_ui_mcp_update_var', { 0: config.title }) ? localize('com_ui_mcp_update_var', { 0: config.title })
: localize('com_ui_mcp_enter_var', { 0: config.title }) : localize('com_ui_mcp_enter_var', { 0: config.title })
} }
className="w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white sm:text-sm" className="w-full shadow-sm sm:text-sm"
/> />
)} )}
/> />
{config.description && (
<p
className="text-xs text-text-secondary [&_a]:text-blue-500 [&_a]:hover:text-blue-600 dark:[&_a]:text-blue-400 dark:[&_a]:hover:text-blue-300"
dangerouslySetInnerHTML={{ __html: config.description }}
/>
)}
{errors[name] && <p className="text-xs text-red-500">{errors[name]?.message}</p>} {errors[name] && <p className="text-xs text-red-500">{errors[name]?.message}</p>}
</div> </div>
); );
@ -114,15 +116,11 @@ export default function CustomUserVarsSection({
}; };
if (!fields || Object.keys(fields).length === 0) { if (!fields || Object.keys(fields).length === 0) {
return ( return null;
<div className="p-4 text-center text-sm text-gray-500">
{localize('com_sidepanel_mcp_no_custom_vars', { '0': serverName })}
</div>
);
} }
return ( return (
<div className="space-y-4"> <div className="flex-1 space-y-4">
<form onSubmit={handleSubmit(onFormSubmit)} className="space-y-4"> <form onSubmit={handleSubmit(onFormSubmit)} className="space-y-4">
{Object.entries(fields).map(([key, config]) => { {Object.entries(fields).map(([key, config]) => {
const hasValue = authValuesData?.authValueFlags?.[key] || false; const hasValue = authValuesData?.authValueFlags?.[key] || false;
@ -140,21 +138,11 @@ export default function CustomUserVarsSection({
})} })}
</form> </form>
<div className="flex justify-end gap-2 pt-2"> <div className="flex justify-end gap-2">
<Button <Button onClick={handleRevokeClick} variant="destructive" disabled={isSubmitting}>
onClick={handleRevokeClick}
className="bg-red-600 text-white hover:bg-red-700 dark:hover:bg-red-800"
disabled={isSubmitting}
size="sm"
>
{localize('com_ui_revoke')} {localize('com_ui_revoke')}
</Button> </Button>
<Button <Button onClick={handleSubmit(onFormSubmit)} variant="submit" disabled={isSubmitting}>
onClick={handleSubmit(onFormSubmit)}
className="bg-green-500 text-white hover:bg-green-600"
disabled={isSubmitting}
size="sm"
>
{isSubmitting ? localize('com_ui_saving') : localize('com_ui_save')} {isSubmitting ? localize('com_ui_saving') : localize('com_ui_save')}
</Button> </Button>
</div> </div>

View file

@ -1,11 +1,11 @@
import React from 'react'; import React from 'react';
import { Loader2, KeyRound, PlugZap, AlertTriangle } from 'lucide-react'; import { KeyRound, PlugZap, AlertTriangle } from 'lucide-react';
import { import {
Spinner,
OGDialog, OGDialog,
OGDialogTitle, OGDialogTitle,
OGDialogHeader, OGDialogHeader,
OGDialogContent, OGDialogContent,
OGDialogDescription,
} from '@librechat/client'; } from '@librechat/client';
import type { MCPServerStatus } from 'librechat-data-provider'; import type { MCPServerStatus } from 'librechat-data-provider';
import ServerInitializationSection from './ServerInitializationSection'; import ServerInitializationSection from './ServerInitializationSection';
@ -45,9 +45,6 @@ export default function MCPConfigDialog({
const dialogTitle = hasFields const dialogTitle = hasFields
? localize('com_ui_configure_mcp_variables_for', { 0: serverName }) ? localize('com_ui_configure_mcp_variables_for', { 0: serverName })
: `${serverName} MCP Server`; : `${serverName} MCP Server`;
const dialogDescription = hasFields
? localize('com_ui_mcp_dialog_desc')
: `Manage connection and settings for the ${serverName} MCP server.`;
// Helper function to render status badge based on connection state // Helper function to render status badge based on connection state
const renderStatusBadge = () => { const renderStatusBadge = () => {
@ -60,7 +57,7 @@ export default function MCPConfigDialog({
if (connectionState === 'connecting') { if (connectionState === 'connecting') {
return ( return (
<div className="flex items-center gap-2 rounded-full bg-blue-50 px-2 py-0.5 text-xs font-medium text-blue-600 dark:bg-blue-950 dark:text-blue-400"> <div className="flex items-center gap-2 rounded-full bg-blue-50 px-2 py-0.5 text-xs font-medium text-blue-600 dark:bg-blue-950 dark:text-blue-400">
<Loader2 className="h-3 w-3 animate-spin" /> <Spinner className="h-3 w-3" />
<span>{localize('com_ui_connecting')}</span> <span>{localize('com_ui_connecting')}</span>
</div> </div>
); );
@ -107,17 +104,16 @@ export default function MCPConfigDialog({
return ( return (
<OGDialog open={isOpen} onOpenChange={onOpenChange}> <OGDialog open={isOpen} onOpenChange={onOpenChange}>
<OGDialogContent className="flex max-h-[90vh] w-full max-w-md flex-col"> <OGDialogContent className="flex max-h-screen w-11/12 max-w-lg flex-col space-y-2">
<OGDialogHeader> <OGDialogHeader>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<OGDialogTitle>{dialogTitle}</OGDialogTitle> <OGDialogTitle className="text-xl">
{dialogTitle.charAt(0).toUpperCase() + dialogTitle.slice(1)}
</OGDialogTitle>
{renderStatusBadge()} {renderStatusBadge()}
</div> </div>
<OGDialogDescription>{dialogDescription}</OGDialogDescription>
</OGDialogHeader> </OGDialogHeader>
{/* Content */}
<div className="flex-1 overflow-y-auto p-6">
{/* Custom User Variables Section */} {/* Custom User Variables Section */}
<CustomUserVarsSection <CustomUserVarsSection
serverName={serverName} serverName={serverName}
@ -126,7 +122,6 @@ export default function MCPConfigDialog({
onRevoke={onRevoke || (() => {})} onRevoke={onRevoke || (() => {})}
isSubmitting={isSubmitting} isSubmitting={isSubmitting}
/> />
</div>
{/* Server Initialization Section */} {/* Server Initialization Section */}
<ServerInitializationSection <ServerInitializationSection

View file

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { SettingsIcon, AlertTriangle, Loader2, KeyRound, PlugZap, X } from 'lucide-react'; import { Spinner } from '@librechat/client';
import { SettingsIcon, AlertTriangle, KeyRound, PlugZap, X } from 'lucide-react';
import type { MCPServerStatus, TPlugin } from 'librechat-data-provider'; import type { MCPServerStatus, TPlugin } from 'librechat-data-provider';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
@ -96,12 +97,12 @@ function InitializingStatusIcon({ serverName, onCancel, canCancel }: Initializin
<button <button
type="button" type="button"
onClick={onCancel} onClick={onCancel}
className="flex h-6 w-6 items-center justify-center rounded p-1 hover:bg-red-100 dark:hover:bg-red-900/20" className="group flex h-6 w-6 items-center justify-center rounded p-1 hover:bg-red-100 dark:hover:bg-red-900/20"
aria-label={localize('com_ui_cancel')} aria-label={localize('com_ui_cancel')}
title={localize('com_ui_cancel')} title={localize('com_ui_cancel')}
> >
<div className="group relative h-4 w-4"> <div className="relative h-4 w-4">
<Loader2 className="h-4 w-4 animate-spin text-blue-500 group-hover:opacity-0" /> <Spinner className="h-4 w-4 group-hover:opacity-0" />
<X className="absolute inset-0 h-4 w-4 text-red-500 opacity-0 group-hover:opacity-100" /> <X className="absolute inset-0 h-4 w-4 text-red-500 opacity-0 group-hover:opacity-100" />
</div> </div>
</button> </button>
@ -110,8 +111,8 @@ function InitializingStatusIcon({ serverName, onCancel, canCancel }: Initializin
return ( return (
<div className="flex h-6 w-6 items-center justify-center rounded p-1"> <div className="flex h-6 w-6 items-center justify-center rounded p-1">
<Loader2 <Spinner
className="h-4 w-4 animate-spin text-blue-500" className="h-4 w-4"
aria-label={localize('com_nav_mcp_status_connecting', { 0: serverName })} aria-label={localize('com_nav_mcp_status_connecting', { 0: serverName })}
/> />
</div> </div>
@ -121,8 +122,8 @@ function InitializingStatusIcon({ serverName, onCancel, canCancel }: Initializin
function ConnectingStatusIcon({ serverName }: StatusIconProps) { function ConnectingStatusIcon({ serverName }: StatusIconProps) {
return ( return (
<div className="flex h-6 w-6 items-center justify-center rounded p-1"> <div className="flex h-6 w-6 items-center justify-center rounded p-1">
<Loader2 <Spinner
className="h-4 w-4 animate-spin text-blue-500" className="h-4 w-4"
aria-label={localize('com_nav_mcp_status_connecting', { 0: serverName })} aria-label={localize('com_nav_mcp_status_connecting', { 0: serverName })}
/> />
</div> </div>

View file

@ -1,23 +1,24 @@
import React, { useCallback } from 'react'; import React from 'react';
import { Button } from '@librechat/client'; import { RefreshCw } from 'lucide-react';
import { RefreshCw, Link } from 'lucide-react'; import { Button, Spinner } from '@librechat/client';
import { useMCPServerManager } from '~/hooks/MCP/useMCPServerManager'; import { useMCPServerManager } from '~/hooks/MCP/useMCPServerManager';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
interface ServerInitializationSectionProps { interface ServerInitializationSectionProps {
sidePanel?: boolean;
serverName: string; serverName: string;
requiresOAuth: boolean; requiresOAuth: boolean;
hasCustomUserVars?: boolean; hasCustomUserVars?: boolean;
} }
export default function ServerInitializationSection({ export default function ServerInitializationSection({
sidePanel = false,
serverName, serverName,
requiresOAuth, requiresOAuth,
hasCustomUserVars = false, hasCustomUserVars = false,
}: ServerInitializationSectionProps) { }: ServerInitializationSectionProps) {
const localize = useLocalize(); const localize = useLocalize();
// Use the centralized server manager instead of the old initialization hook so we can handle multiple oauth flows at once
const { const {
initializeServer, initializeServer,
connectionStatus, connectionStatus,
@ -33,99 +34,66 @@ export default function ServerInitializationSection({
const isServerInitializing = isInitializing(serverName); const isServerInitializing = isInitializing(serverName);
const serverOAuthUrl = getOAuthUrl(serverName); const serverOAuthUrl = getOAuthUrl(serverName);
const handleInitializeClick = useCallback(() => { const shouldShowReinit = isConnected && (requiresOAuth || hasCustomUserVars);
initializeServer(serverName, false); const shouldShowInit = !isConnected && !serverOAuthUrl;
}, [initializeServer, serverName]);
const handleCancelClick = useCallback(() => { if (!shouldShowReinit && !shouldShowInit && !serverOAuthUrl) {
cancelOAuthFlow(serverName);
}, [cancelOAuthFlow, serverName]);
if (isConnected && (requiresOAuth || hasCustomUserVars)) {
return (
<div className="flex justify-start">
<button
onClick={handleInitializeClick}
disabled={isServerInitializing}
className="flex items-center gap-1 text-xs text-gray-400 hover:text-gray-600 disabled:opacity-50 dark:text-gray-500 dark:hover:text-gray-400"
>
<RefreshCw className={`h-3 w-3 ${isServerInitializing ? 'animate-spin' : ''}`} />
{isServerInitializing ? localize('com_ui_loading') : localize('com_ui_reinitialize')}
</button>
</div>
);
}
if (isConnected) {
return null; return null;
} }
if (serverOAuthUrl) {
return ( return (
<div className="rounded-lg border border-amber-200 bg-amber-50 p-4 dark:border-amber-700 dark:bg-amber-900/20">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-amber-800 dark:text-amber-200">
{requiresOAuth
? localize('com_ui_mcp_not_authenticated', { 0: serverName })
: localize('com_ui_mcp_not_initialized', { 0: serverName })}
</span>
</div>
{/* Only show authenticate button when OAuth URL is not present */}
{!serverOAuthUrl && (
<Button
onClick={handleInitializeClick}
disabled={isServerInitializing}
className="btn btn-primary focus:shadow-outline flex w-full items-center justify-center px-4 py-2 font-semibold text-white hover:bg-green-600 focus:border-green-500"
>
{isServerInitializing ? (
<> <>
<RefreshCw className="h-4 w-4 animate-spin" />
{localize('com_ui_loading')}
</>
) : (
<>
<RefreshCw className="h-4 w-4" />
{requiresOAuth
? localize('com_ui_authenticate')
: localize('com_ui_mcp_initialize')}
</>
)}
</Button>
)}
</div>
{/* OAuth URL display */}
{serverOAuthUrl && (
<div className="mt-4 rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-700 dark:bg-blue-900/20">
<div className="mb-2 flex items-center gap-2">
<div className="flex h-4 w-4 items-center justify-center rounded-full bg-blue-500">
<Link className="h-2.5 w-2.5 text-white" />
</div>
<span className="text-sm font-medium text-blue-700 dark:text-blue-300">
{localize('com_ui_auth_url')}
</span>
</div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button <Button
onClick={() => window.open(serverOAuthUrl, '_blank', 'noopener,noreferrer')} onClick={() => cancelOAuthFlow(serverName)}
className="flex-1 bg-green-600 text-white hover:bg-green-700 dark:hover:bg-green-800"
>
{localize('com_ui_continue_oauth')}
</Button>
<Button
onClick={handleCancelClick}
disabled={!canCancel} disabled={!canCancel}
className="bg-gray-200 text-gray-700 hover:bg-gray-300 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600" variant="outline"
title={!canCancel ? 'disabled' : undefined} title={!canCancel ? 'disabled' : undefined}
> >
{localize('com_ui_cancel')} {localize('com_ui_cancel')}
</Button> </Button>
<Button
variant="submit"
onClick={() => window.open(serverOAuthUrl, '_blank', 'noopener,noreferrer')}
className="flex-1"
>
{localize('com_ui_continue_oauth')}
</Button>
</div> </div>
<p className="mt-2 text-xs text-blue-600 dark:text-blue-400"> </>
{localize('com_ui_oauth_flow_desc')} );
</p> }
</div>
)} // Unified button rendering
const isReinit = shouldShowReinit;
const outerClass = isReinit ? 'flex justify-start' : 'flex justify-end';
const buttonVariant = isReinit ? undefined : 'default';
const buttonText = isServerInitializing
? localize('com_ui_loading')
: isReinit
? localize('com_ui_reinitialize')
: requiresOAuth
? localize('com_ui_authenticate')
: localize('com_ui_mcp_initialize');
const icon = isServerInitializing ? (
<Spinner className="h-4 w-4" />
) : (
<RefreshCw className="h-4 w-4" />
);
return (
<div className={outerClass}>
<Button
variant={buttonVariant}
onClick={() => initializeServer(serverName, false)}
disabled={isServerInitializing}
size={sidePanel ? 'sm' : 'default'}
className="w-full"
>
{icon}
{buttonText}
</Button>
</div> </div>
); );
} }

View file

@ -6,8 +6,8 @@ import { Constants, QueryKeys } from 'librechat-data-provider';
import { useUpdateUserPluginsMutation } from 'librechat-data-provider/react-query'; import { useUpdateUserPluginsMutation } from 'librechat-data-provider/react-query';
import type { TUpdateUserPlugins } from 'librechat-data-provider'; import type { TUpdateUserPlugins } from 'librechat-data-provider';
import ServerInitializationSection from '~/components/MCP/ServerInitializationSection'; import ServerInitializationSection from '~/components/MCP/ServerInitializationSection';
import CustomUserVarsSection from '~/components/MCP/CustomUserVarsSection';
import { useMCPConnectionStatusQuery } from '~/data-provider/Tools/queries'; import { useMCPConnectionStatusQuery } from '~/data-provider/Tools/queries';
import CustomUserVarsSection from '~/components/MCP/CustomUserVarsSection';
import BadgeRowProvider from '~/Providers/BadgeRowContext'; import BadgeRowProvider from '~/Providers/BadgeRowContext';
import { useGetStartupConfig } from '~/data-provider'; import { useGetStartupConfig } from '~/data-provider';
import MCPPanelSkeleton from './MCPPanelSkeleton'; import MCPPanelSkeleton from './MCPPanelSkeleton';
@ -127,20 +127,12 @@ function MCPPanelContent() {
const serverStatus = connectionStatus[selectedServerNameForEditing]; const serverStatus = connectionStatus[selectedServerNameForEditing];
return ( return (
<div className="h-auto max-w-full overflow-x-hidden p-3"> <div className="h-auto max-w-full space-y-4 overflow-x-hidden py-2">
<Button <Button variant="outline" onClick={handleGoBackToList} size="sm">
variant="outline"
onClick={handleGoBackToList}
className="mb-3 flex items-center px-3 py-2 text-sm"
>
<ChevronLeft className="mr-1 h-4 w-4" /> <ChevronLeft className="mr-1 h-4 w-4" />
{localize('com_ui_back')} {localize('com_ui_back')}
</Button> </Button>
<h3 className="mb-3 text-lg font-medium">
{localize('com_sidepanel_mcp_variables_for', { '0': serverBeingEdited.serverName })}
</h3>
<div className="mb-4"> <div className="mb-4">
<CustomUserVarsSection <CustomUserVarsSection
serverName={selectedServerNameForEditing} serverName={selectedServerNameForEditing}
@ -160,6 +152,7 @@ function MCPPanelContent() {
</div> </div>
<ServerInitializationSection <ServerInitializationSection
sidePanel={true}
serverName={selectedServerNameForEditing} serverName={selectedServerNameForEditing}
requiresOAuth={serverStatus?.requiresOAuth || false} requiresOAuth={serverStatus?.requiresOAuth || false}
hasCustomUserVars={ hasCustomUserVars={
@ -172,7 +165,7 @@ function MCPPanelContent() {
} else { } else {
// Server List View // Server List View
return ( return (
<div className="h-auto max-w-full overflow-x-hidden p-3"> <div className="h-auto max-w-full overflow-x-hidden py-2">
<div className="space-y-2"> <div className="space-y-2">
{mcpServerDefinitions.map((server) => { {mcpServerDefinitions.map((server) => {
const serverStatus = connectionStatus[server.serverName]; const serverStatus = connectionStatus[server.serverName];
@ -189,7 +182,7 @@ function MCPPanelContent() {
<span>{server.serverName}</span> <span>{server.serverName}</span>
{serverStatus && ( {serverStatus && (
<span <span
className={`rounded px-2 py-0.5 text-xs ${ className={`rounded-xl px-2 py-0.5 text-xs ${
isConnected isConnected
? 'bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300' ? 'bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300'
: 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300' : 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300'

View file

@ -506,8 +506,6 @@
"com_sidepanel_hide_panel": "Hide Panel", "com_sidepanel_hide_panel": "Hide Panel",
"com_sidepanel_manage_files": "Manage Files", "com_sidepanel_manage_files": "Manage Files",
"com_sidepanel_mcp_no_servers_with_vars": "No MCP servers with configurable variables.", "com_sidepanel_mcp_no_servers_with_vars": "No MCP servers with configurable variables.",
"com_sidepanel_mcp_no_custom_vars": "No custom user variables set for {{0}}",
"com_sidepanel_mcp_variables_for": "MCP Variables for {{0}}",
"com_sidepanel_parameters": "Parameters", "com_sidepanel_parameters": "Parameters",
"com_sources_image_alt": "Search result image", "com_sources_image_alt": "Search result image",
"com_sources_more_sources": "+{{count}} sources", "com_sources_more_sources": "+{{count}} sources",
@ -850,13 +848,10 @@
"com_ui_manage": "Manage", "com_ui_manage": "Manage",
"com_ui_max_tags": "Maximum number allowed is {{0}}, using latest values.", "com_ui_max_tags": "Maximum number allowed is {{0}}, using latest values.",
"com_ui_mcp_authenticated_success": "MCP server '{{0}}' authenticated successfully", "com_ui_mcp_authenticated_success": "MCP server '{{0}}' authenticated successfully",
"com_ui_mcp_dialog_desc": "Please enter the necessary information below.",
"com_ui_mcp_enter_var": "Enter value for {{0}}", "com_ui_mcp_enter_var": "Enter value for {{0}}",
"com_ui_mcp_init_failed": "Failed to initialize MCP server", "com_ui_mcp_init_failed": "Failed to initialize MCP server",
"com_ui_mcp_initialize": "Initialize", "com_ui_mcp_initialize": "Initialize",
"com_ui_mcp_initialized_success": "MCP server '{{0}}' initialized successfully", "com_ui_mcp_initialized_success": "MCP server '{{0}}' initialized successfully",
"com_ui_mcp_not_authenticated": "{{0}} not authenticated (OAuth Required)",
"com_ui_mcp_not_initialized": "{{0}} not initialized",
"com_ui_mcp_oauth_cancelled": "OAuth login cancelled for {{0}}", "com_ui_mcp_oauth_cancelled": "OAuth login cancelled for {{0}}",
"com_ui_mcp_oauth_timeout": "OAuth login timed out for {{0}}", "com_ui_mcp_oauth_timeout": "OAuth login timed out for {{0}}",
"com_ui_mcp_server_not_found": "Server not found.", "com_ui_mcp_server_not_found": "Server not found.",
@ -916,7 +911,6 @@
"com_ui_oauth_error_missing_code": "Authorization code is missing. Please try again.", "com_ui_oauth_error_missing_code": "Authorization code is missing. Please try again.",
"com_ui_oauth_error_missing_state": "State parameter is missing. Please try again.", "com_ui_oauth_error_missing_state": "State parameter is missing. Please try again.",
"com_ui_oauth_error_title": "Authentication Failed", "com_ui_oauth_error_title": "Authentication Failed",
"com_ui_oauth_flow_desc": "Complete the OAuth flow in the new window, then return here.",
"com_ui_oauth_success_description": "Your authentication was successful. This window will close in", "com_ui_oauth_success_description": "Your authentication was successful. This window will close in",
"com_ui_oauth_success_title": "Authentication Successful", "com_ui_oauth_success_title": "Authentication Successful",
"com_ui_of": "of", "com_ui_of": "of",

9
package-lock.json generated
View file

@ -28936,10 +28936,11 @@
} }
}, },
"node_modules/@testing-library/react": { "node_modules/@testing-library/react": {
"version": "14.2.1", "version": "14.3.1",
"resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.2.1.tgz", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.3.1.tgz",
"integrity": "sha512-sGdjws32ai5TLerhvzThYFbpnF9XtL65Cjf+gB0Dhr29BGqK+mAeN7SURSdu+eqgET4ANcWoC7FQpkaiGvBr+A==", "integrity": "sha512-H99XjUhWQw0lTgyMN05W3xQG1Nh4lq574D8keFf1dDoNTJgp66VbJozRaczoF+wsiaPJNt/TcnfpLGufGxSrZQ==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.12.5", "@babel/runtime": "^7.12.5",
"@testing-library/dom": "^9.0.0", "@testing-library/dom": "^9.0.0",
@ -51506,7 +51507,7 @@
}, },
"packages/client": { "packages/client": {
"name": "@librechat/client", "name": "@librechat/client",
"version": "0.2.0", "version": "0.2.1",
"devDependencies": { "devDependencies": {
"@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-alias": "^5.1.0",
"@rollup/plugin-commonjs": "^25.0.2", "@rollup/plugin-commonjs": "^25.0.2",

View file

@ -1,6 +1,6 @@
{ {
"name": "@librechat/client", "name": "@librechat/client",
"version": "0.2.0", "version": "0.2.1",
"description": "React components for LibreChat", "description": "React components for LibreChat",
"main": "dist/index.js", "main": "dist/index.js",
"module": "dist/index.es.js", "module": "dist/index.es.js",

View file

@ -121,7 +121,7 @@ const DialogDescription = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DialogPrimitive.Description <DialogPrimitive.Description
ref={ref} ref={ref}
className={cn('text-sm text-muted-foreground', className)} className={cn('text-sm text-text-secondary', className)}
{...props} {...props}
/> />
)); ));