mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 08:50:15 +01:00
🔄 feat: chat direction (LTR-RTL) (#3260)
* feat: chat direction * fix: FileRow * feat: smooth trigger transition
This commit is contained in:
parent
d5782ac66c
commit
237a0de8b6
31 changed files with 145 additions and 111 deletions
|
|
@ -4,16 +4,19 @@ import { ListeningIcon, Spinner } from '~/components/svg';
|
||||||
import { useLocalize, useSpeechToText } from '~/hooks';
|
import { useLocalize, useSpeechToText } from '~/hooks';
|
||||||
import { useChatFormContext } from '~/Providers';
|
import { useChatFormContext } from '~/Providers';
|
||||||
import { globalAudioId } from '~/common';
|
import { globalAudioId } from '~/common';
|
||||||
|
import { cn } from '~/utils';
|
||||||
|
|
||||||
export default function AudioRecorder({
|
export default function AudioRecorder({
|
||||||
textAreaRef,
|
textAreaRef,
|
||||||
methods,
|
methods,
|
||||||
ask,
|
ask,
|
||||||
|
isRTL,
|
||||||
disabled,
|
disabled,
|
||||||
}: {
|
}: {
|
||||||
textAreaRef: React.RefObject<HTMLTextAreaElement>;
|
textAreaRef: React.RefObject<HTMLTextAreaElement>;
|
||||||
methods: ReturnType<typeof useChatFormContext>;
|
methods: ReturnType<typeof useChatFormContext>;
|
||||||
ask: (data: { text: string }) => void;
|
ask: (data: { text: string }) => void;
|
||||||
|
isRTL: boolean;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
}) {
|
}) {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
|
|
@ -77,7 +80,12 @@ export default function AudioRecorder({
|
||||||
<button
|
<button
|
||||||
onClick={isListening ? handleStopRecording : handleStartRecording}
|
onClick={isListening ? handleStopRecording : handleStartRecording}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className="absolute bottom-1.5 right-12 flex h-[30px] w-[30px] items-center justify-center rounded-lg p-0.5 transition-colors hover:bg-gray-200 dark:hover:bg-gray-700 md:bottom-3 md:right-12"
|
className={cn(
|
||||||
|
'absolute flex h-[30px] w-[30px] items-center justify-center rounded-lg p-0.5 transition-colors hover:bg-gray-200 dark:hover:bg-gray-700',
|
||||||
|
isRTL
|
||||||
|
? 'bottom-1.5 left-4 md:bottom-3 md:left-12'
|
||||||
|
: 'bottom-1.5 right-12 md:bottom-3 md:right-12',
|
||||||
|
)}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
{renderIcon()}
|
{renderIcon()}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,9 @@ const ChatForm = ({ index = 0 }) => {
|
||||||
store.showMentionPopoverFamily(index),
|
store.showMentionPopoverFamily(index),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const chatDirection = useRecoilValue(store.chatDirection).toLowerCase();
|
||||||
|
const isRTL = chatDirection === 'rtl';
|
||||||
|
|
||||||
const { requiresKey } = useRequiresKey();
|
const { requiresKey } = useRequiresKey();
|
||||||
const handleKeyUp = useHandleKeyUp({
|
const handleKeyUp = useHandleKeyUp({
|
||||||
index,
|
index,
|
||||||
|
|
@ -149,6 +152,7 @@ const ChatForm = ({ index = 0 }) => {
|
||||||
files={files}
|
files={files}
|
||||||
setFiles={setFiles}
|
setFiles={setFiles}
|
||||||
setFilesLoading={setFilesLoading}
|
setFilesLoading={setFilesLoading}
|
||||||
|
isRTL={isRTL}
|
||||||
Wrapper={({ children }) => (
|
Wrapper={({ children }) => (
|
||||||
<div className="mx-2 mt-2 flex flex-wrap gap-2 px-2.5 md:pl-0 md:pr-4">
|
<div className="mx-2 mt-2 flex flex-wrap gap-2 px-2.5 md:pl-0 md:pr-4">
|
||||||
{children}
|
{children}
|
||||||
|
|
@ -179,7 +183,7 @@ const ChatForm = ({ index = 0 }) => {
|
||||||
? ' pl-10 md:pl-[55px]'
|
? ' pl-10 md:pl-[55px]'
|
||||||
: 'pl-3 md:pl-4',
|
: 'pl-3 md:pl-4',
|
||||||
'm-0 w-full resize-none border-0 bg-transparent py-[10px] placeholder-black/50 focus:ring-0 focus-visible:ring-0 dark:bg-transparent dark:placeholder-white/50 md:py-3.5 ',
|
'm-0 w-full resize-none border-0 bg-transparent py-[10px] placeholder-black/50 focus:ring-0 focus-visible:ring-0 dark:bg-transparent dark:placeholder-white/50 md:py-3.5 ',
|
||||||
SpeechToText ? 'pr-20 md:pr-[85px]' : 'pr-10 md:pr-12',
|
SpeechToText && !isRTL ? 'pr-20 md:pr-[85px]' : 'pr-10 md:pr-12',
|
||||||
'max-h-[65vh] md:max-h-[75vh]',
|
'max-h-[65vh] md:max-h-[75vh]',
|
||||||
removeFocusRings,
|
removeFocusRings,
|
||||||
)}
|
)}
|
||||||
|
|
@ -188,15 +192,21 @@ const ChatForm = ({ index = 0 }) => {
|
||||||
<AttachFile
|
<AttachFile
|
||||||
endpoint={_endpoint ?? ''}
|
endpoint={_endpoint ?? ''}
|
||||||
endpointType={endpointType}
|
endpointType={endpointType}
|
||||||
|
isRTL={isRTL}
|
||||||
disabled={disableInputs}
|
disabled={disableInputs}
|
||||||
/>
|
/>
|
||||||
{(isSubmitting || isSubmittingAdded) && (showStopButton || showStopAdded) ? (
|
{(isSubmitting || isSubmittingAdded) && (showStopButton || showStopAdded) ? (
|
||||||
<StopButton stop={handleStopGenerating} setShowStopButton={setShowStopButton} />
|
<StopButton
|
||||||
|
stop={handleStopGenerating}
|
||||||
|
setShowStopButton={setShowStopButton}
|
||||||
|
isRTL={isRTL}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
endpoint && (
|
endpoint && (
|
||||||
<SendButton
|
<SendButton
|
||||||
ref={submitButtonRef}
|
ref={submitButtonRef}
|
||||||
control={methods.control}
|
control={methods.control}
|
||||||
|
isRTL={isRTL}
|
||||||
disabled={!!(filesLoading || isSubmitting || disableInputs)}
|
disabled={!!(filesLoading || isSubmitting || disableInputs)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
@ -206,6 +216,7 @@ const ChatForm = ({ index = 0 }) => {
|
||||||
disabled={!!disableInputs}
|
disabled={!!disableInputs}
|
||||||
textAreaRef={textAreaRef}
|
textAreaRef={textAreaRef}
|
||||||
ask={submitMessage}
|
ask={submitMessage}
|
||||||
|
isRTL={isRTL}
|
||||||
methods={methods}
|
methods={methods}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,17 @@ import { useGetFileConfig } from '~/data-provider';
|
||||||
import { AttachmentIcon } from '~/components/svg';
|
import { AttachmentIcon } from '~/components/svg';
|
||||||
import { FileUpload } from '~/components/ui';
|
import { FileUpload } from '~/components/ui';
|
||||||
import { useFileHandling } from '~/hooks';
|
import { useFileHandling } from '~/hooks';
|
||||||
|
import { cn } from '~/utils';
|
||||||
|
|
||||||
const AttachFile = ({
|
const AttachFile = ({
|
||||||
endpoint,
|
endpoint,
|
||||||
endpointType,
|
endpointType,
|
||||||
|
isRTL,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
}: {
|
}: {
|
||||||
endpoint: EModelEndpoint | '';
|
endpoint: EModelEndpoint | '';
|
||||||
endpointType?: EModelEndpoint;
|
endpointType?: EModelEndpoint;
|
||||||
|
isRTL: boolean;
|
||||||
disabled?: boolean | null;
|
disabled?: boolean | null;
|
||||||
}) => {
|
}) => {
|
||||||
const { handleFileChange } = useFileHandling();
|
const { handleFileChange } = useFileHandling();
|
||||||
|
|
@ -30,7 +33,14 @@ const AttachFile = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="absolute bottom-2 left-2 md:bottom-3 md:left-4">
|
<div
|
||||||
|
className={cn(
|
||||||
|
'absolute',
|
||||||
|
isRTL
|
||||||
|
? 'bottom-2 right-14 md:bottom-3.5 md:right-3'
|
||||||
|
: 'bottom-2 left-2 md:bottom-3.5 md:left-4',
|
||||||
|
)}
|
||||||
|
>
|
||||||
<FileUpload handleFileChange={handleFileChange} className="flex">
|
<FileUpload handleFileChange={handleFileChange} className="flex">
|
||||||
<button
|
<button
|
||||||
disabled={!!disabled}
|
disabled={!!disabled}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ export default function FileRow({
|
||||||
assistant_id,
|
assistant_id,
|
||||||
tool_resource,
|
tool_resource,
|
||||||
fileFilter,
|
fileFilter,
|
||||||
|
isRTL,
|
||||||
Wrapper,
|
Wrapper,
|
||||||
}: {
|
}: {
|
||||||
files: Map<string, ExtendedFile>;
|
files: Map<string, ExtendedFile>;
|
||||||
|
|
@ -21,6 +22,7 @@ export default function FileRow({
|
||||||
fileFilter?: (file: ExtendedFile) => boolean;
|
fileFilter?: (file: ExtendedFile) => boolean;
|
||||||
assistant_id?: string;
|
assistant_id?: string;
|
||||||
tool_resource?: EToolResources;
|
tool_resource?: EToolResources;
|
||||||
|
isRTL?: boolean;
|
||||||
Wrapper?: React.FC<{ children: React.ReactNode }>;
|
Wrapper?: React.FC<{ children: React.ReactNode }>;
|
||||||
}) {
|
}) {
|
||||||
const files = Array.from(_files.values()).filter((file) =>
|
const files = Array.from(_files.values()).filter((file) =>
|
||||||
|
|
@ -64,8 +66,11 @@ export default function FileRow({
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderFiles = () => {
|
const renderFiles = () => {
|
||||||
|
// Inline style for RTL
|
||||||
|
const rowStyle = isRTL ? { display: 'flex', flexDirection: 'row-reverse' } : {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div style={rowStyle as React.CSSProperties}>
|
||||||
{files
|
{files
|
||||||
.reduce(
|
.reduce(
|
||||||
(acc, current) => {
|
(acc, current) => {
|
||||||
|
|
@ -90,10 +95,9 @@ export default function FileRow({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <FileContainer key={index} file={file} onDelete={handleDelete} />;
|
return <FileContainer key={index} file={file} onDelete={handleDelete} />;
|
||||||
})}
|
})}
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,42 +9,48 @@ import { cn } from '~/utils';
|
||||||
type SendButtonProps = {
|
type SendButtonProps = {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
control: Control<{ text: string }>;
|
control: Control<{ text: string }>;
|
||||||
|
isRTL: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SubmitButton = React.memo(
|
const SubmitButton = React.memo(
|
||||||
forwardRef((props: { disabled: boolean }, ref: React.ForwardedRef<HTMLButtonElement>) => {
|
forwardRef(
|
||||||
const localize = useLocalize();
|
(props: { disabled: boolean; isRTL: boolean }, ref: React.ForwardedRef<HTMLButtonElement>) => {
|
||||||
return (
|
const localize = useLocalize();
|
||||||
<TooltipProvider delayDuration={250}>
|
return (
|
||||||
<Tooltip>
|
<TooltipProvider delayDuration={250}>
|
||||||
<TooltipTrigger asChild>
|
<Tooltip>
|
||||||
<button
|
<TooltipTrigger asChild>
|
||||||
ref={ref}
|
<button
|
||||||
disabled={props.disabled}
|
ref={ref}
|
||||||
className={cn(
|
disabled={props.disabled}
|
||||||
'absolute bottom-1.5 right-2 rounded-lg border border-black p-0.5 text-white transition-colors enabled:bg-black disabled:bg-black disabled:text-gray-400 disabled:opacity-10 dark:border-white dark:bg-white dark:disabled:bg-white md:bottom-3 md:right-3',
|
className={cn(
|
||||||
)}
|
'absolute rounded-lg border border-black p-0.5 text-white transition-colors enabled:bg-black disabled:bg-black disabled:text-gray-400 disabled:opacity-10 dark:border-white dark:bg-white dark:disabled:bg-white',
|
||||||
data-testid="send-button"
|
props.isRTL
|
||||||
type="submit"
|
? 'bottom-1.5 left-2 md:bottom-3 md:left-3'
|
||||||
>
|
: 'bottom-1.5 right-2 md:bottom-3 md:right-3',
|
||||||
<span className="" data-state="closed">
|
)}
|
||||||
<SendIcon size={24} />
|
data-testid="send-button"
|
||||||
</span>
|
type="submit"
|
||||||
</button>
|
>
|
||||||
</TooltipTrigger>
|
<span className="" data-state="closed">
|
||||||
<TooltipContent side="top" sideOffset={10}>
|
<SendIcon size={24} />
|
||||||
{localize('com_nav_send_message')}
|
</span>
|
||||||
</TooltipContent>
|
</button>
|
||||||
</Tooltip>
|
</TooltipTrigger>
|
||||||
</TooltipProvider>
|
<TooltipContent side="top" sideOffset={10}>
|
||||||
);
|
{localize('com_nav_send_message')}
|
||||||
}),
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const SendButton = React.memo(
|
const SendButton = React.memo(
|
||||||
forwardRef((props: SendButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) => {
|
forwardRef((props: SendButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) => {
|
||||||
const data = useWatch({ control: props.control });
|
const data = useWatch({ control: props.control });
|
||||||
return <SubmitButton ref={ref} disabled={props.disabled || !data?.text} />;
|
return <SubmitButton ref={ref} disabled={props.disabled || !data?.text} isRTL={props.isRTL} />;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
export default function StopButton({ stop, setShowStopButton }) {
|
import { cn } from '~/utils';
|
||||||
|
|
||||||
|
export default function StopButton({ stop, setShowStopButton, isRTL }) {
|
||||||
return (
|
return (
|
||||||
<div className="absolute bottom-3 right-2 md:bottom-4 md:right-4">
|
<div
|
||||||
|
className={cn(
|
||||||
|
'absolute',
|
||||||
|
isRTL ? 'bottom-3 left-2 md:bottom-4 md:left-4' : 'bottom-3 right-2 md:bottom-4 md:right-4',
|
||||||
|
)}
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="border-gizmo-gray-900 rounded-full border-2 p-1 dark:border-gray-200"
|
className="border-gizmo-gray-900 rounded-full border-2 p-1 dark:border-gray-200"
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import {
|
||||||
TransitionChild,
|
TransitionChild,
|
||||||
} from '@headlessui/react';
|
} from '@headlessui/react';
|
||||||
import { GearIcon, DataIcon, SpeechIcon, UserIcon, ExperimentIcon } from '~/components/svg';
|
import { GearIcon, DataIcon, SpeechIcon, UserIcon, ExperimentIcon } from '~/components/svg';
|
||||||
import { General, Messages, Speech, Beta, Data, Account } from './SettingsTabs';
|
import { General, Chat, Speech, Beta, Data, Account } from './SettingsTabs';
|
||||||
import { useMediaQuery, useLocalize } from '~/hooks';
|
import { useMediaQuery, useLocalize } from '~/hooks';
|
||||||
import { cn } from '~/utils';
|
import { cn } from '~/utils';
|
||||||
|
|
||||||
|
|
@ -100,7 +100,7 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
||||||
>
|
>
|
||||||
<Tabs.Trigger
|
<Tabs.Trigger
|
||||||
className={cn(
|
className={cn(
|
||||||
'group m-1 flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-black radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600',
|
'group m-1 flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-black transition-all duration-200 ease-in-out radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600',
|
||||||
isSmallScreen
|
isSmallScreen
|
||||||
? 'flex-1 items-center justify-center text-nowrap text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
? 'flex-1 items-center justify-center text-nowrap text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
||||||
: 'bg-white radix-state-active:bg-gray-200',
|
: 'bg-white radix-state-active:bg-gray-200',
|
||||||
|
|
@ -114,21 +114,21 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
||||||
</Tabs.Trigger>
|
</Tabs.Trigger>
|
||||||
<Tabs.Trigger
|
<Tabs.Trigger
|
||||||
className={cn(
|
className={cn(
|
||||||
'group m-1 flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-black radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600',
|
'group m-1 flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-black transition-all duration-200 ease-in-out radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600',
|
||||||
isSmallScreen
|
isSmallScreen
|
||||||
? 'flex-1 items-center justify-center text-nowrap text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
? 'flex-1 items-center justify-center text-nowrap text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
||||||
: 'bg-white radix-state-active:bg-gray-200',
|
: 'bg-white radix-state-active:bg-gray-200',
|
||||||
isSmallScreen ? '' : 'dark:bg-gray-700',
|
isSmallScreen ? '' : 'dark:bg-gray-700',
|
||||||
)}
|
)}
|
||||||
value={SettingsTabValues.MESSAGES}
|
value={SettingsTabValues.CHAT}
|
||||||
style={{ userSelect: 'none' }}
|
style={{ userSelect: 'none' }}
|
||||||
>
|
>
|
||||||
<MessageSquare className="icon-sm" />
|
<MessageSquare className="icon-sm" />
|
||||||
{localize('com_endpoint_messages')}
|
{localize('com_nav_setting_chat')}
|
||||||
</Tabs.Trigger>
|
</Tabs.Trigger>
|
||||||
<Tabs.Trigger
|
<Tabs.Trigger
|
||||||
className={cn(
|
className={cn(
|
||||||
'group m-1 flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-black radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600',
|
'group m-1 flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-black transition-all duration-200 ease-in-out radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600',
|
||||||
isSmallScreen
|
isSmallScreen
|
||||||
? 'flex-1 items-center justify-center text-nowrap text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
? 'flex-1 items-center justify-center text-nowrap text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
||||||
: 'bg-white radix-state-active:bg-gray-200',
|
: 'bg-white radix-state-active:bg-gray-200',
|
||||||
|
|
@ -142,7 +142,7 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
||||||
</Tabs.Trigger>
|
</Tabs.Trigger>
|
||||||
<Tabs.Trigger
|
<Tabs.Trigger
|
||||||
className={cn(
|
className={cn(
|
||||||
'group m-1 flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-black radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600',
|
'group m-1 flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-black transition-all duration-200 ease-in-out radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600',
|
||||||
isSmallScreen
|
isSmallScreen
|
||||||
? 'flex-1 items-center justify-center text-nowrap text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
? 'flex-1 items-center justify-center text-nowrap text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
||||||
: 'bg-white radix-state-active:bg-gray-200',
|
: 'bg-white radix-state-active:bg-gray-200',
|
||||||
|
|
@ -156,7 +156,7 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
||||||
</Tabs.Trigger>
|
</Tabs.Trigger>
|
||||||
<Tabs.Trigger
|
<Tabs.Trigger
|
||||||
className={cn(
|
className={cn(
|
||||||
'group m-1 flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-black radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600',
|
'group m-1 flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-black transition-all duration-200 ease-in-out radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600',
|
||||||
isSmallScreen
|
isSmallScreen
|
||||||
? 'flex-1 items-center justify-center text-nowrap text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
? 'flex-1 items-center justify-center text-nowrap text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
||||||
: 'bg-white radix-state-active:bg-gray-200',
|
: 'bg-white radix-state-active:bg-gray-200',
|
||||||
|
|
@ -170,7 +170,7 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
||||||
</Tabs.Trigger>
|
</Tabs.Trigger>
|
||||||
<Tabs.Trigger
|
<Tabs.Trigger
|
||||||
className={cn(
|
className={cn(
|
||||||
'group m-1 flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-black radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600',
|
'group m-1 flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-black transition-all duration-200 ease-in-out radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600',
|
||||||
isSmallScreen
|
isSmallScreen
|
||||||
? 'flex-1 items-center justify-center text-nowrap text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
? 'flex-1 items-center justify-center text-nowrap text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
||||||
: 'bg-white radix-state-active:bg-gray-200',
|
: 'bg-white radix-state-active:bg-gray-200',
|
||||||
|
|
@ -185,7 +185,7 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
<div className="max-h-[373px] overflow-auto sm:w-full sm:max-w-none md:pr-0.5 md:pt-0.5">
|
<div className="max-h-[373px] overflow-auto sm:w-full sm:max-w-none md:pr-0.5 md:pt-0.5">
|
||||||
<General />
|
<General />
|
||||||
<Messages />
|
<Chat />
|
||||||
<Beta />
|
<Beta />
|
||||||
<Speech />
|
<Speech />
|
||||||
<Data />
|
<Data />
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,19 @@ import { SettingsTabValues } from 'librechat-data-provider';
|
||||||
import SendMessageKeyEnter from './EnterToSend';
|
import SendMessageKeyEnter from './EnterToSend';
|
||||||
import ShowCodeSwitch from './ShowCodeSwitch';
|
import ShowCodeSwitch from './ShowCodeSwitch';
|
||||||
import { ForkSettings } from './ForkSettings';
|
import { ForkSettings } from './ForkSettings';
|
||||||
|
import ChatDirection from './ChatDirection';
|
||||||
import SaveDraft from './SaveDraft';
|
import SaveDraft from './SaveDraft';
|
||||||
|
|
||||||
function Messages() {
|
function Chat() {
|
||||||
return (
|
return (
|
||||||
<Tabs.Content value={SettingsTabValues.MESSAGES} role="tabpanel" className="md: w-full">
|
<Tabs.Content value={SettingsTabValues.CHAT} role="tabpanel" className="md: w-full">
|
||||||
<div className="flex flex-col gap-3 text-sm text-black dark:text-gray-50">
|
<div className="flex flex-col gap-3 text-sm text-black dark:text-gray-50">
|
||||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||||
<SendMessageKeyEnter />
|
<SendMessageKeyEnter />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||||
|
<ChatDirection />
|
||||||
|
</div>
|
||||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||||
<ShowCodeSwitch />
|
<ShowCodeSwitch />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -25,4 +29,4 @@ function Messages() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(Messages);
|
export default memo(Chat);
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
import { useLocalize } from '~/hooks';
|
||||||
|
import store from '~/store';
|
||||||
|
|
||||||
|
const ChatDirection = () => {
|
||||||
|
const [direction, setDirection] = useRecoilState(store.chatDirection);
|
||||||
|
const localize = useLocalize();
|
||||||
|
|
||||||
|
const toggleChatDirection = () => {
|
||||||
|
setDirection((prev) => (prev === 'LTR' ? 'RTL' : 'LTR'));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<span>{localize('com_nav_chat_direction')}</span>
|
||||||
|
</div>
|
||||||
|
<label
|
||||||
|
onClick={toggleChatDirection}
|
||||||
|
data-testid="chatDirection"
|
||||||
|
className="btn btn-neutral relative"
|
||||||
|
style={{ userSelect: 'none' }}
|
||||||
|
>
|
||||||
|
{direction.toLowerCase()}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatDirection;
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
export { default as General } from './General/General';
|
export { default as General } from './General/General';
|
||||||
export { default as Messages } from './Messages/Messages';
|
export { default as Chat } from './Chat/Chat';
|
||||||
export { ClearChatsButton } from './General/General';
|
export { ClearChatsButton } from './General/General';
|
||||||
export { default as Data } from './Data/Data';
|
export { default as Data } from './Data/Data';
|
||||||
export { default as Beta } from './Beta/Beta';
|
export { default as Beta } from './Beta/Beta';
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
import { forwardRef, useLayoutEffect, useState } from 'react';
|
import { forwardRef, useLayoutEffect, useState } from 'react';
|
||||||
import ReactTextareaAutosize from 'react-textarea-autosize';
|
import ReactTextareaAutosize from 'react-textarea-autosize';
|
||||||
import type { TextareaAutosizeProps } from 'react-textarea-autosize';
|
import type { TextareaAutosizeProps } from 'react-textarea-autosize';
|
||||||
|
import store from '~/store';
|
||||||
|
|
||||||
export const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
|
export const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
|
||||||
(props, ref) => {
|
(props, ref) => {
|
||||||
const [, setIsRerendered] = useState(false);
|
const [, setIsRerendered] = useState(false);
|
||||||
|
const chatDirection = useRecoilValue(store.chatDirection).toLowerCase();
|
||||||
useLayoutEffect(() => setIsRerendered(true), []);
|
useLayoutEffect(() => setIsRerendered(true), []);
|
||||||
return <ReactTextareaAutosize dir="auto" {...props} ref={ref} />;
|
return <ReactTextareaAutosize dir={chatDirection} {...props} ref={ref} />;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ export default function useTextarea({
|
||||||
? getAssistantName({ name: assistantName, localize })
|
? getAssistantName({ name: assistantName, localize })
|
||||||
: getSender(conversation as TEndpointOption);
|
: getSender(conversation as TEndpointOption);
|
||||||
|
|
||||||
return `${localize('com_endpoint_message')} ${sender ? sender : 'ChatGPT'}…`;
|
return `${localize('com_endpoint_message')} ${sender ? sender : 'AI'}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const placeholder = getPlaceholderText();
|
const placeholder = getPlaceholderText();
|
||||||
|
|
|
||||||
|
|
@ -469,7 +469,6 @@ export default {
|
||||||
com_ui_max_tags: 'الحد الأقصى المسموح به هو {0}، باستخدام أحدث القيم.',
|
com_ui_max_tags: 'الحد الأقصى المسموح به هو {0}، باستخدام أحدث القيم.',
|
||||||
com_auth_back_to_login: 'العودة إلى تسجيل الدخول',
|
com_auth_back_to_login: 'العودة إلى تسجيل الدخول',
|
||||||
com_endpoint_message: 'رسالة',
|
com_endpoint_message: 'رسالة',
|
||||||
com_endpoint_messages: 'رسائل',
|
|
||||||
com_endpoint_message_not_appendable: 'عدّل رسالتك أو أعد إنشاءها.',
|
com_endpoint_message_not_appendable: 'عدّل رسالتك أو أعد إنشاءها.',
|
||||||
com_endpoint_context_tokens: 'الحد الأقصى لرموز السياق',
|
com_endpoint_context_tokens: 'الحد الأقصى لرموز السياق',
|
||||||
com_endpoint_context_info:
|
com_endpoint_context_info:
|
||||||
|
|
@ -2294,10 +2293,6 @@ export const comparisons = {
|
||||||
english: 'Message',
|
english: 'Message',
|
||||||
translated: 'رسالة',
|
translated: 'رسالة',
|
||||||
},
|
},
|
||||||
com_endpoint_messages: {
|
|
||||||
english: 'Messages',
|
|
||||||
translated: 'رسائل',
|
|
||||||
},
|
|
||||||
com_endpoint_message_not_appendable: {
|
com_endpoint_message_not_appendable: {
|
||||||
english: 'Edit your message or Regenerate.',
|
english: 'Edit your message or Regenerate.',
|
||||||
translated: 'عدّل رسالتك أو أعد إنشاءها.',
|
translated: 'عدّل رسالتك أو أعد إنشاءها.',
|
||||||
|
|
|
||||||
|
|
@ -565,7 +565,6 @@ export default {
|
||||||
com_ui_min_tags:
|
com_ui_min_tags:
|
||||||
'Es können keine weiteren Werte entfernt werden, mindestens {0} sind erforderlich.',
|
'Es können keine weiteren Werte entfernt werden, mindestens {0} sind erforderlich.',
|
||||||
com_ui_max_tags: 'Die maximal erlaubte Anzahl ist {0}, die neuesten Werte werden verwendet.',
|
com_ui_max_tags: 'Die maximal erlaubte Anzahl ist {0}, die neuesten Werte werden verwendet.',
|
||||||
com_endpoint_messages: 'Nachrichten',
|
|
||||||
com_endpoint_context_tokens: 'Max. Kontexttoken',
|
com_endpoint_context_tokens: 'Max. Kontexttoken',
|
||||||
com_endpoint_context_info:
|
com_endpoint_context_info:
|
||||||
'Die maximale Anzahl an Token, die für den Kontext verwendet werden kann. Verwenden Sie dies, um zu steuern, wie viele Token pro Anfrage gesendet werden. Wenn nicht angegeben, werden systemseitige Standardwerte basierend auf der bekannten Kontextgröße der Modelle verwendet. Höhere Werte können zu Fehlern und/oder höheren Tokenkosten führen.',
|
'Die maximale Anzahl an Token, die für den Kontext verwendet werden kann. Verwenden Sie dies, um zu steuern, wie viele Token pro Anfrage gesendet werden. Wenn nicht angegeben, werden systemseitige Standardwerte basierend auf der bekannten Kontextgröße der Modelle verwendet. Höhere Werte können zu Fehlern und/oder höheren Tokenkosten führen.',
|
||||||
|
|
@ -2603,10 +2602,6 @@ export const comparisons = {
|
||||||
english: 'Maximum number allowed is {0}, using latest values.',
|
english: 'Maximum number allowed is {0}, using latest values.',
|
||||||
translated: 'Die maximal erlaubte Anzahl ist {0}, die neuesten Werte werden verwendet.',
|
translated: 'Die maximal erlaubte Anzahl ist {0}, die neuesten Werte werden verwendet.',
|
||||||
},
|
},
|
||||||
com_endpoint_messages: {
|
|
||||||
english: 'Messages',
|
|
||||||
translated: 'Nachrichten',
|
|
||||||
},
|
|
||||||
com_endpoint_context_tokens: {
|
com_endpoint_context_tokens: {
|
||||||
english: 'Max Context Tokens',
|
english: 'Max Context Tokens',
|
||||||
translated: 'Max. Kontexttoken',
|
translated: 'Max. Kontexttoken',
|
||||||
|
|
|
||||||
|
|
@ -371,7 +371,6 @@ export default {
|
||||||
'WARNING: Misuse of this feature can get you BANNED from using Bing! Click on \'System Message\' for full instructions and the default message if omitted, which is the \'Sydney\' preset that is considered safe.',
|
'WARNING: Misuse of this feature can get you BANNED from using Bing! Click on \'System Message\' for full instructions and the default message if omitted, which is the \'Sydney\' preset that is considered safe.',
|
||||||
com_endpoint_system_message: 'System Message',
|
com_endpoint_system_message: 'System Message',
|
||||||
com_endpoint_message: 'Message',
|
com_endpoint_message: 'Message',
|
||||||
com_endpoint_messages: 'Messages',
|
|
||||||
com_endpoint_message_not_appendable: 'Edit your message or Regenerate.',
|
com_endpoint_message_not_appendable: 'Edit your message or Regenerate.',
|
||||||
com_endpoint_default_blank: 'default: blank',
|
com_endpoint_default_blank: 'default: blank',
|
||||||
com_endpoint_default_false: 'default: false',
|
com_endpoint_default_false: 'default: false',
|
||||||
|
|
@ -597,6 +596,7 @@ export default {
|
||||||
com_nav_enter_to_send: 'Press Enter to send messages',
|
com_nav_enter_to_send: 'Press Enter to send messages',
|
||||||
com_nav_user_name_display: 'Display username in messages',
|
com_nav_user_name_display: 'Display username in messages',
|
||||||
com_nav_save_drafts: 'Save drafts locally',
|
com_nav_save_drafts: 'Save drafts locally',
|
||||||
|
com_nav_chat_direction: 'Chat direction',
|
||||||
com_nav_show_code: 'Always show code when using code interpreter',
|
com_nav_show_code: 'Always show code when using code interpreter',
|
||||||
com_nav_auto_send_prompts: 'Auto-send Prompts',
|
com_nav_auto_send_prompts: 'Auto-send Prompts',
|
||||||
com_nav_always_make_prod: 'Always make new versions production',
|
com_nav_always_make_prod: 'Always make new versions production',
|
||||||
|
|
@ -660,6 +660,7 @@ export default {
|
||||||
com_nav_info_delete_cache_storage:
|
com_nav_info_delete_cache_storage:
|
||||||
'This action will delete all cached TTS (Text-to-Speech) audio files stored on your device. Cached audio files are used to speed up playback of previously generated TTS audio, but they can consume storage space on your device.',
|
'This action will delete all cached TTS (Text-to-Speech) audio files stored on your device. Cached audio files are used to speed up playback of previously generated TTS audio, but they can consume storage space on your device.',
|
||||||
com_nav_setting_general: 'General',
|
com_nav_setting_general: 'General',
|
||||||
|
com_nav_setting_chat: 'Chat',
|
||||||
com_nav_setting_beta: 'Beta features',
|
com_nav_setting_beta: 'Beta features',
|
||||||
com_nav_setting_data: 'Data controls',
|
com_nav_setting_data: 'Data controls',
|
||||||
com_nav_setting_account: 'Account',
|
com_nav_setting_account: 'Account',
|
||||||
|
|
|
||||||
|
|
@ -539,7 +539,6 @@ export default {
|
||||||
com_ui_import_conversation_file_type_error:
|
com_ui_import_conversation_file_type_error:
|
||||||
'com_ui_import_conversation_file_type_error: Tipo de archivo no compatible para importar',
|
'com_ui_import_conversation_file_type_error: Tipo de archivo no compatible para importar',
|
||||||
com_ui_min_tags: 'No se pueden eliminar más valores, se requiere un mínimo de {0}.',
|
com_ui_min_tags: 'No se pueden eliminar más valores, se requiere un mínimo de {0}.',
|
||||||
com_endpoint_messages: 'Mensajes',
|
|
||||||
com_endpoint_context_tokens: 'Máximo de tokens de contexto',
|
com_endpoint_context_tokens: 'Máximo de tokens de contexto',
|
||||||
com_endpoint_stop_placeholder: 'Separe los valores presionando `Intro`',
|
com_endpoint_stop_placeholder: 'Separe los valores presionando `Intro`',
|
||||||
com_error_no_base_url:
|
com_error_no_base_url:
|
||||||
|
|
@ -2513,10 +2512,6 @@ export const comparisons = {
|
||||||
english: 'Cannot remove more values, a minimum of {0} are required.',
|
english: 'Cannot remove more values, a minimum of {0} are required.',
|
||||||
translated: 'No se pueden eliminar más valores, se requiere un mínimo de {0}.',
|
translated: 'No se pueden eliminar más valores, se requiere un mínimo de {0}.',
|
||||||
},
|
},
|
||||||
com_endpoint_messages: {
|
|
||||||
english: 'Messages',
|
|
||||||
translated: 'Mensajes',
|
|
||||||
},
|
|
||||||
com_endpoint_context_tokens: {
|
com_endpoint_context_tokens: {
|
||||||
english: 'Max Context Tokens',
|
english: 'Max Context Tokens',
|
||||||
translated: 'Máximo de tokens de contexto',
|
translated: 'Máximo de tokens de contexto',
|
||||||
|
|
|
||||||
|
|
@ -571,7 +571,6 @@ export default {
|
||||||
com_ui_min_tags: 'Impossible de supprimer plus de valeurs, un minimum de {0} est requis.',
|
com_ui_min_tags: 'Impossible de supprimer plus de valeurs, un minimum de {0} est requis.',
|
||||||
com_ui_max_tags: 'Le nombre maximum autorisé est {0}, en utilisant les dernières valeurs.',
|
com_ui_max_tags: 'Le nombre maximum autorisé est {0}, en utilisant les dernières valeurs.',
|
||||||
com_auth_back_to_login: 'Retour à la connexion',
|
com_auth_back_to_login: 'Retour à la connexion',
|
||||||
com_endpoint_messages: 'Messages',
|
|
||||||
com_endpoint_context_tokens: 'Jetons de contexte maximum',
|
com_endpoint_context_tokens: 'Jetons de contexte maximum',
|
||||||
com_endpoint_context_info:
|
com_endpoint_context_info:
|
||||||
'Le nombre maximum de jetons qui peuvent être utilisés pour le contexte. Utilisez ceci pour contrôler le nombre de jetons envoyés par requête. Si non spécifié, les valeurs par défaut du système seront utilisées en fonction de la taille de contexte connue des modèles. Définir des valeurs plus élevées peut entraîner des erreurs et/ou un coût en jetons plus élevé.',
|
'Le nombre maximum de jetons qui peuvent être utilisés pour le contexte. Utilisez ceci pour contrôler le nombre de jetons envoyés par requête. Si non spécifié, les valeurs par défaut du système seront utilisées en fonction de la taille de contexte connue des modèles. Définir des valeurs plus élevées peut entraîner des erreurs et/ou un coût en jetons plus élevé.',
|
||||||
|
|
@ -2733,10 +2732,6 @@ export const comparisons = {
|
||||||
english: 'Back to Login',
|
english: 'Back to Login',
|
||||||
translated: 'Retour à la connexion',
|
translated: 'Retour à la connexion',
|
||||||
},
|
},
|
||||||
com_endpoint_messages: {
|
|
||||||
english: 'Messages',
|
|
||||||
translated: 'Messages',
|
|
||||||
},
|
|
||||||
com_endpoint_context_tokens: {
|
com_endpoint_context_tokens: {
|
||||||
english: 'Max Context Tokens',
|
english: 'Max Context Tokens',
|
||||||
translated: 'Jetons de contexte maximum',
|
translated: 'Jetons de contexte maximum',
|
||||||
|
|
|
||||||
|
|
@ -293,7 +293,6 @@ export default {
|
||||||
'ATTENZIONE: L\'uso improprio di questa funzione può farti BANNARE dall\'utilizzo di Bing! Clicca su "Messaggio di sistema" per le istruzioni complete e il messaggio predefinito se omesso, che è il preset "Sydney" considerato sicuro.',
|
'ATTENZIONE: L\'uso improprio di questa funzione può farti BANNARE dall\'utilizzo di Bing! Clicca su "Messaggio di sistema" per le istruzioni complete e il messaggio predefinito se omesso, che è il preset "Sydney" considerato sicuro.',
|
||||||
com_endpoint_system_message: 'Messaggio di sistema',
|
com_endpoint_system_message: 'Messaggio di sistema',
|
||||||
com_endpoint_message: 'Messaggio',
|
com_endpoint_message: 'Messaggio',
|
||||||
com_endpoint_messages: 'Messaggi',
|
|
||||||
com_endpoint_message_not_appendable: 'Modifica il tuo messaggio o Rigenera.',
|
com_endpoint_message_not_appendable: 'Modifica il tuo messaggio o Rigenera.',
|
||||||
com_endpoint_default_blank: 'predefinito: vuoto',
|
com_endpoint_default_blank: 'predefinito: vuoto',
|
||||||
com_endpoint_default_false: 'predefinito: falso',
|
com_endpoint_default_false: 'predefinito: falso',
|
||||||
|
|
@ -1649,10 +1648,6 @@ export const comparisons = {
|
||||||
english: 'Message',
|
english: 'Message',
|
||||||
translated: 'Messaggio',
|
translated: 'Messaggio',
|
||||||
},
|
},
|
||||||
com_endpoint_messages: {
|
|
||||||
english: 'Messages',
|
|
||||||
translated: 'Messaggi',
|
|
||||||
},
|
|
||||||
com_endpoint_message_not_appendable: {
|
com_endpoint_message_not_appendable: {
|
||||||
english: 'Edit your message or Regenerate.',
|
english: 'Edit your message or Regenerate.',
|
||||||
translated: 'Modifica il tuo messaggio o Rigenera.',
|
translated: 'Modifica il tuo messaggio o Rigenera.',
|
||||||
|
|
|
||||||
|
|
@ -544,7 +544,6 @@ export default {
|
||||||
com_ui_mention:
|
com_ui_mention:
|
||||||
'エンドポイント、アシスタント、またはプリセットを素早く切り替えるには、それらを言及してください。',
|
'エンドポイント、アシスタント、またはプリセットを素早く切り替えるには、それらを言及してください。',
|
||||||
com_ui_import_conversation_file_type_error: 'サポートされていないインポート形式です',
|
com_ui_import_conversation_file_type_error: 'サポートされていないインポート形式です',
|
||||||
com_endpoint_messages: 'メッセージ',
|
|
||||||
com_endpoint_context_tokens: 'コンテキストトークン数の最大値',
|
com_endpoint_context_tokens: 'コンテキストトークン数の最大値',
|
||||||
com_endpoint_context_info:
|
com_endpoint_context_info:
|
||||||
'コンテキストに使用できるトークンの最大数です。リクエストごとに送信されるトークン数を制御するために使用します。指定しない場合は、既知のモデルのコンテキストサイズに基づいてシステムのデフォルト値が使用されます。高い値を設定すると、エラーが発生したり、トークンコストが高くなる可能性があります。',
|
'コンテキストに使用できるトークンの最大数です。リクエストごとに送信されるトークン数を制御するために使用します。指定しない場合は、既知のモデルのコンテキストサイズに基づいてシステムのデフォルト値が使用されます。高い値を設定すると、エラーが発生したり、トークンコストが高くなる可能性があります。',
|
||||||
|
|
@ -2568,10 +2567,6 @@ export const comparisons = {
|
||||||
english: 'Unsupported import type',
|
english: 'Unsupported import type',
|
||||||
translated: 'サポートされていないインポート形式です',
|
translated: 'サポートされていないインポート形式です',
|
||||||
},
|
},
|
||||||
com_endpoint_messages: {
|
|
||||||
english: 'Messages',
|
|
||||||
translated: 'メッセージ',
|
|
||||||
},
|
|
||||||
com_endpoint_context_tokens: {
|
com_endpoint_context_tokens: {
|
||||||
english: 'Max Context Tokens',
|
english: 'Max Context Tokens',
|
||||||
translated: 'コンテキストトークン数の最大値',
|
translated: 'コンテキストトークン数の最大値',
|
||||||
|
|
|
||||||
|
|
@ -455,7 +455,6 @@ export default {
|
||||||
com_auth_error_login_server: '내부 서버 오류가 발생했습니다. 잠시 기다렸다가 다시 시도해 주세요.',
|
com_auth_error_login_server: '내부 서버 오류가 발생했습니다. 잠시 기다렸다가 다시 시도해 주세요.',
|
||||||
com_auth_back_to_login: '로그인 화면으로 돌아가기',
|
com_auth_back_to_login: '로그인 화면으로 돌아가기',
|
||||||
com_endpoint_message: '메시지',
|
com_endpoint_message: '메시지',
|
||||||
com_endpoint_messages: '메시지',
|
|
||||||
com_endpoint_message_not_appendable: '메시지를 수정하거나 다시 생성하세요.',
|
com_endpoint_message_not_appendable: '메시지를 수정하거나 다시 생성하세요.',
|
||||||
com_endpoint_context_tokens: '최대 컨텍스트 토큰 수',
|
com_endpoint_context_tokens: '최대 컨텍스트 토큰 수',
|
||||||
com_endpoint_context_info:
|
com_endpoint_context_info:
|
||||||
|
|
@ -2257,10 +2256,6 @@ export const comparisons = {
|
||||||
english: 'Message',
|
english: 'Message',
|
||||||
translated: '메시지',
|
translated: '메시지',
|
||||||
},
|
},
|
||||||
com_endpoint_messages: {
|
|
||||||
english: 'Messages',
|
|
||||||
translated: '메시지',
|
|
||||||
},
|
|
||||||
com_endpoint_message_not_appendable: {
|
com_endpoint_message_not_appendable: {
|
||||||
english: 'Edit your message or Regenerate.',
|
english: 'Edit your message or Regenerate.',
|
||||||
translated: '메시지를 수정하거나 다시 생성하세요.',
|
translated: '메시지를 수정하거나 다시 생성하세요.',
|
||||||
|
|
|
||||||
|
|
@ -518,7 +518,6 @@ export default {
|
||||||
com_ui_terms_of_service: 'Условия использования',
|
com_ui_terms_of_service: 'Условия использования',
|
||||||
com_ui_min_tags: 'Нельзя удалить больше значений, требуется минимум {0}.',
|
com_ui_min_tags: 'Нельзя удалить больше значений, требуется минимум {0}.',
|
||||||
com_ui_max_tags: 'Максимально допустимое количество - {0}, используются последние значения.',
|
com_ui_max_tags: 'Максимально допустимое количество - {0}, используются последние значения.',
|
||||||
com_endpoint_messages: 'Сообщения',
|
|
||||||
com_endpoint_context_tokens: 'Максимальное количество контекстных токенов',
|
com_endpoint_context_tokens: 'Максимальное количество контекстных токенов',
|
||||||
com_endpoint_context_info:
|
com_endpoint_context_info:
|
||||||
'Максимальное количество токенов, которое может быть использовано для контекста. Используется для контроля количества токенов, отправляемых за один запрос. Если не указано, будут использованы системные значения по умолчанию, основанные на известном размере контекста моделей. Установка более высоких значений может привести к ошибкам и/или более высокой стоимости токенов.',
|
'Максимальное количество токенов, которое может быть использовано для контекста. Используется для контроля количества токенов, отправляемых за один запрос. Если не указано, будут использованы системные значения по умолчанию, основанные на известном размере контекста моделей. Установка более высоких значений может привести к ошибкам и/или более высокой стоимости токенов.',
|
||||||
|
|
@ -2466,10 +2465,6 @@ export const comparisons = {
|
||||||
english: 'Maximum number allowed is {0}, using latest values.',
|
english: 'Maximum number allowed is {0}, using latest values.',
|
||||||
translated: 'Максимально допустимое количество - {0}, используются последние значения.',
|
translated: 'Максимально допустимое количество - {0}, используются последние значения.',
|
||||||
},
|
},
|
||||||
com_endpoint_messages: {
|
|
||||||
english: 'Messages',
|
|
||||||
translated: 'Сообщения',
|
|
||||||
},
|
|
||||||
com_endpoint_context_tokens: {
|
com_endpoint_context_tokens: {
|
||||||
english: 'Max Context Tokens',
|
english: 'Max Context Tokens',
|
||||||
translated: 'Максимальное количество контекстных токенов',
|
translated: 'Максимальное количество контекстных токенов',
|
||||||
|
|
|
||||||
|
|
@ -507,7 +507,6 @@ export default {
|
||||||
com_ui_import_conversation_file_type_error: '不支持的导入类型',
|
com_ui_import_conversation_file_type_error: '不支持的导入类型',
|
||||||
com_ui_min_tags: '无法再移除更多值,至少需要保留{0}个。',
|
com_ui_min_tags: '无法再移除更多值,至少需要保留{0}个。',
|
||||||
com_ui_max_tags: '最多允许{0}个,使用最新值。',
|
com_ui_max_tags: '最多允许{0}个,使用最新值。',
|
||||||
com_endpoint_messages: '消息',
|
|
||||||
com_endpoint_context_tokens: '最大上下文词元数',
|
com_endpoint_context_tokens: '最大上下文词元数',
|
||||||
com_endpoint_context_info:
|
com_endpoint_context_info:
|
||||||
'可用于上下文的最大词元数。用于控制每个请求发送的词元数量。如果未指定,将根据已知模型的上下文大小使用系统默认值。设置较高的值可能会导致错误和/或更高的词元成本。',
|
'可用于上下文的最大词元数。用于控制每个请求发送的词元数量。如果未指定,将根据已知模型的上下文大小使用系统默认值。设置较高的值可能会导致错误和/或更高的词元成本。',
|
||||||
|
|
@ -2501,10 +2500,6 @@ export const comparisons = {
|
||||||
english: 'Maximum number allowed is {0}, using latest values.',
|
english: 'Maximum number allowed is {0}, using latest values.',
|
||||||
translated: '最多允许{0}个,使用最新值。',
|
translated: '最多允许{0}个,使用最新值。',
|
||||||
},
|
},
|
||||||
com_endpoint_messages: {
|
|
||||||
english: 'Messages',
|
|
||||||
translated: '消息',
|
|
||||||
},
|
|
||||||
com_endpoint_context_tokens: {
|
com_endpoint_context_tokens: {
|
||||||
english: 'Max Context Tokens',
|
english: 'Max Context Tokens',
|
||||||
translated: '最大上下文词元数',
|
translated: '最大上下文词元数',
|
||||||
|
|
|
||||||
|
|
@ -446,7 +446,6 @@ export default {
|
||||||
com_ui_max_tags: '允許的最大數量為 {0},已使用最新值。',
|
com_ui_max_tags: '允許的最大數量為 {0},已使用最新值。',
|
||||||
com_auth_back_to_login: '返回登入',
|
com_auth_back_to_login: '返回登入',
|
||||||
com_endpoint_message: '訊息',
|
com_endpoint_message: '訊息',
|
||||||
com_endpoint_messages: '訊息',
|
|
||||||
com_endpoint_message_not_appendable: '無法附加訊息或重新生成。',
|
com_endpoint_message_not_appendable: '無法附加訊息或重新生成。',
|
||||||
com_endpoint_context_tokens: '最大前後文 token 數',
|
com_endpoint_context_tokens: '最大前後文 token 數',
|
||||||
com_endpoint_context_info:
|
com_endpoint_context_info:
|
||||||
|
|
@ -2264,10 +2263,6 @@ export const comparisons = {
|
||||||
english: 'Message',
|
english: 'Message',
|
||||||
translated: '訊息',
|
translated: '訊息',
|
||||||
},
|
},
|
||||||
com_endpoint_messages: {
|
|
||||||
english: 'Messages',
|
|
||||||
translated: '訊息',
|
|
||||||
},
|
|
||||||
com_endpoint_message_not_appendable: {
|
com_endpoint_message_not_appendable: {
|
||||||
english: 'Edit your message or Regenerate.',
|
english: 'Edit your message or Regenerate.',
|
||||||
translated: '無法附加訊息或重新生成。',
|
translated: '無法附加訊息或重新生成。',
|
||||||
|
|
|
||||||
|
|
@ -951,10 +951,6 @@ Write a prompt that is mindful of the nuances in the language with respect to it
|
||||||
- **english**: Message
|
- **english**: Message
|
||||||
- **translated**: Messaggio
|
- **translated**: Messaggio
|
||||||
|
|
||||||
- **com_endpoint_messages**:
|
|
||||||
- **english**: Messages
|
|
||||||
- **translated**: Messaggi
|
|
||||||
|
|
||||||
- **com_endpoint_message_not_appendable**:
|
- **com_endpoint_message_not_appendable**:
|
||||||
- **english**: Edit your message or Regenerate.
|
- **english**: Edit your message or Regenerate.
|
||||||
- **translated**: Modifica il tuo messaggio o Rigenera.
|
- **translated**: Modifica il tuo messaggio o Rigenera.
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ const localStorageAtoms = {
|
||||||
|
|
||||||
// Messages settings
|
// Messages settings
|
||||||
enterToSend: atomWithLocalStorage('enterToSend', true),
|
enterToSend: atomWithLocalStorage('enterToSend', true),
|
||||||
|
chatDirection: atomWithLocalStorage('chatDirection', 'LTR'),
|
||||||
showCode: atomWithLocalStorage('showCode', false),
|
showCode: atomWithLocalStorage('showCode', false),
|
||||||
saveDrafts: atomWithLocalStorage('saveDrafts', false),
|
saveDrafts: atomWithLocalStorage('saveDrafts', false),
|
||||||
forkSetting: atomWithLocalStorage('forkSetting', ''),
|
forkSetting: atomWithLocalStorage('forkSetting', ''),
|
||||||
|
|
|
||||||
|
|
@ -1058,6 +1058,7 @@ button {
|
||||||
border-color:transparent;
|
border-color:transparent;
|
||||||
border-radius:.5rem;
|
border-radius:.5rem;
|
||||||
border-width:1px;
|
border-width:1px;
|
||||||
|
cursor: pointer;
|
||||||
display:inline-flex;
|
display:inline-flex;
|
||||||
font-size:.875rem;
|
font-size:.875rem;
|
||||||
font-weight:500;
|
font-weight:500;
|
||||||
|
|
@ -2182,4 +2183,4 @@ ol ol):not(:where([class~=not-prose] *)) {
|
||||||
.move-up {
|
.move-up {
|
||||||
animation: moveUp 4s ease-in-out infinite;
|
animation: moveUp 4s ease-in-out infinite;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -842,9 +842,9 @@ export enum SettingsTabValues {
|
||||||
*/
|
*/
|
||||||
GENERAL = 'general',
|
GENERAL = 'general',
|
||||||
/**
|
/**
|
||||||
* Tab for Messages Settings
|
* Tab for Chat Settings
|
||||||
*/
|
*/
|
||||||
MESSAGES = 'messages',
|
CHAT = 'chat',
|
||||||
/**
|
/**
|
||||||
* Tab for Speech Settings
|
* Tab for Speech Settings
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue