@helllo-ai/agent-chat-widget 0.1.21 → 0.1.24
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.staging.js +233 -49
- package/package.json +1 -1
package/agent-chat.staging.js
CHANGED
|
@@ -24,8 +24,14 @@
|
|
|
24
24
|
.acw-container.middle-right { right: 16px; top: 50%; transform: translateY(-50%); }
|
|
25
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; }
|
|
26
26
|
.acw-launcher:hover { opacity: 0.95; }
|
|
27
|
+
.acw-launcher img { width: 24px; height: 24px; object-fit: contain; }
|
|
27
28
|
.acw-launcher.vertical { flex-direction: column; padding: 14px 10px; min-width: 48px; height: auto; }
|
|
28
29
|
.acw-launcher.vertical span { writing-mode: sideways-lr; text-orientation: mixed; letter-spacing: 0.5px; }
|
|
30
|
+
.acw-header-left { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; }
|
|
31
|
+
.acw-header-icon { width: 28px; height: 28px; object-fit: contain; flex-shrink: 0; }
|
|
32
|
+
.acw-header-title-wrap { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
33
|
+
.acw-unread-badge { background: rgba(255,255,255,0.9); color: ${safePrimary}; font-size: 11px; font-weight: 700; min-width: 18px; height: 18px; border-radius: 9px; display: inline-flex; align-items: center; justify-content: center; padding: 0 5px; }
|
|
34
|
+
.acw-unread-badge.hidden { display: none; }
|
|
29
35
|
.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
36
|
.acw-panel.bottom-right { right: 0; bottom: 56px; }
|
|
31
37
|
.acw-panel.middle-right { right: 56px; top: 50%; transform: translateY(-50%); }
|
|
@@ -42,13 +48,15 @@
|
|
|
42
48
|
.acw-msg-bot { margin-right: auto; background: rgba(0,0,0,0.05); color: #111; }
|
|
43
49
|
.acw-input { border-top: 1px solid rgba(0,0,0,0.08); padding: 10px; display: flex; gap: 8px; }
|
|
44
50
|
.acw-input input { flex: 1; padding: 10px 12px; border: 1px solid rgba(0,0,0,0.12); border-radius: 8px; font-size: 14px; }
|
|
51
|
+
.acw-input input:disabled, .acw-input button:disabled { opacity: 0.6; cursor: not-allowed; }
|
|
45
52
|
.acw-input button { background: ${safePrimary}; color: #fff; border: none; border-radius: 8px; padding: 0 14px; cursor: pointer; font-weight: 600; }
|
|
53
|
+
.acw-footer { font-size: 11px; color: #94a3b8; padding: 6px 12px 10px; text-align: center; border-top: 1px solid rgba(0,0,0,0.06); }
|
|
54
|
+
.acw-footer a { color: #64748b; text-decoration: none; }
|
|
55
|
+
.acw-footer a:hover { text-decoration: underline; }
|
|
46
56
|
.acw-status { font-size: 12px; color: #666; padding: 8px 12px; display: flex; align-items: center; gap: 10px; border-bottom: 1px solid rgba(0,0,0,0.06); }
|
|
47
57
|
.acw-dot { width: 8px; height: 8px; border-radius: 999px; box-shadow: 0 0 0 6px rgba(0,0,0,0.04); }
|
|
48
58
|
.acw-dot.connected { background: #22c55e; box-shadow: 0 0 0 6px rgba(34,197,94,0.18); }
|
|
49
59
|
.acw-dot.disconnected { background: #ef4444; box-shadow: 0 0 0 6px rgba(239,68,68,0.18); }
|
|
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; }
|
|
51
|
-
.acw-disconnect:hover { background: #eef2f7; }
|
|
52
60
|
.acw-customer-form { position: absolute; inset: 0; background: ${safeBg}; z-index: 10; display: flex; flex-direction: column; padding: 20px; }
|
|
53
61
|
.acw-customer-form h3 { margin: 0 0 12px; font-size: 16px; font-weight: 600; color: #0f172a; }
|
|
54
62
|
.acw-customer-form p { margin: 0 0 16px; font-size: 13px; color: #64748b; }
|
|
@@ -61,6 +69,9 @@
|
|
|
61
69
|
.acw-customer-form .submit-btn:hover { opacity: 0.95; }
|
|
62
70
|
.acw-customer-form .skip-btn { background: #f8fafc; color: #64748b; border: 1px solid rgba(0,0,0,0.08); }
|
|
63
71
|
.acw-customer-form .skip-btn:hover { background: #eef2f7; }
|
|
72
|
+
.acw-start-cta { flex: 1; display: flex; align-items: center; justify-content: center; padding: 24px; }
|
|
73
|
+
.acw-start-cta button { background: ${safePrimary}; color: #fff; border: none; border-radius: 10px; padding: 14px 24px; font-size: 15px; font-weight: 600; cursor: pointer; box-shadow: 0 4px 12px rgba(0,0,0,0.12); }
|
|
74
|
+
.acw-start-cta button:hover { opacity: 0.95; }
|
|
64
75
|
@media (max-width: 480px) {
|
|
65
76
|
.acw-panel { width: calc(100vw - 24px); height: 70vh; }
|
|
66
77
|
}
|
|
@@ -80,9 +91,29 @@
|
|
|
80
91
|
title = 'Chat with us',
|
|
81
92
|
greeting = null,
|
|
82
93
|
captureCustomerInfo = false,
|
|
94
|
+
allowSkipCustomerInfo = true,
|
|
83
95
|
position = 'bottom-right',
|
|
96
|
+
iconUrl = null,
|
|
97
|
+
showIcon = true,
|
|
98
|
+
launcherText = 'Chat',
|
|
84
99
|
} = config
|
|
85
100
|
|
|
101
|
+
// Resolve icon: explicit iconUrl, then page favicon, then /favicon.ico (only used when showIcon is true)
|
|
102
|
+
const resolvedIconUrl = (() => {
|
|
103
|
+
if (!showIcon) return null
|
|
104
|
+
if (iconUrl && typeof iconUrl === 'string' && iconUrl.trim()) return iconUrl.trim()
|
|
105
|
+
try {
|
|
106
|
+
const link = document.querySelector('link[rel="icon"]') || document.querySelector('link[rel="shortcut icon"]')
|
|
107
|
+
if (link && link.href) return link.href
|
|
108
|
+
} catch (_) {}
|
|
109
|
+
try {
|
|
110
|
+
const origin = window.location.origin || ''
|
|
111
|
+
if (origin) return origin + '/favicon.ico'
|
|
112
|
+
} catch (_) {}
|
|
113
|
+
return null
|
|
114
|
+
})()
|
|
115
|
+
const launcherLabel = (typeof launcherText === 'string' && launcherText.trim()) ? launcherText.trim() : 'Chat'
|
|
116
|
+
|
|
86
117
|
// Valid positions
|
|
87
118
|
const validPositions = ['bottom-right', 'middle-right', 'top-right', 'bottom-left', 'middle-left', 'top-left']
|
|
88
119
|
const finalPosition = validPositions.includes(position) ? position : 'bottom-right'
|
|
@@ -145,13 +176,42 @@
|
|
|
145
176
|
|
|
146
177
|
const launcher = document.createElement('button')
|
|
147
178
|
launcher.className = isVertical ? 'acw-launcher vertical' : 'acw-launcher'
|
|
148
|
-
|
|
179
|
+
if (resolvedIconUrl) {
|
|
180
|
+
const launcherImg = document.createElement('img')
|
|
181
|
+
launcherImg.src = resolvedIconUrl
|
|
182
|
+
launcherImg.alt = launcherLabel
|
|
183
|
+
launcher.appendChild(launcherImg)
|
|
184
|
+
const launcherTextSpan = document.createElement('span')
|
|
185
|
+
launcherTextSpan.textContent = launcherLabel
|
|
186
|
+
launcher.appendChild(launcherTextSpan)
|
|
187
|
+
} else {
|
|
188
|
+
const span = document.createElement('span')
|
|
189
|
+
span.textContent = launcherLabel
|
|
190
|
+
launcher.appendChild(span)
|
|
191
|
+
}
|
|
149
192
|
|
|
150
193
|
const header = document.createElement('div')
|
|
151
194
|
header.className = 'acw-header'
|
|
195
|
+
const headerLeft = document.createElement('div')
|
|
196
|
+
headerLeft.className = 'acw-header-left'
|
|
197
|
+
if (resolvedIconUrl) {
|
|
198
|
+
const headerIcon = document.createElement('img')
|
|
199
|
+
headerIcon.className = 'acw-header-icon'
|
|
200
|
+
headerIcon.src = resolvedIconUrl
|
|
201
|
+
headerIcon.alt = ''
|
|
202
|
+
headerLeft.appendChild(headerIcon)
|
|
203
|
+
}
|
|
204
|
+
const titleWrap = document.createElement('div')
|
|
205
|
+
titleWrap.className = 'acw-header-title-wrap'
|
|
152
206
|
const titleEl = document.createElement('div')
|
|
153
207
|
titleEl.className = 'acw-title'
|
|
154
208
|
titleEl.textContent = title
|
|
209
|
+
const unreadBadge = document.createElement('span')
|
|
210
|
+
unreadBadge.className = 'acw-unread-badge hidden'
|
|
211
|
+
unreadBadge.textContent = '0'
|
|
212
|
+
titleWrap.appendChild(titleEl)
|
|
213
|
+
titleWrap.appendChild(unreadBadge)
|
|
214
|
+
headerLeft.appendChild(titleWrap)
|
|
155
215
|
const headerActions = document.createElement('div')
|
|
156
216
|
headerActions.style.display = 'flex'
|
|
157
217
|
headerActions.style.alignItems = 'center'
|
|
@@ -166,7 +226,7 @@
|
|
|
166
226
|
|
|
167
227
|
headerActions.appendChild(minimizeBtn)
|
|
168
228
|
headerActions.appendChild(closeBtn)
|
|
169
|
-
header.appendChild(
|
|
229
|
+
header.appendChild(headerLeft)
|
|
170
230
|
header.appendChild(headerActions)
|
|
171
231
|
|
|
172
232
|
const statusEl = document.createElement('div')
|
|
@@ -175,15 +235,17 @@
|
|
|
175
235
|
statusDot.className = 'acw-dot disconnected'
|
|
176
236
|
const statusText = document.createElement('span')
|
|
177
237
|
statusText.textContent = 'Disconnected'
|
|
178
|
-
const disconnectBtn = document.createElement('button')
|
|
179
|
-
disconnectBtn.className = 'acw-disconnect'
|
|
180
|
-
disconnectBtn.textContent = 'Connect'
|
|
181
238
|
statusEl.appendChild(statusDot)
|
|
182
239
|
statusEl.appendChild(statusText)
|
|
183
|
-
|
|
240
|
+
|
|
241
|
+
const startCtaEl = document.createElement('div')
|
|
242
|
+
startCtaEl.className = 'acw-start-cta'
|
|
243
|
+
const startConversationBtn = document.createElement('button')
|
|
244
|
+
startConversationBtn.textContent = 'Click to Start Conversation'
|
|
245
|
+
startCtaEl.appendChild(startConversationBtn)
|
|
184
246
|
|
|
185
247
|
const messages = document.createElement('div')
|
|
186
|
-
|
|
248
|
+
messages.className = 'acw-messages'
|
|
187
249
|
|
|
188
250
|
const inputWrap = document.createElement('div')
|
|
189
251
|
inputWrap.className = 'acw-input'
|
|
@@ -195,11 +257,27 @@
|
|
|
195
257
|
inputWrap.appendChild(input)
|
|
196
258
|
inputWrap.appendChild(sendBtn)
|
|
197
259
|
|
|
260
|
+
const footerEl = document.createElement('div')
|
|
261
|
+
footerEl.className = 'acw-footer'
|
|
262
|
+
footerEl.style.display = 'none'
|
|
263
|
+
const footerLink = document.createElement('a')
|
|
264
|
+
footerLink.href = 'https://helllo.ai'
|
|
265
|
+
footerLink.target = '_blank'
|
|
266
|
+
footerLink.rel = 'noopener noreferrer'
|
|
267
|
+
footerLink.textContent = 'helllo.ai'
|
|
268
|
+
footerEl.appendChild(document.createTextNode('Powered by '))
|
|
269
|
+
footerEl.appendChild(footerLink)
|
|
270
|
+
|
|
198
271
|
// Customer info form (if enabled)
|
|
199
272
|
let customerForm = null
|
|
200
273
|
let sessionId = null
|
|
201
274
|
let customerInfoSubmitted = false
|
|
202
275
|
let firstMessageReceived = false
|
|
276
|
+
let conversationStarted = false
|
|
277
|
+
let formShownBeforeConnect = false
|
|
278
|
+
let pendingCustomerName = null
|
|
279
|
+
let pendingCustomerPhone = null
|
|
280
|
+
let unreadCount = 0
|
|
203
281
|
|
|
204
282
|
if (captureCustomerInfo) {
|
|
205
283
|
customerForm = document.createElement('div')
|
|
@@ -244,7 +322,9 @@
|
|
|
244
322
|
skipBtn.className = 'skip-btn'
|
|
245
323
|
skipBtn.textContent = 'Skip'
|
|
246
324
|
formActions.appendChild(submitBtn)
|
|
247
|
-
|
|
325
|
+
if (allowSkipCustomerInfo) {
|
|
326
|
+
formActions.appendChild(skipBtn)
|
|
327
|
+
}
|
|
248
328
|
|
|
249
329
|
customerForm.appendChild(formTitle)
|
|
250
330
|
customerForm.appendChild(formDesc)
|
|
@@ -261,20 +341,29 @@
|
|
|
261
341
|
return
|
|
262
342
|
}
|
|
263
343
|
|
|
344
|
+
// Form shown before connect: save and connect; customer_info sent in connection_established
|
|
345
|
+
if (formShownBeforeConnect) {
|
|
346
|
+
pendingCustomerName = name
|
|
347
|
+
pendingCustomerPhone = phone
|
|
348
|
+
customerForm.style.display = 'none'
|
|
349
|
+
messages.style.display = 'flex'
|
|
350
|
+
inputWrap.style.display = 'none'
|
|
351
|
+
connect()
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
|
|
264
355
|
if (!sessionId) {
|
|
265
356
|
console.error('[AgentChatWidget] No session ID available')
|
|
266
357
|
alert('Session not established. Please wait for connection.')
|
|
267
358
|
return
|
|
268
359
|
}
|
|
269
360
|
|
|
270
|
-
// Ensure WebSocket is connected
|
|
271
361
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
272
362
|
alert('WebSocket not connected. Please wait for connection.')
|
|
273
363
|
return
|
|
274
364
|
}
|
|
275
365
|
|
|
276
366
|
try {
|
|
277
|
-
// Send customer info via WebSocket
|
|
278
367
|
ws.send(JSON.stringify({
|
|
279
368
|
type: 'customer_info',
|
|
280
369
|
session_id: sessionId,
|
|
@@ -282,8 +371,6 @@
|
|
|
282
371
|
customer_name: name,
|
|
283
372
|
embed_key: embedKey
|
|
284
373
|
}))
|
|
285
|
-
|
|
286
|
-
// Mark as submitted and show chat interface
|
|
287
374
|
customerInfoSubmitted = true
|
|
288
375
|
customerForm.style.display = 'none'
|
|
289
376
|
messages.style.display = 'flex'
|
|
@@ -295,6 +382,13 @@
|
|
|
295
382
|
}
|
|
296
383
|
|
|
297
384
|
function skipCustomerInfo() {
|
|
385
|
+
if (formShownBeforeConnect) {
|
|
386
|
+
customerForm.style.display = 'none'
|
|
387
|
+
messages.style.display = 'flex'
|
|
388
|
+
inputWrap.style.display = 'none'
|
|
389
|
+
connect()
|
|
390
|
+
return
|
|
391
|
+
}
|
|
298
392
|
customerInfoSubmitted = true
|
|
299
393
|
customerForm.style.display = 'none'
|
|
300
394
|
messages.style.display = 'flex'
|
|
@@ -302,7 +396,7 @@
|
|
|
302
396
|
}
|
|
303
397
|
|
|
304
398
|
submitBtn.onclick = submitCustomerInfo
|
|
305
|
-
skipBtn.onclick = skipCustomerInfo
|
|
399
|
+
if (allowSkipCustomerInfo) skipBtn.onclick = skipCustomerInfo
|
|
306
400
|
|
|
307
401
|
// Allow Enter key to submit
|
|
308
402
|
nameInput.addEventListener('keydown', (e) => {
|
|
@@ -323,18 +417,59 @@
|
|
|
323
417
|
|
|
324
418
|
panel.appendChild(header)
|
|
325
419
|
panel.appendChild(statusEl)
|
|
420
|
+
panel.appendChild(startCtaEl)
|
|
326
421
|
panel.appendChild(messages)
|
|
327
422
|
panel.appendChild(inputWrap)
|
|
423
|
+
panel.appendChild(footerEl)
|
|
328
424
|
shadow.appendChild(panel)
|
|
329
425
|
shadow.appendChild(launcher)
|
|
330
426
|
document.body.appendChild(container)
|
|
331
427
|
|
|
428
|
+
// Initial state: show start CTA, hide messages and input until user starts conversation
|
|
429
|
+
messages.style.display = 'none'
|
|
430
|
+
inputWrap.style.display = 'none'
|
|
431
|
+
|
|
432
|
+
function showStartCta() {
|
|
433
|
+
startCtaEl.style.display = 'flex'
|
|
434
|
+
messages.style.display = 'none'
|
|
435
|
+
inputWrap.style.display = 'none'
|
|
436
|
+
footerEl.style.display = 'none'
|
|
437
|
+
if (customerForm) customerForm.style.display = 'none'
|
|
438
|
+
}
|
|
439
|
+
function showFormOnly() {
|
|
440
|
+
startCtaEl.style.display = 'none'
|
|
441
|
+
messages.style.display = 'none'
|
|
442
|
+
inputWrap.style.display = 'none'
|
|
443
|
+
footerEl.style.display = 'none'
|
|
444
|
+
if (customerForm) customerForm.style.display = 'flex'
|
|
445
|
+
}
|
|
446
|
+
function showChat() {
|
|
447
|
+
startCtaEl.style.display = 'none'
|
|
448
|
+
if (customerForm) customerForm.style.display = 'none'
|
|
449
|
+
messages.style.display = 'flex'
|
|
450
|
+
inputWrap.style.display = 'flex'
|
|
451
|
+
footerEl.style.display = 'block'
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function updateUnreadBadge() {
|
|
455
|
+
unreadBadge.textContent = String(unreadCount)
|
|
456
|
+
if (unreadCount > 0) {
|
|
457
|
+
unreadBadge.classList.remove('hidden')
|
|
458
|
+
} else {
|
|
459
|
+
unreadBadge.classList.add('hidden')
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
332
463
|
function appendMessage(text, role) {
|
|
333
464
|
const msg = document.createElement('div')
|
|
334
465
|
msg.className = 'acw-msg ' + (role === 'user' ? 'acw-msg-user' : 'acw-msg-bot')
|
|
335
466
|
msg.textContent = text
|
|
336
467
|
messages.appendChild(msg)
|
|
337
468
|
messages.scrollTop = messages.scrollHeight
|
|
469
|
+
if (role === 'bot' && panel.style.display !== 'flex') {
|
|
470
|
+
unreadCount++
|
|
471
|
+
updateUnreadBadge()
|
|
472
|
+
}
|
|
338
473
|
}
|
|
339
474
|
|
|
340
475
|
// No default greeting; only show messages after connection
|
|
@@ -381,26 +516,34 @@
|
|
|
381
516
|
console.info('[AgentChatWidget] Resolved WS URL:', resolvedWsUrl)
|
|
382
517
|
} catch (_) {}
|
|
383
518
|
|
|
519
|
+
function setInputEnabled(enabled) {
|
|
520
|
+
input.disabled = !enabled
|
|
521
|
+
sendBtn.disabled = !enabled
|
|
522
|
+
input.placeholder = enabled ? 'Type a message...' : 'Connect to start chatting'
|
|
523
|
+
}
|
|
524
|
+
|
|
384
525
|
function updateStatus(text, isConnected) {
|
|
385
526
|
statusText.textContent = text
|
|
386
527
|
statusDot.classList.remove('connected', 'disconnected')
|
|
387
528
|
statusDot.classList.add(isConnected ? 'connected' : 'disconnected')
|
|
388
|
-
|
|
389
|
-
// Update button text and handler based on connection status
|
|
390
|
-
disconnectBtn.textContent = isConnected ? 'Disconnect' : 'Connect'
|
|
391
|
-
disconnectBtn.onclick = isConnected ? disconnect : connect
|
|
529
|
+
setInputEnabled(isConnected)
|
|
392
530
|
}
|
|
393
531
|
|
|
394
532
|
function connect() {
|
|
395
533
|
if (connected || connecting) return
|
|
396
534
|
connecting = true
|
|
397
|
-
firstMessageReceived = false
|
|
535
|
+
firstMessageReceived = false
|
|
536
|
+
startCtaEl.style.display = 'none'
|
|
537
|
+
// Show chat UI immediately so the panel is not empty while connecting
|
|
538
|
+
showChat()
|
|
539
|
+
setInputEnabled(false)
|
|
398
540
|
const url = resolvedWsUrl
|
|
399
541
|
try {
|
|
400
542
|
ws = new WebSocket(url)
|
|
401
543
|
} catch (err) {
|
|
402
544
|
connecting = false
|
|
403
|
-
updateStatus('
|
|
545
|
+
updateStatus('Disconnected', false)
|
|
546
|
+
appendMessage('Failed to connect. Please try again or check your connection.', 'bot')
|
|
404
547
|
console.error('[AgentChatWidget] WS error', err)
|
|
405
548
|
return
|
|
406
549
|
}
|
|
@@ -409,11 +552,8 @@
|
|
|
409
552
|
connected = true
|
|
410
553
|
connecting = false
|
|
411
554
|
updateStatus('Connected', true)
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
// Don't show customer form yet - wait for first message
|
|
415
|
-
messages.style.display = 'flex'
|
|
416
|
-
inputWrap.style.display = 'flex'
|
|
555
|
+
// If captureCustomerInfo, connection_established will show form or chat
|
|
556
|
+
if (!captureCustomerInfo) showChat()
|
|
417
557
|
}
|
|
418
558
|
|
|
419
559
|
ws.onmessage = (event) => {
|
|
@@ -422,42 +562,55 @@
|
|
|
422
562
|
|
|
423
563
|
// Handle connection_established message
|
|
424
564
|
if (data.type === 'connection_established') {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
565
|
+
if (data.session_id) sessionId = data.session_id
|
|
566
|
+
|
|
567
|
+
// Form was shown before connect (Start Conversation → form → submit/skip): send customer_info if pending, then show chat
|
|
568
|
+
if (formShownBeforeConnect) {
|
|
569
|
+
if (pendingCustomerName && pendingCustomerPhone && ws && ws.readyState === WebSocket.OPEN) {
|
|
570
|
+
try {
|
|
571
|
+
ws.send(JSON.stringify({
|
|
572
|
+
type: 'customer_info',
|
|
573
|
+
session_id: sessionId,
|
|
574
|
+
phone_number: pendingCustomerPhone,
|
|
575
|
+
customer_name: pendingCustomerName,
|
|
576
|
+
embed_key: embedKey
|
|
577
|
+
}))
|
|
578
|
+
} catch (e) {
|
|
579
|
+
console.error('[AgentChatWidget] Error sending customer info:', e)
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
pendingCustomerName = null
|
|
583
|
+
pendingCustomerPhone = null
|
|
584
|
+
customerInfoSubmitted = true
|
|
585
|
+
if (data.welcome_message) appendMessage(data.welcome_message, 'bot')
|
|
586
|
+
showChat()
|
|
587
|
+
return
|
|
428
588
|
}
|
|
429
589
|
|
|
430
590
|
// Display welcome message if provided
|
|
431
591
|
if (data.welcome_message) {
|
|
432
592
|
appendMessage(data.welcome_message, 'bot')
|
|
433
|
-
|
|
434
|
-
// Show customer info form after welcome message
|
|
435
593
|
if (!firstMessageReceived && captureCustomerInfo && customerForm && !customerInfoSubmitted) {
|
|
436
594
|
firstMessageReceived = true
|
|
437
|
-
// Small delay to let the message render first
|
|
438
595
|
setTimeout(() => {
|
|
439
|
-
|
|
440
|
-
messages.style.display = 'none'
|
|
441
|
-
inputWrap.style.display = 'none'
|
|
442
|
-
// Focus first input
|
|
596
|
+
showFormOnly()
|
|
443
597
|
const nameInput = customerForm.querySelector('#acw-customer-name')
|
|
444
598
|
if (nameInput) setTimeout(() => nameInput.focus(), 100)
|
|
445
599
|
}, 300)
|
|
446
600
|
}
|
|
447
601
|
} else {
|
|
448
|
-
// No welcome message, but still show form if needed
|
|
449
602
|
if (!firstMessageReceived && captureCustomerInfo && customerForm && !customerInfoSubmitted) {
|
|
450
603
|
firstMessageReceived = true
|
|
451
604
|
setTimeout(() => {
|
|
452
|
-
|
|
453
|
-
messages.style.display = 'none'
|
|
454
|
-
inputWrap.style.display = 'none'
|
|
605
|
+
showFormOnly()
|
|
455
606
|
const nameInput = customerForm.querySelector('#acw-customer-name')
|
|
456
607
|
if (nameInput) setTimeout(() => nameInput.focus(), 100)
|
|
457
608
|
}, 100)
|
|
458
609
|
}
|
|
459
610
|
}
|
|
460
|
-
|
|
611
|
+
if (captureCustomerInfo && !customerInfoSubmitted && customerForm) return
|
|
612
|
+
showChat()
|
|
613
|
+
return
|
|
461
614
|
}
|
|
462
615
|
|
|
463
616
|
// Handle regular chat messages
|
|
@@ -476,17 +629,16 @@
|
|
|
476
629
|
|
|
477
630
|
ws.onerror = (e) => {
|
|
478
631
|
console.error('[AgentChatWidget] WS error', e)
|
|
479
|
-
updateStatus('
|
|
480
|
-
appendMessage('
|
|
632
|
+
updateStatus('Disconnected', false)
|
|
633
|
+
appendMessage('Failed to connect. Please try again or check your connection.', 'bot')
|
|
481
634
|
}
|
|
482
635
|
|
|
483
636
|
ws.onclose = (ev) => {
|
|
484
637
|
connected = false
|
|
485
638
|
connecting = false
|
|
486
|
-
const reason = ev && (ev.reason || ev.code)
|
|
487
639
|
updateStatus('Disconnected', false)
|
|
488
640
|
if (!ev.wasClean) {
|
|
489
|
-
appendMessage(
|
|
641
|
+
appendMessage('Failed to connect. Please try again or check your connection.', 'bot')
|
|
490
642
|
}
|
|
491
643
|
}
|
|
492
644
|
}
|
|
@@ -502,6 +654,10 @@
|
|
|
502
654
|
sessionId = null
|
|
503
655
|
customerInfoSubmitted = false
|
|
504
656
|
firstMessageReceived = false
|
|
657
|
+
conversationStarted = false
|
|
658
|
+
formShownBeforeConnect = false
|
|
659
|
+
pendingCustomerName = null
|
|
660
|
+
pendingCustomerPhone = null
|
|
505
661
|
if (customerForm) {
|
|
506
662
|
customerForm.style.display = 'none'
|
|
507
663
|
const nameInput = customerForm.querySelector('#acw-customer-name')
|
|
@@ -509,6 +665,7 @@
|
|
|
509
665
|
if (nameInput) nameInput.value = ''
|
|
510
666
|
if (phoneInput) phoneInput.value = ''
|
|
511
667
|
}
|
|
668
|
+
showStartCta()
|
|
512
669
|
}
|
|
513
670
|
|
|
514
671
|
function sendMessage() {
|
|
@@ -521,8 +678,11 @@
|
|
|
521
678
|
}
|
|
522
679
|
|
|
523
680
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
524
|
-
|
|
525
|
-
|
|
681
|
+
if (connecting) {
|
|
682
|
+
appendMessage('Connecting... Please wait.', 'bot')
|
|
683
|
+
} else {
|
|
684
|
+
appendMessage('Please click "Click to Start Conversation" to start chatting.', 'bot')
|
|
685
|
+
}
|
|
526
686
|
return
|
|
527
687
|
}
|
|
528
688
|
appendMessage(text, 'user')
|
|
@@ -530,6 +690,19 @@
|
|
|
530
690
|
input.value = ''
|
|
531
691
|
}
|
|
532
692
|
|
|
693
|
+
startConversationBtn.onclick = () => {
|
|
694
|
+
conversationStarted = true
|
|
695
|
+
startCtaEl.style.display = 'none'
|
|
696
|
+
if (captureCustomerInfo && customerForm) {
|
|
697
|
+
formShownBeforeConnect = true
|
|
698
|
+
showFormOnly()
|
|
699
|
+
const nameInput = customerForm.querySelector('#acw-customer-name')
|
|
700
|
+
if (nameInput) setTimeout(() => nameInput.focus(), 100)
|
|
701
|
+
} else {
|
|
702
|
+
connect()
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
533
706
|
launcher.onclick = () => {
|
|
534
707
|
const isOpen = panel.style.display === 'flex'
|
|
535
708
|
if (isOpen) {
|
|
@@ -537,7 +710,9 @@
|
|
|
537
710
|
disconnect()
|
|
538
711
|
} else {
|
|
539
712
|
panel.style.display = 'flex'
|
|
540
|
-
|
|
713
|
+
unreadCount = 0
|
|
714
|
+
updateUnreadBadge()
|
|
715
|
+
if (!connected && !connecting && !conversationStarted) showStartCta()
|
|
541
716
|
}
|
|
542
717
|
}
|
|
543
718
|
|
|
@@ -566,7 +741,7 @@
|
|
|
566
741
|
},
|
|
567
742
|
open() {
|
|
568
743
|
panel.style.display = 'flex'
|
|
569
|
-
|
|
744
|
+
// Connection only via explicit Connect button
|
|
570
745
|
},
|
|
571
746
|
close() {
|
|
572
747
|
panel.style.display = 'none'
|
|
@@ -582,9 +757,14 @@
|
|
|
582
757
|
const captureCustomerInfo = userConfig.captureCustomerInfo !== undefined
|
|
583
758
|
? userConfig.captureCustomerInfo
|
|
584
759
|
: ds.captureCustomerInfo === 'true' || ds.capture_customer_info === 'true'
|
|
760
|
+
const allowSkipCustomerInfo = userConfig.allowSkipCustomerInfo !== undefined
|
|
761
|
+
? userConfig.allowSkipCustomerInfo
|
|
762
|
+
: ds.allowSkipCustomerInfo !== 'false' && ds.allow_skip_customer_info !== 'false'
|
|
585
763
|
const position = userConfig.position || ds.position || ds.launcherPosition || 'bottom-right'
|
|
586
764
|
const validPositions = ['bottom-right', 'middle-right', 'top-right', 'bottom-left', 'middle-left', 'top-left']
|
|
587
765
|
const validPosition = validPositions.includes(position) ? position : 'bottom-right'
|
|
766
|
+
const showIcon = userConfig.showIcon !== undefined ? userConfig.showIcon : ds.showIcon !== 'false' && ds.show_icon !== 'false'
|
|
767
|
+
const launcherText = userConfig.launcherText ?? userConfig.launcher_text ?? ds.launcherText ?? ds.launcher_text ?? 'Chat'
|
|
588
768
|
return {
|
|
589
769
|
agentId: userConfig.agentId || ds.agentId,
|
|
590
770
|
embedKey: userConfig.embedKey || ds.embedKey || ds.embed_key,
|
|
@@ -597,7 +777,11 @@
|
|
|
597
777
|
title: userConfig.title || ds.title,
|
|
598
778
|
greeting: userConfig.greeting || ds.greeting,
|
|
599
779
|
captureCustomerInfo: captureCustomerInfo,
|
|
780
|
+
allowSkipCustomerInfo: allowSkipCustomerInfo,
|
|
600
781
|
position: validPosition,
|
|
782
|
+
iconUrl: userConfig.iconUrl || userConfig.icon_url || ds.iconUrl || ds.icon_url,
|
|
783
|
+
showIcon: showIcon,
|
|
784
|
+
launcherText: launcherText,
|
|
601
785
|
}
|
|
602
786
|
}
|
|
603
787
|
|