🧠 fix: Messages View Height Expansion from #11142 & improve Thinking/Code-Block UX (#11148)

This commit fixes the messages view height auto-expansion issue introduced
in PR #11142 and improves the UX of both Thinking and CodeBlock components.

## Bug Fix

The ThinkingFooter component added in PR #11142 was causing the messages
view height to automatically expand. The footer was placed inside the
CSS grid's overflow-hidden container, but its presence affected the
grid height calculation, causing layout issues during streaming.

## Solution: FloatingThinkingBar

Replaced ThinkingFooter with a new FloatingThinkingBar component that:
- Uses absolute positioning (bottom-right) like CodeBlock's FloatingCodeBar
- Only appears on hover/focus, not affecting layout
- Shows expand/collapse button with dynamic icon based on state
- Uses TooltipAnchor for accessible tooltips
- Supports keyboard navigation by showing when any element in the
  container is focused (top bar buttons or content)

## Changes

### Thinking.tsx
- Added FloatingThinkingBar component with hover/focus visibility
- Updated ThinkingContent with additional bottom padding (pb-10)
- Added containerRef and hover/focus event handlers on outer container
- Removed ThinkingFooter component (replaced by FloatingThinkingBar)

### Reasoning.tsx
- Integrated FloatingThinkingBar with same hover/focus pattern
- Added containerRef and event handlers on outer container
- Supports keyboard navigation through entire component

### CodeBlock.tsx
- Updated FloatingCodeBar to show icons only (removed text labels)
- Added TooltipAnchor wrapper for copy button with localized tooltips
- Improved accessibility with proper aria-label and aria-hidden

### Localization
- Added com_ui_expand_thoughts: "Expand Thoughts"

## Accessibility

- Full keyboard navigation support: tabbing through ThinkingButton,
  copy button, and FloatingThinkingBar
- TooltipAnchor provides hover tooltips for icon-only buttons
- Proper aria-label attributes on all interactive elements
- tabIndex management based on visibility state
This commit is contained in:
Danny Avila 2025-12-29 21:21:50 -05:00 committed by GitHub
parent 06ba025bd9
commit eb1a59d2fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 148 additions and 53 deletions

View file

@ -2,7 +2,7 @@ import React, { useRef, useState, useMemo, useEffect, useCallback } from 'react'
import copy from 'copy-to-clipboard';
import { InfoIcon } from 'lucide-react';
import { Tools } from 'librechat-data-provider';
import { Clipboard, CheckMark } from '@librechat/client';
import { Clipboard, CheckMark, TooltipAnchor } from '@librechat/client';
import type { CodeBarProps } from '~/common';
import ResultSwitcher from '~/components/Messages/Content/ResultSwitcher';
import { useToolCallsMapContext, useMessageContext } from '~/Providers';
@ -114,26 +114,28 @@ const FloatingCodeBar: React.FC<FloatingCodeBarProps> = React.memo(
{allowExecution === true && (
<RunCode lang={lang} codeRef={codeRef} blockIndex={blockIndex} />
)}
<button
ref={copyButtonRef}
type="button"
tabIndex={isVisible ? 0 : -1}
className={cn(
'flex gap-2 rounded px-2 py-1 hover:bg-gray-700 focus:bg-gray-700 focus:outline focus:outline-white',
error === true ? 'h-4 w-4 items-start text-white/50' : '',
)}
onClick={handleCopy}
>
{isCopied ? <CheckMark className="h-[18px] w-[18px]" /> : <Clipboard />}
{error !== true && (
<span className="relative">
<span className="invisible">{localize('com_ui_copy_code')}</span>
<span className="absolute inset-0 flex items-center">
{isCopied ? localize('com_ui_copied') : localize('com_ui_copy_code')}
</span>
</span>
)}
</button>
<TooltipAnchor
description={isCopied ? localize('com_ui_copied') : localize('com_ui_copy_code')}
render={
<button
ref={copyButtonRef}
type="button"
tabIndex={isVisible ? 0 : -1}
aria-label={isCopied ? localize('com_ui_copied') : localize('com_ui_copy_code')}
className={cn(
'flex items-center justify-center rounded p-1.5 hover:bg-gray-700 focus:bg-gray-700 focus:outline focus:outline-white',
error === true ? 'h-4 w-4 text-white/50' : '',
)}
onClick={handleCopy}
>
{isCopied ? (
<CheckMark className="h-[18px] w-[18px]" aria-hidden="true" />
) : (
<Clipboard aria-hidden="true" />
)}
</button>
}
/>
</>
)}
</div>