@helllo-ai/agent-chat-widget 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-chat.prod.js +271 -20
- package/agent-chat.staging.js +275 -24
- package/package.json +1 -11
package/agent-chat.prod.js
CHANGED
|
@@ -19,10 +19,20 @@
|
|
|
19
19
|
const safeBg = background || '#ffffff'
|
|
20
20
|
return `
|
|
21
21
|
:host, .acw * { box-sizing: border-box; }
|
|
22
|
-
.acw-container { position: fixed;
|
|
23
|
-
.acw-
|
|
22
|
+
.acw-container { position: fixed; z-index: 2147483000; font-family: Inter, system-ui, -apple-system, sans-serif; }
|
|
23
|
+
.acw-container.bottom-right { right: 16px; bottom: 16px; }
|
|
24
|
+
.acw-container.middle-right { right: 16px; top: 50%; transform: translateY(-50%); }
|
|
25
|
+
.acw-launcher { background: ${safePrimary}; color: #fff; border: none; border-radius: 999px; padding: 10px 14px; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.18); font-size: 14px; transition: transform 0.2s; }
|
|
24
26
|
.acw-launcher:hover { opacity: 0.95; }
|
|
25
|
-
.acw-
|
|
27
|
+
.acw-launcher.vertical { flex-direction: column; padding: 14px 10px; min-width: 48px; height: auto; }
|
|
28
|
+
.acw-launcher.vertical span { writing-mode: sideways-lr; text-orientation: mixed; letter-spacing: 0.5px; }
|
|
29
|
+
.acw-panel { position: absolute; width: 360px; max-width: 90vw; height: 520px; max-height: 80vh; background: ${safeBg}; border: 1px solid rgba(0,0,0,0.08); border-radius: 12px; box-shadow: 0 16px 40px rgba(0,0,0,0.22); display: none; flex-direction: column; overflow: hidden; }
|
|
30
|
+
.acw-panel.bottom-right { right: 0; bottom: 56px; }
|
|
31
|
+
.acw-panel.middle-right { right: 56px; top: 50%; transform: translateY(-50%); }
|
|
32
|
+
.acw-panel.top-right { right: 0; top: 56px; }
|
|
33
|
+
.acw-panel.bottom-left { left: 0; bottom: 56px; }
|
|
34
|
+
.acw-panel.middle-left { left: 56px; top: 50%; transform: translateY(-50%); }
|
|
35
|
+
.acw-panel.top-left { left: 0; top: 56px; }
|
|
26
36
|
.acw-header { background: ${safePrimary}; color: #fff; padding: 12px 14px; display: flex; align-items: center; justify-content: space-between; }
|
|
27
37
|
.acw-title { font-weight: 600; font-size: 14px; }
|
|
28
38
|
.acw-close { background: transparent; border: none; color: #fff; cursor: pointer; font-size: 16px; }
|
|
@@ -39,6 +49,18 @@
|
|
|
39
49
|
.acw-dot.disconnected { background: #ef4444; box-shadow: 0 0 0 6px rgba(239,68,68,0.18); }
|
|
40
50
|
.acw-disconnect { margin-left: auto; background: #f8fafc; color: #0f172a; border: 1px solid rgba(0,0,0,0.08); border-radius: 6px; padding: 6px 8px; font-size: 12px; cursor: pointer; }
|
|
41
51
|
.acw-disconnect:hover { background: #eef2f7; }
|
|
52
|
+
.acw-customer-form { position: absolute; inset: 0; background: ${safeBg}; z-index: 10; display: flex; flex-direction: column; padding: 20px; }
|
|
53
|
+
.acw-customer-form h3 { margin: 0 0 12px; font-size: 16px; font-weight: 600; color: #0f172a; }
|
|
54
|
+
.acw-customer-form p { margin: 0 0 16px; font-size: 13px; color: #64748b; }
|
|
55
|
+
.acw-customer-form .field { margin-bottom: 16px; }
|
|
56
|
+
.acw-customer-form label { display: block; font-size: 13px; font-weight: 500; margin-bottom: 6px; color: #475569; }
|
|
57
|
+
.acw-customer-form input { width: 100%; padding: 10px 12px; border: 1px solid rgba(0,0,0,0.12); border-radius: 8px; font-size: 14px; }
|
|
58
|
+
.acw-customer-form .actions { display: flex; gap: 8px; margin-top: auto; }
|
|
59
|
+
.acw-customer-form button { flex: 1; padding: 10px; border: none; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; }
|
|
60
|
+
.acw-customer-form .submit-btn { background: ${safePrimary}; color: #fff; }
|
|
61
|
+
.acw-customer-form .submit-btn:hover { opacity: 0.95; }
|
|
62
|
+
.acw-customer-form .skip-btn { background: #f8fafc; color: #64748b; border: 1px solid rgba(0,0,0,0.08); }
|
|
63
|
+
.acw-customer-form .skip-btn:hover { background: #eef2f7; }
|
|
42
64
|
@media (max-width: 480px) {
|
|
43
65
|
.acw-panel { width: calc(100vw - 24px); height: 70vh; }
|
|
44
66
|
}
|
|
@@ -57,14 +79,22 @@
|
|
|
57
79
|
wsUrl,
|
|
58
80
|
title = 'Chat with us',
|
|
59
81
|
greeting = null,
|
|
82
|
+
captureCustomerInfo = false,
|
|
83
|
+
position = 'bottom-right',
|
|
60
84
|
} = config
|
|
61
85
|
|
|
86
|
+
// Valid positions
|
|
87
|
+
const validPositions = ['bottom-right', 'middle-right', 'top-right', 'bottom-left', 'middle-left', 'top-left']
|
|
88
|
+
const finalPosition = validPositions.includes(position) ? position : 'bottom-right'
|
|
89
|
+
const isVertical = finalPosition === 'middle-right' || finalPosition === 'middle-left'
|
|
90
|
+
const isLeft = finalPosition.includes('left')
|
|
91
|
+
|
|
62
92
|
if (!agentId) {
|
|
63
|
-
console.warn('[AgentChatWidget] Missing data-agent-id')
|
|
93
|
+
console.warn('[AgentChatWidget] Missing agentId. Provide it via init({ agentId: "..." }) or data-agent-id attribute')
|
|
64
94
|
return
|
|
65
95
|
}
|
|
66
96
|
if (!embedKey) {
|
|
67
|
-
console.warn('[AgentChatWidget] Missing data-embed-key')
|
|
97
|
+
console.warn('[AgentChatWidget] Missing embedKey. Provide it via init({ embedKey: "..." }) or data-embed-key attribute')
|
|
68
98
|
return
|
|
69
99
|
}
|
|
70
100
|
|
|
@@ -75,17 +105,46 @@
|
|
|
75
105
|
}
|
|
76
106
|
|
|
77
107
|
const container = document.createElement('div')
|
|
78
|
-
container.className =
|
|
108
|
+
container.className = `acw-container ${finalPosition}`
|
|
109
|
+
|
|
110
|
+
// Apply position styles directly to container (since it's in regular DOM, not shadow)
|
|
111
|
+
container.style.position = 'fixed'
|
|
112
|
+
container.style.zIndex = '2147483000'
|
|
113
|
+
container.style.fontFamily = 'Inter, system-ui, -apple-system, sans-serif'
|
|
114
|
+
|
|
115
|
+
// Set container position based on finalPosition
|
|
116
|
+
if (finalPosition === 'bottom-right') {
|
|
117
|
+
container.style.right = '16px'
|
|
118
|
+
container.style.bottom = '16px'
|
|
119
|
+
} else if (finalPosition === 'middle-right') {
|
|
120
|
+
container.style.right = '16px'
|
|
121
|
+
container.style.top = '50%'
|
|
122
|
+
container.style.transform = 'translateY(-50%)'
|
|
123
|
+
} else if (finalPosition === 'top-right') {
|
|
124
|
+
container.style.right = '16px'
|
|
125
|
+
container.style.top = '16px'
|
|
126
|
+
} else if (finalPosition === 'bottom-left') {
|
|
127
|
+
container.style.left = '16px'
|
|
128
|
+
container.style.bottom = '16px'
|
|
129
|
+
} else if (finalPosition === 'middle-left') {
|
|
130
|
+
container.style.left = '16px'
|
|
131
|
+
container.style.top = '50%'
|
|
132
|
+
container.style.transform = 'translateY(-50%)'
|
|
133
|
+
} else if (finalPosition === 'top-left') {
|
|
134
|
+
container.style.left = '16px'
|
|
135
|
+
container.style.top = '16px'
|
|
136
|
+
}
|
|
137
|
+
|
|
79
138
|
const shadow = container.attachShadow({ mode: 'open' })
|
|
80
139
|
const style = document.createElement('style')
|
|
81
140
|
style.textContent = createStyles(primaryColor, backgroundColor)
|
|
82
141
|
shadow.appendChild(style)
|
|
83
142
|
|
|
84
143
|
const panel = document.createElement('div')
|
|
85
|
-
panel.className =
|
|
144
|
+
panel.className = `acw-panel ${finalPosition}`
|
|
86
145
|
|
|
87
146
|
const launcher = document.createElement('button')
|
|
88
|
-
launcher.className = 'acw-launcher'
|
|
147
|
+
launcher.className = isVertical ? 'acw-launcher vertical' : 'acw-launcher'
|
|
89
148
|
launcher.innerHTML = '<span>Chat</span>'
|
|
90
149
|
|
|
91
150
|
const header = document.createElement('div')
|
|
@@ -136,6 +195,142 @@
|
|
|
136
195
|
inputWrap.appendChild(input)
|
|
137
196
|
inputWrap.appendChild(sendBtn)
|
|
138
197
|
|
|
198
|
+
// Customer info form (if enabled)
|
|
199
|
+
let customerForm = null
|
|
200
|
+
let sessionId = null
|
|
201
|
+
let customerInfoSubmitted = false
|
|
202
|
+
|
|
203
|
+
if (captureCustomerInfo) {
|
|
204
|
+
customerForm = document.createElement('div')
|
|
205
|
+
customerForm.className = 'acw-customer-form'
|
|
206
|
+
customerForm.style.display = 'none'
|
|
207
|
+
|
|
208
|
+
const formTitle = document.createElement('h3')
|
|
209
|
+
formTitle.textContent = 'Welcome! Please share your details'
|
|
210
|
+
const formDesc = document.createElement('p')
|
|
211
|
+
formDesc.textContent = 'We need a few details to get started'
|
|
212
|
+
|
|
213
|
+
const nameField = document.createElement('div')
|
|
214
|
+
nameField.className = 'field'
|
|
215
|
+
const nameLabel = document.createElement('label')
|
|
216
|
+
nameLabel.textContent = 'Your Name'
|
|
217
|
+
nameLabel.setAttribute('for', 'acw-customer-name')
|
|
218
|
+
const nameInput = document.createElement('input')
|
|
219
|
+
nameInput.id = 'acw-customer-name'
|
|
220
|
+
nameInput.type = 'text'
|
|
221
|
+
nameInput.placeholder = 'Enter your name'
|
|
222
|
+
nameField.appendChild(nameLabel)
|
|
223
|
+
nameField.appendChild(nameInput)
|
|
224
|
+
|
|
225
|
+
const phoneField = document.createElement('div')
|
|
226
|
+
phoneField.className = 'field'
|
|
227
|
+
const phoneLabel = document.createElement('label')
|
|
228
|
+
phoneLabel.textContent = 'Phone Number'
|
|
229
|
+
phoneLabel.setAttribute('for', 'acw-customer-phone')
|
|
230
|
+
const phoneInput = document.createElement('input')
|
|
231
|
+
phoneInput.id = 'acw-customer-phone'
|
|
232
|
+
phoneInput.type = 'tel'
|
|
233
|
+
phoneInput.placeholder = 'Enter your phone number'
|
|
234
|
+
phoneField.appendChild(phoneLabel)
|
|
235
|
+
phoneField.appendChild(phoneInput)
|
|
236
|
+
|
|
237
|
+
const formActions = document.createElement('div')
|
|
238
|
+
formActions.className = 'actions'
|
|
239
|
+
const submitBtn = document.createElement('button')
|
|
240
|
+
submitBtn.className = 'submit-btn'
|
|
241
|
+
submitBtn.textContent = 'Start Chat'
|
|
242
|
+
const skipBtn = document.createElement('button')
|
|
243
|
+
skipBtn.className = 'skip-btn'
|
|
244
|
+
skipBtn.textContent = 'Skip'
|
|
245
|
+
formActions.appendChild(submitBtn)
|
|
246
|
+
formActions.appendChild(skipBtn)
|
|
247
|
+
|
|
248
|
+
customerForm.appendChild(formTitle)
|
|
249
|
+
customerForm.appendChild(formDesc)
|
|
250
|
+
customerForm.appendChild(nameField)
|
|
251
|
+
customerForm.appendChild(phoneField)
|
|
252
|
+
customerForm.appendChild(formActions)
|
|
253
|
+
|
|
254
|
+
async function submitCustomerInfo() {
|
|
255
|
+
const name = nameInput.value.trim()
|
|
256
|
+
const phone = phoneInput.value.trim()
|
|
257
|
+
|
|
258
|
+
if (!name || !phone) {
|
|
259
|
+
alert('Please fill in both name and phone number')
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (!sessionId) {
|
|
264
|
+
console.error('[AgentChatWidget] No session ID available')
|
|
265
|
+
return
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const baseUrl = voiceServiceUrl || apiBaseUrl || new URL(document.currentScript?.src || window.location.href).origin
|
|
269
|
+
let apiBase = (baseUrl || '').replace(/\/$/, '')
|
|
270
|
+
// Convert ws:// to http:// for HTTP requests
|
|
271
|
+
if (apiBase.startsWith('ws://')) {
|
|
272
|
+
apiBase = apiBase.replace(/^ws/, 'http')
|
|
273
|
+
} else if (apiBase.startsWith('wss://')) {
|
|
274
|
+
apiBase = apiBase.replace(/^wss/, 'https')
|
|
275
|
+
}
|
|
276
|
+
const endpoint = `${apiBase}/api/v1/agents/text-chat/customer-info`
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
const response = await fetch(endpoint, {
|
|
280
|
+
method: 'POST',
|
|
281
|
+
headers: {
|
|
282
|
+
'Content-Type': 'application/json',
|
|
283
|
+
},
|
|
284
|
+
body: JSON.stringify({
|
|
285
|
+
session_id: sessionId,
|
|
286
|
+
phone_number: phone,
|
|
287
|
+
customer_name: name,
|
|
288
|
+
}),
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
if (response.ok) {
|
|
292
|
+
customerInfoSubmitted = true
|
|
293
|
+
customerForm.style.display = 'none'
|
|
294
|
+
messages.style.display = 'flex'
|
|
295
|
+
inputWrap.style.display = 'flex'
|
|
296
|
+
} else {
|
|
297
|
+
const errorText = await response.text()
|
|
298
|
+
console.error('[AgentChatWidget] Failed to submit customer info:', errorText)
|
|
299
|
+
alert('Failed to submit information. Please try again.')
|
|
300
|
+
}
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.error('[AgentChatWidget] Error submitting customer info:', error)
|
|
303
|
+
alert('Error submitting information. Please try again.')
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function skipCustomerInfo() {
|
|
308
|
+
customerInfoSubmitted = true
|
|
309
|
+
customerForm.style.display = 'none'
|
|
310
|
+
messages.style.display = 'flex'
|
|
311
|
+
inputWrap.style.display = 'flex'
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
submitBtn.onclick = submitCustomerInfo
|
|
315
|
+
skipBtn.onclick = skipCustomerInfo
|
|
316
|
+
|
|
317
|
+
// Allow Enter key to submit
|
|
318
|
+
nameInput.addEventListener('keydown', (e) => {
|
|
319
|
+
if (e.key === 'Enter') {
|
|
320
|
+
e.preventDefault()
|
|
321
|
+
phoneInput.focus()
|
|
322
|
+
}
|
|
323
|
+
})
|
|
324
|
+
phoneInput.addEventListener('keydown', (e) => {
|
|
325
|
+
if (e.key === 'Enter') {
|
|
326
|
+
e.preventDefault()
|
|
327
|
+
submitCustomerInfo()
|
|
328
|
+
}
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
panel.appendChild(customerForm)
|
|
332
|
+
}
|
|
333
|
+
|
|
139
334
|
panel.appendChild(header)
|
|
140
335
|
panel.appendChild(statusEl)
|
|
141
336
|
panel.appendChild(messages)
|
|
@@ -159,13 +354,32 @@
|
|
|
159
354
|
let connecting = false
|
|
160
355
|
|
|
161
356
|
const resolvedWsUrl = (() => {
|
|
357
|
+
// Explicit wsUrl takes highest priority
|
|
162
358
|
if (wsUrl) return wsUrl
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
359
|
+
|
|
360
|
+
// Determine environment-specific WebSocket URL based on VERSION
|
|
361
|
+
let wsBase
|
|
362
|
+
if (VERSION.includes('-staging')) {
|
|
363
|
+
wsBase = 'wss://talk2ai-staging.helllo.ai'
|
|
364
|
+
} else if (VERSION.includes('-prod')) {
|
|
365
|
+
wsBase = 'wss://talk2ai-prod.helllo.ai'
|
|
366
|
+
} else {
|
|
367
|
+
// For dev/latest, use existing fallback logic
|
|
368
|
+
const baseCandidate = voiceServiceUrl || apiBaseUrl || new URL(document.currentScript?.src || window.location.href).origin
|
|
369
|
+
const voiceBase = (baseCandidate || '').replace(/\/$/, '')
|
|
370
|
+
// Handle both http/https and ws/wss URLs
|
|
371
|
+
if (voiceBase.startsWith('http://')) {
|
|
372
|
+
wsBase = voiceBase.replace(/^http/, 'ws')
|
|
373
|
+
} else if (voiceBase.startsWith('https://')) {
|
|
374
|
+
wsBase = voiceBase.replace(/^https/, 'wss')
|
|
375
|
+
} else if (voiceBase.startsWith('ws://') || voiceBase.startsWith('wss://')) {
|
|
376
|
+
wsBase = voiceBase // Already a WebSocket URL
|
|
377
|
+
} else {
|
|
378
|
+
// Default to ws if no protocol
|
|
379
|
+
wsBase = 'ws://' + voiceBase
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
169
383
|
const params = new URLSearchParams()
|
|
170
384
|
params.set('embed_key', embedKey)
|
|
171
385
|
params.set('host', window.location.hostname)
|
|
@@ -174,7 +388,7 @@
|
|
|
174
388
|
|
|
175
389
|
// Log resolved URL once for debugging
|
|
176
390
|
try {
|
|
177
|
-
console.info('[AgentChatWidget
|
|
391
|
+
console.info('[AgentChatWidget] Resolved WS URL:', resolvedWsUrl)
|
|
178
392
|
} catch (_) {}
|
|
179
393
|
|
|
180
394
|
function updateStatus(text, isConnected) {
|
|
@@ -196,7 +410,7 @@
|
|
|
196
410
|
} catch (err) {
|
|
197
411
|
connecting = false
|
|
198
412
|
updateStatus('Connection error', false)
|
|
199
|
-
console.error('[AgentChatWidget
|
|
413
|
+
console.error('[AgentChatWidget] WS error', err)
|
|
200
414
|
return
|
|
201
415
|
}
|
|
202
416
|
|
|
@@ -204,6 +418,22 @@
|
|
|
204
418
|
connected = true
|
|
205
419
|
connecting = false
|
|
206
420
|
updateStatus('Connected', true)
|
|
421
|
+
|
|
422
|
+
// Generate session ID when websocket connects
|
|
423
|
+
sessionId = 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9)
|
|
424
|
+
|
|
425
|
+
// Show customer info form if enabled and not yet submitted
|
|
426
|
+
if (captureCustomerInfo && customerForm && !customerInfoSubmitted) {
|
|
427
|
+
customerForm.style.display = 'flex'
|
|
428
|
+
messages.style.display = 'none'
|
|
429
|
+
inputWrap.style.display = 'none'
|
|
430
|
+
// Focus first input
|
|
431
|
+
const nameInput = customerForm.querySelector('#acw-customer-name')
|
|
432
|
+
if (nameInput) setTimeout(() => nameInput.focus(), 100)
|
|
433
|
+
} else {
|
|
434
|
+
messages.style.display = 'flex'
|
|
435
|
+
inputWrap.style.display = 'flex'
|
|
436
|
+
}
|
|
207
437
|
}
|
|
208
438
|
|
|
209
439
|
ws.onmessage = (event) => {
|
|
@@ -219,7 +449,7 @@
|
|
|
219
449
|
}
|
|
220
450
|
|
|
221
451
|
ws.onerror = (e) => {
|
|
222
|
-
console.error('[AgentChatWidget
|
|
452
|
+
console.error('[AgentChatWidget] WS error', e)
|
|
223
453
|
updateStatus('Connection error', false)
|
|
224
454
|
appendMessage('Connection error. Check WS service and URL.', 'bot')
|
|
225
455
|
}
|
|
@@ -243,11 +473,26 @@
|
|
|
243
473
|
connected = false
|
|
244
474
|
connecting = false
|
|
245
475
|
updateStatus('Disconnected', false)
|
|
476
|
+
sessionId = null
|
|
477
|
+
customerInfoSubmitted = false
|
|
478
|
+
if (customerForm) {
|
|
479
|
+
customerForm.style.display = 'none'
|
|
480
|
+
const nameInput = customerForm.querySelector('#acw-customer-name')
|
|
481
|
+
const phoneInput = customerForm.querySelector('#acw-customer-phone')
|
|
482
|
+
if (nameInput) nameInput.value = ''
|
|
483
|
+
if (phoneInput) phoneInput.value = ''
|
|
484
|
+
}
|
|
246
485
|
}
|
|
247
486
|
|
|
248
487
|
function sendMessage() {
|
|
249
488
|
const text = input.value.trim()
|
|
250
489
|
if (!text) return
|
|
490
|
+
|
|
491
|
+
// Don't allow sending messages if customer info form is required and not submitted
|
|
492
|
+
if (captureCustomerInfo && !customerInfoSubmitted) {
|
|
493
|
+
return
|
|
494
|
+
}
|
|
495
|
+
|
|
251
496
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
252
497
|
connect()
|
|
253
498
|
setTimeout(sendMessage, 200)
|
|
@@ -279,9 +524,6 @@
|
|
|
279
524
|
disconnect()
|
|
280
525
|
}
|
|
281
526
|
|
|
282
|
-
disconnectBtn.onclick = () => {
|
|
283
|
-
disconnect()
|
|
284
|
-
}
|
|
285
527
|
sendBtn.onclick = sendMessage
|
|
286
528
|
input.addEventListener('keydown', (e) => {
|
|
287
529
|
if (e.key === 'Enter') {
|
|
@@ -310,6 +552,12 @@
|
|
|
310
552
|
const scriptEl = document.currentScript || document.querySelector('script[data-agent-id]')
|
|
311
553
|
const ds = (scriptEl && scriptEl.dataset) || {}
|
|
312
554
|
const allowed = (ds.allowedDomains || ds.allowed_domains || '').split(',').map((d) => d.trim()).filter(Boolean)
|
|
555
|
+
const captureCustomerInfo = userConfig.captureCustomerInfo !== undefined
|
|
556
|
+
? userConfig.captureCustomerInfo
|
|
557
|
+
: ds.captureCustomerInfo === 'true' || ds.capture_customer_info === 'true'
|
|
558
|
+
const position = userConfig.position || ds.position || ds.launcherPosition || 'bottom-right'
|
|
559
|
+
const validPositions = ['bottom-right', 'middle-right', 'top-right', 'bottom-left', 'middle-left', 'top-left']
|
|
560
|
+
const validPosition = validPositions.includes(position) ? position : 'bottom-right'
|
|
313
561
|
return {
|
|
314
562
|
agentId: userConfig.agentId || ds.agentId,
|
|
315
563
|
embedKey: userConfig.embedKey || ds.embedKey || ds.embed_key,
|
|
@@ -321,6 +569,8 @@
|
|
|
321
569
|
wsUrl: userConfig.wsUrl || ds.wsUrl || ds.ws_url,
|
|
322
570
|
title: userConfig.title || ds.title,
|
|
323
571
|
greeting: userConfig.greeting || ds.greeting,
|
|
572
|
+
captureCustomerInfo: captureCustomerInfo,
|
|
573
|
+
position: validPosition,
|
|
324
574
|
}
|
|
325
575
|
}
|
|
326
576
|
|
|
@@ -348,3 +598,4 @@
|
|
|
348
598
|
init()
|
|
349
599
|
}
|
|
350
600
|
})()
|
|
601
|
+
|
package/agent-chat.staging.js
CHANGED
|
@@ -19,10 +19,20 @@
|
|
|
19
19
|
const safeBg = background || '#ffffff'
|
|
20
20
|
return `
|
|
21
21
|
:host, .acw * { box-sizing: border-box; }
|
|
22
|
-
.acw-container { position: fixed;
|
|
23
|
-
.acw-
|
|
22
|
+
.acw-container { position: fixed; z-index: 2147483000; font-family: Inter, system-ui, -apple-system, sans-serif; }
|
|
23
|
+
.acw-container.bottom-right { right: 16px; bottom: 16px; }
|
|
24
|
+
.acw-container.middle-right { right: 16px; top: 50%; transform: translateY(-50%); }
|
|
25
|
+
.acw-launcher { background: ${safePrimary}; color: #fff; border: none; border-radius: 999px; padding: 10px 14px; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.18); font-size: 14px; transition: transform 0.2s; }
|
|
24
26
|
.acw-launcher:hover { opacity: 0.95; }
|
|
25
|
-
.acw-
|
|
27
|
+
.acw-launcher.vertical { flex-direction: column; padding: 14px 10px; min-width: 48px; height: auto; }
|
|
28
|
+
.acw-launcher.vertical span { writing-mode: sideways-lr; text-orientation: mixed; letter-spacing: 0.5px; }
|
|
29
|
+
.acw-panel { position: absolute; width: 360px; max-width: 90vw; height: 520px; max-height: 80vh; background: ${safeBg}; border: 1px solid rgba(0,0,0,0.08); border-radius: 12px; box-shadow: 0 16px 40px rgba(0,0,0,0.22); display: none; flex-direction: column; overflow: hidden; }
|
|
30
|
+
.acw-panel.bottom-right { right: 0; bottom: 56px; }
|
|
31
|
+
.acw-panel.middle-right { right: 56px; top: 50%; transform: translateY(-50%); }
|
|
32
|
+
.acw-panel.top-right { right: 0; top: 56px; }
|
|
33
|
+
.acw-panel.bottom-left { left: 0; bottom: 56px; }
|
|
34
|
+
.acw-panel.middle-left { left: 56px; top: 50%; transform: translateY(-50%); }
|
|
35
|
+
.acw-panel.top-left { left: 0; top: 56px; }
|
|
26
36
|
.acw-header { background: ${safePrimary}; color: #fff; padding: 12px 14px; display: flex; align-items: center; justify-content: space-between; }
|
|
27
37
|
.acw-title { font-weight: 600; font-size: 14px; }
|
|
28
38
|
.acw-close { background: transparent; border: none; color: #fff; cursor: pointer; font-size: 16px; }
|
|
@@ -39,6 +49,18 @@
|
|
|
39
49
|
.acw-dot.disconnected { background: #ef4444; box-shadow: 0 0 0 6px rgba(239,68,68,0.18); }
|
|
40
50
|
.acw-disconnect { margin-left: auto; background: #f8fafc; color: #0f172a; border: 1px solid rgba(0,0,0,0.08); border-radius: 6px; padding: 6px 8px; font-size: 12px; cursor: pointer; }
|
|
41
51
|
.acw-disconnect:hover { background: #eef2f7; }
|
|
52
|
+
.acw-customer-form { position: absolute; inset: 0; background: ${safeBg}; z-index: 10; display: flex; flex-direction: column; padding: 20px; }
|
|
53
|
+
.acw-customer-form h3 { margin: 0 0 12px; font-size: 16px; font-weight: 600; color: #0f172a; }
|
|
54
|
+
.acw-customer-form p { margin: 0 0 16px; font-size: 13px; color: #64748b; }
|
|
55
|
+
.acw-customer-form .field { margin-bottom: 16px; }
|
|
56
|
+
.acw-customer-form label { display: block; font-size: 13px; font-weight: 500; margin-bottom: 6px; color: #475569; }
|
|
57
|
+
.acw-customer-form input { width: 100%; padding: 10px 12px; border: 1px solid rgba(0,0,0,0.12); border-radius: 8px; font-size: 14px; }
|
|
58
|
+
.acw-customer-form .actions { display: flex; gap: 8px; margin-top: auto; }
|
|
59
|
+
.acw-customer-form button { flex: 1; padding: 10px; border: none; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; }
|
|
60
|
+
.acw-customer-form .submit-btn { background: ${safePrimary}; color: #fff; }
|
|
61
|
+
.acw-customer-form .submit-btn:hover { opacity: 0.95; }
|
|
62
|
+
.acw-customer-form .skip-btn { background: #f8fafc; color: #64748b; border: 1px solid rgba(0,0,0,0.08); }
|
|
63
|
+
.acw-customer-form .skip-btn:hover { background: #eef2f7; }
|
|
42
64
|
@media (max-width: 480px) {
|
|
43
65
|
.acw-panel { width: calc(100vw - 24px); height: 70vh; }
|
|
44
66
|
}
|
|
@@ -55,16 +77,24 @@
|
|
|
55
77
|
apiBaseUrl,
|
|
56
78
|
voiceServiceUrl,
|
|
57
79
|
wsUrl,
|
|
58
|
-
title = 'Chat with us
|
|
80
|
+
title = 'Chat with us',
|
|
59
81
|
greeting = null,
|
|
82
|
+
captureCustomerInfo = false,
|
|
83
|
+
position = 'bottom-right',
|
|
60
84
|
} = config
|
|
61
85
|
|
|
86
|
+
// Valid positions
|
|
87
|
+
const validPositions = ['bottom-right', 'middle-right', 'top-right', 'bottom-left', 'middle-left', 'top-left']
|
|
88
|
+
const finalPosition = validPositions.includes(position) ? position : 'bottom-right'
|
|
89
|
+
const isVertical = finalPosition === 'middle-right' || finalPosition === 'middle-left'
|
|
90
|
+
const isLeft = finalPosition.includes('left')
|
|
91
|
+
|
|
62
92
|
if (!agentId) {
|
|
63
|
-
console.warn('[AgentChatWidget] Missing data-agent-id')
|
|
93
|
+
console.warn('[AgentChatWidget] Missing agentId. Provide it via init({ agentId: "..." }) or data-agent-id attribute')
|
|
64
94
|
return
|
|
65
95
|
}
|
|
66
96
|
if (!embedKey) {
|
|
67
|
-
console.warn('[AgentChatWidget] Missing data-embed-key')
|
|
97
|
+
console.warn('[AgentChatWidget] Missing embedKey. Provide it via init({ embedKey: "..." }) or data-embed-key attribute')
|
|
68
98
|
return
|
|
69
99
|
}
|
|
70
100
|
|
|
@@ -75,18 +105,47 @@
|
|
|
75
105
|
}
|
|
76
106
|
|
|
77
107
|
const container = document.createElement('div')
|
|
78
|
-
container.className =
|
|
108
|
+
container.className = `acw-container ${finalPosition}`
|
|
109
|
+
|
|
110
|
+
// Apply position styles directly to container (since it's in regular DOM, not shadow)
|
|
111
|
+
container.style.position = 'fixed'
|
|
112
|
+
container.style.zIndex = '2147483000'
|
|
113
|
+
container.style.fontFamily = 'Inter, system-ui, -apple-system, sans-serif'
|
|
114
|
+
|
|
115
|
+
// Set container position based on finalPosition
|
|
116
|
+
if (finalPosition === 'bottom-right') {
|
|
117
|
+
container.style.right = '16px'
|
|
118
|
+
container.style.bottom = '16px'
|
|
119
|
+
} else if (finalPosition === 'middle-right') {
|
|
120
|
+
container.style.right = '16px'
|
|
121
|
+
container.style.top = '50%'
|
|
122
|
+
container.style.transform = 'translateY(-50%)'
|
|
123
|
+
} else if (finalPosition === 'top-right') {
|
|
124
|
+
container.style.right = '16px'
|
|
125
|
+
container.style.top = '16px'
|
|
126
|
+
} else if (finalPosition === 'bottom-left') {
|
|
127
|
+
container.style.left = '16px'
|
|
128
|
+
container.style.bottom = '16px'
|
|
129
|
+
} else if (finalPosition === 'middle-left') {
|
|
130
|
+
container.style.left = '16px'
|
|
131
|
+
container.style.top = '50%'
|
|
132
|
+
container.style.transform = 'translateY(-50%)'
|
|
133
|
+
} else if (finalPosition === 'top-left') {
|
|
134
|
+
container.style.left = '16px'
|
|
135
|
+
container.style.top = '16px'
|
|
136
|
+
}
|
|
137
|
+
|
|
79
138
|
const shadow = container.attachShadow({ mode: 'open' })
|
|
80
139
|
const style = document.createElement('style')
|
|
81
140
|
style.textContent = createStyles(primaryColor, backgroundColor)
|
|
82
141
|
shadow.appendChild(style)
|
|
83
142
|
|
|
84
143
|
const panel = document.createElement('div')
|
|
85
|
-
panel.className =
|
|
144
|
+
panel.className = `acw-panel ${finalPosition}`
|
|
86
145
|
|
|
87
146
|
const launcher = document.createElement('button')
|
|
88
|
-
launcher.className = 'acw-launcher'
|
|
89
|
-
launcher.innerHTML = '<span>Chat
|
|
147
|
+
launcher.className = isVertical ? 'acw-launcher vertical' : 'acw-launcher'
|
|
148
|
+
launcher.innerHTML = '<span>Chat</span>'
|
|
90
149
|
|
|
91
150
|
const header = document.createElement('div')
|
|
92
151
|
header.className = 'acw-header'
|
|
@@ -115,7 +174,7 @@
|
|
|
115
174
|
const statusDot = document.createElement('div')
|
|
116
175
|
statusDot.className = 'acw-dot disconnected'
|
|
117
176
|
const statusText = document.createElement('span')
|
|
118
|
-
statusText.textContent = 'Disconnected
|
|
177
|
+
statusText.textContent = 'Disconnected'
|
|
119
178
|
const disconnectBtn = document.createElement('button')
|
|
120
179
|
disconnectBtn.className = 'acw-disconnect'
|
|
121
180
|
disconnectBtn.textContent = 'Connect'
|
|
@@ -136,6 +195,142 @@
|
|
|
136
195
|
inputWrap.appendChild(input)
|
|
137
196
|
inputWrap.appendChild(sendBtn)
|
|
138
197
|
|
|
198
|
+
// Customer info form (if enabled)
|
|
199
|
+
let customerForm = null
|
|
200
|
+
let sessionId = null
|
|
201
|
+
let customerInfoSubmitted = false
|
|
202
|
+
|
|
203
|
+
if (captureCustomerInfo) {
|
|
204
|
+
customerForm = document.createElement('div')
|
|
205
|
+
customerForm.className = 'acw-customer-form'
|
|
206
|
+
customerForm.style.display = 'none'
|
|
207
|
+
|
|
208
|
+
const formTitle = document.createElement('h3')
|
|
209
|
+
formTitle.textContent = 'Welcome! Please share your details'
|
|
210
|
+
const formDesc = document.createElement('p')
|
|
211
|
+
formDesc.textContent = 'We need a few details to get started'
|
|
212
|
+
|
|
213
|
+
const nameField = document.createElement('div')
|
|
214
|
+
nameField.className = 'field'
|
|
215
|
+
const nameLabel = document.createElement('label')
|
|
216
|
+
nameLabel.textContent = 'Your Name'
|
|
217
|
+
nameLabel.setAttribute('for', 'acw-customer-name')
|
|
218
|
+
const nameInput = document.createElement('input')
|
|
219
|
+
nameInput.id = 'acw-customer-name'
|
|
220
|
+
nameInput.type = 'text'
|
|
221
|
+
nameInput.placeholder = 'Enter your name'
|
|
222
|
+
nameField.appendChild(nameLabel)
|
|
223
|
+
nameField.appendChild(nameInput)
|
|
224
|
+
|
|
225
|
+
const phoneField = document.createElement('div')
|
|
226
|
+
phoneField.className = 'field'
|
|
227
|
+
const phoneLabel = document.createElement('label')
|
|
228
|
+
phoneLabel.textContent = 'Phone Number'
|
|
229
|
+
phoneLabel.setAttribute('for', 'acw-customer-phone')
|
|
230
|
+
const phoneInput = document.createElement('input')
|
|
231
|
+
phoneInput.id = 'acw-customer-phone'
|
|
232
|
+
phoneInput.type = 'tel'
|
|
233
|
+
phoneInput.placeholder = 'Enter your phone number'
|
|
234
|
+
phoneField.appendChild(phoneLabel)
|
|
235
|
+
phoneField.appendChild(phoneInput)
|
|
236
|
+
|
|
237
|
+
const formActions = document.createElement('div')
|
|
238
|
+
formActions.className = 'actions'
|
|
239
|
+
const submitBtn = document.createElement('button')
|
|
240
|
+
submitBtn.className = 'submit-btn'
|
|
241
|
+
submitBtn.textContent = 'Start Chat'
|
|
242
|
+
const skipBtn = document.createElement('button')
|
|
243
|
+
skipBtn.className = 'skip-btn'
|
|
244
|
+
skipBtn.textContent = 'Skip'
|
|
245
|
+
formActions.appendChild(submitBtn)
|
|
246
|
+
formActions.appendChild(skipBtn)
|
|
247
|
+
|
|
248
|
+
customerForm.appendChild(formTitle)
|
|
249
|
+
customerForm.appendChild(formDesc)
|
|
250
|
+
customerForm.appendChild(nameField)
|
|
251
|
+
customerForm.appendChild(phoneField)
|
|
252
|
+
customerForm.appendChild(formActions)
|
|
253
|
+
|
|
254
|
+
async function submitCustomerInfo() {
|
|
255
|
+
const name = nameInput.value.trim()
|
|
256
|
+
const phone = phoneInput.value.trim()
|
|
257
|
+
|
|
258
|
+
if (!name || !phone) {
|
|
259
|
+
alert('Please fill in both name and phone number')
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (!sessionId) {
|
|
264
|
+
console.error('[AgentChatWidget] No session ID available')
|
|
265
|
+
return
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const baseUrl = voiceServiceUrl || apiBaseUrl || new URL(document.currentScript?.src || window.location.href).origin
|
|
269
|
+
let apiBase = (baseUrl || '').replace(/\/$/, '')
|
|
270
|
+
// Convert ws:// to http:// for HTTP requests
|
|
271
|
+
if (apiBase.startsWith('ws://')) {
|
|
272
|
+
apiBase = apiBase.replace(/^ws/, 'http')
|
|
273
|
+
} else if (apiBase.startsWith('wss://')) {
|
|
274
|
+
apiBase = apiBase.replace(/^wss/, 'https')
|
|
275
|
+
}
|
|
276
|
+
const endpoint = `${apiBase}/api/v1/agents/text-chat/customer-info`
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
const response = await fetch(endpoint, {
|
|
280
|
+
method: 'POST',
|
|
281
|
+
headers: {
|
|
282
|
+
'Content-Type': 'application/json',
|
|
283
|
+
},
|
|
284
|
+
body: JSON.stringify({
|
|
285
|
+
session_id: sessionId,
|
|
286
|
+
phone_number: phone,
|
|
287
|
+
customer_name: name,
|
|
288
|
+
}),
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
if (response.ok) {
|
|
292
|
+
customerInfoSubmitted = true
|
|
293
|
+
customerForm.style.display = 'none'
|
|
294
|
+
messages.style.display = 'flex'
|
|
295
|
+
inputWrap.style.display = 'flex'
|
|
296
|
+
} else {
|
|
297
|
+
const errorText = await response.text()
|
|
298
|
+
console.error('[AgentChatWidget] Failed to submit customer info:', errorText)
|
|
299
|
+
alert('Failed to submit information. Please try again.')
|
|
300
|
+
}
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.error('[AgentChatWidget] Error submitting customer info:', error)
|
|
303
|
+
alert('Error submitting information. Please try again.')
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function skipCustomerInfo() {
|
|
308
|
+
customerInfoSubmitted = true
|
|
309
|
+
customerForm.style.display = 'none'
|
|
310
|
+
messages.style.display = 'flex'
|
|
311
|
+
inputWrap.style.display = 'flex'
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
submitBtn.onclick = submitCustomerInfo
|
|
315
|
+
skipBtn.onclick = skipCustomerInfo
|
|
316
|
+
|
|
317
|
+
// Allow Enter key to submit
|
|
318
|
+
nameInput.addEventListener('keydown', (e) => {
|
|
319
|
+
if (e.key === 'Enter') {
|
|
320
|
+
e.preventDefault()
|
|
321
|
+
phoneInput.focus()
|
|
322
|
+
}
|
|
323
|
+
})
|
|
324
|
+
phoneInput.addEventListener('keydown', (e) => {
|
|
325
|
+
if (e.key === 'Enter') {
|
|
326
|
+
e.preventDefault()
|
|
327
|
+
submitCustomerInfo()
|
|
328
|
+
}
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
panel.appendChild(customerForm)
|
|
332
|
+
}
|
|
333
|
+
|
|
139
334
|
panel.appendChild(header)
|
|
140
335
|
panel.appendChild(statusEl)
|
|
141
336
|
panel.appendChild(messages)
|
|
@@ -159,13 +354,32 @@
|
|
|
159
354
|
let connecting = false
|
|
160
355
|
|
|
161
356
|
const resolvedWsUrl = (() => {
|
|
357
|
+
// Explicit wsUrl takes highest priority
|
|
162
358
|
if (wsUrl) return wsUrl
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
359
|
+
|
|
360
|
+
// Determine environment-specific WebSocket URL based on VERSION
|
|
361
|
+
let wsBase
|
|
362
|
+
if (VERSION.includes('-staging')) {
|
|
363
|
+
wsBase = 'wss://talk2ai-staging.helllo.ai'
|
|
364
|
+
} else if (VERSION.includes('-prod')) {
|
|
365
|
+
wsBase = 'wss://talk2ai-prod.helllo.ai'
|
|
366
|
+
} else {
|
|
367
|
+
// For dev/latest, use existing fallback logic
|
|
368
|
+
const baseCandidate = voiceServiceUrl || apiBaseUrl || new URL(document.currentScript?.src || window.location.href).origin
|
|
369
|
+
const voiceBase = (baseCandidate || '').replace(/\/$/, '')
|
|
370
|
+
// Handle both http/https and ws/wss URLs
|
|
371
|
+
if (voiceBase.startsWith('http://')) {
|
|
372
|
+
wsBase = voiceBase.replace(/^http/, 'ws')
|
|
373
|
+
} else if (voiceBase.startsWith('https://')) {
|
|
374
|
+
wsBase = voiceBase.replace(/^https/, 'wss')
|
|
375
|
+
} else if (voiceBase.startsWith('ws://') || voiceBase.startsWith('wss://')) {
|
|
376
|
+
wsBase = voiceBase // Already a WebSocket URL
|
|
377
|
+
} else {
|
|
378
|
+
// Default to ws if no protocol
|
|
379
|
+
wsBase = 'ws://' + voiceBase
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
169
383
|
const params = new URLSearchParams()
|
|
170
384
|
params.set('embed_key', embedKey)
|
|
171
385
|
params.set('host', window.location.hostname)
|
|
@@ -174,11 +388,11 @@
|
|
|
174
388
|
|
|
175
389
|
// Log resolved URL once for debugging
|
|
176
390
|
try {
|
|
177
|
-
console.info('[AgentChatWidget
|
|
391
|
+
console.info('[AgentChatWidget] Resolved WS URL:', resolvedWsUrl)
|
|
178
392
|
} catch (_) {}
|
|
179
393
|
|
|
180
394
|
function updateStatus(text, isConnected) {
|
|
181
|
-
statusText.textContent = text
|
|
395
|
+
statusText.textContent = text
|
|
182
396
|
statusDot.classList.remove('connected', 'disconnected')
|
|
183
397
|
statusDot.classList.add(isConnected ? 'connected' : 'disconnected')
|
|
184
398
|
|
|
@@ -196,7 +410,7 @@
|
|
|
196
410
|
} catch (err) {
|
|
197
411
|
connecting = false
|
|
198
412
|
updateStatus('Connection error', false)
|
|
199
|
-
console.error('[AgentChatWidget
|
|
413
|
+
console.error('[AgentChatWidget] WS error', err)
|
|
200
414
|
return
|
|
201
415
|
}
|
|
202
416
|
|
|
@@ -204,6 +418,22 @@
|
|
|
204
418
|
connected = true
|
|
205
419
|
connecting = false
|
|
206
420
|
updateStatus('Connected', true)
|
|
421
|
+
|
|
422
|
+
// Generate session ID when websocket connects
|
|
423
|
+
sessionId = 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9)
|
|
424
|
+
|
|
425
|
+
// Show customer info form if enabled and not yet submitted
|
|
426
|
+
if (captureCustomerInfo && customerForm && !customerInfoSubmitted) {
|
|
427
|
+
customerForm.style.display = 'flex'
|
|
428
|
+
messages.style.display = 'none'
|
|
429
|
+
inputWrap.style.display = 'none'
|
|
430
|
+
// Focus first input
|
|
431
|
+
const nameInput = customerForm.querySelector('#acw-customer-name')
|
|
432
|
+
if (nameInput) setTimeout(() => nameInput.focus(), 100)
|
|
433
|
+
} else {
|
|
434
|
+
messages.style.display = 'flex'
|
|
435
|
+
inputWrap.style.display = 'flex'
|
|
436
|
+
}
|
|
207
437
|
}
|
|
208
438
|
|
|
209
439
|
ws.onmessage = (event) => {
|
|
@@ -219,7 +449,7 @@
|
|
|
219
449
|
}
|
|
220
450
|
|
|
221
451
|
ws.onerror = (e) => {
|
|
222
|
-
console.error('[AgentChatWidget
|
|
452
|
+
console.error('[AgentChatWidget] WS error', e)
|
|
223
453
|
updateStatus('Connection error', false)
|
|
224
454
|
appendMessage('Connection error. Check WS service and URL.', 'bot')
|
|
225
455
|
}
|
|
@@ -243,11 +473,26 @@
|
|
|
243
473
|
connected = false
|
|
244
474
|
connecting = false
|
|
245
475
|
updateStatus('Disconnected', false)
|
|
476
|
+
sessionId = null
|
|
477
|
+
customerInfoSubmitted = false
|
|
478
|
+
if (customerForm) {
|
|
479
|
+
customerForm.style.display = 'none'
|
|
480
|
+
const nameInput = customerForm.querySelector('#acw-customer-name')
|
|
481
|
+
const phoneInput = customerForm.querySelector('#acw-customer-phone')
|
|
482
|
+
if (nameInput) nameInput.value = ''
|
|
483
|
+
if (phoneInput) phoneInput.value = ''
|
|
484
|
+
}
|
|
246
485
|
}
|
|
247
486
|
|
|
248
487
|
function sendMessage() {
|
|
249
488
|
const text = input.value.trim()
|
|
250
489
|
if (!text) return
|
|
490
|
+
|
|
491
|
+
// Don't allow sending messages if customer info form is required and not submitted
|
|
492
|
+
if (captureCustomerInfo && !customerInfoSubmitted) {
|
|
493
|
+
return
|
|
494
|
+
}
|
|
495
|
+
|
|
251
496
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
252
497
|
connect()
|
|
253
498
|
setTimeout(sendMessage, 200)
|
|
@@ -279,9 +524,6 @@
|
|
|
279
524
|
disconnect()
|
|
280
525
|
}
|
|
281
526
|
|
|
282
|
-
disconnectBtn.onclick = () => {
|
|
283
|
-
disconnect()
|
|
284
|
-
}
|
|
285
527
|
sendBtn.onclick = sendMessage
|
|
286
528
|
input.addEventListener('keydown', (e) => {
|
|
287
529
|
if (e.key === 'Enter') {
|
|
@@ -310,6 +552,12 @@
|
|
|
310
552
|
const scriptEl = document.currentScript || document.querySelector('script[data-agent-id]')
|
|
311
553
|
const ds = (scriptEl && scriptEl.dataset) || {}
|
|
312
554
|
const allowed = (ds.allowedDomains || ds.allowed_domains || '').split(',').map((d) => d.trim()).filter(Boolean)
|
|
555
|
+
const captureCustomerInfo = userConfig.captureCustomerInfo !== undefined
|
|
556
|
+
? userConfig.captureCustomerInfo
|
|
557
|
+
: ds.captureCustomerInfo === 'true' || ds.capture_customer_info === 'true'
|
|
558
|
+
const position = userConfig.position || ds.position || ds.launcherPosition || 'bottom-right'
|
|
559
|
+
const validPositions = ['bottom-right', 'middle-right', 'top-right', 'bottom-left', 'middle-left', 'top-left']
|
|
560
|
+
const validPosition = validPositions.includes(position) ? position : 'bottom-right'
|
|
313
561
|
return {
|
|
314
562
|
agentId: userConfig.agentId || ds.agentId,
|
|
315
563
|
embedKey: userConfig.embedKey || ds.embedKey || ds.embed_key,
|
|
@@ -321,6 +569,8 @@
|
|
|
321
569
|
wsUrl: userConfig.wsUrl || ds.wsUrl || ds.ws_url,
|
|
322
570
|
title: userConfig.title || ds.title,
|
|
323
571
|
greeting: userConfig.greeting || ds.greeting,
|
|
572
|
+
captureCustomerInfo: captureCustomerInfo,
|
|
573
|
+
position: validPosition,
|
|
324
574
|
}
|
|
325
575
|
}
|
|
326
576
|
|
|
@@ -348,3 +598,4 @@
|
|
|
348
598
|
init()
|
|
349
599
|
}
|
|
350
600
|
})()
|
|
601
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helllo-ai/agent-chat-widget",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "Bot Swarm Agent Chat Widget - Embeddable chat widget for AI agents",
|
|
5
5
|
"main": "agent-chat.latest.js",
|
|
6
6
|
"files": [
|
|
@@ -26,16 +26,6 @@
|
|
|
26
26
|
"bugs": {
|
|
27
27
|
"url": "https://github.com/bot-swarm/agent-chat-widget/issues"
|
|
28
28
|
},
|
|
29
|
-
"scripts": {
|
|
30
|
-
"build": "node build.js",
|
|
31
|
-
"build:staging": "node build.js --staging",
|
|
32
|
-
"build:prod": "node build.js --prod",
|
|
33
|
-
"version:patch": "npm version patch",
|
|
34
|
-
"version:minor": "npm version minor",
|
|
35
|
-
"version:major": "npm version major",
|
|
36
|
-
"publish:public": "npm publish --access public",
|
|
37
|
-
"publish:staging": "npm publish --tag staging --access public"
|
|
38
|
-
},
|
|
39
29
|
"unpkg": "agent-chat.latest.js",
|
|
40
30
|
"jsdelivr": "agent-chat.latest.js"
|
|
41
31
|
}
|