mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-31 15:48:51 +01:00
🎨 refactor: UI stlye (#4438)
* feat: Refactor ChatForm and StopButton components for improved styling and localization * feat: Refactor AudioRecorder, ChatForm, AttachFile, and SendButton components for improved styling and layout * feat: Add RevokeAllKeys component and update styling for buttons and inputs * feat: Refactor ClearChats component and update ClearConvos functionality for improved clarity and user experience * feat: Remove ClearConvos component and update related imports and functionality in Avatar and DeleteCacheButton components * feat: Rename DeleteCacheButton to DeleteCache and update related imports; enhance confirmation message in localization * feat: Update ChatForm layout for RTL support and improve component structure * feat: Adjust ChatForm layout for improved RTL support and alignment * feat: Refactor Bookmark components to use new UI elements and improve styling * feat: Update FileSearch and ShareAgent components for improved button styling and layout * feat: Update ChatForm and TextareaHeader styles for improved UI consistency * feat: Refactor Nav components for improved styling and layout adjustments * feat: Update button sizes and padding for improved UI consistency across chat components * feat: Remove ClearChatsButton test file as part of code cleanup
This commit is contained in:
parent
20fb7f05ae
commit
8f3de7d11f
38 changed files with 471 additions and 564 deletions
|
|
@ -80,10 +80,8 @@ export default function AudioRecorder({
|
|||
onClick={isListening ? handleStopRecording : handleStartRecording}
|
||||
disabled={disabled}
|
||||
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',
|
||||
'absolute flex size-[35px] items-center justify-center rounded-full p-1 transition-colors hover:bg-surface-hover',
|
||||
isRTL ? 'bottom-2 left-2' : 'bottom-2 right-2',
|
||||
)}
|
||||
description={localize('com_ui_use_micrphone')}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ const ChatForm = ({ index = 0 }) => {
|
|||
/>
|
||||
)}
|
||||
<PromptsCommand index={index} textAreaRef={textAreaRef} submitPrompt={submitPrompt} />
|
||||
<div className="bg-token-main-surface-primary relative flex w-full flex-grow flex-col overflow-hidden rounded-2xl border dark:border-gray-600 dark:text-white [&:has(textarea:focus)]:border-gray-300 [&:has(textarea:focus)]:shadow-[0_2px_6px_rgba(0,0,0,.05)] dark:[&:has(textarea:focus)]:border-gray-500">
|
||||
<div className="transitional-all relative flex w-full flex-grow flex-col overflow-hidden rounded-3xl bg-surface-tertiary text-text-primary duration-200">
|
||||
<TextareaHeader addedConvo={addedConvo} setAddedConvo={setAddedConvo} />
|
||||
<FileFormWrapper disableInputs={disableInputs}>
|
||||
{endpoint && (
|
||||
|
|
@ -181,7 +181,7 @@ const ChatForm = ({ index = 0 }) => {
|
|||
endpointSupportsFiles && !isUploadDisabled
|
||||
? 'pl-10 md:pl-[55px]'
|
||||
: '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 ',
|
||||
'md:py-3.5- m-0 w-full resize-none bg-surface-tertiary py-[13px] placeholder-black/50 dark:placeholder-white/50 [&:has(textarea:focus)]:shadow-[0_2px_6px_rgba(0,0,0,.05)]',
|
||||
SpeechToText && !isRTL ? 'pr-20 md:pr-[85px]' : 'pr-10 md:pr-12',
|
||||
'max-h-[65vh] md:max-h-[75vh]',
|
||||
removeFocusRings,
|
||||
|
|
@ -189,22 +189,6 @@ const ChatForm = ({ index = 0 }) => {
|
|||
/>
|
||||
)}
|
||||
</FileFormWrapper>
|
||||
{(isSubmitting || isSubmittingAdded) && (showStopButton || showStopAdded) ? (
|
||||
<StopButton
|
||||
stop={handleStopGenerating}
|
||||
setShowStopButton={setShowStopButton}
|
||||
isRTL={isRTL}
|
||||
/>
|
||||
) : (
|
||||
endpoint && (
|
||||
<SendButton
|
||||
ref={submitButtonRef}
|
||||
control={methods.control}
|
||||
isRTL={isRTL}
|
||||
disabled={!!(filesLoading || isSubmitting || disableInputs)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
{SpeechToText && (
|
||||
<AudioRecorder
|
||||
disabled={!!disableInputs}
|
||||
|
|
@ -216,6 +200,25 @@ const ChatForm = ({ index = 0 }) => {
|
|||
)}
|
||||
{TextToSpeech && automaticPlayback && <StreamAudio index={index} />}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'mb-[5px] ml-[8px] flex flex-col items-end justify-end',
|
||||
isRTL && 'order-first mr-[8px]',
|
||||
)}
|
||||
style={{ alignSelf: 'flex-end' }}
|
||||
>
|
||||
{(isSubmitting || isSubmittingAdded) && (showStopButton || showStopAdded) ? (
|
||||
<StopButton stop={handleStopGenerating} setShowStopButton={setShowStopButton} />
|
||||
) : (
|
||||
endpoint && (
|
||||
<SendButton
|
||||
ref={submitButtonRef}
|
||||
control={methods.control}
|
||||
disabled={!!(filesLoading || isSubmitting || disableInputs)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -17,29 +17,22 @@ const AttachFile = ({
|
|||
const isUploadDisabled = disabled ?? false;
|
||||
|
||||
return (
|
||||
<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">
|
||||
<TooltipAnchor
|
||||
id="audio-recorder"
|
||||
disabled={isUploadDisabled}
|
||||
aria-label={localize('com_sidepanel_attach_files')}
|
||||
className="btn relative text-black focus:outline-none focus:ring-2 focus:ring-border-xheavy focus:ring-opacity-50 dark:text-white"
|
||||
style={{ padding: 0 }}
|
||||
description={localize('com_sidepanel_attach_files')}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2">
|
||||
<AttachmentIcon />
|
||||
</div>
|
||||
</TooltipAnchor>
|
||||
</FileUpload>
|
||||
</div>
|
||||
<FileUpload handleFileChange={handleFileChange} className="flex">
|
||||
<TooltipAnchor
|
||||
id="audio-recorder"
|
||||
aria-label={localize('com_sidepanel_attach_files')}
|
||||
disabled={isUploadDisabled}
|
||||
className={cn(
|
||||
'absolute flex size-[35px] items-center justify-center rounded-full p-1 transition-colors hover:bg-surface-hover',
|
||||
isRTL ? 'bottom-2 right-2' : 'bottom-2 left-2',
|
||||
)}
|
||||
description={localize('com_sidepanel_attach_files')}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2">
|
||||
<AttachmentIcon />
|
||||
</div>
|
||||
</TooltipAnchor>
|
||||
</FileUpload>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,19 +13,17 @@ import AttachFile from './AttachFile';
|
|||
import FileRow from './FileRow';
|
||||
import store from '~/store';
|
||||
|
||||
function FileFormWrapper({ children, disableInputs } : {
|
||||
function FileFormWrapper({
|
||||
children,
|
||||
disableInputs,
|
||||
}: {
|
||||
disableInputs: boolean;
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
const { handleFileChange, abortUpload } = useFileHandling();
|
||||
const chatDirection = useRecoilValue(store.chatDirection).toLowerCase();
|
||||
|
||||
const {
|
||||
files,
|
||||
setFiles,
|
||||
conversation,
|
||||
setFilesLoading,
|
||||
} = useChatContext();
|
||||
const { files, setFiles, conversation, setFilesLoading } = useChatContext();
|
||||
const { data: fileConfig = defaultFileConfig } = useGetFileConfig({
|
||||
select: (data) => mergeFileConfig(data),
|
||||
});
|
||||
|
|
@ -33,30 +31,30 @@ function FileFormWrapper({ children, disableInputs } : {
|
|||
const isRTL = chatDirection === 'rtl';
|
||||
|
||||
const { endpoint: _endpoint, endpointType } = conversation ?? { endpoint: null };
|
||||
const endpointFileConfig = fileConfig.endpoints[_endpoint ?? ''] as EndpointFileConfig | undefined;
|
||||
const endpointFileConfig = fileConfig.endpoints[_endpoint ?? ''] as
|
||||
| EndpointFileConfig
|
||||
| undefined;
|
||||
const endpointSupportsFiles: boolean = supportsFiles[endpointType ?? _endpoint ?? ''] ?? false;
|
||||
const isUploadDisabled = (disableInputs || endpointFileConfig?.disabled) ?? false;
|
||||
|
||||
return (<>
|
||||
<FileRow
|
||||
files={files}
|
||||
setFiles={setFiles}
|
||||
abortUpload={abortUpload}
|
||||
setFilesLoading={setFilesLoading}
|
||||
isRTL={isRTL}
|
||||
Wrapper={({ children }) => (
|
||||
<div className="mx-2 mt-2 flex flex-wrap gap-2 px-2.5 md:pl-0 md:pr-4">
|
||||
{children}
|
||||
</div>
|
||||
return (
|
||||
<>
|
||||
<FileRow
|
||||
files={files}
|
||||
setFiles={setFiles}
|
||||
abortUpload={abortUpload}
|
||||
setFilesLoading={setFilesLoading}
|
||||
isRTL={isRTL}
|
||||
Wrapper={({ children }) => (
|
||||
<div className="mx-2 mt-2 flex flex-wrap gap-2 px-2.5 md:pl-0 md:pr-4">{children}</div>
|
||||
)}
|
||||
/>
|
||||
{children}
|
||||
{endpointSupportsFiles && !isUploadDisabled && (
|
||||
<AttachFile isRTL={isRTL} disabled={disableInputs} handleFileChange={handleFileChange} />
|
||||
)}
|
||||
/>
|
||||
{children}
|
||||
{endpointSupportsFiles && !isUploadDisabled && <AttachFile
|
||||
isRTL={isRTL}
|
||||
disabled={disableInputs}
|
||||
handleFileChange={handleFileChange}
|
||||
/>}
|
||||
</>);
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(FileFormWrapper);
|
||||
export default memo(FileFormWrapper);
|
||||
|
|
|
|||
|
|
@ -9,46 +9,40 @@ import { cn } from '~/utils';
|
|||
type SendButtonProps = {
|
||||
disabled: boolean;
|
||||
control: Control<{ text: string }>;
|
||||
isRTL: boolean;
|
||||
};
|
||||
|
||||
const SubmitButton = React.memo(
|
||||
forwardRef(
|
||||
(props: { disabled: boolean; isRTL: boolean }, ref: React.ForwardedRef<HTMLButtonElement>) => {
|
||||
const localize = useLocalize();
|
||||
return (
|
||||
<TooltipAnchor
|
||||
description={localize('com_nav_send_message')}
|
||||
render={
|
||||
<button
|
||||
ref={ref}
|
||||
aria-label={localize('com_nav_send_message')}
|
||||
id="send-button"
|
||||
disabled={props.disabled}
|
||||
className={cn(
|
||||
'absolute rounded-lg border border-black p-0.5 text-white outline-offset-4 transition-colors enabled:bg-black disabled:cursor-not-allowed disabled:bg-black disabled:text-gray-400 disabled:opacity-10 dark:border-white dark:bg-white dark:disabled:bg-white',
|
||||
props.isRTL
|
||||
? 'bottom-1.5 left-2 md:bottom-3 md:left-3'
|
||||
: 'bottom-1.5 right-2 md:bottom-3 md:right-3',
|
||||
)}
|
||||
data-testid="send-button"
|
||||
type="submit"
|
||||
>
|
||||
<span className="" data-state="closed">
|
||||
<SendIcon size={24} />
|
||||
</span>
|
||||
</button>
|
||||
}
|
||||
></TooltipAnchor>
|
||||
);
|
||||
},
|
||||
),
|
||||
forwardRef((props: { disabled: boolean }, ref: React.ForwardedRef<HTMLButtonElement>) => {
|
||||
const localize = useLocalize();
|
||||
return (
|
||||
<TooltipAnchor
|
||||
description={localize('com_nav_send_message')}
|
||||
render={
|
||||
<button
|
||||
ref={ref}
|
||||
aria-label={localize('com_nav_send_message')}
|
||||
id="send-button"
|
||||
disabled={props.disabled}
|
||||
className={cn(
|
||||
'rounded-full bg-text-primary p-2 text-text-primary outline-offset-4 transition-all duration-200 disabled:cursor-not-allowed disabled:text-text-secondary disabled:opacity-10',
|
||||
)}
|
||||
data-testid="send-button"
|
||||
type="submit"
|
||||
>
|
||||
<span className="" data-state="closed">
|
||||
<SendIcon size={24} />
|
||||
</span>
|
||||
</button>
|
||||
}
|
||||
></TooltipAnchor>
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
const SendButton = React.memo(
|
||||
forwardRef((props: SendButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) => {
|
||||
const data = useWatch({ control: props.control });
|
||||
return <SubmitButton ref={ref} disabled={props.disabled || !data.text} isRTL={props.isRTL} />;
|
||||
return <SubmitButton ref={ref} disabled={props.disabled || !data.text} />;
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,36 +1,37 @@
|
|||
import { TooltipAnchor } from '~/components/ui';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
export default function StopButton({ stop, setShowStopButton, isRTL }) {
|
||||
export default function StopButton({ stop, setShowStopButton }) {
|
||||
const localize = useLocalize();
|
||||
|
||||
return (
|
||||
<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
|
||||
type="button"
|
||||
className="border-gizmo-gray-900 rounded-full border-2 p-1 dark:border-gray-200"
|
||||
aria-label="Stop generating"
|
||||
onClick={(e) => {
|
||||
setShowStopButton(false);
|
||||
stop(e);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
className="text-gizmo-gray-900 h-2 w-2 dark:text-gray-200"
|
||||
height="16"
|
||||
width="16"
|
||||
<TooltipAnchor
|
||||
description={localize('com_nav_stop_generating')}
|
||||
render={
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'rounded-full bg-text-primary p-1.5 text-text-primary outline-offset-4 transition-all duration-200 disabled:cursor-not-allowed disabled:text-text-secondary disabled:opacity-10',
|
||||
)}
|
||||
aria-label={localize('com_nav_stop_generating')}
|
||||
onClick={(e) => {
|
||||
setShowStopButton(false);
|
||||
stop(e);
|
||||
}}
|
||||
>
|
||||
<path
|
||||
d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2z"
|
||||
strokeWidth="0"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="icon-lg text-surface-primary"
|
||||
>
|
||||
<rect x="7" y="7" width="10" height="10" rx="1.25" fill="currentColor"></rect>
|
||||
</svg>
|
||||
</button>
|
||||
}
|
||||
></TooltipAnchor>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export default function TextareaHeader({
|
|||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="divide-token-border-light m-1.5 flex flex-col divide-y overflow-hidden rounded-b-lg rounded-t-2xl bg-surface-primary-contrast">
|
||||
<div className="divide-token-border-light m-1.5 flex flex-col divide-y overflow-hidden rounded-b-lg rounded-t-2xl bg-surface-secondary-alt">
|
||||
<AddedConvo addedConvo={addedConvo} setAddedConvo={setAddedConvo} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue