mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 14:57:42 +00:00
refactor(client): migrate low-risk primitives to @librechat/client
Swap raw <label>, <textarea>, and native title= tooltips for the @librechat/client Label, Textarea, and TooltipAnchor components across Agents, Endpoints settings, Export modal, Prompts, Sharing, and Memory dialogs. Add localization keys (scroll, sibling navigation, none selected, select var) for the remaining swap waves.
This commit is contained in:
parent
f658ffdb3c
commit
a1b22b745d
10 changed files with 120 additions and 78 deletions
|
|
@ -1,7 +1,13 @@
|
|||
import React, { useRef } from 'react';
|
||||
import { Link, Pin, PinOff } from 'lucide-react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { OGDialog, OGDialogContent, Button, useToastContext } from '@librechat/client';
|
||||
import {
|
||||
OGDialog,
|
||||
OGDialogContent,
|
||||
Button,
|
||||
TooltipAnchor,
|
||||
useToastContext,
|
||||
} from '@librechat/client';
|
||||
import {
|
||||
QueryKeys,
|
||||
Constants,
|
||||
|
|
@ -165,24 +171,32 @@ const AgentDetail: React.FC<AgentDetailProps> = ({ agent, isOpen, onClose }) =>
|
|||
|
||||
{/* Action button */}
|
||||
<div className="mb-4 mt-6 flex justify-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleFavoriteClick}
|
||||
title={isFavorite ? localize('com_ui_unpin') : localize('com_ui_pin')}
|
||||
aria-label={isFavorite ? localize('com_ui_unpin') : localize('com_ui_pin')}
|
||||
>
|
||||
{isFavorite ? <PinOff className="h-4 w-4" /> : <Pin className="h-4 w-4" />}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleCopyLink}
|
||||
title={localize('com_agents_copy_link')}
|
||||
aria-label={localize('com_agents_copy_link')}
|
||||
>
|
||||
<Link className="h-4 w-4" aria-hidden="true" />
|
||||
</Button>
|
||||
<TooltipAnchor
|
||||
description={isFavorite ? localize('com_ui_unpin') : localize('com_ui_pin')}
|
||||
render={
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleFavoriteClick}
|
||||
aria-label={isFavorite ? localize('com_ui_unpin') : localize('com_ui_pin')}
|
||||
>
|
||||
{isFavorite ? <PinOff className="h-4 w-4" /> : <Pin className="h-4 w-4" />}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<TooltipAnchor
|
||||
description={localize('com_agents_copy_link')}
|
||||
render={
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleCopyLink}
|
||||
aria-label={localize('com_agents_copy_link')}
|
||||
>
|
||||
<Link className="h-4 w-4" aria-hidden="true" />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
variant="submit"
|
||||
className="w-full max-w-xs"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Link, Pin, PinOff } from 'lucide-react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { OGDialogContent, Button, useToastContext } from '@librechat/client';
|
||||
import { OGDialogContent, Button, TooltipAnchor, useToastContext } from '@librechat/client';
|
||||
import {
|
||||
QueryKeys,
|
||||
Constants,
|
||||
|
|
@ -119,24 +119,32 @@ const AgentDetailContent: React.FC<AgentDetailContentProps> = ({ agent }) => {
|
|||
|
||||
{/* Action button */}
|
||||
<div className="mb-4 mt-6 flex justify-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleFavoriteClick}
|
||||
title={isFavorite ? localize('com_ui_unpin') : localize('com_ui_pin')}
|
||||
aria-label={isFavorite ? localize('com_ui_unpin') : localize('com_ui_pin')}
|
||||
>
|
||||
{isFavorite ? <PinOff className="h-4 w-4" /> : <Pin className="h-4 w-4" />}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleCopyLink}
|
||||
title={localize('com_agents_copy_link')}
|
||||
aria-label={localize('com_agents_copy_link')}
|
||||
>
|
||||
<Link className="h-4 w-4" aria-hidden="true" />
|
||||
</Button>
|
||||
<TooltipAnchor
|
||||
description={isFavorite ? localize('com_ui_unpin') : localize('com_ui_pin')}
|
||||
render={
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleFavoriteClick}
|
||||
aria-label={isFavorite ? localize('com_ui_unpin') : localize('com_ui_pin')}
|
||||
>
|
||||
{isFavorite ? <PinOff className="h-4 w-4" /> : <Pin className="h-4 w-4" />}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<TooltipAnchor
|
||||
description={localize('com_agents_copy_link')}
|
||||
render={
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleCopyLink}
|
||||
aria-label={localize('com_agents_copy_link')}
|
||||
>
|
||||
<Link className="h-4 w-4" aria-hidden="true" />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
variant="submit"
|
||||
className="w-full max-w-xs"
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ jest.mock(
|
|||
Button: ({ children, ...props }: React.ButtonHTMLAttributes<HTMLButtonElement>) => (
|
||||
<button {...props}>{children}</button>
|
||||
),
|
||||
TooltipAnchor: ({ render }: { render: React.ReactNode }) => render,
|
||||
useToastContext: () => ({
|
||||
showToast: jest.fn(),
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -283,18 +283,18 @@ export default function Settings({
|
|||
</HoverCard>
|
||||
<div className="w-full">
|
||||
<div className="mb-2 flex w-full justify-between gap-2">
|
||||
<label
|
||||
<Label
|
||||
htmlFor="resend-files"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
<small>{localize('com_endpoint_plug_resend_files')}</small>
|
||||
</label>
|
||||
<label
|
||||
</Label>
|
||||
<Label
|
||||
htmlFor="image-detail-value"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
<small>{localize('com_endpoint_plug_image_detail')}</small>
|
||||
</label>
|
||||
</Label>
|
||||
<Input
|
||||
id="image-detail-value"
|
||||
disabled={true}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import filenamify from 'filenamify';
|
||||
import { useEffect, useState, useMemo, useCallback } from 'react';
|
||||
import filenamify from 'filenamify';
|
||||
import {
|
||||
Input,
|
||||
Label,
|
||||
|
|
@ -126,7 +126,7 @@ export default function ExportModal({
|
|||
onCheckedChange={setIncludeOptions}
|
||||
aria-labelledby="includeOptions-label"
|
||||
/>
|
||||
<label
|
||||
<Label
|
||||
id="includeOptions-label"
|
||||
htmlFor="includeOptions"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
|
|
@ -134,7 +134,7 @@ export default function ExportModal({
|
|||
{exportOptionsSupport
|
||||
? localize('com_nav_export_include_endpoint_options')
|
||||
: localize('com_nav_not_supported')}
|
||||
</label>
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -150,7 +150,7 @@ export default function ExportModal({
|
|||
onCheckedChange={setExportBranches}
|
||||
aria-labelledby="exportBranches-label"
|
||||
/>
|
||||
<label
|
||||
<Label
|
||||
id="exportBranches-label"
|
||||
htmlFor="exportBranches"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
|
|
@ -158,7 +158,7 @@ export default function ExportModal({
|
|||
{exportBranchesSupport
|
||||
? localize('com_nav_export_all_message_branches')
|
||||
: localize('com_nav_not_supported')}
|
||||
</label>
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
{type === 'json' ? (
|
||||
|
|
@ -173,13 +173,13 @@ export default function ExportModal({
|
|||
onCheckedChange={setRecursive}
|
||||
aria-labelledby="recursive-label"
|
||||
/>
|
||||
<label
|
||||
<Label
|
||||
id="recursive-label"
|
||||
htmlFor="recursive"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
{localize('com_nav_export_recursive')}
|
||||
</label>
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { useState, memo, useRef, useCallback, useId, useMemo } from 'react';
|
||||
import * as Ariakit from '@ariakit/react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Ellipsis, Eye, SquarePen, Trash, EarthIcon, User } from 'lucide-react';
|
||||
import { PermissionBits, ResourceType } from 'librechat-data-provider';
|
||||
import type { TPromptGroup } from 'librechat-data-provider';
|
||||
import { Ellipsis, Eye, SquarePen, Trash, EarthIcon, User } from 'lucide-react';
|
||||
import {
|
||||
Label,
|
||||
Button,
|
||||
|
|
@ -14,12 +13,13 @@ import {
|
|||
OGDialogTemplate,
|
||||
useToastContext,
|
||||
} from '@librechat/client';
|
||||
import type { TPromptGroup } from 'librechat-data-provider';
|
||||
import { useLocalize, useAuthContext, useSubmitMessage, useResourcePermissions } from '~/hooks';
|
||||
import { useRecordPromptUsage, useDeletePromptGroup } from '~/data-provider';
|
||||
import { useLiveAnnouncer } from '~/Providers';
|
||||
import VariableDialog from '../dialogs/VariableDialog';
|
||||
import PreviewPrompt from '../dialogs/PreviewPrompt';
|
||||
import CategoryIcon from '../utils/CategoryIcon';
|
||||
import { useLiveAnnouncer } from '~/Providers';
|
||||
import { detectVariables, cn } from '~/utils';
|
||||
|
||||
const PROMPT_PATH = '/prompts';
|
||||
|
|
@ -157,9 +157,15 @@ function ChatGroupItem({
|
|||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="truncate text-sm font-semibold text-text-primary" title={group.name}>
|
||||
{group.name}
|
||||
</span>
|
||||
<TooltipAnchor
|
||||
description={group.name}
|
||||
side="top"
|
||||
render={
|
||||
<span className="truncate text-sm font-semibold text-text-primary">
|
||||
{group.name}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
{isSharedPrompt && (
|
||||
<TooltipAnchor
|
||||
description={localize('com_ui_by_author', { 0: group.authorName })}
|
||||
|
|
|
|||
|
|
@ -380,31 +380,35 @@ export default function GenericGrantAccessDialog({
|
|||
<div className="flex justify-between pt-4">
|
||||
<div className="flex gap-2">
|
||||
{resourceId && resourceUrl && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
if (isCopying) return;
|
||||
copyResourceUrl(setIsCopying);
|
||||
showToast({
|
||||
message: localize('com_ui_agent_url_copied'),
|
||||
status: 'success',
|
||||
});
|
||||
}}
|
||||
disabled={isCopying}
|
||||
className={cn('shrink-0', isCopying ? 'cursor-default' : '')}
|
||||
aria-label={localize('com_ui_copy_url_to_clipboard')}
|
||||
title={
|
||||
<TooltipAnchor
|
||||
description={
|
||||
isCopying
|
||||
? config?.getCopyUrlMessage()
|
||||
: localize('com_ui_copy_url_to_clipboard')
|
||||
}
|
||||
>
|
||||
{isCopying ? (
|
||||
<CopyCheck className="h-4 w-4" aria-hidden="true" />
|
||||
) : (
|
||||
<Link className="h-4 w-4" aria-hidden="true" />
|
||||
)}
|
||||
</Button>
|
||||
render={
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
if (isCopying) return;
|
||||
copyResourceUrl(setIsCopying);
|
||||
showToast({
|
||||
message: localize('com_ui_agent_url_copied'),
|
||||
status: 'success',
|
||||
});
|
||||
}}
|
||||
disabled={isCopying}
|
||||
className={cn('shrink-0', isCopying ? 'cursor-default' : '')}
|
||||
aria-label={localize('com_ui_copy_url_to_clipboard')}
|
||||
>
|
||||
{isCopying ? (
|
||||
<CopyCheck className="h-4 w-4" aria-hidden="true" />
|
||||
) : (
|
||||
<Link className="h-4 w-4" aria-hidden="true" />
|
||||
)}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
Label,
|
||||
Input,
|
||||
Spinner,
|
||||
Textarea,
|
||||
useToastContext,
|
||||
} from '@librechat/client';
|
||||
import { useCreateMemoryMutation } from '~/data-provider';
|
||||
|
|
@ -129,7 +130,7 @@ export default function MemoryCreateDialog({
|
|||
<Label htmlFor="memory-value" className="text-sm font-medium text-text-primary">
|
||||
{localize('com_ui_value')}
|
||||
</Label>
|
||||
<textarea
|
||||
<Textarea
|
||||
id="memory-value"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
Label,
|
||||
Input,
|
||||
Spinner,
|
||||
Textarea,
|
||||
useToastContext,
|
||||
} from '@librechat/client';
|
||||
import type { TUserMemory } from 'librechat-data-provider';
|
||||
|
|
@ -200,7 +201,7 @@ export default function MemoryEditDialog({
|
|||
<Label htmlFor="memory-value" className="text-sm font-medium text-text-primary">
|
||||
{localize('com_ui_value')}
|
||||
</Label>
|
||||
<textarea
|
||||
<Textarea
|
||||
id="memory-value"
|
||||
value={value}
|
||||
onChange={(e) => hasUpdateAccess && setValue(e.target.value)}
|
||||
|
|
|
|||
|
|
@ -1464,6 +1464,7 @@
|
|||
"com_ui_new_project": "New project",
|
||||
"com_ui_next": "Next",
|
||||
"com_ui_next_result": "Next result",
|
||||
"com_ui_next_sibling": "Next sibling message",
|
||||
"com_ui_no": "No",
|
||||
"com_ui_no_auth": "None (Auto-detect)",
|
||||
"com_ui_no_bookmarks": "it seems like you have no bookmarks yet. Click on a chat and add a new one",
|
||||
|
|
@ -1487,6 +1488,7 @@
|
|||
"com_ui_no_skills_found": "No skills found",
|
||||
"com_ui_no_terms_content": "No terms and conditions content to display",
|
||||
"com_ui_none": "None",
|
||||
"com_ui_none_selected": "None selected",
|
||||
"com_ui_not_used": "Not Used",
|
||||
"com_ui_nothing_found": "Nothing found",
|
||||
"com_ui_oauth": "OAuth",
|
||||
|
|
@ -1541,6 +1543,7 @@
|
|||
"com_ui_preview_failed": "Preview unavailable",
|
||||
"com_ui_preview_preparing": "Preparing preview…",
|
||||
"com_ui_preview_unavailable": "Preview not available for this file type",
|
||||
"com_ui_previous_sibling": "Previous sibling message",
|
||||
"com_ui_privacy_policy": "Privacy policy",
|
||||
"com_ui_privacy_policy_url": "Privacy Policy URL",
|
||||
"com_ui_pro": "Pro",
|
||||
|
|
@ -1686,6 +1689,8 @@
|
|||
"com_ui_saving": "Saving...",
|
||||
"com_ui_schema": "Schema",
|
||||
"com_ui_scope": "Scope",
|
||||
"com_ui_scroll_left": "Scroll left",
|
||||
"com_ui_scroll_right": "Scroll right",
|
||||
"com_ui_scroll_to_bottom": "Scroll to bottom",
|
||||
"com_ui_search": "Search",
|
||||
"com_ui_search_above_to_add": "Search above to add users or groups",
|
||||
|
|
@ -1717,6 +1722,7 @@
|
|||
"com_ui_select_search_model": "Search model by name",
|
||||
"com_ui_select_search_provider": "Search provider by name",
|
||||
"com_ui_select_search_region": "Search region by name",
|
||||
"com_ui_select_var": "Select {{0}}",
|
||||
"com_ui_send_now": "Send now",
|
||||
"com_ui_set": "Set",
|
||||
"com_ui_settings_label_2fa": "Two-factor authentication",
|
||||
|
|
@ -1819,6 +1825,7 @@
|
|||
"com_ui_show_more": "Show more",
|
||||
"com_ui_show_n_files": "Show {{0}} files",
|
||||
"com_ui_show_qr": "Show QR Code",
|
||||
"com_ui_sibling_navigation": "Sibling message navigation",
|
||||
"com_ui_sign_in_to_domain": "Sign-in to {{0}}",
|
||||
"com_ui_size": "Size",
|
||||
"com_ui_size_sort": "Sort by Size",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue