mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-07 02:58:50 +01:00
🌎 i18n: React-i18next & i18next Integration (#5720)
* better i18n support an internationalization-framework. * removed unused package * auto sort for translation.json * fixed tests with the new locales function * added new CI actions from locize * to use locize a mention in the README.md * to use locize a mention in the README.md * updated README.md and added TRANSLATION.md to the repo * updated TRANSLATION.md badges * updated README.md to go to the TRANSLATION.md when clicking on the Translation Progress badge * updated TRANSLATION.md and added a new issue template. * updated TRANSLATION.md and added a new issue template. * updated issue template to add the iso code link. * updated the new GitHub actions for `locize` * updated label for new issue template --> i18n * fixed type issue * Fix eslint * Fix eslint with key-spacing spacing * fix: error type * fix: handle undefined values in SortFilterHeader component * fix: typing in Image component * fix: handle optional promptGroup in PromptCard component * fix: update localize function to accept string type and remove unnecessary JSX element * fix: update localize function to enforce TranslationKeys type for better type safety * fix: improve type safety and handle null values in Assistants component * fix: enhance null checks for fileId in FilesListView component * fix: localize 'Go back' button text in FilesListView component * fix: update aria-label for menu buttons and add translation for 'Close Menu' * docs: add Reasoning UI section for Chain-of-Thought AI models in README * fix: enhance type safety by adding type for message in MultiMessage component * fix: improve null checks and optional chaining in useAutoSave hook * fix: improve handling of optional properties in cleanupPreset function * fix: ensure isFetchingNextPage defaults to false and improve null checks for messages in Search component * fix: enhance type safety and null checks in useBuildMessageTree hook --------- Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
parent
2e8d969e35
commit
aae413cc71
153 changed files with 13448 additions and 38224 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { useLocalize } from '~/hooks';
|
||||
import { TranslationKeys, useLocalize } from '~/hooks';
|
||||
import { BlinkAnimation } from './BlinkAnimation';
|
||||
import { TStartupConfig } from 'librechat-data-provider';
|
||||
import SocialLoginRender from './SocialLoginRender';
|
||||
|
|
@ -33,7 +33,7 @@ function AuthLayout({
|
|||
startupConfig: TStartupConfig | null | undefined;
|
||||
startupConfigError: unknown | null | undefined;
|
||||
pathname: string;
|
||||
error: string | null;
|
||||
error: TranslationKeys | null;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ function AuthLayout({
|
|||
<img
|
||||
src="/assets/logo.svg"
|
||||
className="h-full w-full object-contain"
|
||||
alt={localize('com_ui_logo', startupConfig?.appTitle ?? 'LibreChat')}
|
||||
alt={localize('com_ui_logo', { 0: startupConfig?.appTitle ?? 'LibreChat' })}
|
||||
/>
|
||||
</div>
|
||||
</BlinkAnimation>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import type { TRegisterUser, TError } from 'librechat-data-provider';
|
|||
import type { TLoginLayoutContext } from '~/common';
|
||||
import { ErrorMessage } from './ErrorMessage';
|
||||
import { Spinner } from '~/components/svg';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { useLocalize, TranslationKeys } from '~/hooks';
|
||||
|
||||
const Registration: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
|
|
@ -56,7 +56,7 @@ const Registration: React.FC = () => {
|
|||
},
|
||||
});
|
||||
|
||||
const renderInput = (id: string, label: string, type: string, validation: object) => (
|
||||
const renderInput = (id: string, label: TranslationKeys, type: string, validation: object) => (
|
||||
<div className="mb-4">
|
||||
<div className="relative">
|
||||
<input
|
||||
|
|
@ -114,7 +114,7 @@ const Registration: React.FC = () => {
|
|||
: 'com_auth_registration_success_insecure',
|
||||
) +
|
||||
' ' +
|
||||
localize('com_auth_email_verification_redirecting', countdown.toString())}
|
||||
localize('com_auth_email_verification_redirecting', { 0: countdown.toString() })}
|
||||
</div>
|
||||
)}
|
||||
{!startupConfigError && !isFetching && (
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ function RequestPasswordReset() {
|
|||
</h1>
|
||||
{countdown > 0 && (
|
||||
<p className="text-center text-lg text-gray-600 dark:text-gray-400">
|
||||
{localize('com_auth_email_verification_redirecting', countdown.toString())}
|
||||
{localize('com_auth_email_verification_redirecting', { 0: countdown.toString() })}
|
||||
</p>
|
||||
)}
|
||||
{showResendLink && countdown === 0 && (
|
||||
|
|
|
|||
|
|
@ -220,8 +220,10 @@ export default function DataTable<TData, TValue>({ columns, data }: DataTablePro
|
|||
<span className="hidden sm:inline">
|
||||
{localize(
|
||||
'com_files_number_selected',
|
||||
`${table.getFilteredSelectedRowModel().rows.length}`,
|
||||
`${table.getFilteredRowModel().rows.length}`,
|
||||
{
|
||||
0: `${table.getFilteredSelectedRowModel().rows.length}`,
|
||||
1: `${table.getFilteredRowModel().rows.length}`,
|
||||
},
|
||||
)}
|
||||
</span>
|
||||
<span className="sm:hidden">
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
DropdownMenuTrigger,
|
||||
} from '~/components/ui/DropdownMenu';
|
||||
import { Button } from '~/components/ui/Button';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import { useLocalize, TranslationKeys } from '~/hooks';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
interface SortFilterHeaderProps<TData, TValue> extends React.HTMLAttributes<HTMLDivElement> {
|
||||
|
|
@ -78,9 +78,12 @@ export function SortFilterHeader<TData, TValue>({
|
|||
<DropdownMenuSeparator className="dark:bg-gray-500" />
|
||||
{filters &&
|
||||
Object.entries(filters).map(([key, values]) =>
|
||||
values.map((value: string | number) => {
|
||||
const localizedValue = localize(valueMap?.[value] ?? '');
|
||||
const filterValue = localizedValue.length ? localizedValue : valueMap?.[value];
|
||||
values.map((value?: string | number) => {
|
||||
const translationKey = valueMap?.[value ?? ''];
|
||||
const filterValue =
|
||||
translationKey != null && translationKey.length
|
||||
? localize(translationKey as TranslationKeys)
|
||||
: String(value);
|
||||
if (!filterValue) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ export default function PopoverButtons({
|
|||
const endpoint = overrideEndpoint ?? endpointType ?? _endpoint ?? '';
|
||||
const model = overrideModel ?? _model;
|
||||
|
||||
const isGenerativeModel = model?.toLowerCase()?.includes('gemini') ?? false;
|
||||
const isChatModel = (!isGenerativeModel && model?.toLowerCase()?.includes('chat')) ?? false;
|
||||
const isGenerativeModel = model?.toLowerCase().includes('gemini') ?? false;
|
||||
const isChatModel = (!isGenerativeModel && model?.toLowerCase().includes('chat')) ?? false;
|
||||
const isTextModel = !isGenerativeModel && !isChatModel && /code|text/.test(model ?? '');
|
||||
|
||||
const { showExamples } = optionSettings;
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ const scaleImage = ({
|
|||
originalHeight,
|
||||
containerRef,
|
||||
}: {
|
||||
originalWidth: number;
|
||||
originalHeight: number;
|
||||
originalWidth?: number;
|
||||
originalHeight?: number;
|
||||
containerRef: React.RefObject<HTMLDivElement>;
|
||||
}) => {
|
||||
const containerWidth = containerRef.current?.offsetWidth ?? 0;
|
||||
if (containerWidth === 0 || originalWidth === undefined || originalHeight === undefined) {
|
||||
if (containerWidth === 0 || originalWidth == null || originalHeight == null) {
|
||||
return { width: 'auto', height: 'auto' };
|
||||
}
|
||||
const aspectRatio = originalWidth / originalHeight;
|
||||
|
|
@ -35,8 +35,8 @@ const Image = ({
|
|||
height: number;
|
||||
width: number;
|
||||
placeholderDimensions?: {
|
||||
height: string;
|
||||
width: string;
|
||||
height?: string;
|
||||
width?: string;
|
||||
};
|
||||
}) => {
|
||||
const [isLoaded, setIsLoaded] = useState(false);
|
||||
|
|
@ -47,8 +47,8 @@ const Image = ({
|
|||
const { width: scaledWidth, height: scaledHeight } = useMemo(
|
||||
() =>
|
||||
scaleImage({
|
||||
originalWidth: Number(placeholderDimensions?.width?.split('px')[0]) ?? width,
|
||||
originalHeight: Number(placeholderDimensions?.height?.split('px')[0]) ?? height,
|
||||
originalWidth: Number(placeholderDimensions?.width?.split('px')[0] ?? width),
|
||||
originalHeight: Number(placeholderDimensions?.height?.split('px')[0] ?? height),
|
||||
containerRef,
|
||||
}),
|
||||
[placeholderDimensions, height, width],
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ const LogContent: React.FC<LogContentProps> = ({ output = '', renderImages, atta
|
|||
}
|
||||
|
||||
// const expirationText = expiresAt
|
||||
// ? ` ${localize('com_download_expires', format(expiresAt, 'MM/dd/yy HH:mm'))}`
|
||||
// ? ` ${localize('com_download_expires', { 0: format(expiresAt, 'MM/dd/yy HH:mm') })}`
|
||||
// : ` ${localize('com_click_to_download')}`;
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -106,12 +106,12 @@ export default function ToolCall({
|
|||
|
||||
const getFinishedText = () => {
|
||||
if (isMCPToolCall === true) {
|
||||
return localize('com_assistants_completed_function', function_name);
|
||||
return localize('com_assistants_completed_function', { 0: function_name });
|
||||
}
|
||||
if (domain != null && domain && domain.length !== Constants.ENCODED_DOMAIN_LENGTH) {
|
||||
return localize('com_assistants_completed_action', domain);
|
||||
return localize('com_assistants_completed_action', { 0: domain });
|
||||
}
|
||||
return localize('com_assistants_completed_function', function_name);
|
||||
return localize('com_assistants_completed_function', { 0: function_name });
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ export default function ToolPopover({
|
|||
<div className="bg-token-surface-primary max-w-sm rounded-md p-2 shadow-[0_0_24px_0_rgba(0,0,0,0.05),inset_0_0.5px_0_0_rgba(0,0,0,0.05),0_2px_8px_0_rgba(0,0,0,0.05)]">
|
||||
<div className="mb-2 text-sm font-medium dark:text-gray-100">
|
||||
{domain != null && domain
|
||||
? localize('com_assistants_domain_info', domain)
|
||||
: localize('com_assistants_function_use', function_name)}
|
||||
? localize('com_assistants_domain_info', { 0: domain })
|
||||
: localize('com_assistants_function_use', { 0: function_name })}
|
||||
</div>
|
||||
<div className="bg-token-surface-secondary text-token-text-primary dark rounded-md text-xs">
|
||||
<div className="max-h-32 overflow-y-auto rounded-md p-2 dark:bg-gray-700">
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { TPromptGroup } from 'librechat-data-provider';
|
||||
import CategoryIcon from '~/components/Prompts/Groups/CategoryIcon';
|
||||
|
||||
export default function PromptCard({ promptGroup }: { promptGroup: TPromptGroup }) {
|
||||
export default function PromptCard({ promptGroup }: { promptGroup?: TPromptGroup }) {
|
||||
return (
|
||||
<div className="hover:bg-token-main-surface-secondary relative flex w-40 cursor-pointer flex-col gap-2 rounded-2xl border px-3 pb-4 pt-3 text-start align-top text-[15px] shadow-[0_0_2px_0_rgba(0,0,0,0.05),0_4px_6px_0_rgba(0,0,0,0.02)] transition-colors duration-300 ease-in-out fade-in hover:bg-slate-100 dark:border-gray-600 dark:hover:bg-gray-700">
|
||||
<div className="">
|
||||
<CategoryIcon className="size-4" category={promptGroup.category || ''} />
|
||||
<CategoryIcon className="size-4" category={promptGroup?.category ?? ''} />
|
||||
</div>
|
||||
<p className="break-word line-clamp-3 text-balance text-gray-600 dark:text-gray-400">
|
||||
{promptGroup?.oneliner || promptGroup?.productionPrompt?.prompt}
|
||||
{(promptGroup?.oneliner ?? '') || promptGroup?.productionPrompt?.prompt}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useMemo, memo } from 'react';
|
||||
import { parseISO, isToday } from 'date-fns';
|
||||
import { TConversation } from 'librechat-data-provider';
|
||||
import { useLocalize, TranslationKeys } from '~/hooks';
|
||||
import { groupConversationsByDate } from '~/utils';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import Convo from './Convo';
|
||||
|
||||
const Conversations = ({
|
||||
|
|
@ -41,8 +41,7 @@ const Conversations = ({
|
|||
paddingLeft: '10px',
|
||||
}}
|
||||
>
|
||||
{/* eslint-disable-next-line @typescript-eslint/strict-boolean-expressions */}
|
||||
{localize(groupName) || groupName}
|
||||
{localize(groupName as TranslationKeys) || groupName}
|
||||
</div>
|
||||
{convos.map((convo, i) => (
|
||||
<Convo
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
HoverCardContent,
|
||||
} from '~/components/ui';
|
||||
import OptionHover from '~/components/SidePanel/Parameters/OptionHover';
|
||||
import { useLocalize, useNavigateToConvo } from '~/hooks';
|
||||
import { TranslationKeys, useLocalize, useNavigateToConvo } from '~/hooks';
|
||||
import { useForkConvoMutation } from '~/data-provider';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import { ESide } from '~/common';
|
||||
|
|
@ -201,7 +201,7 @@ export default function Fork({
|
|||
align="center"
|
||||
>
|
||||
<div className="flex h-6 w-full items-center justify-center text-sm dark:text-gray-200">
|
||||
{localize(activeSetting)}
|
||||
{localize(activeSetting as TranslationKeys)}
|
||||
<HoverCard openDelay={50}>
|
||||
<HoverCardTrigger asChild>
|
||||
<InfoIcon className="ml-auto flex h-4 w-4 gap-2 text-gray-500 dark:text-white/50" />
|
||||
|
|
@ -216,7 +216,9 @@ export default function Fork({
|
|||
<span>{localize('com_ui_fork_info_1')}</span>
|
||||
<span>{localize('com_ui_fork_info_2')}</span>
|
||||
<span>
|
||||
{localize('com_ui_fork_info_3', localize('com_ui_fork_split_target'))}
|
||||
{localize('com_ui_fork_info_3', {
|
||||
0: localize('com_ui_fork_split_target'),
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
|
|
@ -233,7 +235,7 @@ export default function Fork({
|
|||
hoverTitle={
|
||||
<>
|
||||
<GitCommit className="h-5 w-5 rotate-90" />
|
||||
{localize(optionLabels[ForkOptions.DIRECT_PATH])}
|
||||
{localize(optionLabels[ForkOptions.DIRECT_PATH] as TranslationKeys)}
|
||||
</>
|
||||
}
|
||||
hoverDescription={localize('com_ui_fork_info_visible')}
|
||||
|
|
@ -251,7 +253,7 @@ export default function Fork({
|
|||
hoverTitle={
|
||||
<>
|
||||
<GitBranchPlus className="h-4 w-4 rotate-180" />
|
||||
{localize(optionLabels[ForkOptions.INCLUDE_BRANCHES])}
|
||||
{localize(optionLabels[ForkOptions.INCLUDE_BRANCHES] as TranslationKeys)}
|
||||
</>
|
||||
}
|
||||
hoverDescription={localize('com_ui_fork_info_branches')}
|
||||
|
|
@ -269,9 +271,9 @@ export default function Fork({
|
|||
hoverTitle={
|
||||
<>
|
||||
<ListTree className="h-5 w-5" />
|
||||
{`${localize(optionLabels[ForkOptions.TARGET_LEVEL])} (${localize(
|
||||
'com_endpoint_default',
|
||||
)})`}
|
||||
{`${localize(
|
||||
optionLabels[ForkOptions.TARGET_LEVEL] as TranslationKeys,
|
||||
)} (${localize('com_endpoint_default')})`}
|
||||
</>
|
||||
}
|
||||
hoverDescription={localize('com_ui_fork_info_target')}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ export default function Settings({
|
|||
<Label htmlFor="temp-int" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_temperature')}{' '}
|
||||
<small className="opacity-40">
|
||||
({localize('com_endpoint_default_with_num', '1')})
|
||||
({localize('com_endpoint_default_with_num', { 0: '1' })})
|
||||
</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
|
|
|
|||
|
|
@ -27,12 +27,12 @@ export default function Settings({ conversation, setOption, models, readonly }:
|
|||
conversation ?? {};
|
||||
|
||||
const currentList = useMemo(
|
||||
() => Object.values(assistantListMap?.[endpoint ?? ''] ?? {}) as Assistant[],
|
||||
() => Object.values(assistantListMap[endpoint ?? ''] ?? {}) as Assistant[],
|
||||
[assistantListMap, endpoint],
|
||||
);
|
||||
|
||||
const assistants = useMemo(() => {
|
||||
const currentAssistants = (currentList ?? []).map(({ id, name }) => ({
|
||||
const currentAssistants = currentList.map(({ id, name }) => ({
|
||||
label: name,
|
||||
value: id,
|
||||
}));
|
||||
|
|
@ -52,8 +52,8 @@ export default function Settings({ conversation, setOption, models, readonly }:
|
|||
});
|
||||
|
||||
const activeAssistant = useMemo(() => {
|
||||
if (assistant_id) {
|
||||
return assistantListMap[endpoint ?? '']?.[assistant_id];
|
||||
if (assistant_id != null && assistant_id) {
|
||||
return assistantListMap[endpoint ?? '']?.[assistant_id] as Assistant | null;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
@ -70,11 +70,13 @@ export default function Settings({ conversation, setOption, models, readonly }:
|
|||
}, [models, activeAssistant, localize]);
|
||||
|
||||
const [assistantValue, setAssistantValue] = useState<Option>(
|
||||
activeAssistant ? { label: activeAssistant.name, value: activeAssistant.id } : defaultOption,
|
||||
activeAssistant != null
|
||||
? { label: activeAssistant.name ?? '', value: activeAssistant.id }
|
||||
: defaultOption,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (assistantValue && assistantValue.value === '') {
|
||||
if (assistantValue.value === '') {
|
||||
setOption('presetOverride')({
|
||||
assistant_id: assistantValue.value,
|
||||
} as Partial<TPreset>);
|
||||
|
|
@ -95,7 +97,7 @@ export default function Settings({ conversation, setOption, models, readonly }:
|
|||
return;
|
||||
}
|
||||
|
||||
const assistant = assistantListMap[endpoint ?? '']?.[value];
|
||||
const assistant = assistantListMap[endpoint ?? '']?.[value] as Assistant | null;
|
||||
if (!assistant) {
|
||||
setAssistantValue(defaultOption);
|
||||
return;
|
||||
|
|
@ -103,7 +105,7 @@ export default function Settings({ conversation, setOption, models, readonly }:
|
|||
|
||||
setAssistantValue({
|
||||
label: assistant.name ?? '',
|
||||
value: assistant.id ?? '',
|
||||
value: assistant.id || '',
|
||||
});
|
||||
setOption('assistant_id')(assistant.id);
|
||||
if (assistant.model) {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ function Examples({ readonly, examples, setExample, addExample, removeExample }:
|
|||
<TextareaAutosize
|
||||
id={`input-${idx}`}
|
||||
disabled={readonly}
|
||||
value={example?.input?.content || ''}
|
||||
value={example.input.content || ''}
|
||||
onChange={(e) => setExample(idx, 'input', e.target.value ?? null)}
|
||||
placeholder="Set example input. Example is ignored if empty."
|
||||
className={cn(
|
||||
|
|
@ -62,7 +62,7 @@ function Examples({ readonly, examples, setExample, addExample, removeExample }:
|
|||
<TextareaAutosize
|
||||
id={`output-${idx}`}
|
||||
disabled={readonly}
|
||||
value={example?.output?.content || ''}
|
||||
value={example.output.content || ''}
|
||||
onChange={(e) => setExample(idx, 'output', e.target.value ?? null)}
|
||||
placeholder={'Set example output. Example is ignored if empty.'}
|
||||
className={cn(
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ export default function Settings({ conversation, setOption, models, readonly }:
|
|||
<Label htmlFor="top-p-int" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_top_p')}{' '}
|
||||
<small className="opacity-40">
|
||||
({localize('com_endpoint_default_with_num', google.topP.default + '')})
|
||||
({localize('com_endpoint_default_with_num', { 0: google.topP.default + '' })})
|
||||
</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
|
|
@ -221,7 +221,7 @@ export default function Settings({ conversation, setOption, models, readonly }:
|
|||
<Label htmlFor="top-k-int" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_top_k')}{' '}
|
||||
<small className="opacity-40">
|
||||
({localize('com_endpoint_default_with_num', google.topK.default + '')})
|
||||
({localize('com_endpoint_default_with_num',{ 0: google.topK.default + '' })})
|
||||
</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
|
|
@ -261,7 +261,7 @@ export default function Settings({ conversation, setOption, models, readonly }:
|
|||
<Label htmlFor="max-tokens-int" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_max_output_tokens')}{' '}
|
||||
<small className="opacity-40">
|
||||
({localize('com_endpoint_default_with_num', google.maxOutputTokens.default + '')})
|
||||
({localize('com_endpoint_default_with_num', { 0: google.maxOutputTokens.default + '' })})
|
||||
</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ export default function Settings({
|
|||
<Label htmlFor="temp-int" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_temperature')}{' '}
|
||||
<small className="opacity-40">
|
||||
({localize('com_endpoint_default_with_num', '0.8')})
|
||||
({localize('com_endpoint_default_with_num', { 0: '0.8' })})
|
||||
</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
|
|
@ -266,7 +266,7 @@ export default function Settings({
|
|||
<Label htmlFor="top-p-int" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_top_p')}{' '}
|
||||
<small className="opacity-40">
|
||||
({localize('com_endpoint_default_with_num', '1')})
|
||||
({localize('com_endpoint_default_with_num', { 0: '1' })})
|
||||
</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
|
|
@ -307,7 +307,7 @@ export default function Settings({
|
|||
<Label htmlFor="freq-penalty-int" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_frequency_penalty')}{' '}
|
||||
<small className="opacity-40">
|
||||
({localize('com_endpoint_default_with_num', '0')})
|
||||
({localize('com_endpoint_default_with_num', { 0: '0' })})
|
||||
</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
|
|
@ -348,7 +348,7 @@ export default function Settings({
|
|||
<Label htmlFor="pres-penalty-int" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_presence_penalty')}{' '}
|
||||
<small className="opacity-40">
|
||||
({localize('com_endpoint_default_with_num', '0')})
|
||||
({localize('com_endpoint_default_with_num', { 0: '0' })})
|
||||
</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ const FileDashboardView = () => {
|
|||
return (
|
||||
<div className="bg-[#f9f9f9] p-0 lg:p-7">
|
||||
<div className="ml-3 mt-3 flex flex-row justify-between">
|
||||
{params?.vectorStoreId && (
|
||||
{params.vectorStoreId && (
|
||||
<Button
|
||||
className="block lg:hidden"
|
||||
variant={'outline'}
|
||||
|
|
|
|||
|
|
@ -244,9 +244,10 @@ export default function DataTableFile<TData, TValue>({
|
|||
<div className="ml-4 mr-4 mt-4 flex h-auto items-center justify-end space-x-2 py-4 sm:ml-0 sm:mr-0 sm:h-0">
|
||||
<div className="text-muted-foreground ml-2 flex-1 text-sm">
|
||||
{localize(
|
||||
'com_files_number_selected',
|
||||
`${table.getFilteredSelectedRowModel().rows.length}`,
|
||||
`${table.getFilteredRowModel().rows.length}`,
|
||||
'com_files_number_selected', {
|
||||
0: `${table.getFilteredSelectedRowModel().rows.length}`,
|
||||
1: `${table.getFilteredRowModel().rows.length}`,
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -2,16 +2,18 @@ import React from 'react';
|
|||
import FileSidePanel from './FileList/FileSidePanel';
|
||||
import { Outlet, useNavigate, useParams } from 'react-router-dom';
|
||||
import FilesSectionSelector from './FilesSectionSelector';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { Button } from '../ui';
|
||||
|
||||
export default function FilesListView() {
|
||||
const params = useParams();
|
||||
const navigate = useNavigate();
|
||||
const localize = useLocalize();
|
||||
return (
|
||||
<div className="bg-[#f9f9f9] p-0 lg:p-7">
|
||||
<div className="m-4 flex w-full flex-row justify-between md:m-2">
|
||||
<FilesSectionSelector />
|
||||
{params?.fileId && (
|
||||
{params.fileId != null && params.fileId && (
|
||||
<Button
|
||||
className="block lg:hidden"
|
||||
variant={'outline'}
|
||||
|
|
@ -20,21 +22,21 @@ export default function FilesListView() {
|
|||
navigate('/d/files');
|
||||
}}
|
||||
>
|
||||
Go back
|
||||
{localize('com_ui_go_back')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex w-full flex-row divide-x">
|
||||
<div
|
||||
className={`mr-2 w-full xl:w-1/3 ${
|
||||
params.fileId ? 'hidden w-1/2 lg:block lg:w-1/2' : 'md:w-full'
|
||||
params.fileId != null && params.fileId ? 'hidden w-1/2 lg:block lg:w-1/2' : 'md:w-full'
|
||||
}`}
|
||||
>
|
||||
<FileSidePanel />
|
||||
</div>
|
||||
<div
|
||||
className={`h-[85vh] w-full overflow-y-auto xl:w-2/3 ${
|
||||
params.fileId ? 'lg:w-1/2' : 'hidden md:w-1/2 lg:block'
|
||||
params.fileId != null && params.fileId ? 'lg:w-1/2' : 'hidden md:w-1/2 lg:block'
|
||||
}`}
|
||||
>
|
||||
<Outlet />
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ export default function VectorStorePreview() {
|
|||
<Clock3 className="text-base text-gray-500 md:text-lg lg:text-xl" />
|
||||
Created At
|
||||
</span>
|
||||
<span className="w-1/2 text-gray-500 md:w-3/5">{vectorStore.createdAt?.toString()}</span>
|
||||
<span className="w-1/2 text-gray-500 md:w-3/5">{vectorStore.createdAt.toString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export default function ModelSelect({
|
|||
}
|
||||
|
||||
const { endpoint: _endpoint, endpointType } = conversation;
|
||||
const models = modelsQuery?.data?.[_endpoint] ?? [];
|
||||
const models = modelsQuery.data?.[_endpoint] ?? [];
|
||||
const endpoint = endpointType ?? _endpoint;
|
||||
|
||||
const OptionComponent = multiChatOptions[endpoint];
|
||||
|
|
|
|||
|
|
@ -47,11 +47,11 @@ const errorMessages = {
|
|||
[ErrorTypes.NO_SYSTEM_MESSAGES]: `com_error_${ErrorTypes.NO_SYSTEM_MESSAGES}`,
|
||||
[ErrorTypes.EXPIRED_USER_KEY]: (json: TExpiredKey, localize: LocalizeFunction) => {
|
||||
const { expiredAt, endpoint } = json;
|
||||
return localize('com_error_expired_user_key', endpoint, expiredAt);
|
||||
return localize('com_error_expired_user_key', { 0: endpoint, 1: expiredAt });
|
||||
},
|
||||
[ErrorTypes.INPUT_LENGTH]: (json: TGenericError, localize: LocalizeFunction) => {
|
||||
const { info } = json;
|
||||
return localize('com_error_input_length', info);
|
||||
return localize('com_error_input_length', { 0: info });
|
||||
},
|
||||
[ErrorTypes.GOOGLE_ERROR]: (json: TGenericError) => {
|
||||
const { info } = json;
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ function Avatar() {
|
|||
const megabytes =
|
||||
fileConfig.avatarSizeLimit != null ? formatBytes(fileConfig.avatarSizeLimit) : 2;
|
||||
showToast({
|
||||
message: localize('com_ui_upload_invalid_var', megabytes + ''),
|
||||
message: localize('com_ui_upload_invalid_var', { 0: megabytes + '' }),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export const RevokeKeysButton = ({
|
|||
|
||||
const dialogTitle = all
|
||||
? localize('com_ui_revoke_keys')
|
||||
: localize('com_ui_revoke_key_endpoint', endpoint);
|
||||
: localize('com_ui_revoke_key_endpoint', { 0: endpoint });
|
||||
|
||||
const dialogMessage = all
|
||||
? localize('com_ui_revoke_keys_confirm')
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export default function DecibelSelector() {
|
|||
<div className="flex items-center justify-between">
|
||||
<div>{localize('com_nav_db_sensitivity')}</div>
|
||||
<div className="w-2" />
|
||||
<small className="opacity-40">({localize('com_endpoint_default_with_num', '-45')})</small>
|
||||
<small className="opacity-40">({localize('com_endpoint_default_with_num', { 0: '-45' })})</small>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<Slider
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export default function DecibelSelector() {
|
|||
<div className="flex items-center justify-between">
|
||||
<div>{localize('com_nav_playback_rate')}</div>
|
||||
<div className="w-2" />
|
||||
<small className="opacity-40">({localize('com_endpoint_default_with_num', '1')})</small>
|
||||
<small className="opacity-40">({localize('com_endpoint_default_with_num', { 0: '1' })})</small>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<Slider
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ const DeleteVersion = ({
|
|||
htmlFor="dialog-delete-confirm-prompt"
|
||||
className="text-left text-sm font-medium"
|
||||
>
|
||||
{localize('com_ui_delete_confirm_prompt_version_var', name)}
|
||||
{localize('com_ui_delete_confirm_prompt_version_var', { 0: name })}
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ export default function VariableForm({
|
|||
return (
|
||||
<InputCombobox
|
||||
options={field.config.options || []}
|
||||
placeholder={localize('com_ui_enter_var', field.config.variable)}
|
||||
placeholder={localize('com_ui_enter_var', { 0: field.config.variable })}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'rounded px-3 py-2 focus:bg-surface-tertiary',
|
||||
|
|
@ -192,7 +192,7 @@ export default function VariableForm({
|
|||
defaultTextProps,
|
||||
'rounded px-3 py-2 focus:bg-surface-tertiary',
|
||||
)}
|
||||
placeholder={localize('com_ui_enter_var', field.config.variable)}
|
||||
placeholder={localize('com_ui_enter_var', { 0: field.config.variable })}
|
||||
maxRows={8}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ const PromptForm = () => {
|
|||
|
||||
const selectedPrompt = useMemo(
|
||||
() => (prompts.length > 0 ? prompts[selectionIndex] : undefined),
|
||||
[prompts, /* eslint-disable-line react-hooks/exhaustive-deps */ selectionIndex],
|
||||
[prompts, selectionIndex],
|
||||
);
|
||||
|
||||
const { groupsQuery } = useOutletContext<ReturnType<typeof usePromptGroupsNav>>();
|
||||
|
|
@ -102,7 +102,7 @@ const PromptForm = () => {
|
|||
);
|
||||
},
|
||||
onSuccess(data) {
|
||||
if (alwaysMakeProd && data.prompt._id && data.prompt.groupId) {
|
||||
if (alwaysMakeProd && data.prompt._id != null && data.prompt._id && data.prompt.groupId) {
|
||||
makeProductionMutation.mutate({
|
||||
id: data.prompt._id,
|
||||
groupId: data.prompt.groupId,
|
||||
|
|
@ -336,7 +336,7 @@ const PromptForm = () => {
|
|||
variant="ghost"
|
||||
className="h-10 w-10 border border-border-light p-0 lg:hidden"
|
||||
onClick={() => setShowSidePanel(true)}
|
||||
aria-label={localize('com_ui_open_menu')}
|
||||
aria-label={localize('com_endpoint_open_menu')}
|
||||
>
|
||||
<Menu className="size-5" />
|
||||
</Button>
|
||||
|
|
@ -382,8 +382,8 @@ const PromptForm = () => {
|
|||
onClick={() => setShowSidePanel(false)}
|
||||
aria-hidden={!showSidePanel}
|
||||
tabIndex={showSidePanel ? 0 : -1}
|
||||
aria-label={localize('com_ui_close_menu')}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="absolute inset-y-0 right-0 z-50 lg:hidden"
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -111,12 +111,12 @@ const VersionCard = ({
|
|||
onClick={onClick}
|
||||
aria-selected={isSelected}
|
||||
role="tab"
|
||||
aria-label={localize('com_ui_version_var', `${totalVersions - index}`)}
|
||||
aria-label={localize('com_ui_version_var', { 0: `${totalVersions - index}` })}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-start justify-between lg:flex-col xl:flex-row">
|
||||
<h3 className="font-bold text-text-primary">
|
||||
{localize('com_ui_version_var', `${totalVersions - index}`)}
|
||||
{localize('com_ui_version_var', { 0: `${totalVersions - index}` })}
|
||||
</h3>
|
||||
<time className="text-xs text-text-secondary" dateTime={prompt.createdAt}>
|
||||
{format(new Date(prompt.createdAt), 'yyyy-MM-dd HH:mm')}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ const SharePrompt = ({ group, disabled }: { group?: TPromptGroup; disabled: bool
|
|||
</OGDialogTrigger>
|
||||
<OGDialogContent className="w-11/12 max-w-lg" role="dialog" aria-labelledby="dialog-title">
|
||||
<OGDialogTitle id="dialog-title" className="truncate pr-2" title={group.name}>
|
||||
{localize('com_ui_share_var', `"${group.name}"`)}
|
||||
{localize('com_ui_share_var', { 0: `"${group.name}"` })}
|
||||
</OGDialogTitle>
|
||||
<form className="p-2" onSubmit={handleSubmit(onSubmit)} aria-describedby="form-description">
|
||||
<div id="form-description" className="sr-only">
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export default function MessagesView({
|
|||
}}
|
||||
>
|
||||
<div className="flex flex-col pb-9 text-sm dark:bg-transparent">
|
||||
{(_messagesTree && _messagesTree?.length == 0) || _messagesTree === null ? (
|
||||
{(_messagesTree && _messagesTree.length == 0) || _messagesTree === null ? (
|
||||
<div className="flex w-full items-center justify-center gap-1 bg-gray-50 p-3 text-sm text-gray-500 dark:border-gray-800/50 dark:bg-gray-800 dark:text-gray-300">
|
||||
Nothing found
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import type { TMessage } from 'librechat-data-provider';
|
||||
import type { TMessageProps } from '~/common';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import Message from './Message';
|
||||
|
|
@ -25,17 +26,16 @@ export default function MultiMessage({
|
|||
}, [messagesTree?.length]);
|
||||
|
||||
useEffect(() => {
|
||||
if (messagesTree?.length && siblingIdx >= messagesTree?.length) {
|
||||
if (messagesTree?.length != null && siblingIdx >= messagesTree.length) {
|
||||
setSiblingIdx(0);
|
||||
}
|
||||
}, [siblingIdx, messagesTree?.length, setSiblingIdx]);
|
||||
|
||||
if (!(messagesTree && messagesTree?.length)) {
|
||||
if (!(messagesTree && messagesTree.length)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const message = messagesTree[messagesTree.length - siblingIdx - 1];
|
||||
|
||||
const message = messagesTree[messagesTree.length - siblingIdx - 1] as TMessage | null;
|
||||
if (!message) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ function Avatar({
|
|||
} else {
|
||||
const megabytes = sizeLimit ? formatBytes(sizeLimit) : 2;
|
||||
showToast({
|
||||
message: localize('com_ui_upload_invalid_var', megabytes + ''),
|
||||
message: localize('com_ui_upload_invalid_var', { 0: megabytes + '' }),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export default function FileSearchCheckbox() {
|
|||
type="button"
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() =>
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
|
||||
setValue(AgentCapabilities.file_search, !getValues(AgentCapabilities.file_search), {
|
||||
shouldDirty: true,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export default function ImageVision() {
|
|||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
value={field.value?.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ export default function Parameters({
|
|||
className="btn btn-neutral flex w-full items-center justify-center gap-2 px-4 py-2 text-sm"
|
||||
>
|
||||
<RotateCcw className="h-4 w-4" aria-hidden="true" />
|
||||
{localize('com_ui_reset_var', localize('com_ui_model_parameters'))}
|
||||
{localize('com_ui_reset_var', { 0: localize('com_ui_model_parameters') })}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export default function Retrieval({ retrievalModels }: { retrievalModels: Set<st
|
|||
disabled={isDisabled}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
value={field.value?.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export default function HideSequential() {
|
|||
type="button"
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() =>
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
|
||||
setValue(
|
||||
AgentCapabilities.hide_sequential_outputs,
|
||||
!getValues(AgentCapabilities.hide_sequential_outputs),
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ export default function ShareAgent({
|
|||
)}
|
||||
aria-label={localize(
|
||||
'com_ui_share_var',
|
||||
agentName != null && agentName !== '' ? `"${agentName}"` : localize('com_ui_agent'),
|
||||
{ 0: agentName != null && agentName !== '' ? `"${agentName}"` : localize('com_ui_agent') },
|
||||
)}
|
||||
type="button"
|
||||
>
|
||||
|
|
@ -150,7 +150,7 @@ export default function ShareAgent({
|
|||
<OGDialogTitle>
|
||||
{localize(
|
||||
'com_ui_share_var',
|
||||
agentName != null && agentName !== '' ? `"${agentName}"` : localize('com_ui_agent'),
|
||||
{ 0: agentName != null && agentName !== '' ? `"${agentName}"` : localize('com_ui_agent') },
|
||||
)}
|
||||
</OGDialogTitle>
|
||||
<form
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ function Avatar({
|
|||
} else {
|
||||
const megabytes = fileConfig.avatarSizeLimit ? formatBytes(fileConfig.avatarSizeLimit) : 2;
|
||||
showToast({
|
||||
message: localize('com_ui_upload_invalid_var', megabytes + ''),
|
||||
message: localize('com_ui_upload_invalid_var', { 0: megabytes + '' }),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export default function ImageVision() {
|
|||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
value={field.value.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export default function Retrieval({
|
|||
disabled={isDisabled}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
value={field.value.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { OptionTypes } from 'librechat-data-provider';
|
|||
import type { DynamicSettingProps } from 'librechat-data-provider';
|
||||
import { Label, Input, HoverCard, HoverCardTrigger, Tag } from '~/components/ui';
|
||||
import { useChatContext, useToastContext } from '~/Providers';
|
||||
import { useLocalize, useParameterEffects } from '~/hooks';
|
||||
import { TranslationKeys, useLocalize, useParameterEffects } from '~/hooks';
|
||||
import { cn, defaultTextProps } from '~/utils';
|
||||
import OptionHover from './OptionHover';
|
||||
import { ESide } from '~/common';
|
||||
|
|
@ -75,7 +75,7 @@ function DynamicTags({
|
|||
|
||||
if (minTags != null && currentTags.length <= minTags) {
|
||||
showToast({
|
||||
message: localize('com_ui_min_tags', minTags + ''),
|
||||
message: localize('com_ui_min_tags',{ 0: minTags + '' }),
|
||||
status: 'warning',
|
||||
});
|
||||
return;
|
||||
|
|
@ -94,7 +94,7 @@ function DynamicTags({
|
|||
let update = [...(currentTags ?? []), tagText];
|
||||
if (maxTags != null && update.length > maxTags) {
|
||||
showToast({
|
||||
message: localize('com_ui_max_tags', maxTags + ''),
|
||||
message: localize('com_ui_max_tags', { 0: maxTags + '' }),
|
||||
status: 'warning',
|
||||
});
|
||||
update = update.slice(-maxTags);
|
||||
|
|
@ -126,7 +126,7 @@ function DynamicTags({
|
|||
htmlFor={`${settingKey}-dynamic-input`}
|
||||
className="text-left text-sm font-medium"
|
||||
>
|
||||
{labelCode ? localize(label) ?? label : label || settingKey}{' '}
|
||||
{labelCode ? localize(label as TranslationKeys) ?? label : label || settingKey}{' '}
|
||||
{showDefault && (
|
||||
<small className="opacity-40">
|
||||
(
|
||||
|
|
@ -174,7 +174,7 @@ function DynamicTags({
|
|||
}
|
||||
}}
|
||||
onChange={(e) => setTagText(e.target.value)}
|
||||
placeholder={placeholderCode ? localize(placeholder) ?? placeholder : placeholder}
|
||||
placeholder={placeholderCode ? localize(placeholder as TranslationKeys) ?? placeholder : placeholder}
|
||||
className={cn('flex h-10 max-h-10 border-none bg-surface-secondary px-3 py-2')}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -182,7 +182,7 @@ function DynamicTags({
|
|||
</HoverCardTrigger>
|
||||
{description && (
|
||||
<OptionHover
|
||||
description={descriptionCode ? localize(description) ?? description : description}
|
||||
description={descriptionCode ? localize(description as TranslationKeys) ?? description : description}
|
||||
side={descriptionSide as ESide}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ function ToolItem({ tool, onAddTool, onRemoveTool, isInstalled = false }: ToolIt
|
|||
{tool.icon != null && tool.icon ? (
|
||||
<img
|
||||
src={tool.icon}
|
||||
alt={localize('com_ui_logo', tool.name)}
|
||||
alt={localize('com_ui_logo', { 0: tool.name })}
|
||||
className="h-full w-full rounded-[5px] bg-white"
|
||||
/>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable indent */
|
||||
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
export default function AzureMinimalIcon({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue