mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-03-03 14:50:19 +01:00
💡 feat: Improve Reasoning Content UI, copy-to-clipboard, and error handling (#10278)
* ✨ 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:
parent
ea45d0b9c6
commit
c0f1cfcaba
13 changed files with 528 additions and 186 deletions
|
|
@ -3,6 +3,7 @@ import { useForm } from 'react-hook-form';
|
|||
import { TextareaAutosize } from '@librechat/client';
|
||||
import { ContentTypes } from 'librechat-data-provider';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { Lightbulb, MessageSquare } from 'lucide-react';
|
||||
import { useUpdateMessageContentMutation } from 'librechat-data-provider/react-query';
|
||||
import type { Agents } from 'librechat-data-provider';
|
||||
import type { TEditProps } from '~/common';
|
||||
|
|
@ -153,6 +154,22 @@ const EditTextPart = ({
|
|||
|
||||
return (
|
||||
<Container message={message}>
|
||||
{part.type === ContentTypes.THINK && (
|
||||
<div className="mt-2 flex items-center gap-1.5 text-xs text-text-secondary">
|
||||
<span className="flex gap-2 rounded-lg bg-surface-tertiary px-1.5 py-1 font-medium">
|
||||
<Lightbulb className="size-3.5" />
|
||||
{localize('com_ui_thoughts')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{part.type !== ContentTypes.THINK && (
|
||||
<div className="mt-2 flex items-center gap-1.5 text-xs text-text-secondary">
|
||||
<span className="flex gap-2 rounded-lg bg-surface-tertiary px-1.5 py-1 font-medium">
|
||||
<MessageSquare className="size-3.5" />
|
||||
{localize('com_ui_response')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="bg-token-main-surface-primary relative flex w-full flex-grow flex-col overflow-hidden rounded-2xl border border-border-medium text-text-primary [&:has(textarea:focus)]:border-border-heavy [&:has(textarea:focus)]:shadow-[0_2px_6px_rgba(0,0,0,.05)]">
|
||||
<TextareaAutosize
|
||||
{...registerProps}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,32 @@ type ReasoningProps = {
|
|||
reasoning: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reasoning Component (MODERN SYSTEM)
|
||||
*
|
||||
* Used for structured content parts with ContentTypes.THINK type.
|
||||
* This handles modern message format where content is an array of typed parts.
|
||||
*
|
||||
* Pattern: `{ content: [{ type: "think", think: "<think>content</think>" }, ...] }`
|
||||
*
|
||||
* Used by:
|
||||
* - ContentParts.tsx → Part.tsx for structured messages
|
||||
* - Agent/Assistant responses (OpenAI Assistants, custom agents)
|
||||
* - O-series models (o1, o3) with reasoning capabilities
|
||||
* - Modern Claude responses with thinking blocks
|
||||
*
|
||||
* Key differences from legacy Thinking.tsx:
|
||||
* - Works with content parts array instead of plain text
|
||||
* - Strips `<think>` tags instead of `:::thinking:::` markers
|
||||
* - Uses shared ThinkingButton via ContentParts.tsx
|
||||
* - Controlled by MessageContext isExpanded state
|
||||
*
|
||||
* For legacy text-based messages, see Thinking.tsx component.
|
||||
*/
|
||||
const Reasoning = memo(({ reasoning }: ReasoningProps) => {
|
||||
const { isExpanded, nextType } = useMessageContext();
|
||||
|
||||
// Strip <think> tags from the reasoning content (modern format)
|
||||
const reasoningText = useMemo(() => {
|
||||
return reasoning
|
||||
.replace(/^<think>\s*/, '')
|
||||
|
|
@ -21,18 +45,20 @@ const Reasoning = memo(({ reasoning }: ReasoningProps) => {
|
|||
return null;
|
||||
}
|
||||
|
||||
// Note: The toggle button is rendered separately in ContentParts.tsx
|
||||
// This component only handles the collapsible content area
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'grid transition-all duration-300 ease-out',
|
||||
nextType !== ContentTypes.THINK && isExpanded && 'mb-8',
|
||||
nextType !== ContentTypes.THINK && isExpanded && 'mb-4',
|
||||
)}
|
||||
style={{
|
||||
gridTemplateRows: isExpanded ? '1fr' : '0fr',
|
||||
}}
|
||||
>
|
||||
<div className="overflow-hidden">
|
||||
<ThinkingContent isPart={true}>{reasoningText}</ThinkingContent>
|
||||
<ThinkingContent>{reasoningText}</ThinkingContent>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue