🧩 feat: Standardize empty states, improve MCP icon upload accessibility, and refine UI layout (#11195)

* feat: Enhance accessibility and localization for empty states in prompts, bookmarks, and memories

* feat: Improve accessibility and layout for icon upload component

* fix: Update dialog content width for improved accessibility
This commit is contained in:
Marco Beretta 2026-01-05 19:46:35 +01:00 committed by GitHub
parent ca58d70c44
commit 1544491737
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 54 additions and 21 deletions

View file

@ -1,4 +1,4 @@
import { Plus } from 'lucide-react';
import { FileText, Plus } from 'lucide-react';
import { Link } from 'react-router-dom';
import { Button, Skeleton } from '@librechat/client';
import { PermissionTypes, Permissions } from 'librechat-data-provider';
@ -53,14 +53,22 @@ export default function List({
Array.from({ length: 10 }).map((_, index: number) => (
<Skeleton key={index} className="w-100 mx-2 my-2 flex h-14 rounded-lg border-0 p-4" />
))}
{!isLoading && groups.length === 0 && isChatRoute && (
<div className="my-2 flex h-[84px] w-full items-center justify-center rounded-2xl border border-border-light bg-transparent px-3 pb-4 pt-3 text-text-primary">
{localize('com_ui_nothing_found')}
</div>
)}
{!isLoading && groups.length === 0 && !isChatRoute && (
<div className="my-12 flex w-full items-center justify-center text-lg font-semibold text-text-primary">
{localize('com_ui_nothing_found')}
{!isLoading && groups.length === 0 && (
<div
className={cn(
'flex flex-col items-center justify-center rounded-lg border border-border-light bg-transparent p-6 text-center',
isChatRoute ? 'my-2' : 'mx-2 my-4',
)}
>
<div className="mb-2 flex size-10 items-center justify-center rounded-full bg-surface-tertiary">
<FileText className="size-5 text-text-secondary" aria-hidden="true" />
</div>
<p className="text-sm font-medium text-text-primary">
{localize('com_ui_no_prompts_title')}
</p>
<p className="mt-0.5 text-xs text-text-secondary">
{localize('com_ui_add_first_prompt')}
</p>
</div>
)}
{groups.map((group) => {

View file

@ -27,11 +27,22 @@ export default function MCPIcon({ icon, onIconChange }: MCPIconProps) {
}
};
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleClick();
}
};
return (
<div className="flex items-center gap-4">
<div
role="button"
tabIndex={0}
onClick={handleClick}
className="bg-token-surface-secondary dark:bg-token-surface-tertiary border-token-border-medium flex h-16 w-16 shrink-0 cursor-pointer items-center justify-center rounded-xl border-2 border-dashed"
onKeyDown={handleKeyDown}
aria-label={localize('com_ui_upload_icon')}
className="bg-token-surface-secondary dark:bg-token-surface-tertiary border-token-border-medium flex h-16 w-16 shrink-0 cursor-pointer items-center justify-center rounded-xl border-2 border-dashed focus:outline-none focus-visible:ring-2 focus-visible:ring-border-heavy"
>
{previewUrl ? (
<img

View file

@ -9,13 +9,22 @@ export default function BookmarkEmptyState({ isFiltered = false }: BookmarkEmpty
const localize = useLocalize();
return (
<div className="flex flex-col items-center justify-center py-8 text-center">
<div className="mb-3 rounded-full bg-surface-secondary p-3">
<Bookmark className="size-6 text-text-tertiary" aria-hidden="true" />
<div className="flex flex-col items-center justify-center rounded-lg border border-border-light bg-transparent p-6 text-center">
<div className="mb-2 flex size-10 items-center justify-center rounded-full bg-surface-tertiary">
<Bookmark className="size-5 text-text-secondary" aria-hidden="true" />
</div>
<p className="text-sm text-text-secondary">
{isFiltered ? localize('com_ui_no_bookmarks_match') : localize('com_ui_no_bookmarks')}
</p>
{isFiltered ? (
<p className="text-sm text-text-secondary">{localize('com_ui_no_bookmarks_match')}</p>
) : (
<>
<p className="text-sm font-medium text-text-primary">
{localize('com_ui_no_bookmarks_title')}
</p>
<p className="mt-0.5 text-xs text-text-secondary">
{localize('com_ui_add_first_bookmark')}
</p>
</>
)}
</div>
);
}

View file

@ -15,7 +15,7 @@ export default function MCPServerForm({ formHook }: MCPServerFormProps) {
return (
<FormProvider {...methods}>
<div className="max-h-[70vh] space-y-4 overflow-y-auto px-1 py-1">
<div className="space-y-4 px-1 py-1">
<BasicInfoSection />
<ConnectionSection />

View file

@ -31,11 +31,11 @@ export default function BasicInfoSection() {
return (
<div className="space-y-3">
{/* Icon + Name row */}
<div className="flex items-start gap-4">
<div className="flex flex-col gap-4 sm:flex-row sm:items-start">
<div className="flex-shrink-0">
<MCPIcon icon={iconValue} onIconChange={handleIconChange} />
</div>
<div className="flex-1 space-y-1.5">
<div className="w-full space-y-1.5 sm:flex-1">
<Label htmlFor="title" className="text-sm font-medium">
{localize('com_ui_name')} <span className="text-text-secondary">*</span>
</Label>

View file

@ -9,7 +9,7 @@ export default function MemoryEmptyState({ isFiltered = false }: MemoryEmptyStat
const localize = useLocalize();
return (
<div className="flex flex-col items-center justify-center rounded-lg border border-border-light bg-surface-primary p-6 text-center">
<div className="flex flex-col items-center justify-center rounded-lg border border-border-light bg-transparent p-6 text-center">
<div className="mb-2 flex size-10 items-center justify-center rounded-full bg-surface-tertiary">
<Brain className="size-5 text-text-secondary" aria-hidden="true" />
</div>

View file

@ -183,7 +183,7 @@ const AdminSettingsDialog: React.FC<AdminSettingsDialogProps> = ({
<OGDialogContent
className={
dialogContentClassName ??
'border-border-light bg-surface-primary text-text-primary lg:w-1/4'
'w-11/12 max-w-lg border-border-light bg-surface-primary text-text-primary'
}
>
<OGDialogTitle>

View file

@ -636,7 +636,9 @@
"com_ui_active": "Active",
"com_ui_add": "Add",
"com_ui_add_code_interpreter_api_key": "Add Code Interpreter API Key",
"com_ui_add_first_bookmark": "Click on a chat to add one",
"com_ui_add_first_mcp_server": "Create your first MCP server to get started",
"com_ui_add_first_prompt": "Create your first prompt to get started",
"com_ui_add_mcp": "Add MCP",
"com_ui_add_mcp_server": "Add MCP Server",
"com_ui_add_model_preset": "Add a model or preset for an additional response",
@ -1139,6 +1141,7 @@
"com_ui_no_auth": "No Auth",
"com_ui_no_bookmarks": "it seems like you have no bookmarks yet. Click on a chat and add a new one",
"com_ui_no_bookmarks_match": "No bookmarks match your search",
"com_ui_no_bookmarks_title": "No bookmarks yet",
"com_ui_no_categories": "No categories available",
"com_ui_no_category": "No category",
"com_ui_no_changes": "No changes were made",
@ -1149,6 +1152,7 @@
"com_ui_no_memories_match": "No memories match your search",
"com_ui_no_memories_title": "No memories yet",
"com_ui_no_personalization_available": "No personalization options are currently available",
"com_ui_no_prompts_title": "No prompts yet",
"com_ui_no_read_access": "You don't have permission to view memories",
"com_ui_no_results_found": "No results found",
"com_ui_no_terms_content": "No terms and conditions content to display",
@ -1385,6 +1389,7 @@
"com_ui_upload_file_context": "Upload File Context",
"com_ui_upload_file_search": "Upload for File Search",
"com_ui_upload_files": "Upload files",
"com_ui_upload_icon": "Upload icon image",
"com_ui_upload_image": "Upload an image",
"com_ui_upload_image_input": "Upload Image",
"com_ui_upload_invalid": "Invalid file for upload. Must be an image not exceeding the limit",