mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-06 02:28:51 +01:00
🎨 style: settings tab update (#3088)
* style: settings UI update * style: update UI * style: update button style * fix: scroll settings on mobile * feat: `?` for settings
This commit is contained in:
parent
4319c62e66
commit
0424f8fe55
36 changed files with 359 additions and 173 deletions
|
|
@ -15,9 +15,10 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent
|
||||
disableScroll={isSmallScreen}
|
||||
className={cn(
|
||||
'overflow-hidden shadow-2xl md:min-h-[373px] md:w-[680px]',
|
||||
isSmallScreen ? 'top-5 -translate-y-0' : '',
|
||||
'max-h-[90vh] overflow-auto shadow-2xl md:min-h-[500px] md:w-[680px]',
|
||||
isSmallScreen ? 'min-h-[200px]' : '',
|
||||
)}
|
||||
>
|
||||
<DialogHeader>
|
||||
|
|
@ -28,7 +29,7 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
<div className="max-h-[373px] overflow-auto px-6 md:min-h-[373px] md:w-[680px]">
|
||||
<Tabs.Root
|
||||
defaultValue={SettingsTabValues.GENERAL}
|
||||
className="flex flex-col gap-10 md:flex-row"
|
||||
className="flex flex-col gap-3 md:flex-row"
|
||||
orientation="horizontal"
|
||||
>
|
||||
<Tabs.List
|
||||
|
|
@ -36,17 +37,18 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
role="tablist"
|
||||
aria-orientation="horizontal"
|
||||
className={cn(
|
||||
'min-w-auto max-w-auto -ml-[8px] flex flex-shrink-0 flex-col flex-wrap overflow-auto sm:max-w-none',
|
||||
isSmallScreen ? 'flex-row rounded-lg bg-gray-200 p-1 dark:bg-gray-800' : '',
|
||||
isSmallScreen
|
||||
? 'hide-scrollbar flex flex-row space-x-4 overflow-x-auto'
|
||||
: 'min-w-auto max-w-auto -ml-[8px] flex flex-shrink-0 flex-col flex-col flex-wrap overflow-auto sm:max-w-none',
|
||||
)}
|
||||
style={{ outline: 'none' }}
|
||||
>
|
||||
<Tabs.Trigger
|
||||
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
|
||||
? 'flex-1 flex-col items-center justify-center text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
||||
: 'bg-white radix-state-active:bg-gray-200',
|
||||
? 'flex-row items-center justify-center text-sm radix-state-active:bg-gray-100'
|
||||
: 'bg-white radix-state-active:bg-gray-100',
|
||||
isSmallScreen ? '' : 'dark:bg-gray-700',
|
||||
)}
|
||||
value={SettingsTabValues.GENERAL}
|
||||
|
|
@ -57,10 +59,10 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
</Tabs.Trigger>
|
||||
<Tabs.Trigger
|
||||
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
|
||||
? 'flex-1 flex-col items-center justify-center text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
||||
: 'bg-white radix-state-active:bg-gray-200',
|
||||
? 'flex-row items-center justify-center text-sm radix-state-active:bg-gray-100'
|
||||
: 'bg-white radix-state-active:bg-gray-100',
|
||||
isSmallScreen ? '' : 'dark:bg-gray-700',
|
||||
)}
|
||||
value={SettingsTabValues.MESSAGES}
|
||||
|
|
@ -71,10 +73,10 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
</Tabs.Trigger>
|
||||
<Tabs.Trigger
|
||||
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
|
||||
? 'flex-1 flex-col items-center justify-center text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
||||
: 'bg-white radix-state-active:bg-gray-200',
|
||||
? 'flex-row items-center justify-center text-sm radix-state-active:bg-gray-100'
|
||||
: 'bg-white radix-state-active:bg-gray-100',
|
||||
isSmallScreen ? '' : 'dark:bg-gray-700',
|
||||
)}
|
||||
value={SettingsTabValues.BETA}
|
||||
|
|
@ -85,10 +87,10 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
</Tabs.Trigger>
|
||||
<Tabs.Trigger
|
||||
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
|
||||
? 'flex-1 flex-col items-center justify-center text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
||||
: 'bg-white radix-state-active:bg-gray-200',
|
||||
? 'flex-row items-center justify-center text-sm radix-state-active:bg-gray-100'
|
||||
: 'bg-white radix-state-active:bg-gray-100',
|
||||
isSmallScreen ? '' : 'dark:bg-gray-700',
|
||||
)}
|
||||
value={SettingsTabValues.SPEECH}
|
||||
|
|
@ -99,10 +101,10 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
</Tabs.Trigger>
|
||||
<Tabs.Trigger
|
||||
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
|
||||
? 'flex-1 flex-col items-center justify-center text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
||||
: 'bg-white radix-state-active:bg-gray-200',
|
||||
? 'flex-row items-center justify-center text-sm radix-state-active:bg-gray-100'
|
||||
: 'bg-white radix-state-active:bg-gray-100',
|
||||
isSmallScreen ? '' : 'dark:bg-gray-700',
|
||||
)}
|
||||
value={SettingsTabValues.DATA}
|
||||
|
|
@ -113,10 +115,10 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
</Tabs.Trigger>
|
||||
<Tabs.Trigger
|
||||
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
|
||||
? 'flex-1 flex-col items-center justify-center text-sm dark:text-gray-500 dark:radix-state-active:text-white'
|
||||
: 'bg-white radix-state-active:bg-gray-200',
|
||||
? 'flex-row items-center justify-center text-sm radix-state-active:bg-gray-100'
|
||||
: 'bg-white radix-state-active:bg-gray-100',
|
||||
isSmallScreen ? '' : 'dark:bg-gray-700',
|
||||
)}
|
||||
value={SettingsTabValues.ACCOUNT}
|
||||
|
|
@ -126,7 +128,7 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
{localize('com_nav_setting_account')}
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
<div className="h-screen max-h-[373px] overflow-auto sm:w-full sm:max-w-none">
|
||||
<div className="h-auto min-h-[280px] overflow-auto sm:w-full sm:max-w-none">
|
||||
<General />
|
||||
<Messages />
|
||||
<Beta />
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import { useRecoilState } from 'recoil';
|
||||
import * as Tabs from '@radix-ui/react-tabs';
|
||||
import { SettingsTabValues } from 'librechat-data-provider';
|
||||
import HoverCardSettings from '../HoverCardSettings';
|
||||
import DeleteAccount from './DeleteAccount';
|
||||
import { Switch } from '~/components/ui';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
|
@ -25,7 +26,7 @@ function Account({ onCheckedChange }: { onCheckedChange?: (value: boolean) => vo
|
|||
role="tabpanel"
|
||||
className="w-full md:min-h-[271px]"
|
||||
>
|
||||
<div className="flex flex-col gap-3 text-sm text-gray-600 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">
|
||||
<Avatar />
|
||||
</div>
|
||||
|
|
@ -33,7 +34,10 @@ function Account({ onCheckedChange }: { onCheckedChange?: (value: boolean) => vo
|
|||
<DeleteAccount />
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_nav_user_name_display')} </div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div>{localize('com_nav_user_name_display')}</div>
|
||||
<HoverCardSettings side="bottom" text="com_nav_info_user_name_display" />
|
||||
</div>
|
||||
<Switch
|
||||
id="UsernameDisplay"
|
||||
checked={UsernameDisplay}
|
||||
|
|
|
|||
|
|
@ -80,11 +80,8 @@ function Avatar() {
|
|||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<span>{localize('com_nav_profile_picture')}</span>
|
||||
<label
|
||||
htmlFor={'file-upload-avatar'}
|
||||
className="flex h-auto cursor-pointer items-center rounded bg-transparent px-2 py-1 text-xs font-normal transition-colors hover:bg-gray-100 hover:text-green-700 dark:bg-transparent dark:text-white dark:hover:bg-gray-600 dark:hover:text-green-500"
|
||||
>
|
||||
<FileImage className="mr-1 flex w-[22px] items-center stroke-1" />
|
||||
<label htmlFor={'file-upload-avatar'} className="btn btn-neutral relative">
|
||||
<FileImage className="mr-2 flex w-[22px] items-center stroke-1" />
|
||||
<span>{localize('com_nav_change_picture')}</span>
|
||||
<input
|
||||
id={'file-upload-avatar'}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,11 @@
|
|||
import React, { useState, useCallback } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogButton,
|
||||
Input,
|
||||
} from '~/components/ui';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, Input } from '~/components/ui';
|
||||
import { cn, defaultTextProps, removeFocusOutlines } from '~/utils';
|
||||
import { useDeleteUserMutation } from '~/data-provider';
|
||||
import { Spinner, LockIcon } from '~/components/svg';
|
||||
import { useAuthContext } from '~/hooks/AuthContext';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import DangerButton from '../DangerButton';
|
||||
|
||||
const DeleteAccount = ({ disabled = false }: { title?: string; disabled?: boolean }) => {
|
||||
const localize = useLocalize();
|
||||
|
|
@ -50,16 +44,19 @@ const DeleteAccount = ({ disabled = false }: { title?: string; disabled?: boolea
|
|||
<div className="flex items-center justify-between">
|
||||
<span>{localize('com_nav_delete_account')}</span>
|
||||
<label>
|
||||
<DialogButton
|
||||
<DangerButton
|
||||
id={'delete-user-account'}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
actionTextCode="com_ui_delete"
|
||||
className={cn(
|
||||
'btn btn-danger relative border-none bg-red-700 text-white hover:bg-red-800 dark:hover:bg-red-800',
|
||||
'btn relative border-none bg-red-500 text-white hover:bg-red-700 dark:hover:bg-red-700',
|
||||
)}
|
||||
>
|
||||
{localize('com_ui_delete')}
|
||||
</DialogButton>
|
||||
confirmClear={false}
|
||||
infoTextCode={''}
|
||||
dataTestIdInitial={''}
|
||||
dataTestIdConfirm={''}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<Dialog open={isDialogOpen} onOpenChange={() => setDialogOpen(false)}>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ function Beta() {
|
|||
role="tabpanel"
|
||||
className="w-full md:min-h-[271px]"
|
||||
>
|
||||
<div className="flex flex-col gap-3 text-sm text-gray-600 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">
|
||||
<ModularChat />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useRecoilState } from 'recoil';
|
||||
import HoverCardSettings from '../HoverCardSettings';
|
||||
import { Switch } from '~/components/ui';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import store from '~/store';
|
||||
|
|
@ -20,7 +21,10 @@ export default function LaTeXParsingSwitch({
|
|||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div>{localize('com_nav_latex_parsing')} </div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div>{localize('com_nav_latex_parsing')}</div>
|
||||
<HoverCardSettings side="bottom" text="com_nav_info_latex_parsing" />
|
||||
</div>
|
||||
<Switch
|
||||
id="LaTeXParsing"
|
||||
checked={LaTeXParsing}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export default function ModularChatSwitch({
|
|||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div>{localize('com_nav_modular_chat')} </div>
|
||||
<div> {localize('com_nav_modular_chat')} </div>
|
||||
<Switch
|
||||
id="modularChat"
|
||||
checked={modularChat}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { Spinner } from '~/components/svg';
|
|||
import type { TDangerButtonProps } from '~/common';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { cn } from '~/utils';
|
||||
import HoverCardSettings from './HoverCardSettings';
|
||||
|
||||
const DangerButton = (props: TDangerButtonProps, ref: ForwardedRef<HTMLButtonElement>) => {
|
||||
const {
|
||||
|
|
@ -20,6 +21,7 @@ const DangerButton = (props: TDangerButtonProps, ref: ForwardedRef<HTMLButtonEle
|
|||
showText = true,
|
||||
dataTestIdInitial,
|
||||
dataTestIdConfirm,
|
||||
infoDescriptionCode,
|
||||
confirmActionTextCode = 'com_ui_confirm_action',
|
||||
} = props;
|
||||
const localize = useLocalize();
|
||||
|
|
@ -33,14 +35,19 @@ const DangerButton = (props: TDangerButtonProps, ref: ForwardedRef<HTMLButtonEle
|
|||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
{showText && <div> {localize(infoTextCode)} </div>}
|
||||
{showText && (
|
||||
<div className={`flex items-center ${infoDescriptionCode ? 'space-x-2' : ''}`}>
|
||||
<div>{localize(infoTextCode)}</div>
|
||||
{infoDescriptionCode && <HoverCardSettings side="bottom" text={infoDescriptionCode} />}
|
||||
</div>
|
||||
)}
|
||||
<DialogButton
|
||||
id={id}
|
||||
ref={ref}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
' btn btn-danger relative min-w-[70px] border-none bg-red-700 text-white hover:bg-red-800 dark:hover:bg-red-800',
|
||||
'btn relative border-none bg-red-500 text-white hover:bg-red-700 dark:hover:bg-red-700',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ function Data() {
|
|||
className="w-full md:min-h-[271px]"
|
||||
ref={dataTabRef}
|
||||
>
|
||||
<div className="flex flex-col gap-3 text-sm text-gray-600 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">
|
||||
<ImportConversations />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ export const DeleteCacheButton = ({
|
|||
id={'delete-cache'}
|
||||
actionTextCode={'com_ui_delete'}
|
||||
infoTextCode={'com_nav_delete_cache_storage'}
|
||||
infoDescriptionCode={'com_nav_info_delete_cache_storage'}
|
||||
dataTestIdInitial={'delete-cache-initial'}
|
||||
dataTestIdConfirm={'delete-cache-confirm'}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -68,11 +68,8 @@ function ImportConversations() {
|
|||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<span>{localize('com_ui_import_conversation_info')}</span>
|
||||
<label
|
||||
htmlFor={'import-conversations-file'}
|
||||
className="flex h-auto cursor-pointer items-center rounded bg-transparent px-2 py-3 text-xs font-medium transition-colors hover:bg-gray-100 hover:text-green-700 dark:bg-transparent dark:text-white dark:hover:bg-gray-600 dark:hover:text-green-500"
|
||||
>
|
||||
<div>{localize('com_ui_import_conversation_info')}</div>
|
||||
<label htmlFor={'import-conversations-file'} className="btn btn-neutral relative">
|
||||
{allowImport ? (
|
||||
<Import className="mr-1 flex h-4 w-4 items-center stroke-1" />
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ export const RevokeKeysButton = ({
|
|||
id={'revoke-all-user-keys'}
|
||||
actionTextCode={'com_ui_revoke'}
|
||||
infoTextCode={'com_ui_revoke_info'}
|
||||
infoDescriptionCode={'com_nav_info_revoke'}
|
||||
dataTestIdInitial={'revoke-all-keys-initial'}
|
||||
dataTestIdConfirm={'revoke-all-keys-confirm'}
|
||||
mutation={all ? revokeKeysMutation : revokeKeyMutation}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export default function SharedLinks() {
|
|||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_nav_shared_links')} </div>
|
||||
<div>{localize('com_nav_shared_links')}</div>
|
||||
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useLocalize } from '~/hooks';
|
||||
import { Dialog, DialogTrigger } from '~/components/ui';
|
||||
import DialogTemplate from '~/components/ui/DialogTemplate';
|
||||
import { Dialog, DialogTrigger } from '~/components/ui';
|
||||
|
||||
import ArchivedChatsTable from './ArchivedChatsTable';
|
||||
|
||||
|
|
@ -9,8 +9,7 @@ export default function ArchivedChats() {
|
|||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_nav_archived_chats')} </div>
|
||||
|
||||
<div>{localize('com_nav_archived_chats')}</div>
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<button className="btn btn-neutral relative ">
|
||||
|
|
|
|||
|
|
@ -28,12 +28,13 @@ export const ThemeSelector = ({
|
|||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_nav_theme')} </div>
|
||||
<div>{localize('com_nav_theme')}</div>
|
||||
|
||||
<Dropdown
|
||||
value={theme}
|
||||
onChange={onChange}
|
||||
options={themeOptions}
|
||||
width={220}
|
||||
width={180}
|
||||
position={'left'}
|
||||
maxHeight="200px"
|
||||
testId="theme-selector"
|
||||
|
|
@ -64,6 +65,7 @@ export const ClearChatsButton = ({
|
|||
confirmActionTextCode="com_nav_confirm_clear"
|
||||
dataTestIdInitial="clear-convos-initial"
|
||||
dataTestIdConfirm="clear-convos-confirm"
|
||||
infoDescriptionCode="com_nav_info_clear_all_chats"
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
|
|
@ -104,7 +106,8 @@ export const LangSelector = ({
|
|||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_nav_language')} </div>
|
||||
<div>{localize('com_nav_language')}</div>
|
||||
|
||||
<Dropdown
|
||||
value={langcode}
|
||||
onChange={onChange}
|
||||
|
|
@ -153,7 +156,7 @@ function General() {
|
|||
className="w-full md:min-h-[271px]"
|
||||
ref={contentRef}
|
||||
>
|
||||
<div className="flex flex-col gap-3 text-sm text-gray-600 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">
|
||||
<ThemeSelector theme={theme} onChange={changeTheme} />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ export default function HideSidePanelSwitch({
|
|||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_nav_hide_panel')} </div>
|
||||
<div>{localize('com_nav_hide_panel')}</div>
|
||||
|
||||
<Switch
|
||||
id="hideSidePanel"
|
||||
checked={hideSidePanel}
|
||||
|
|
|
|||
25
client/src/components/Nav/SettingsTabs/HoverCardSettings.tsx
Normal file
25
client/src/components/Nav/SettingsTabs/HoverCardSettings.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import React from 'react';
|
||||
import { HoverCard, HoverCardTrigger, HoverCardPortal, HoverCardContent } from '~/components/ui';
|
||||
import { CircleHelpIcon } from '~/components/svg';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
const HoverCardSettings = ({ side, text }) => {
|
||||
const localize = useLocalize();
|
||||
|
||||
return (
|
||||
<HoverCard openDelay={500}>
|
||||
<HoverCardTrigger>
|
||||
<CircleHelpIcon className="h-5 w-5 text-gray-500" />{' '}
|
||||
</HoverCardTrigger>
|
||||
<HoverCardPortal>
|
||||
<HoverCardContent side={side} className="z-[999] w-80">
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">{localize(text)}</p>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCardPortal>
|
||||
</HoverCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default HoverCardSettings;
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { useRecoilState } from 'recoil';
|
||||
import HoverCardSettings from '../HoverCardSettings';
|
||||
import { Switch } from '~/components/ui/Switch';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import store from '~/store';
|
||||
|
|
@ -20,7 +21,10 @@ export default function SendMessageKeyEnter({
|
|||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_nav_enter_to_send')} </div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div>{localize('com_nav_enter_to_send')}</div>
|
||||
<HoverCardSettings side="bottom" text="com_nav_info_enter_to_send" />
|
||||
</div>
|
||||
<Switch
|
||||
id="enterToSend"
|
||||
checked={enterToSend}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useRecoilState } from 'recoil';
|
||||
import HoverCardSettings from '../HoverCardSettings';
|
||||
import { ForkOptions } from 'librechat-data-provider';
|
||||
import { Dropdown, Switch } from '~/components/ui';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
|
@ -20,12 +21,14 @@ export const ForkSettings = () => {
|
|||
<>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_ui_fork_change_default')} </div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div>{localize('com_ui_fork_change_default')}</div>
|
||||
<HoverCardSettings side="bottom" text="com_nav_info_fork_change_default" />
|
||||
</div>
|
||||
<Dropdown
|
||||
value={forkSetting}
|
||||
onChange={setForkSetting}
|
||||
options={forkOptions}
|
||||
width={200}
|
||||
position={'left'}
|
||||
maxHeight="199px"
|
||||
testId="fork-setting-dropdown"
|
||||
|
|
@ -46,7 +49,10 @@ export const ForkSettings = () => {
|
|||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_ui_fork_split_target_setting')} </div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div>{localize('com_ui_fork_split_target_setting')}</div>
|
||||
<HoverCardSettings side="bottom" text="com_nav_info_fork_split_target_setting" />
|
||||
</div>
|
||||
<Switch
|
||||
id="splitAtTarget"
|
||||
checked={splitAtTarget}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import SaveDraft from './SaveDraft';
|
|||
function Messages() {
|
||||
return (
|
||||
<Tabs.Content value={SettingsTabValues.MESSAGES} role="tabpanel" className="md: w-full">
|
||||
<div className="flex flex-col gap-3 text-sm text-gray-600 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">
|
||||
<SendMessageKeyEnter />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useRecoilState } from 'recoil';
|
||||
import { Switch } from '~/components/ui/Switch';
|
||||
import HoverCardSettings from '../HoverCardSettings';
|
||||
import { Switch } from '~/components/ui';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import store from '~/store';
|
||||
|
||||
|
|
@ -20,7 +21,10 @@ export default function SaveDraft({
|
|||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div>{localize('com_nav_save_drafts')}</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div>{localize('com_nav_save_drafts')}</div>
|
||||
<HoverCardSettings side="bottom" text="com_nav_info_save_draft" />
|
||||
</div>
|
||||
<Switch
|
||||
id="saveDrafts"
|
||||
checked={saveDrafts}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useRecoilState } from 'recoil';
|
||||
import HoverCardSettings from '../HoverCardSettings';
|
||||
import { Switch } from '~/components/ui';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import store from '~/store';
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export default function ConversationModeSwitch({
|
|||
}) {
|
||||
const localize = useLocalize();
|
||||
const [conversationMode, setConversationMode] = useRecoilState<boolean>(store.conversationMode);
|
||||
const [advancedMode, setAdvancedMode] = useRecoilState<boolean>(store.advancedMode);
|
||||
const [advancedMode] = useRecoilState<boolean>(store.advancedMode);
|
||||
const [textToSpeech] = useRecoilState<boolean>(store.TextToSpeech);
|
||||
const [, setAutoSendText] = useRecoilState<boolean>(store.autoSendText);
|
||||
const [, setDecibelValue] = useRecoilState(store.decibelValue);
|
||||
|
|
@ -34,13 +34,6 @@ export default function ConversationModeSwitch({
|
|||
<strong>{localize('com_nav_conversation_mode')}</strong>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<label
|
||||
className="flex h-auto cursor-pointer items-center rounded border border-gray-500/70 bg-transparent px-2 py-1 text-xs font-medium font-normal transition-colors hover:border-gray-500/95 hover:bg-gray-100 hover:text-green-700 dark:bg-transparent dark:text-white dark:hover:bg-gray-600 dark:hover:text-green-500"
|
||||
onClick={() => setAdvancedMode(!advancedMode)}
|
||||
>
|
||||
<span>{advancedMode ? 'Advanced Mode' : 'Simple Mode'}</span>
|
||||
</label>
|
||||
<div className="w-2" />
|
||||
<Switch
|
||||
id="ConversationMode"
|
||||
checked={conversationMode}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export default function EngineSTTDropdown() {
|
|||
value={endpointSTT}
|
||||
onChange={handleSelect}
|
||||
options={endpointOptions}
|
||||
width={220}
|
||||
width={180}
|
||||
position={'left'}
|
||||
testId="EngineSTTDropdown"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ import * as Tabs from '@radix-ui/react-tabs';
|
|||
import { SettingsTabValues } from 'librechat-data-provider';
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useOnClickOutside } from '~/hooks';
|
||||
import { Lightbulb, Cog } from 'lucide-react';
|
||||
import { useOnClickOutside, useMediaQuery } from '~/hooks';
|
||||
import store from '~/store';
|
||||
import { cn } from '~/utils';
|
||||
import ConversationModeSwitch from './ConversationModeSwitch';
|
||||
import {
|
||||
TextToSpeechSwitch,
|
||||
|
|
@ -22,9 +24,10 @@ import {
|
|||
} from './STT';
|
||||
|
||||
function Speech() {
|
||||
const [confirmClear, setConfirmClear] = useState(false);
|
||||
const [advancedMode] = useRecoilState<boolean>(store.advancedMode);
|
||||
const isSmallScreen = useMediaQuery('(max-width: 767px)');
|
||||
const [advancedMode, setAdvancedMode] = useRecoilState<boolean>(store.advancedMode);
|
||||
const [autoTranscribeAudio] = useRecoilState<boolean>(store.autoTranscribeAudio);
|
||||
const [confirmClear, setConfirmClear] = useState(false);
|
||||
|
||||
const contentRef = useRef(null);
|
||||
useOnClickOutside(contentRef, () => confirmClear && setConfirmClear(false), []);
|
||||
|
|
@ -36,56 +39,119 @@ function Speech() {
|
|||
className="w-full px-4 md:min-h-[300px]"
|
||||
ref={contentRef}
|
||||
>
|
||||
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-50">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<ConversationModeSwitch />
|
||||
<Tabs.Root
|
||||
defaultValue={'simple'}
|
||||
orientation="horizontal"
|
||||
value={advancedMode ? 'advanced' : 'simple'}
|
||||
>
|
||||
<div className="sticky top-0 z-50 bg-white dark:bg-gray-700">
|
||||
<Tabs.List className="sticky top-0 mb-4 flex justify-center bg-white dark:bg-gray-700">
|
||||
<Tabs.Trigger
|
||||
onClick={() => setAdvancedMode(false)}
|
||||
className={cn(
|
||||
'group m-1 flex items-center justify-center gap-2 rounded-md px-4 py-2 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
|
||||
? 'flex-row items-center justify-center text-sm text-gray-700 radix-state-active:bg-gray-100 radix-state-active:text-black dark:text-gray-300 dark:radix-state-active:text-white'
|
||||
: 'bg-white radix-state-active:bg-gray-100 dark:bg-gray-700',
|
||||
'w-full',
|
||||
)}
|
||||
value="simple"
|
||||
style={{ userSelect: 'none' }}
|
||||
>
|
||||
<Lightbulb />
|
||||
Simple
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger
|
||||
onClick={() => setAdvancedMode(true)}
|
||||
className={cn(
|
||||
'group m-1 flex items-center justify-center gap-2 rounded-md px-4 py-2 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
|
||||
? 'flex-row items-center justify-center text-sm text-gray-700 radix-state-active:bg-gray-100 radix-state-active:text-black dark:text-gray-300 dark:radix-state-active:text-white'
|
||||
: 'bg-white radix-state-active:bg-gray-100 dark:bg-gray-700',
|
||||
'w-full',
|
||||
)}
|
||||
value="advanced"
|
||||
style={{ userSelect: 'none' }}
|
||||
>
|
||||
<Cog />
|
||||
Advanced
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
</div>
|
||||
<div className="h-px bg-black/20 bg-white/20" role="none" />
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<SpeechToTextSwitch />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<EngineSTTDropdown />
|
||||
</div>
|
||||
{advancedMode && (
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<AutoTranscribeAudioSwitch />
|
||||
|
||||
<Tabs.Content value={'simple'}>
|
||||
<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-700">
|
||||
<ConversationModeSwitch />
|
||||
</div>
|
||||
<div className="h-px bg-black/20 bg-white/20" role="none" />
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<SpeechToTextSwitch />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<EngineSTTDropdown />
|
||||
</div>
|
||||
<div className="h-px bg-black/20 bg-white/20" role="none" />
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<TextToSpeechSwitch />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<AutomaticPlayback />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<EngineTTSDropdown />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<VoiceDropdown />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{autoTranscribeAudio && advancedMode && (
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<DecibelSelector />
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value={'advanced'}>
|
||||
<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-700">
|
||||
<ConversationModeSwitch />
|
||||
</div>
|
||||
<div className="h-px bg-black/20 bg-white/20" role="none" />
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<SpeechToTextSwitch />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<EngineSTTDropdown />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<AutoTranscribeAudioSwitch />
|
||||
</div>
|
||||
{autoTranscribeAudio && (
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<DecibelSelector />
|
||||
</div>
|
||||
)}
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<AutoSendTextSwitch />
|
||||
</div>
|
||||
<div className="h-px bg-black/20 bg-white/20" role="none" />
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<TextToSpeechSwitch />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<AutomaticPlayback />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<EngineTTSDropdown />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<VoiceDropdown />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<PlaybackRate />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<CacheTTSSwitch />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{advancedMode && (
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<AutoSendTextSwitch />
|
||||
</div>
|
||||
)}
|
||||
<div className="h-px bg-black/20 bg-white/20" role="none" />
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<TextToSpeechSwitch />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<AutomaticPlayback />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<EngineTTSDropdown />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<VoiceDropdown />
|
||||
</div>
|
||||
{advancedMode && (
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<PlaybackRate />
|
||||
</div>
|
||||
)}
|
||||
{advancedMode && (
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<CacheTTSSwitch />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
</Tabs.Content>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export default function EngineTTSDropdown() {
|
|||
value={endpointTTS}
|
||||
onChange={handleSelect}
|
||||
options={endpointOptions}
|
||||
width={220}
|
||||
width={180}
|
||||
position={'left'}
|
||||
testId="EngineTTSDropdown"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ export default function VoiceDropdown() {
|
|||
value={voice}
|
||||
onChange={(value: string) => setVoice(value)}
|
||||
options={voiceOptions}
|
||||
width={220}
|
||||
position={'left'}
|
||||
testId="VoiceDropdown"
|
||||
/>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue