mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-21 10:50:14 +01:00
* chore: add i18n localization comment for AlwaysMakeProd component * feat: enhance accessibility by adding aria-label and aria-labelledby to Switch component * feat: add aria-labels for accessibility in Agent and Assistant avatar buttons * fix: add switch aria-labels for accessibility in various components * feat: add aria-labels and localization keys for accessibility in DataTable, DataTableColumnHeader, and OGDialogTemplate components * chore: refactor out nested ternary * feat: add aria-label to DataTable filter button for My Files modal * feat: add aria-labels for Buttons and localization strings * feat: add aria-labels to Checkboxes in Agent Builder * feat: enhance accessibility by adding aria-label and aria-labelledby to Checkbox component * feat: add aria-label to FileSearchCheckbox in Agent Builder * feat: add aria-label to Prompts text input area * feat: enhance accessibility by adding aria-label and aria-labelledby to TextAreaAutosize component * feat: remove improper role: "list" prop from List in Conversations.tsx to enhance accessibility and stop aria rules conflicting within react-virtualized component * feat: enhance accessibility by allowing tab navigation and adding ring highlights for conversation title editing accept/reject buttons * feat: add aria-label to Copy Link button in the conversation share modal * feat: add title to QR code svg in conversation share modal to describe the image content * feat: enhance accessibility by making Agent Avatar upload keyboard navigable and round out highlight border on focus * feat: enhance accessibility by adding aria attributes around alerting users with screen readers to invalid email address inputs in the Agent Builder * feat: add aria-labels to buttons in Advanced panel of Agent Builder * feat: enhance accessibility by making FileUpload and Clear All buttons in PresetItems keyboard navigable * feat: enchance accessiblity by indexing view and delete button aria-labels in shared links management modal to their specific chat titles * feat: add border highlighting on focus for AnimatedSearchInput * feat: add category description to aria-labels for prompts in ListCard * feat: add proper scoping to rows and columns in table headers * feat: add localized aria-labelling to EditTextPart's TextAreaAutosize component and base dynamic paramters panel components and their supporting translation keys * feat: add localized aria-labels and aria-labelledBy to Checkbox components without them * feat: add localized aria-labeledBy for endpoint settings Sliders * feat: add localized aria-labels for TextareaAutosize components * chore: remove unused i18n string * feat: add localized aria-label for BookmarkForm Checkbox * fix: add stopPropagation onKeyDown for Preview and Edit menu items in prompts that was causing the prompts to inadvertently be sent when triggered with keyboard navigation when Auto-send Prompts was toggled on * fix: switch TableCell to TableHead for title cells according to harvard issue #789 * fix: add more descriptive localization key for file filter button in DataTable * chore: remove self-explanatory code comment from RenameForm * fix: remove stray bg-yellow highlight that was left in during debugging * fix: add aria-label to model configurator panel back button * fix: undo incorrect hoist of tool name split for aria-label and span in MCPInput --------- Co-authored-by: Danny Avila <danny@librechat.ai>
137 lines
4.3 KiB
TypeScript
137 lines
4.3 KiB
TypeScript
import { useRef } from 'react';
|
|
import * as Popover from '@radix-ui/react-popover';
|
|
import { useLocalize } from '~/hooks';
|
|
|
|
export function NoImage() {
|
|
return (
|
|
<div className="border-token-border-medium flex h-full w-full items-center justify-center rounded-full border-2 border-dashed border-black">
|
|
<svg
|
|
stroke="currentColor"
|
|
fill="none"
|
|
strokeWidth="2"
|
|
viewBox="0 0 24 24"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className="text-4xl"
|
|
height="1em"
|
|
width="1em"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<line x1="12" y1="5" x2="12" y2="19" />
|
|
<line x1="5" y1="12" x2="19" y2="12" />
|
|
</svg>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export const AgentAvatarRender = ({
|
|
url,
|
|
progress = 1,
|
|
}: {
|
|
url?: string;
|
|
progress: number; // between 0 and 1
|
|
}) => {
|
|
const radius = 55; // Radius of the SVG circle
|
|
const circumference = 2 * Math.PI * radius;
|
|
|
|
// Calculate the offset based on the loading progress
|
|
const offset = circumference - progress * circumference;
|
|
const circleCSSProperties = {
|
|
transition: 'stroke-dashoffset 0.3s linear',
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<div className="relative h-20 w-20 overflow-hidden rounded-full">
|
|
<img
|
|
src={url}
|
|
className="bg-token-surface-secondary dark:bg-token-surface-tertiary h-full w-full rounded-full object-cover"
|
|
alt="GPT"
|
|
width="80"
|
|
height="80"
|
|
style={{ opacity: progress < 1 ? 0.4 : 1 }}
|
|
key={url || 'default-key'}
|
|
/>
|
|
{progress < 1 && (
|
|
<div className="absolute inset-0 flex items-center justify-center bg-black/5 text-white">
|
|
<svg width="120" height="120" viewBox="0 0 120 120" className="h-6 w-6">
|
|
<circle
|
|
className="origin-[50%_50%] -rotate-90 stroke-gray-400"
|
|
strokeWidth="10"
|
|
fill="transparent"
|
|
r="55"
|
|
cx="60"
|
|
cy="60"
|
|
/>
|
|
<circle
|
|
className="origin-[50%_50%] -rotate-90 transition-[stroke-dashoffset]"
|
|
stroke="currentColor"
|
|
strokeWidth="10"
|
|
strokeDasharray={`${circumference} ${circumference}`}
|
|
strokeDashoffset={offset}
|
|
fill="transparent"
|
|
r="55"
|
|
cx="60"
|
|
cy="60"
|
|
style={circleCSSProperties}
|
|
/>
|
|
</svg>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export function AvatarMenu({
|
|
handleFileChange,
|
|
}: {
|
|
handleFileChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
}) {
|
|
const localize = useLocalize();
|
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
const onItemClick = () => {
|
|
if (fileInputRef.current) {
|
|
fileInputRef.current.value = '';
|
|
}
|
|
fileInputRef.current?.click();
|
|
};
|
|
|
|
return (
|
|
<Popover.Portal>
|
|
<Popover.Content
|
|
className="flex min-w-[100px] max-w-xs flex-col rounded-xl border border-gray-400 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-850 dark:text-white"
|
|
sideOffset={5}
|
|
>
|
|
<button
|
|
type="button"
|
|
role="menuitem"
|
|
className="group m-1.5 flex cursor-pointer gap-2 rounded-lg p-2.5 text-sm hover:bg-gray-100 focus:ring-0 radix-disabled:pointer-events-none radix-disabled:opacity-50 dark:hover:bg-gray-800 dark:hover:bg-white/5"
|
|
tabIndex={0}
|
|
data-orientation="vertical"
|
|
onClick={onItemClick}
|
|
>
|
|
{localize('com_ui_upload_image')}
|
|
</button>
|
|
{/* <Popover.Close
|
|
role="menuitem"
|
|
className="group m-1.5 flex cursor-pointer gap-2 rounded p-2.5 text-sm hover:bg-black/5 focus:ring-0 radix-disabled:pointer-events-none radix-disabled:opacity-50 dark:hover:bg-white/5"
|
|
tabIndex={-1}
|
|
data-orientation="vertical"
|
|
>
|
|
Use DALL·E
|
|
</Popover.Close> */}
|
|
<input
|
|
accept="image/png,.png,image/jpeg,.jpg,.jpeg,image/gif,.gif,image/webp,.webp"
|
|
multiple={false}
|
|
type="file"
|
|
style={{ display: 'none' }}
|
|
onChange={handleFileChange}
|
|
ref={fileInputRef}
|
|
tabIndex={-1}
|
|
/>
|
|
</Popover.Content>
|
|
</Popover.Portal>
|
|
);
|
|
}
|