@kolari-ai/widget-sdk
v0.1.10
Published
Embeddable voice AI widget SDK for Kolari AI platform
Readme
@kolari-ai/widget-sdk
Embeddable voice AI widget SDK for Kolari AI platform
A sleek, unobtrusive pill widget that adds voice AI to any website. Features a live 3D orb animation, one-click voice interaction, and seamless integration. Perfect for customer support, lead qualification, appointment booking, and more.
🔗 Server URL: Use https://www.kolari.ai as your serverUrl in production.
🐛 Found an issue? Report it on our GitHub Issues page.
Features
- 🎙️ One-Click Voice Interaction - Users click to talk, click to end
- ✨ 3D Animated Orb - Live visualization that responds to voice activity
- 📱 Fully Responsive - Works on desktop and mobile browsers
- 🎨 Customizable Branding - Match your brand colors and style
- 🔒 Secure & Private - HTTPS required, microphone permissions handled automatically
- ⚡ Fast & Lightweight - Minimal bundle size, instant loading
Installation
Via npm
npm install @kolari-ai/widget-sdkVia Local Build (Development)
<!-- Use the locally built browser bundle -->
<script src="./dist/index.browser.js"></script>Via CDN (Coming Soon)
<script src="https://cdn.kolari.ai/widget/latest/kolari-widget.browser.js"></script>Quick Start
<!DOCTYPE html>
<html>
<head>
<title>My Website</title>
</head>
<body>
<h1>Welcome to my website</h1>
<!-- Load the widget SDK -->
<script src="./dist/index.browser.js"></script>
<!-- Initialize the widget -->
<script>
KolariWidget.init({
// Required
apiKey: 'pk_live_...', // Your public API key
agentId: 'your-agent-id', // Your agent ID
serverUrl: 'https://www.kolari.ai', // Kolari server URL
// Branding (optional)
branding: {
agentName: 'Support Assistant',
primaryColor: '#6366f1',
secondaryColor: '#8b5cf6'
},
// Behavior (optional)
autoStart: false, // Set true to auto-connect on page load
debug: false, // Enable for development debugging
// Event handlers (optional)
onReady: () => console.log('Widget ready!'),
onConnect: () => console.log('Connected to agent'),
onDisconnect: () => console.log('Disconnected'),
onError: (error) => console.error('Widget error:', error)
});
</script>
</body>
</html>That's it! The widget will appear as a pill at the bottom-right of your page with a live orb and "VOICE CHAT" button.
Using with React
import { KolariWidget } from '@kolari-ai/widget-sdk';
import { useEffect } from 'react';
function App() {
useEffect(() => {
const widget = KolariWidget.init({
apiKey: 'pk_live_...',
agentId: 'your-agent-id',
serverUrl: 'https://www.kolari.ai',
branding: {
primaryColor: '#6366f1',
agentName: 'Assistant'
},
onConnect: () => console.log('Connected!'),
onDisconnect: () => console.log('Disconnected'),
});
return () => widget.destroy();
}, []);
return <div>Your app content</div>;
}Using with Vue
<template>
<div>Your app content</div>
</template>
<script>
import { KolariWidget } from '@kolari-ai/widget-sdk';
export default {
mounted() {
this.widget = KolariWidget.init({
apiKey: 'pk_live_...',
agentId: 'your-agent-id',
serverUrl: 'https://www.kolari.ai',
branding: {
primaryColor: '#6366f1',
agentName: 'Assistant'
}
});
},
beforeUnmount() {
this.widget?.destroy();
}
};
</script>Using with Next.js
'use client'; // For Next.js App Router
import { useEffect } from 'react';
export default function Home() {
useEffect(() => {
// Dynamically import to avoid SSR issues
import('@kolari-ai/widget-sdk').then(({ KolariWidget }) => {
const widget = KolariWidget.init({
apiKey: process.env.NEXT_PUBLIC_KOLARI_API_KEY!,
agentId: process.env.NEXT_PUBLIC_KOLARI_AGENT_ID!,
serverUrl: process.env.NEXT_PUBLIC_KOLARI_SERVER_URL!,
branding: {
primaryColor: '#6366f1',
agentName: 'Support Assistant'
}
});
return () => widget.destroy();
});
}, []);
return <main>Your app content</main>;
}Configuration Options
interface WidgetConfig {
// ===== REQUIRED =====
apiKey: string; // Your public API key from Kolari dashboard
agentId: string; // The agent ID to connect to
serverUrl: string; // Kolari server URL
// Production: 'https://www.kolari.ai'
// Development: 'http://localhost:3000'
// ===== BRANDING =====
branding?: {
primaryColor?: string; // Hex color code (e.g., '#6366f1')
secondaryColor?: string; // Hex color code (e.g., '#8b5cf6')
agentName?: string; // Display name for the agent (e.g., 'Support Assistant')
companyName?: string; // Your company name
};
// ===== DISPLAY =====
displayMode?: 'compact' | 'expanded'; // Widget display mode (default: 'compact')
// ===== BEHAVIOR =====
autoStart?: boolean; // Auto-connect on page load (default: false)
enableRecording?: boolean; // Enable session recording (default: true)
// ===== EVENT HANDLERS =====
onReady?: () => void; // Widget initialized and ready
onConnect?: () => void; // Connected to agent
onDisconnect?: () => void; // Disconnected from agent
onError?: (error: WidgetError) => void; // Error occurred
onStateChange?: (state: WidgetState) => void; // Widget state changed
onAudioLevel?: (level: number) => void; // Audio level update (0-1)
// ===== ADVANCED =====
debug?: boolean; // Enable debug logging (default: false)
reconnectAttempts?: number; // Max reconnection attempts (default: 3)
reconnectDelay?: number; // Delay between reconnects in ms (default: 2000)
}Configuration Examples
Minimal Setup:
KolariWidget.init({
apiKey: 'pk_live_...',
agentId: 'agent-id',
serverUrl: 'https://www.kolari.ai'
});With Branding:
KolariWidget.init({
apiKey: 'pk_live_...',
agentId: 'agent-id',
serverUrl: 'https://www.kolari.ai',
branding: {
agentName: 'Sales Assistant',
primaryColor: '#6366f1',
secondaryColor: '#8b5cf6'
}
});With Event Handlers:
KolariWidget.init({
apiKey: 'pk_live_...',
agentId: 'agent-id',
serverUrl: 'https://www.kolari.ai',
branding: {
agentName: 'Support Agent',
primaryColor: '#0066cc'
},
onReady: () => {
console.log('Widget is ready!');
},
onConnect: () => {
console.log('User connected to agent');
// Track analytics, update UI, etc.
},
onDisconnect: () => {
console.log('User disconnected');
},
onError: (error) => {
console.error('Widget error:', error.code, error.message);
// Show user-friendly error message
},
onStateChange: (state) => {
console.log('Widget state:', state);
// Update UI based on widget state
}
});Full Production Configuration:
KolariWidget.init({
// Required
apiKey: process.env.KOLARI_API_KEY,
agentId: process.env.KOLARI_AGENT_ID,
serverUrl: 'https://www.kolari.ai',
// Branding
branding: {
agentName: 'Customer Support',
primaryColor: '#6366f1',
secondaryColor: '#8b5cf6',
companyName: 'Acme Inc'
},
// Behavior
autoStart: false,
enableRecording: true,
// Advanced
reconnectAttempts: 3,
reconnectDelay: 2000,
debug: false,
// Event Handlers
onReady: () => trackEvent('widget_ready'),
onConnect: () => trackEvent('call_started'),
onDisconnect: () => trackEvent('call_ended'),
onError: (error) => {
logError(error);
showUserMessage('Connection failed. Please try again.');
}
});API Methods
const widget = KolariWidget.init({ ... });
// Connection control
await widget.connect(); // Connect to agent
await widget.disconnect(); // Disconnect from agent
// UI control
widget.show(); // Show the widget
widget.hide(); // Hide the widget
widget.minimize(); // Minimize the widget
widget.maximize(); // Maximize the widget
// Audio control
widget.mute(); // Mute microphone
widget.unmute(); // Unmute microphone
widget.setVolume(0.8); // Set volume (0-1)
// State queries
widget.isConnected(); // Check connection status
widget.isReady(); // Check if widget is ready
widget.getState(); // Get current widget state
widget.getSession(); // Get session metadata
widget.getStats(); // Get session statistics
// Event listeners
widget.on('connect', () => { ... });
widget.on('disconnect', () => { ... });
widget.on('error', (error) => { ... });
widget.on('stateChange', (state) => { ... });
widget.on('transcript', (message) => { ... });
// Cleanup
widget.destroy(); // Remove widget and cleanupEvents
The widget emits the following events that you can listen to:
| Event | Payload | Description |
|-------|---------|-------------|
| ready | void | Widget is initialized and ready |
| connect | void | Connected to voice agent |
| disconnect | void | Disconnected from agent |
| error | WidgetError | An error occurred |
| stateChange | WidgetState | Widget state changed |
| transcript | TranscriptMessage | New transcript message (expanded mode) |
| audioLevel | number | Audio level update (0-1) |
| participantJoined | Participant | Participant joined session |
| participantLeft | Participant | Participant left session |
| reconnecting | number | Attempting to reconnect (attempt number) |
| reconnected | void | Successfully reconnected |
Example Usage:
const widget = KolariWidget.init({ ... });
widget.on('stateChange', (state) => {
console.log('Widget state:', state);
// Update your UI based on state
});
widget.on('error', (error) => {
console.error('Widget error:', error.code, error.message);
// Show error message to user
});
widget.on('audioLevel', (level) => {
// level is 0-1, visualize audio activity
updateAudioVisualizer(level);
});Widget States
The widget transitions through the following states:
| State | Description | Button Text (Compact) |
|-------|-------------|----------------------|
| idle | Widget initialized, not connected | "VOICE CHAT" |
| connecting | Connecting to agent | "CONNECTING..." |
| connected | Connected and ready | "END CHAT" |
| listening | Listening for user input | "LISTENING..." |
| speaking | Agent is speaking | "SPEAKING..." |
| disconnected | Disconnected from agent | "VOICE CHAT" |
| error | Error occurred | "TRY AGAIN" |
Microphone Permissions
The widget automatically handles microphone permissions through WebRTC connection. When a user clicks "VOICE CHAT" (or your custom connect button), the browser will:
- Request microphone permission (first time only)
- Remember the permission for future sessions
- Auto-connect if permission was previously granted
Important Notes:
- Permission is requested only once - the widget delegates all microphone access to Kolari
- Users can revoke permissions through their browser settings
- HTTPS is required for microphone access (WebRTC security requirement)
- If permission is denied, the widget will show an error state
Testing Permissions:
KolariWidget.init({
apiKey: 'pk_live_...',
agentId: 'agent-id',
serverUrl: 'https://www.kolari.ai',
displayMode: 'compact',
onError: (error) => {
if (error.code === 'MICROPHONE_PERMISSION_DENIED') {
console.error('Microphone access denied by user');
// Show custom UI to guide user to enable permissions
}
}
});Widget UI States
The widget provides visual feedback through:
Button Text Changes
- Idle/Disconnected: "VOICE CHAT" with 📞 phone icon
- Connecting: "CONNECTING..." with 📞 phone icon
- Connected/Listening/Speaking: "END CHAT" with 📵 phone-slash icon
Orb Animation
- Idle: Static colored orb
- Connecting: Pulsing animation
- Listening: Gentle breathing animation
- Speaking: Active wave animation responding to audio levels
Visual States
- Hover: Pill lifts up with enhanced shadow
- Active: Border changes to brand color
- Pulsing: Subtle opacity animation when connected
Browser Support
Desktop Browsers
- ✅ Chrome/Edge 90+
- ✅ Firefox 88+
- ✅ Safari 15+
- ✅ Opera 76+
Mobile Browsers
- ✅ Mobile Safari (iOS 15+)
- ✅ Chrome Android (90+)
- ✅ Samsung Internet 14+
Not Supported
- ❌ Internet Explorer (any version)
- ❌ Legacy Edge (pre-Chromium)
- ❌ iOS Safari < 15
Requirements
Technical Requirements
- ✅ HTTPS connection (required for WebRTC and microphone access)
- ✅ Modern browser with WebRTC support
- ✅ Microphone access (will be requested on first connection)
- ✅ JavaScript enabled
Development Requirements
- Node.js 18+ (for building from source)
- npm or yarn package manager
Server Requirements
- Kolari API server
- Valid API keys and agent configuration
Troubleshooting
Widget Not Appearing
Problem: Widget doesn't render on the page
Solutions:
- Check that the script is loaded:
console.log(typeof KolariWidget); // Should output "function" - Verify API key and agent ID are correct
- Check browser console for errors
- Ensure you're calling
KolariWidget.init()after the script loads
Microphone Permission Issues
Problem: Permission requested multiple times or not working
Solutions:
- Ensure you're using HTTPS (required for WebRTC)
- Check browser microphone permissions in settings
- Clear browser cache and test again
- On iOS Safari, ensure iOS 15+ is being used
Checking Permissions:
navigator.permissions.query({ name: 'microphone' as PermissionName })
.then((result) => {
console.log('Microphone permission:', result.state);
// 'granted', 'denied', or 'prompt'
});Connection Errors
Problem: Widget fails to connect to agent
Solutions:
- Verify
serverUrlis correct and accessible - Check API key has proper permissions
- Ensure agent ID exists and is active
- Check network tab for failed requests
- Enable debug mode:
debug: true
Debug Mode:
KolariWidget.init({
apiKey: '...',
agentId: '...',
serverUrl: '...',
debug: true, // Enable detailed logging
});No Audio or Voice
Problem: Connected but no audio or agent doesn't respond
Solutions:
- Check system volume and browser audio settings
- Verify agent has proper voice configuration
- Test microphone with another application
- Check for console errors related to audio
Styling Issues
Problem: Widget styling conflicts with site CSS
Solutions:
- Widget uses isolated styles with
kw-prefix - Check for CSS conflicts in browser DevTools
- Override styles if needed:
/* Override widget styles */ .kw-compact-pill { bottom: 30px !important; }
React/Next.js SSR Issues
Problem: Errors during server-side rendering
Solutions:
- Use dynamic import to load widget client-side only:
useEffect(() => { import('@kolari-ai/widget-sdk').then(({ KolariWidget }) => { // Initialize here }); }, []); - For Next.js, use
'use client'directive - Ensure widget initialization happens in
useEffect
Performance Issues
Problem: Widget causing page slowdown
Solutions:
- Don't initialize widget multiple times
- Properly cleanup with
widget.destroy() - Check for memory leaks in browser DevTools
- Disable debug mode in production (
debug: false)
Getting Help
Still having issues? Here's how to get support:
- Check the logs: Enable
debug: trueand check console - Test in isolation: Create a minimal HTML test page
- Browser compatibility: Verify you're using a supported browser
- Network issues: Check network tab for failed requests
- Contact support: Provide error messages, browser version, and code snippet
License
MIT
Support
- Report Issues: https://github.com/Anpu-Labs/kolari-ai-widget-sdk/issues
- Email: [email protected]
- Website: https://www.kolari.ai
Contributing
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our GitHub repository.
Changelog
v0.1.4 (Latest)
- ✨ DEFAULT COMPACT MODE: Widget now defaults to
'compact'display mode - 📝 Users no longer need to specify
displayMode: 'compact'- it's automatic - 🎯 Improved out-of-the-box experience with sleek pill widget by default
v0.1.3
- 🐛 CRITICAL FIX: Resolved duplicate widget pills appearing on page
- 🔒 Added singleton pattern to prevent multiple widget instances
- 🛡️ Implemented render guard in UI renderer to prevent duplicate rendering
- 🧹 Enhanced destroy() method with robust error handling and cleanup verification
- 🔄 Added persistent initialization flag for React Strict Mode compatibility
- 🧰 Improved DOM cleanup to remove orphaned widget elements
- 📊 Enhanced logging for better debugging of widget lifecycle
- ✨ Widget now properly handles React component remounts in development
- 🎯 All widget operations now have proper error boundaries
v0.1.0
- ✨ Added compact display mode
- ✨ Dynamic phone icon (📞/📵) based on connection state
- ✨ 3D animated orb with audio level visualization
- ✨ Single microphone permission request
- ✨ Improved state management and visual feedback
- 🐛 Fixed multiple permission request issue
- 🐛 Fixed button color transitions in compact mode
- 📝 Comprehensive README and documentation
- 🎨 Enhanced branding customization options
