Prerequisites
Get your Workspace ID
In the Trillet Portal sidebar, click the workspace name dropdown at the top. Your Workspace ID is displayed next to each workspace - click to copy.
Get your Agent ID
Navigate to Call Flows in the sidebar. Each call flow card displays its Agent ID - copy it from the card.
Whitelist your domain
Go to Settings → Domain and add the domain where you’ll embed the widget (e.g.
https://yoursite.com). This is required to avoid CORS errors. Allow up to 24 hours for propagation. Skip this if you’re using a whitelabelled custom domain.Never expose your API key in client-side code. The widget uses
startPublicCall() which only requires the Workspace ID and Agent ID.Integration
Choose between a minimal quick-start snippet or a complete styled widget, then pick your framework.Quick Start - Minimal snippet
Quick Start - Minimal snippet
Paste this before the closing Usage:
</body> tag. A chat bubble appears in the bottom-right corner.- HTML / CDN
- React / Next.js
Copy
Ask AI
<script type="module">
import { TrilletAgent } from 'https://cdn.jsdelivr.net/npm/@trillet-ai/web-sdk/+esm';
const WORKSPACE_ID = 'your-workspace-id';
const AGENT_ID = 'your-agent-id';
const container = document.createElement('div');
container.innerHTML = `
<div id="trillet-chat-bubble" style="position:fixed;bottom:24px;right:24px;width:52px;height:52px;background:#0066ff;border-radius:12px;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 1px 3px rgba(0,0,0,0.08);z-index:9999;border:1px solid rgba(0,102,255,0.2);transition:all 0.1s ease-in-out;">
<svg width="22" height="22" fill="white" viewBox="0 0 24 24"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>
</div>
`;
document.body.appendChild(container);
const bubble = document.getElementById('trillet-chat-bubble');
let chatWindow = null;
let agent = null;
let isConnected = false;
bubble.addEventListener('click', () => {
if (chatWindow) {
chatWindow.style.display = chatWindow.style.display === 'none' ? 'flex' : 'none';
return;
}
openChat();
});
function openChat() {
chatWindow = document.createElement('div');
chatWindow.style.cssText = 'position:fixed;bottom:88px;right:24px;width:380px;height:520px;background:#fff;border-radius:12px;border:1px solid #e5e7eb;box-shadow:0 4px 12px -1px rgba(0,0,0,0.05);display:flex;flex-direction:column;overflow:hidden;z-index:9999;font-family:-apple-system,BlinkMacSystemFont,system-ui,sans-serif;';
chatWindow.innerHTML = `
<div style="background:#fff;border-bottom:1px solid #e5e7eb;padding:14px 16px;display:flex;justify-content:space-between;align-items:center;">
<div><div style="font-weight:600;font-size:14px;color:#111827;">Assistant</div><div id="trillet-status" style="font-size:12px;color:#6b7280;">Connecting...</div></div>
<button id="trillet-close" style="background:none;border:none;color:#9ca3af;cursor:pointer;font-size:16px;padding:4px;">✕</button>
</div>
<div id="trillet-messages" style="flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:8px;background:#f9fafb;"></div>
<div style="padding:12px;border-top:1px solid #e5e7eb;display:flex;gap:8px;">
<input id="trillet-input" type="text" placeholder="Type a message..." disabled style="flex:1;padding:9px 14px;border:1px solid #e5e7eb;border-radius:8px;font-size:13px;outline:none;color:#111827;background:#fff;transition:border-color 0.2s;">
<button id="trillet-send" disabled style="padding:9px 14px;border:none;border-radius:8px;background:#0066ff;color:#fff;font-size:13px;font-weight:500;cursor:pointer;transition:background 0.1s;">Send</button>
</div>
`;
document.body.appendChild(chatWindow);
document.getElementById('trillet-close').onclick = () => chatWindow.style.display = 'none';
document.getElementById('trillet-input').onkeypress = (e) => { if (e.key === 'Enter') sendMessage(); };
document.getElementById('trillet-send').onclick = sendMessage;
connectAgent();
}
async function connectAgent() {
try {
agent = new TrilletAgent({ workspaceId: WORKSPACE_ID, agentId: AGENT_ID, mode: 'text' });
agent.on('connected', () => {
isConnected = true;
document.getElementById('trillet-status').textContent = 'Online';
document.getElementById('trillet-status').style.color = '#10b981';
document.getElementById('trillet-input').disabled = false;
document.getElementById('trillet-send').disabled = false;
document.getElementById('trillet-input').focus();
});
agent.on('disconnected', () => {
isConnected = false;
document.getElementById('trillet-status').textContent = 'Offline';
document.getElementById('trillet-status').style.color = '#6b7280';
document.getElementById('trillet-input').disabled = true;
document.getElementById('trillet-send').disabled = true;
});
agent.on('error', () => addMsg('Something went wrong. Please try again.', 'system'));
['message', 'transcript', 'transcriptUpdate'].forEach(evt => {
agent.on(evt, (data) => {
let text = typeof data === 'string' ? data : data?.text || data?.content || data?.message;
if (data?.role && data.role !== 'assistant') return;
if (data?.isFinal === false) return;
if (text) addMsg(text, 'assistant');
});
});
await agent.startPublicCall();
} catch (err) {
document.getElementById('trillet-status').textContent = 'Failed to connect';
addMsg('Could not connect. Please refresh and try again.', 'system');
}
}
function sendMessage() {
const input = document.getElementById('trillet-input');
const text = input.value.trim();
if (!text || !isConnected) return;
addMsg(text, 'user');
input.value = '';
try {
agent.sendTextMessage?.(text) || agent.sendText?.(text) || agent.sendMessage?.(text);
} catch (e) {
addMsg('Failed to send message.', 'system');
}
}
const displayed = new Set();
function addMsg(text, type) {
const key = type + ':' + text;
if (displayed.has(key)) return;
displayed.add(key);
const el = document.createElement('div');
const isUser = type === 'user';
el.style.cssText = `max-width:80%;padding:9px 13px;border-radius:10px;font-size:13px;line-height:1.5;${
isUser ? 'background:#0066ff;color:#fff;align-self:flex-end;'
: type === 'system' ? 'background:#f3f4f6;color:#6b7280;align-self:center;font-size:12px;padding:6px 12px;'
: 'background:#fff;color:#111827;align-self:flex-start;border:1px solid #e5e7eb;'
}`;
el.textContent = text;
const container = document.getElementById('trillet-messages');
container.appendChild(el);
container.scrollTop = container.scrollHeight;
}
</script>
Copy
Ask AI
npm install @trillet-ai/web-sdk
Copy
Ask AI
import { useRef, useState, useEffect } from 'react';
import { TrilletAgent } from '@trillet-ai/web-sdk';
export default function TrilletChat({ workspaceId, agentId }) {
const agentRef = useRef(null);
const [isOpen, setIsOpen] = useState(false);
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [connected, setConnected] = useState(false);
useEffect(() => {
if (!isOpen || agentRef.current) return;
const agent = new TrilletAgent({ workspaceId, agentId, mode: 'text' });
agentRef.current = agent;
agent.on('connected', () => setConnected(true));
agent.on('disconnected', () => setConnected(false));
['message', 'transcript'].forEach(evt => {
agent.on(evt, (data) => {
const text = typeof data === 'string' ? data : data?.text || data?.content;
if (data?.role && data.role !== 'assistant') return;
if (text) setMessages(m => [...m, { type: 'assistant', text }]);
});
});
agent.startPublicCall();
return () => agent.endCall?.();
}, [isOpen]);
const send = () => {
if (!input.trim() || !connected) return;
setMessages(m => [...m, { type: 'user', text: input }]);
agentRef.current?.sendTextMessage?.(input);
setInput('');
};
return (
<>
<button onClick={() => setIsOpen(!isOpen)}
style={{ position: 'fixed', bottom: 24, right: 24, width: 52, height: 52,
borderRadius: 12, background: '#0066ff', border: '1px solid rgba(0,102,255,0.2)',
cursor: 'pointer', color: 'white', fontSize: 20, zIndex: 9999,
boxShadow: '0 1px 3px rgba(0,0,0,0.08)' }}>
{isOpen ? '✕' : '💬'}
</button>
{isOpen && (
<div style={{ position: 'fixed', bottom: 88, right: 24, width: 380, height: 520,
background: '#fff', borderRadius: 12, border: '1px solid #e5e7eb',
boxShadow: '0 4px 12px -1px rgba(0,0,0,0.05)',
display: 'flex', flexDirection: 'column', zIndex: 9999,
fontFamily: '-apple-system, BlinkMacSystemFont, system-ui, sans-serif' }}>
<div style={{ padding: '14px 16px', borderBottom: '1px solid #e5e7eb' }}>
<div style={{ fontWeight: 600, fontSize: 14, color: '#111827' }}>Assistant</div>
<div style={{ fontSize: 12, color: connected ? '#10b981' : '#6b7280' }}>
{connected ? 'Online' : 'Connecting...'}
</div>
</div>
<div style={{ flex: 1, overflowY: 'auto', padding: 16, display: 'flex',
flexDirection: 'column', gap: 8, background: '#f9fafb' }}>
{messages.map((m, i) => (
<div key={i} style={{ alignSelf: m.type === 'user' ? 'flex-end' : 'flex-start',
background: m.type === 'user' ? '#0066ff' : '#fff',
color: m.type === 'user' ? '#fff' : '#111827',
border: m.type === 'user' ? 'none' : '1px solid #e5e7eb',
padding: '9px 13px', borderRadius: 10, maxWidth: '80%', fontSize: 13 }}>
{m.text}
</div>
))}
</div>
<div style={{ padding: 12, borderTop: '1px solid #e5e7eb', display: 'flex', gap: 8 }}>
<input value={input} onChange={e => setInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && send()}
placeholder="Type a message..." disabled={!connected}
style={{ flex: 1, padding: '9px 14px', borderRadius: 8,
border: '1px solid #e5e7eb', outline: 'none', fontSize: 13 }} />
<button onClick={send} disabled={!connected}
style={{ padding: '9px 14px', borderRadius: 8, background: '#0066ff',
color: '#fff', border: 'none', cursor: 'pointer', fontSize: 13, fontWeight: 500 }}>
Send
</button>
</div>
</div>
)}
</>
);
}
Copy
Ask AI
<TrilletChat workspaceId="your-workspace-id" agentId="your-agent-id" />
Full Widget - Complete styled chat widget
Full Widget - Complete styled chat widget
A polished chat widget with status indicators, typing animation, and transcript polling. Self-contained HTML file you can use directly or hand off.Usage:
- HTML / CDN
- React / Next.js
Copy
Ask AI
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Trillet Chat Widget</title>
<style>
* { box-sizing: border-box; }
#trillet-bubble {
position: fixed;
bottom: 24px;
right: 24px;
width: 52px;
height: 52px;
background: #0066ff;
border-radius: 12px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
border: 1px solid rgba(0, 102, 255, 0.2);
z-index: 9999;
transition: all 0.1s ease-in-out;
}
#trillet-bubble:hover { background: #0052cc; }
#trillet-bubble svg { width: 22px; height: 22px; fill: white; }
#trillet-panel {
position: fixed;
bottom: 88px;
right: 24px;
width: 380px;
height: 540px;
background: #fff;
border-radius: 12px;
border: 1px solid #e5e7eb;
box-shadow: 0 4px 12px -1px rgba(0, 0, 0, 0.05);
display: none;
flex-direction: column;
overflow: hidden;
z-index: 9999;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
}
#trillet-panel.open { display: flex; animation: trillet-in 0.15s cubic-bezier(0.16,1,0.3,1); }
@keyframes trillet-in {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.trillet-header {
padding: 14px 16px;
border-bottom: 1px solid #e5e7eb;
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
}
.trillet-header h3 { font-size: 14px; font-weight: 600; margin: 0; color: #111827; }
.trillet-header .status {
font-size: 12px;
color: #6b7280;
display: flex;
align-items: center;
gap: 5px;
margin-top: 1px;
}
.trillet-header .status-dot {
width: 6px; height: 6px;
border-radius: 50%;
background: #d1d5db;
}
.trillet-header .status-dot.online { background: #10b981; }
.trillet-close {
background: none;
border: none;
color: #9ca3af;
cursor: pointer;
font-size: 16px;
padding: 4px;
line-height: 1;
transition: color 0.1s;
}
.trillet-close:hover { color: #6b7280; }
.trillet-messages {
flex: 1;
overflow-y: auto;
padding: 16px;
display: flex;
flex-direction: column;
gap: 8px;
background: #f9fafb;
}
.trillet-msg {
max-width: 82%;
padding: 9px 13px;
border-radius: 10px;
font-size: 13px;
line-height: 1.5;
}
.trillet-msg.user {
background: #0066ff;
color: #fff;
align-self: flex-end;
}
.trillet-msg.assistant {
background: #fff;
color: #111827;
align-self: flex-start;
border: 1px solid #e5e7eb;
}
.trillet-msg.system {
background: #f3f4f6;
color: #6b7280;
align-self: center;
font-size: 11px;
padding: 4px 10px;
border-radius: 6px;
}
.trillet-typing {
display: flex;
gap: 4px;
padding: 10px 14px;
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 10px;
align-self: flex-start;
}
.trillet-typing span {
width: 5px; height: 5px;
background: #9ca3af;
border-radius: 50%;
animation: trillet-dot 1.4s infinite ease-in-out;
}
.trillet-typing span:nth-child(2) { animation-delay: 0.2s; }
.trillet-typing span:nth-child(3) { animation-delay: 0.4s; }
@keyframes trillet-dot {
0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
30% { transform: translateY(-3px); opacity: 1; }
}
.trillet-input-area {
padding: 12px;
border-top: 1px solid #e5e7eb;
display: flex;
gap: 8px;
background: #fff;
}
.trillet-input-area input {
flex: 1;
padding: 9px 14px;
border: 1px solid #e5e7eb;
border-radius: 8px;
font-size: 13px;
outline: none;
color: #111827;
transition: border-color 0.2s;
}
.trillet-input-area input:focus { border-color: #0066ff; }
.trillet-input-area input:disabled { background: #f9fafb; color: #9ca3af; }
.trillet-send {
padding: 9px 14px;
border: none;
border-radius: 8px;
background: #0066ff;
color: #fff;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: background 0.1s;
}
.trillet-send:hover:not(:disabled) { background: #0052cc; }
.trillet-send:disabled { background: #d1d5db; cursor: not-allowed; }
@media (max-width: 480px) {
#trillet-panel {
width: calc(100vw - 24px);
height: calc(100vh - 140px);
right: 12px;
bottom: 88px;
}
}
</style>
</head>
<body>
<!-- Paste everything below into your website -->
<button id="trillet-bubble">
<svg viewBox="0 0 24 24"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>
</button>
<div id="trillet-panel">
<div class="trillet-header">
<div>
<h3>Assistant</h3>
<div class="status">
<div class="status-dot" id="trillet-dot"></div>
<span id="trillet-status-text">Ready</span>
</div>
</div>
<button class="trillet-close" id="trillet-close">✕</button>
</div>
<div class="trillet-messages" id="trillet-messages"></div>
<div class="trillet-input-area">
<input type="text" id="trillet-input" placeholder="Type a message..." disabled>
<button class="trillet-send" id="trillet-send" disabled>Send</button>
</div>
</div>
<script type="module">
import { TrilletAgent } from 'https://cdn.jsdelivr.net/npm/@trillet-ai/web-sdk/+esm';
// ── REPLACE THESE WITH YOUR VALUES ──
const WORKSPACE_ID = 'your-workspace-id';
const AGENT_ID = 'your-agent-id';
// ─────────────────────────────────────
const $ = (id) => document.getElementById(id);
const bubble = $('trillet-bubble');
const panel = $('trillet-panel');
let agent = null;
let isConnected = false;
let hasConnected = false;
const displayed = new Set();
bubble.addEventListener('click', () => {
panel.classList.toggle('open');
if (!hasConnected) {
hasConnected = true;
connectAgent();
}
});
$('trillet-close').addEventListener('click', () => panel.classList.remove('open'));
$('trillet-input').addEventListener('keypress', (e) => { if (e.key === 'Enter') sendMessage(); });
$('trillet-send').addEventListener('click', sendMessage);
async function connectAgent() {
setStatus('Connecting...', false);
try {
agent = new TrilletAgent({
workspaceId: WORKSPACE_ID,
agentId: AGENT_ID,
mode: 'text',
});
agent.on('connected', () => {
isConnected = true;
setStatus('Online', true);
$('trillet-input').disabled = false;
$('trillet-send').disabled = false;
$('trillet-input').focus();
startPolling();
});
agent.on('disconnected', () => {
isConnected = false;
setStatus('Offline', false);
$('trillet-input').disabled = true;
$('trillet-send').disabled = true;
addMsg('Session ended.', 'system');
});
agent.on('error', () => addMsg('Something went wrong. Please try again.', 'system'));
['message', 'transcript', 'transcriptUpdate', 'response'].forEach(evt => {
agent.on(evt, (data) => {
hideTyping();
let text = typeof data === 'string' ? data : data?.text || data?.content || data?.message;
if (data?.role && data.role !== 'assistant') return;
if (data?.isFinal === false) return;
if (text) addMsg(text, 'assistant');
});
});
agent.on('assistantStartedSpeaking', showTyping);
agent.on('assistantStoppedSpeaking', hideTyping);
await agent.startPublicCall();
} catch (err) {
setStatus('Failed', false);
addMsg('Could not connect. Please refresh and try again.', 'system');
}
}
let pollInterval = null;
function startPolling() {
let lastCount = 0;
pollInterval = setInterval(() => {
if (!agent || !isConnected) return;
try {
const transcripts = agent.getTranscripts?.() || [];
for (let i = lastCount; i < transcripts.length; i++) {
const t = transcripts[i];
if (t.role === 'assistant' && t.text) { hideTyping(); addMsg(t.text, 'assistant'); }
}
lastCount = transcripts.length;
} catch (e) {}
}, 500);
}
function sendMessage() {
const input = $('trillet-input');
const text = input.value.trim();
if (!text || !isConnected) return;
addMsg(text, 'user');
input.value = '';
showTyping();
try {
if (agent.sendTextMessage) agent.sendTextMessage(text);
else if (agent.sendText) agent.sendText(text);
else if (agent.sendMessage) agent.sendMessage(text);
} catch (e) {
hideTyping();
addMsg('Failed to send message.', 'system');
}
}
function setStatus(text, online) {
$('trillet-status-text').textContent = text;
$('trillet-dot').classList.toggle('online', online);
}
function addMsg(text, type) {
if (!text?.trim()) return;
const key = type + ':' + text;
if (displayed.has(key)) return;
displayed.add(key);
const el = document.createElement('div');
el.className = 'trillet-msg ' + type;
el.textContent = text;
$('trillet-messages').appendChild(el);
$('trillet-messages').scrollTop = $('trillet-messages').scrollHeight;
}
function showTyping() {
if (document.getElementById('trillet-typing')) return;
const el = document.createElement('div');
el.className = 'trillet-typing';
el.id = 'trillet-typing';
el.innerHTML = '<span></span><span></span><span></span>';
$('trillet-messages').appendChild(el);
$('trillet-messages').scrollTop = $('trillet-messages').scrollHeight;
}
function hideTyping() {
document.getElementById('trillet-typing')?.remove();
}
</script>
</body>
</html>
Copy
Ask AI
npm install @trillet-ai/web-sdk
Copy
Ask AI
import { useRef, useState, useEffect, useCallback } from 'react';
import { TrilletAgent } from '@trillet-ai/web-sdk';
export default function TrilletChat({ workspaceId, agentId }) {
const agentRef = useRef(null);
const messagesRef = useRef(null);
const [isOpen, setIsOpen] = useState(false);
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [connected, setConnected] = useState(false);
useEffect(() => {
if (!isOpen || agentRef.current) return;
const agent = new TrilletAgent({ workspaceId, agentId, mode: 'text' });
agentRef.current = agent;
agent.on('connected', () => setConnected(true));
agent.on('disconnected', () => setConnected(false));
['message', 'transcript'].forEach(evt => {
agent.on(evt, (data) => {
const text = typeof data === 'string' ? data : data?.text || data?.content;
if (data?.role && data.role !== 'assistant') return;
if (text) setMessages(m => [...m, { type: 'assistant', text }]);
});
});
agent.startPublicCall();
// Poll transcripts as fallback
let lastCount = 0;
const poll = setInterval(() => {
const transcripts = agent.getTranscripts?.() || [];
for (let i = lastCount; i < transcripts.length; i++) {
const t = transcripts[i];
if (t.role === 'assistant' && t.text) {
setMessages(m => [...m, { type: 'assistant', text: t.text }]);
}
}
lastCount = transcripts.length;
}, 500);
return () => { clearInterval(poll); agent.endCall?.(); };
}, [isOpen]);
useEffect(() => {
messagesRef.current?.scrollTo(0, messagesRef.current.scrollHeight);
}, [messages]);
const send = useCallback(() => {
if (!input.trim() || !connected) return;
setMessages(m => [...m, { type: 'user', text: input }]);
agentRef.current?.sendTextMessage?.(input);
setInput('');
}, [input, connected]);
const styles = {
bubble: { position: 'fixed', bottom: 24, right: 24, width: 52, height: 52,
borderRadius: 12, background: '#0066ff', border: '1px solid rgba(0,102,255,0.2)',
cursor: 'pointer', color: '#fff', display: 'flex', alignItems: 'center',
justifyContent: 'center', zIndex: 9999, boxShadow: '0 1px 3px rgba(0,0,0,0.08)' },
panel: { position: 'fixed', bottom: 88, right: 24, width: 380, height: 540,
background: '#fff', borderRadius: 12, border: '1px solid #e5e7eb',
boxShadow: '0 4px 12px -1px rgba(0,0,0,0.05)',
display: 'flex', flexDirection: 'column', zIndex: 9999,
fontFamily: '-apple-system, BlinkMacSystemFont, system-ui, sans-serif' },
header: { padding: '14px 16px', borderBottom: '1px solid #e5e7eb',
display: 'flex', justifyContent: 'space-between', alignItems: 'center' },
msgUser: { alignSelf: 'flex-end', background: '#0066ff', color: '#fff',
padding: '9px 13px', borderRadius: 10, maxWidth: '80%', fontSize: 13 },
msgBot: { alignSelf: 'flex-start', background: '#fff', color: '#111827',
border: '1px solid #e5e7eb', padding: '9px 13px', borderRadius: 10,
maxWidth: '80%', fontSize: 13 },
};
return (
<>
<button onClick={() => setIsOpen(!isOpen)} style={styles.bubble}>
{isOpen
? <svg width="18" height="18" fill="none" stroke="#fff" strokeWidth="2" viewBox="0 0 24 24"><path d="M18 6L6 18M6 6l12 12"/></svg>
: <svg width="22" height="22" fill="#fff" viewBox="0 0 24 24"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>}
</button>
{isOpen && (
<div style={styles.panel}>
<div style={styles.header}>
<div>
<div style={{ fontWeight: 600, fontSize: 14, color: '#111827' }}>Assistant</div>
<div style={{ fontSize: 12, color: connected ? '#10b981' : '#6b7280' }}>
{connected ? 'Online' : 'Connecting...'}
</div>
</div>
<button onClick={() => setIsOpen(false)}
style={{ background: 'none', border: 'none', color: '#9ca3af', cursor: 'pointer', fontSize: 16 }}>
✕
</button>
</div>
<div ref={messagesRef} style={{ flex: 1, overflowY: 'auto', padding: 16,
display: 'flex', flexDirection: 'column', gap: 8, background: '#f9fafb' }}>
{messages.map((m, i) => (
<div key={i} style={m.type === 'user' ? styles.msgUser : styles.msgBot}>
{m.text}
</div>
))}
</div>
<div style={{ padding: 12, borderTop: '1px solid #e5e7eb', display: 'flex', gap: 8 }}>
<input value={input} onChange={e => setInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && send()}
placeholder="Type a message..." disabled={!connected}
style={{ flex: 1, padding: '9px 14px', borderRadius: 8,
border: '1px solid #e5e7eb', outline: 'none', fontSize: 13 }} />
<button onClick={send} disabled={!connected}
style={{ padding: '9px 14px', borderRadius: 8, background: '#0066ff',
color: '#fff', border: 'none', cursor: 'pointer', fontSize: 13, fontWeight: 500 }}>
Send
</button>
</div>
</div>
)}
</>
);
}
Copy
Ask AI
<TrilletChat workspaceId="your-workspace-id" agentId="your-agent-id" />
Events Reference
The SDK emits these events you can listen to:| Event | Description |
|---|---|
connected | Agent is connected and ready |
disconnected | Session ended |
error | An error occurred |
message | New message received |
transcript | Transcript update received |
assistantStartedSpeaking | Agent begins responding |
assistantStoppedSpeaking | Agent finished responding |
Copy
Ask AI
agent.on('connected', (details) => {
console.log('Connected:', details.callId);
});
