Implement a Live Chat Widget¶
Overview¶
The Primethink Live Chat Widget is a floating support chat interface that embeds Primethink public chat sessions into any website. It provides a seamless way to offer AI-powered support to your users without leaving your page.
What It Does¶
- Floating Button: A customizable circular button that floats in the corner of your page
- Chat Interface: A draggable, resizable chat window that opens when the button is clicked
- Session Management: Automatically creates and persists chat sessions across page refreshes
- iframe Integration: Loads Primethink chat sessions in a secure, sandboxed iframe
- Local Storage: Maintains session continuity using browser localStorage
Key Features¶
- ✅ Session Persistence - Chat sessions survive page refreshes and navigation
- ✅ Draggable Interface - Users can move the chat window anywhere on screen
- ✅ Minimize/Maximize - Collapse the chat to title bar only
- ✅ Mobile Responsive - Adapts to mobile screens automatically
- ✅ No Dependencies - Pure HTML, CSS, and JavaScript
- ✅ Easy Configuration - Just update the chat ID to use your own chat
How It Works¶
Flow Diagram¶
User Action Widget Action Backend Action
─────────── ───────────── ──────────────
1. Click Button → Check localStorage
├─ Has session? → Load saved URL
└─ No session? → Continue to step 2
2. Create Session → POST /public-session → Generate session ID
→ Return session ID
3. Receive Response ← Get session ID: "abc-123"
Build URL: chat?sid=abc-123
4. Load Chat → Set iframe.src = URL
Save URL to localStorage
5. Chat Ready → User interacts with chat
6. Page Refresh → Check localStorage
Load saved session URL
Restore chat window
Technical Flow¶
- Initialization: Widget loads and checks localStorage for existing session
- Button Click: User clicks the floating button
- API Call: POST request to
https://app.primethink.ai/api/v1/public/chats/{chat-id}/public-session - Session Creation: Backend returns a session ID (e.g.,
"00136af9-a102-42e1-9ca1-e24ba6c30b4a") - URL Construction: Widget builds URL:
https://app.primethink.ai/public/chats/{chat-id}?sid={session-id} - Load Chat: iframe loads the chat with session ID
- Persist Session: URL saved to localStorage
- Restore on Refresh: Next page load/refresh restores the saved session
Minimal Implementation¶
Step 1: Basic HTML Structure¶
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Support Chat Example</title>
<style>
/* Styles will go here */
</style>
</head>
<body>
<!-- Your page content -->
<h1>My Website</h1>
<!-- Floating Chat Button -->
<button id="chat-button">💬</button>
<!-- Chat Frame -->
<div id="chat-frame">
<div class="title-bar">
<h3>Support</h3>
<button id="close-btn">×</button>
</div>
<div class="chat-content">
<iframe id="support-iframe"></iframe>
</div>
</div>
<script>
/* JavaScript will go here */
</script>
</body>
</html>
Step 2: Essential Styles¶
/* Floating Button */
#chat-button {
position: fixed;
bottom: 30px;
right: 30px;
width: 60px;
height: 60px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
color: white;
font-size: 24px;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
z-index: 1000;
}
#chat-button:hover {
transform: scale(1.1);
}
#chat-button.hidden {
display: none;
}
/* Chat Frame */
#chat-frame {
position: fixed;
bottom: 100px;
right: 30px;
width: 380px;
height: 600px;
background: white;
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0,0,0,0.3);
display: none;
flex-direction: column;
z-index: 1001;
overflow: hidden;
}
#chat-frame.open {
display: flex;
}
/* Title Bar */
.title-bar {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 16px 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.title-bar button {
background: rgba(255,255,255,0.2);
border: none;
color: white;
width: 28px;
height: 28px;
border-radius: 50%;
cursor: pointer;
font-size: 20px;
}
/* Chat Content */
.chat-content {
flex: 1;
position: relative;
}
#support-iframe {
width: 100%;
height: 100%;
border: none;
}
Step 3: Core JavaScript¶
class SupportChat {
constructor() {
// DOM elements
this.chatButton = document.getElementById('chat-button');
this.chatFrame = document.getElementById('chat-frame');
this.closeBtn = document.getElementById('close-btn');
this.iframe = document.getElementById('support-iframe');
// Configuration - CHANGE THESE VALUES
this.chatId = '00afb862-4ae8-432e-ae49-f34d1c484724';
this.baseUrl = `https://app.primethink.ai/public/chats/${this.chatId}`;
this.apiUrl = `https://app.primethink.ai/api/v1/public/chats/${this.chatId}/public-session`;
// Storage keys
this.STORAGE_KEY = 'support_chat_session_url';
this.init();
}
init() {
// Event listeners
this.chatButton.addEventListener('click', () => this.openChat());
this.closeBtn.addEventListener('click', () => this.closeChat());
// Restore saved session
this.restoreSession();
}
async openChat() {
// Show chat
this.chatFrame.classList.add('open');
this.chatButton.classList.add('hidden');
// Check for saved session
const savedUrl = localStorage.getItem(this.STORAGE_KEY);
if (savedUrl) {
this.iframe.src = savedUrl;
return;
}
// Create new session
try {
const response = await fetch(this.apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({})
});
const sessionId = await response.json();
const sessionUrl = `${this.baseUrl}?sid=${sessionId}`;
this.iframe.src = sessionUrl;
localStorage.setItem(this.STORAGE_KEY, sessionUrl);
} catch (error) {
console.error('Error creating session:', error);
this.iframe.src = this.baseUrl; // Fallback
}
}
closeChat() {
this.chatFrame.classList.remove('open');
this.chatButton.classList.remove('hidden');
this.iframe.src = '';
localStorage.removeItem(this.STORAGE_KEY);
}
restoreSession() {
const savedUrl = localStorage.getItem(this.STORAGE_KEY);
if (savedUrl) {
this.chatFrame.classList.add('open');
this.chatButton.classList.add('hidden');
this.iframe.src = savedUrl;
}
}
}
// Initialize
new SupportChat();
Configuration¶
Required Values¶
To use your own Primethink chat, you need:
- Chat ID: Your public chat UUID
- Base URL: The public chat page URL
- API URL: The endpoint to create sessions
// Example for chat ID: abc-123-def-456
this.chatId = 'abc-123-def-456';
this.baseUrl = `https://app.primethink.ai/public/chats/${this.chatId}`;
this.apiUrl = `https://app.primethink.ai/api/v1/public/chats/${this.chatId}/public-session`;
Finding Your Chat ID¶
Your Primethink public chat URL looks like:
https://app.primethink.ai/public/chats/00afb862-4ae8-432e-ae49-f34d1c484724
└─────────────┬─────────────┘
This is your Chat ID
API Details¶
Create Session Endpoint¶
URL: POST https://app.primethink.ai/api/v1/public/chats/{chat-id}/public-session
Headers:
Request Body:
Response: Plain JSON string containing session ID
Usage: Append to URL as ?sid= parameter
Example with cURL¶
curl -X POST "https://app.primethink.ai/api/v1/public/chats/<chat_share_id>/public-session" \
-H "Content-Type: application/json" \
-H "Accept: */*" \
-d '{}'
Response:
LocalStorage Structure¶
Keys Used¶
// Session URL with sid parameter
'support_chat_session_url' = 'https://app.primethink.ai/public/chats/abc-123?sid=def-456'
// Chat open state
'support_chat_is_open' = 'true' | 'false'
Session Lifecycle¶
-
Create: When user opens chat for first time
-
Restore: On page load/refresh
-
Clear: When user closes chat
Advanced Features¶
1. Loading Indicator¶
Add a loading spinner while the chat loads:
<div class="chat-content">
<div class="loading-spinner" id="spinner">Loading...</div>
<iframe id="support-iframe"></iframe>
</div>
iframe.addEventListener('load', () => {
document.getElementById('spinner').style.display = 'none';
});
2. Minimize Feature¶
Allow users to minimize the chat:
3. Draggable Window¶
Make the chat draggable:
dragStart(e) {
this.isDragging = true;
const rect = this.chatFrame.getBoundingClientRect();
this.offsetX = e.clientX - rect.left;
this.offsetY = e.clientY - rect.top;
}
drag(e) {
if (!this.isDragging) return;
this.chatFrame.style.left = (e.clientX - this.offsetX) + 'px';
this.chatFrame.style.top = (e.clientY - this.offsetY) + 'px';
}
4. Mobile Responsive¶
Adjust for mobile devices:
@media (max-width: 768px) {
#chat-frame {
width: calc(100vw - 40px);
height: calc(100vh - 120px);
right: 20px;
bottom: 90px;
}
}
Multiple Chats Example¶
Use Case: City-Specific Support¶
If you want to offer different chat sessions based on user selection (e.g., London vs Sydney support):
HTML Structure¶
<!-- Floating Button -->
<button id="chat-button">💬</button>
<!-- Chat Frame -->
<div id="chat-frame">
<div class="title-bar">
<h3>Support</h3>
<button id="close-btn">×</button>
</div>
<!-- Welcome Screen - Choose Location -->
<div id="welcome-screen" class="chat-content">
<h2>Select Your Location</h2>
<button class="city-btn" data-city="london">🇬🇧 London</button>
<button class="city-btn" data-city="sydney">🇦🇺 Sydney</button>
</div>
<!-- Chat Screen - After Selection -->
<div id="chat-screen" class="chat-content" style="display: none;">
<div class="chat-header">
<span id="city-badge"></span>
<button id="back-btn">← Back</button>
</div>
<iframe id="support-iframe"></iframe>
</div>
</div>
JavaScript Implementation¶
class MultiChatSupport {
constructor() {
this.chatButton = document.getElementById('chat-button');
this.chatFrame = document.getElementById('chat-frame');
this.closeBtn = document.getElementById('close-btn');
this.welcomeScreen = document.getElementById('welcome-screen');
this.chatScreen = document.getElementById('chat-screen');
this.iframe = document.getElementById('support-iframe');
this.cityBadge = document.getElementById('city-badge');
this.backBtn = document.getElementById('back-btn');
// Configuration - Multiple Chats
this.chats = {
london: {
chatId: '00afb862-4ae8-432e-ae49-f34d1c484724',
name: 'London Support'
},
sydney: {
chatId: '00a668d6-e2b7-4240-9ae5-b4c877f65a18',
name: 'Sydney Support'
}
};
this.STORAGE_KEYS = {
SESSION_URL: 'chat_session_url',
SELECTED_CITY: 'chat_selected_city',
IS_OPEN: 'chat_is_open'
};
this.init();
}
init() {
// Button event listeners
this.chatButton.addEventListener('click', () => this.openChat());
this.closeBtn.addEventListener('click', () => this.closeChat());
this.backBtn.addEventListener('click', () => this.showWelcome());
// City selection buttons
document.querySelectorAll('.city-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const city = e.target.dataset.city;
this.selectCity(city);
});
});
// Restore session
this.restoreSession();
}
openChat() {
this.chatFrame.classList.add('open');
this.chatButton.classList.add('hidden');
localStorage.setItem(this.STORAGE_KEYS.IS_OPEN, 'true');
}
closeChat() {
this.chatFrame.classList.remove('open');
this.chatButton.classList.remove('hidden');
this.iframe.src = '';
// Clear session
localStorage.removeItem(this.STORAGE_KEYS.SESSION_URL);
localStorage.removeItem(this.STORAGE_KEYS.SELECTED_CITY);
localStorage.setItem(this.STORAGE_KEYS.IS_OPEN, 'false');
// Show welcome screen
this.showWelcome();
}
showWelcome() {
this.welcomeScreen.style.display = 'flex';
this.chatScreen.style.display = 'none';
}
async selectCity(city) {
const chatConfig = this.chats[city];
if (!chatConfig) return;
// Update UI
this.cityBadge.textContent = chatConfig.name;
this.welcomeScreen.style.display = 'none';
this.chatScreen.style.display = 'flex';
// Create session
try {
const apiUrl = `https://app.primethink.ai/api/v1/public/chats/${chatConfig.chatId}/public-session`;
const response = await fetch(apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({})
});
const sessionId = await response.json();
const sessionUrl = `https://app.primethink.ai/public/chats/${chatConfig.chatId}?sid=${sessionId}`;
// Load chat
this.iframe.src = sessionUrl;
// Save session
localStorage.setItem(this.STORAGE_KEYS.SESSION_URL, sessionUrl);
localStorage.setItem(this.STORAGE_KEYS.SELECTED_CITY, city);
} catch (error) {
console.error('Error creating session:', error);
}
}
restoreSession() {
const savedUrl = localStorage.getItem(this.STORAGE_KEYS.SESSION_URL);
const selectedCity = localStorage.getItem(this.STORAGE_KEYS.SELECTED_CITY);
const wasOpen = localStorage.getItem(this.STORAGE_KEYS.IS_OPEN) === 'true';
if (savedUrl && selectedCity && wasOpen) {
const chatConfig = this.chats[selectedCity];
// Restore chat state
this.chatFrame.classList.add('open');
this.chatButton.classList.add('hidden');
this.welcomeScreen.style.display = 'none';
this.chatScreen.style.display = 'flex';
// Restore content
this.cityBadge.textContent = chatConfig.name;
this.iframe.src = savedUrl;
}
}
}
// Initialize
new MultiChatSupport();
CSS for Multiple Chats¶
/* Welcome Screen */
#welcome-screen {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
}
#welcome-screen h2 {
font-size: 18px;
margin-bottom: 30px;
text-align: center;
color: #333;
}
.city-btn {
width: 100%;
max-width: 250px;
padding: 16px 24px;
margin: 8px 0;
border: 2px solid #667eea;
background: white;
color: #667eea;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.city-btn:hover {
background: #667eea;
color: white;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
/* Chat Screen */
#chat-screen {
flex-direction: column;
}
.chat-header {
padding: 16px 20px;
background: #f8f9fa;
border-bottom: 1px solid #e0e0e0;
display: flex;
justify-content: space-between;
align-items: center;
}
#city-badge {
background: #667eea;
color: white;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
#back-btn {
padding: 6px 16px;
background: white;
border: 1px solid #ddd;
border-radius: 6px;
color: #666;
cursor: pointer;
font-size: 13px;
}
#back-btn:hover {
border-color: #667eea;
color: #667eea;
}
#chat-screen iframe {
flex: 1;
width: 100%;
border: none;
}
Multiple Chats Flow¶
1. User clicks button → Welcome screen shows
2. User selects "London" →
- POST /chats/london-id/public-session
- Get session ID
- Load London chat with ?sid=session-id
- Save "london" + session URL to localStorage
3. User refreshes page →
- Check localStorage
- Find "london" + saved URL
- Restore London chat automatically
4. User clicks "Back" →
- Return to welcome screen
- Keep session in localStorage (optional)
5. User closes chat →
- Clear all localStorage
- Reset to initial state
Best Practices¶
1. Error Handling¶
Always provide fallbacks:
try {
// Create session
const sessionId = await response.json();
const sessionUrl = `${baseUrl}?sid=${sessionId}`;
iframe.src = sessionUrl;
} catch (error) {
console.error('Session creation failed:', error);
// Fallback: load chat without session
iframe.src = baseUrl;
}
2. CORS Considerations¶
Ensure your domain is allowed to make requests to Primethink API. If you encounter CORS errors, the Primethink backend needs to allow your domain.
3. Security¶
- Use
sandboxattribute on iframe for security - Don't store sensitive data in localStorage
- Clear session on logout if needed
4. Performance¶
- Load chat only when user clicks button (lazy loading)
- Clear iframe when chat closes to free memory
- Limit localStorage size by removing old sessions
5. Accessibility¶
- Add ARIA labels for screen readers
- Ensure keyboard navigation works
- Provide clear visual indicators
Troubleshooting¶
Chat doesn't load¶
Problem: iframe remains blank after clicking button
Solutions: - Check browser console for errors - Verify chat ID is correct - Ensure API endpoint is accessible - Check CORS configuration - Verify network connectivity
Session not persisting¶
Problem: Chat resets after page refresh
Solutions: - Check localStorage is enabled in browser - Verify storage key is correct - Ensure session URL is being saved - Check browser privacy settings
API returns 404¶
Problem: POST request fails with 404
Solutions: - Verify chat ID is correct - Ensure chat is set to public in Primethink - Check API URL format - Test with cURL first
Multiple instances conflict¶
Problem: Multiple chat widgets on same page interfere
Solutions: - Use unique storage keys per widget - Namespace your class/variables - Consider using data attributes for configuration
Examples Repository¶
For complete working examples, see: - generic_support/canvas.html - Single chat implementation - wundr_support_chat/canvas.html - Multiple chats with city selection
Both examples include: - Full HTML, CSS, and JavaScript - Session persistence - Drag and drop - Minimize/maximize - Mobile responsive - Loading indicators
Support¶
For issues or questions: 1. Check browser console for errors 2. Verify API responses with cURL 3. Review localStorage in DevTools 4. Test with minimal example first
License¶
This implementation guide is provided for developers integrating Primethink chat into their applications.