💡 feat: Improve Reasoning Content UI, copy-to-clipboard, and error handling (#10278)
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run

*  feat: Refactor error handling and improve loading states in MessageContent component

*  feat: Enhance Thinking and ContentParts components with improved hover functionality and clipboard support

* fix: Adjust padding in Thinking and ContentParts components for consistent layout

*  feat: Add response label and improve message editing UI with contextual indicators

*  feat: Add isEditing prop to Feedback and Fork components for improved editing state handling

* refactor: Remove isEditing prop from Feedback and Fork components for cleaner state management

* refactor: Migrate state management from Recoil to Jotai for font size and show thinking features

* refactor: Separate ToggleSwitch into RecoilToggle and JotaiToggle components for improved clarity and state management

* refactor: Remove unnecessary comments in ToggleSwitch and MessageContent components for cleaner code

* chore: reorder import statements in Thinking.tsx

* chore: reorder import statement in EditTextPart.tsx

* chore: reorder import statement

* chore: Reorganize imports in ToggleSwitch.tsx

---------

Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
Marco Beretta 2025-10-30 22:14:38 +01:00 committed by GitHub
parent ea45d0b9c6
commit c0f1cfcaba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 528 additions and 186 deletions

View file

@ -1,4 +1,5 @@
import { memo } from 'react';
import { showThinkingAtom } from '~/store/showThinking';
import FontSizeSelector from './FontSizeSelector';
import { ForkSettings } from './ForkSettings';
import ChatDirection from './ChatDirection';
@ -28,7 +29,7 @@ const toggleSwitchConfigs = [
key: 'centerFormOnLanding',
},
{
stateAtom: store.showThinking,
stateAtom: showThinkingAtom,
localizationKey: 'com_nav_show_thinking',
switchId: 'showThinking',
hoverCardText: undefined,

View file

@ -1,18 +1,18 @@
import { useRecoilState } from 'recoil';
import { useAtom } from 'jotai';
import { Switch, InfoHoverCard, ESide } from '@librechat/client';
import { showThinkingAtom } from '~/store/showThinking';
import { useLocalize } from '~/hooks';
import store from '~/store';
export default function SaveDraft({
onCheckedChange,
}: {
onCheckedChange?: (value: boolean) => void;
}) {
const [showThinking, setSaveDrafts] = useRecoilState<boolean>(store.showThinking);
const [showThinking, setShowThinking] = useAtom(showThinkingAtom);
const localize = useLocalize();
const handleCheckedChange = (value: boolean) => {
setSaveDrafts(value);
setShowThinking(value);
if (onCheckedChange) {
onCheckedChange(value);
}

View file

@ -1,3 +1,4 @@
import { WritableAtom, useAtom } from 'jotai';
import { RecoilState, useRecoilState } from 'recoil';
import { Switch, InfoHoverCard, ESide } from '@librechat/client';
import { useLocalize } from '~/hooks';
@ -6,7 +7,7 @@ type LocalizeFn = ReturnType<typeof useLocalize>;
type LocalizeKey = Parameters<LocalizeFn>[0];
interface ToggleSwitchProps {
stateAtom: RecoilState<boolean>;
stateAtom: RecoilState<boolean> | WritableAtom<boolean, [boolean], void>;
localizationKey: LocalizeKey;
hoverCardText?: LocalizeKey;
switchId: string;
@ -16,13 +17,18 @@ interface ToggleSwitchProps {
strongLabel?: boolean;
}
const ToggleSwitch: React.FC<ToggleSwitchProps> = ({
function isRecoilState<T>(atom: unknown): atom is RecoilState<T> {
return atom != null && typeof atom === 'object' && 'key' in atom;
}
const RecoilToggle: React.FC<
Omit<ToggleSwitchProps, 'stateAtom'> & { stateAtom: RecoilState<boolean> }
> = ({
stateAtom,
localizationKey,
hoverCardText,
switchId,
onCheckedChange,
showSwitch = true,
disabled = false,
strongLabel = false,
}) => {
@ -36,9 +42,47 @@ const ToggleSwitch: React.FC<ToggleSwitchProps> = ({
const labelId = `${switchId}-label`;
if (!showSwitch) {
return null;
}
return (
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<div id={labelId}>
{strongLabel ? <strong>{localize(localizationKey)}</strong> : localize(localizationKey)}
</div>
{hoverCardText && <InfoHoverCard side={ESide.Bottom} text={localize(hoverCardText)} />}
</div>
<Switch
id={switchId}
checked={switchState}
onCheckedChange={handleCheckedChange}
disabled={disabled}
className="ml-4"
data-testid={switchId}
aria-labelledby={labelId}
/>
</div>
);
};
const JotaiToggle: React.FC<
Omit<ToggleSwitchProps, 'stateAtom'> & { stateAtom: WritableAtom<boolean, [boolean], void> }
> = ({
stateAtom,
localizationKey,
hoverCardText,
switchId,
onCheckedChange,
disabled = false,
strongLabel = false,
}) => {
const [switchState, setSwitchState] = useAtom(stateAtom);
const localize = useLocalize();
const handleCheckedChange = (value: boolean) => {
setSwitchState(value);
onCheckedChange?.(value);
};
const labelId = `${switchId}-label`;
return (
<div className="flex items-center justify-between">
@ -52,13 +96,29 @@ const ToggleSwitch: React.FC<ToggleSwitchProps> = ({
id={switchId}
checked={switchState}
onCheckedChange={handleCheckedChange}
disabled={disabled}
className="ml-4"
data-testid={switchId}
aria-labelledby={labelId}
disabled={disabled}
/>
</div>
);
};
const ToggleSwitch: React.FC<ToggleSwitchProps> = (props) => {
const { stateAtom, showSwitch = true } = props;
if (!showSwitch) {
return null;
}
const isRecoil = isRecoilState(stateAtom);
if (isRecoil) {
return <RecoilToggle {...props} stateAtom={stateAtom as RecoilState<boolean>} />;
}
return <JotaiToggle {...props} stateAtom={stateAtom as WritableAtom<boolean, [boolean], void>} />;
};
export default ToggleSwitch;