THINKING_PANEL_COMPONENT.md
Component documentation for the ThinkingPanel AI reasoning display.
Overview
The ThinkingPanel component provides a dedicated interface for viewing AI reasoning and thought processes. It extracts, processes, and displays “thinking” content from chat messages, allowing users to understand how AI assistants approach problems and make decisions.
Component Location
- File:
src/components/ThinkingPanel.tsx - Type: Display Panel Component
- Framework: React with TypeScript
Features
- Intelligent Content Extraction: Automatically extracts thinking content from messages
- Nested Tag Handling: Properly processes nested
<thinking>tags - Multiple Positioning: Supports bottom and right panel positioning
- Expandable Interface: Collapsible panel with toggle controls
- Rich Formatting: Markdown rendering with GitHub Flavored Markdown
- Temporal Context: Shows timestamps for each thinking session
- Responsive Design: Adapts to different screen sizes
Props Interface
interface ThinkingPanelProps {
messages: ChatMessage[]; // Messages to extract thinking from
position?: 'bottom' | 'right'; // Panel positioning (default: 'bottom')
className?: string; // Additional CSS classes
}
Usage Examples
Basic Implementation
import ThinkingPanel from '@/components/ThinkingPanel';
function ChatLayout({ messages }) {
return (
<div className="chat-container">
<div className="messages-area">
{/* Chat messages */}
</div>
<ThinkingPanel
messages={messages}
position="bottom"
/>
</div>
);
}
Right-Side Panel
<div className="flex">
<div className="flex-1">
{/* Main content */}
</div>
<ThinkingPanel
messages={messages}
position="right"
className="border-l-2"
/>
</div>
Conditional Display
function EnhancedChat({ messages, showThinking }) {
return (
<div>
{/* Chat interface */}
{showThinking && (
<ThinkingPanel messages={messages} />
)}
</div>
);
}
Thinking Content Extraction
The component uses sophisticated regex to extract thinking content:
/**
* Extracts content from innermost <thinking> tags in a message
* Handles nested thinking tags by extracting only innermost content
*/
const extractThinkingContent = (content: string): string | null => {
// Regex finds innermost thinking tags (those that don't contain other thinking tags)
const innermostTagsRegex = /<thinking>((?:[^<]|<(?!thinking>))*?)<\/thinking>/g;
const results: string[] = [];
let match: RegExpExecArray | null;
// Find all innermost tags in a single pass
while ((match = innermostTagsRegex.exec(content)) !== null) {
results.push(match[1]);
}
if (results.length === 0) return null;
return results.join('\n\n');
};
Extraction Examples
<!-- Simple case -->
<thinking>This is a simple thought</thinking>
<!-- Multiple thinking blocks -->
<thinking>First thought</thinking>
Some other content
<thinking>Second thought</thinking>
<!-- Nested thinking (extracts innermost) -->
<thinking>
Outer thought
<thinking>Inner thought that gets extracted</thinking>
More outer content
</thinking>
Message Processing
The component processes messages to create thinking entries:
useEffect(() => {
const newThinkingMessages = messages
.map(message => {
const thinkingContent = extractThinkingContent(message.content);
if (!thinkingContent) return null;
return {
messageId: `${message.sender}-${message.timestamp}`,
senderName: message.userName || message.sender,
timestamp: message.timestamp || Date.now(),
content: thinkingContent
};
})
.filter(Boolean);
setThinkingMessages(newThinkingMessages);
}, [messages]);
Panel Positioning
Bottom Panel Configuration
const bottomPanelStyles = {
container: 'fixed bottom-0 left-0 right-0 z-40',
header: 'flex items-center justify-between p-2 border-t bg-background',
body: 'max-h-[300px]',
expandButton: 'absolute -top-8 right-4 bg-background border rounded-t-md'
};
Right Panel Configuration
const rightPanelStyles = {
container: 'fixed top-16 bottom-0 right-0 z-40 w-80',
header: 'flex items-center justify-between p-2 border-l bg-background',
body: 'h-full',
expandButton: 'absolute -left-8 top-4 bg-background border rounded-l-md'
};
Component Structure
Toggle Button
<Button
variant="ghost"
size="sm"
className={panelStyles.expandButton}
onClick={() => setIsExpanded(!isExpanded)}
>
{position === 'bottom' ? (
isExpanded ? <ChevronDown /> : <ChevronUp />
) : (
<ChevronDown />
)}
<span className="ml-1 text-xs">Thinking</span>
</Button>
Panel Header
<div className={panelStyles.header}>
<h3 className="text-sm font-medium">Thinking Process</h3>
<Button variant="ghost" size="sm" onClick={() => setIsExpanded(false)}>
<X className="h-4 w-4" />
</Button>
</div>
Content Area
<ScrollArea className={panelStyles.body}>
<div className="p-3 space-y-4">
{thinkingMessages.map(msg => (
<div key={msg.messageId} className="border-b pb-3 last:border-b-0">
<div className="flex items-center justify-between mb-1">
<span className="text-xs font-medium">{msg.senderName}</span>
<span className="text-xs text-muted-foreground">
{formatDistanceToNow(msg.timestamp, { addSuffix: true })}
</span>
</div>
<div className="prose prose-sm max-w-none text-xs bg-slate-50 p-2 rounded">
<Markdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw]}
>
{msg.content}
</Markdown>
</div>
</div>
))}
</div>
</ScrollArea>
Markdown Configuration
Enhanced markdown rendering for thinking content:
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
// Supports:
// - GitHub Flavored Markdown
// - Raw HTML content
// - Code blocks and syntax highlighting
// - Lists, tables, and other rich formatting
Conditional Rendering
The panel only renders when thinking content is found:
// If no thinking content found, don't render anything
if (thinkingMessages.length === 0) {
return null;
}
Styling Classes
Panel Container
.fixed .z-40 /* Positioning and z-index */
.bottom-0 .left-0 .right-0 /* Bottom panel positioning */
.top-16 .bottom-0 .right-0 .w-80 /* Right panel positioning */
Content Styling
.prose .prose-sm .max-w-none .text-xs /* Markdown content */
.bg-slate-50 .p-2 .rounded /* Content background */
.border-b .pb-3 .last:border-b-0 /* Message separation */
Interactive Elements
.absolute .-top-8 .right-4 /* Bottom panel toggle */
.absolute .-left-8 .top-4 /* Right panel toggle */
Accessibility Features
- Semantic HTML: Proper heading and section structure
- ARIA Labels: Screen reader friendly controls
- Keyboard Navigation: Full keyboard support
- Focus Management: Proper focus handling for expand/collapse
Performance Considerations
- Efficient Extraction: Single-pass regex for content extraction
- Memoized Processing: Processes messages only when they change
- Conditional Rendering: No DOM overhead when no thinking content exists
- Scroll Optimization: Virtual scrolling for large thinking content
Time Formatting
Uses date-fns for human-readable timestamps:
import { formatDistanceToNow } from 'date-fns';
// Examples:
// "2 minutes ago"
// "1 hour ago"
// "yesterday"
Error Handling
- Safe regex processing with null checks
- Graceful handling of malformed thinking tags
- Fallback values for missing timestamps or sender names
- Robust message filtering to prevent render errors
Dependencies
Core Libraries
- React hooks for state management
date-fnsfor time formattingtailwind-mergefor class composition
UI Components
@/components/ui/scroll-areafor scrollable content@/components/ui/buttonfor controls- Lucide icons for UI elements
Markdown Support
react-markdownfor content renderingremark-gfmfor GitHub Flavored Markdownrehype-rawfor HTML support
Related Components
- Chat message components that contain thinking content
- AI assistant interfaces
- Debug and development tools
- Content analysis components