AI Chat Assistant
The AI Chat Assistant is a sophisticated conversational interface built with React and TypeScript that provides an interactive way for visitors to learn about your experience, projects, and skills. It features context-aware responses and blog post summarization capabilities.
Overview
The chat assistant is powered by OpenAI’s GPT-4 model and integrates seamlessly with your portfolio, offering both general information about your work and specific insights into blog posts.
Key Features
Context-aware responses about your experience and projects
Blog post summarization and Q&A
Markdown rendering with syntax highlighting
Fullscreen and windowed modes
Chat history management
Responsive design for mobile and desktop
Implementation
The chat assistant is implemented in src/components/ChatBox.tsx and uses React hooks for state management.
Core Architecture
Message State Management
The component maintains conversation history using React’s useState hook: const [ messages , setMessages ] = useState < Message []>([]);
const [ isLoading , setIsLoading ] = useState ( false );
const [ error , setError ] = useState < string | null >( null );
Location: src/components/ChatBox.tsx:34-36
API Integration
Messages are sent to a Netlify serverless function that interfaces with OpenAI: const API_URL = process . env . NODE_ENV === 'development'
? '/.netlify/functions/chat'
: 'https://ethanclinick.netlify.app/.netlify/functions/chat' ;
Location: src/components/ChatBox.tsx:9-11
Context-Aware Responses
The system prompt can be enhanced with blog context for post-specific conversations: const createSystemPromptWithBlogContext = useCallback (( context ?: BlogContext ) => {
const basePrompt = createEnhancedSystemPrompt ();
if ( context ) {
return ` ${ basePrompt }
You are also helping the user understand a blog post. Here is the blog context:
Blog Title: ${ context . title }
Blog Excerpt: ${ context . excerpt }
Blog Content:
${ context . content } ` ;
}
return basePrompt ;
}, []);
Location: src/components/ChatBox.tsx:73-91
Configuration
API Parameters
The chat assistant uses the following OpenAI API configuration:
body : JSON . stringify ({
messages: [
{
role: 'system' ,
content: createSystemPromptWithBlogContext ( activeContext )
},
... messages ,
userMessage
],
temperature: 0.7 ,
top_p: 0.9 ,
max_tokens: 4000 ,
model: "gpt-4o-mini"
})
Location: src/components/ChatBox.tsx:114-126
temperature: 0.7 - Balanced creativity and consistency
top_p: 0.9 - Nucleus sampling for diverse responses
max_tokens: 4000 - Sufficient length for detailed answers
model: gpt-4o-mini - Cost-effective GPT-4 variant
Features in Detail
Markdown Rendering
The assistant uses react-markdown for rich text formatting with custom components:
code : ({ inline , className , children }) => {
if ( inline ) {
return < code className = "bg-gray-800 text-orange-400 px-1.5 py-0.5 rounded text-sm font-mono" > { children } </ code > ;
}
return (
< div className = "my-4" >
< pre className = "bg-gray-900 border border-gray-800 p-4 rounded-lg overflow-x-auto" >
< code className = { ` ${ className } text-sm font-mono` } > { children } </ code >
</ pre >
</ div >
);
}
Location: src/components/ChatBox.tsx:203-213a : ({ href , children }) => (
< a
href = { href }
target = "_blank"
rel = "noopener noreferrer"
className = "text-orange-400 hover:text-orange-300 hover:underline transition-colors duration-200"
>
{ children }
</ a >
)
Location: src/components/ChatBox.tsx:220-229ul : ({ children }) => (
< ul className = "list-disc pl-5 mb-4 space-y-2 marker:text-gray-500" > { children } </ ul >
),
ol : ({ children }) => (
< ol className = "list-decimal pl-5 mb-4 space-y-2 marker:text-gray-500" > { children } </ ol >
)
Location: src/components/ChatBox.tsx:194-199
AI responses are automatically formatted for better readability:
const formatAIResponse = ( text : string ) : string => {
// Add double line breaks between sections
text = text . replace ( / \n (?= [ A-Z ] ) / g , ' \n\n ' );
// Add spacing after punctuation if missing
text = text . replace ( / ( [ .!? ] )( [ A-Z ] ) / g , '$1 \n\n $2' );
// Ensure proper spacing around lists
text = text . replace ( / ( [ .!? ] ) \n ( [ •\-* ] ) / g , '$1 \n\n $2' );
// Add spacing around code blocks
text = text . replace ( /```/ g , ' \n ``` \n ' );
return text . trim ();
};
Location: src/components/ChatBox.tsx:14-30
Blog Context Integration
When opened from a blog post, the assistant can summarize and discuss the content:
const handleSummarize = () => {
if ( post ) {
setBlogContextForChat ({
title: post . title ,
content: post . content ,
excerpt: post . excerpt ,
});
setIsChatOpen ( true );
}
};
Source: src/app/BlogPost.tsx:52-61
Visitors can click the “AI Summary” button on any blog post to get an instant summary and ask follow-up questions about the content.
User Interface
Layout Modes
The chat assistant supports two display modes:
Windowed Mode (Default)
Fixed position at bottom-right on desktop
Full screen on mobile
Width: 480px on desktop
Max height: 80vh
Fullscreen Mode (Desktop only)
Expands to cover entire viewport
Centered content with max-width constraint
Better for extended conversations
< div className = { `fixed ${
isFullscreen
? 'inset-0 w-full h-screen rounded-none'
: 'inset-0 md:inset-auto md:bottom-8 md:right-8 w-full md:w-[480px] h-screen md:h-auto md:max-h-[80vh] rounded-none md:rounded-2xl'
} bg-black border border-gray-800 shadow-2xl flex flex-col z-50` } >
Location: src/components/ChatBox.tsx:261-265
Interactive Controls
Clear Chat
Removes all messages except the greeting: const handleClearChat = () => {
setMessages ([{
role: 'assistant' ,
content: currentBlogContext
? `Chat cleared. I still have context about the blog " ${ currentBlogContext . title } ".`
: "Hi! I'm Ethan's AI assistant. Feel free to ask me about his experience..."
}]);
};
Location: src/components/ChatBox.tsx:245-252
Toggle Fullscreen
Switches between windowed and fullscreen modes on desktop: const toggleFullscreen = () => {
setIsFullscreen ( ! isFullscreen );
};
Location: src/components/ChatBox.tsx:254-256
Close Chat
Closes the chat window and optionally clears blog context: const handleChatClose = () => {
setIsChatOpen ( false );
setBlogContextForChat ( undefined );
};
Source: src/app/BlogPost.tsx:63-66
Usage Examples
Opening the Chat Assistant
import ChatBox from './components/ChatBox' ;
function App () {
const [ isChatOpen , setIsChatOpen ] = useState ( false );
return (
<>
< button onClick = { () => setIsChatOpen ( true ) } >
Open Chat
</ button >
< ChatBox
isOpen = { isChatOpen }
onClose = { () => setIsChatOpen ( false ) }
isDarkMode = { true }
/>
</>
);
}
With Blog Context
< ChatBox
isOpen = { isChatOpen }
onClose = { handleChatClose }
blogContext = { {
title: post . title ,
content: post . content ,
excerpt: post . excerpt
} }
initialMessage = "Please summarize this blog post for me."
/>
TypeScript Interfaces
interface Message {
role : 'user' | 'assistant' | 'system' ;
content : string ;
}
interface BlogContext {
title : string ;
content : string ;
excerpt : string ;
}
interface ChatBoxProps {
isOpen : boolean ;
onClose : () => void ;
isDarkMode ?: boolean ;
initialMessage ?: string ;
blogContext ?: BlogContext ;
}
Source: src/types/types.ts
Best Practices
API Security - Never expose API keys in client-side code. Use serverless functions or backend proxies.
Rate Limiting - Implement rate limiting to prevent API abuse.
Error Handling - Always provide user-friendly error messages.
Loading States - Show clear loading indicators during API calls.
Accessibility - Ensure keyboard navigation and screen reader compatibility.
Next Steps
Blog System Learn how blog posts integrate with the AI assistant
Project Showcase Explore the interactive project display system