ttp-agent-sdk 2.33.5 → 2.34.3
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/dist/agent-widget.dev.js +29049 -0
- package/dist/agent-widget.dev.js.map +1 -0
- package/dist/agent-widget.esm.js +1 -1
- package/dist/agent-widget.esm.js.map +1 -1
- package/dist/agent-widget.js +1 -1
- package/dist/agent-widget.js.map +1 -1
- package/dist/audio-processor.js +321 -1
- package/dist/demos/index.html +15 -0
- package/dist/demos/widget-customization.html +4373 -0
- package/dist/examples/demo-v2.html +25 -45
- package/dist/examples/test-index.html +15 -0
- package/dist/examples/widget-customization.html +4373 -0
- package/dist/index.html +3 -3
- package/examples/demo-v2.html +25 -45
- package/examples/test-index.html +15 -0
- package/examples/widget-customization.html +4373 -0
- package/package.json +8 -4
|
@@ -0,0 +1,4373 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Widget Live Customization - TTP Agent SDK</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
16
|
+
background: #f9fafb;
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
overflow-x: hidden;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.header {
|
|
22
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
23
|
+
color: white;
|
|
24
|
+
padding: 40px 20px;
|
|
25
|
+
text-align: center;
|
|
26
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.header h1 {
|
|
30
|
+
font-size: 36px;
|
|
31
|
+
font-weight: 700;
|
|
32
|
+
margin-bottom: 8px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.header p {
|
|
36
|
+
font-size: 18px;
|
|
37
|
+
opacity: 0.9;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.back-link {
|
|
41
|
+
position: absolute;
|
|
42
|
+
top: 20px;
|
|
43
|
+
left: 20px;
|
|
44
|
+
color: white;
|
|
45
|
+
text-decoration: none;
|
|
46
|
+
font-weight: 500;
|
|
47
|
+
padding: 8px 16px;
|
|
48
|
+
background: rgba(255, 255, 255, 0.2);
|
|
49
|
+
border-radius: 8px;
|
|
50
|
+
transition: background 0.2s;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.back-link:hover {
|
|
54
|
+
background: rgba(255, 255, 255, 0.3);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.container {
|
|
58
|
+
max-width: 1800px;
|
|
59
|
+
margin: 0 auto;
|
|
60
|
+
padding: 40px 20px;
|
|
61
|
+
display: grid;
|
|
62
|
+
grid-template-columns: minmax(600px, 1fr) 420px;
|
|
63
|
+
gap: 24px;
|
|
64
|
+
align-items: start;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.preview-section {
|
|
68
|
+
background: white;
|
|
69
|
+
border-radius: 16px;
|
|
70
|
+
padding: 24px;
|
|
71
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
72
|
+
min-height: 600px;
|
|
73
|
+
height: 100%;
|
|
74
|
+
position: relative;
|
|
75
|
+
overflow: visible;
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.preview-section h2 {
|
|
81
|
+
font-size: 24px;
|
|
82
|
+
font-weight: 700;
|
|
83
|
+
color: #111827;
|
|
84
|
+
margin-bottom: 20px;
|
|
85
|
+
flex-shrink: 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.preview-area {
|
|
89
|
+
background: #f3f4f6;
|
|
90
|
+
border-radius: 12px;
|
|
91
|
+
height: 600px;
|
|
92
|
+
position: relative;
|
|
93
|
+
overflow: scroll !important;
|
|
94
|
+
border: 2px dashed #d1d5db;
|
|
95
|
+
padding: 60px;
|
|
96
|
+
box-sizing: border-box;
|
|
97
|
+
/* Ensure enough space for widget (360px width + margins) */
|
|
98
|
+
min-width: 500px;
|
|
99
|
+
flex: 1;
|
|
100
|
+
/* Force scrollbar to always be visible */
|
|
101
|
+
scrollbar-width: auto;
|
|
102
|
+
-webkit-overflow-scrolling: touch;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Inner container that's taller than the preview area to enable scrolling */
|
|
106
|
+
.preview-area > div {
|
|
107
|
+
min-height: 800px;
|
|
108
|
+
position: relative;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.customization-panel {
|
|
112
|
+
background: white;
|
|
113
|
+
border-radius: 16px;
|
|
114
|
+
padding: 24px;
|
|
115
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
116
|
+
overflow-y: visible;
|
|
117
|
+
overflow-x: hidden;
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-direction: column;
|
|
120
|
+
position: relative;
|
|
121
|
+
min-height: fit-content;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Ensure code output is always visible at bottom */
|
|
125
|
+
.customization-panel > div:last-child {
|
|
126
|
+
margin-top: auto;
|
|
127
|
+
padding-top: 16px;
|
|
128
|
+
border-top: 1px solid #e5e7eb;
|
|
129
|
+
flex-shrink: 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Removed custom scrollbars - using main window scroll only */
|
|
133
|
+
|
|
134
|
+
.customization-panel h2 {
|
|
135
|
+
font-size: 20px;
|
|
136
|
+
font-weight: 700;
|
|
137
|
+
color: #111827;
|
|
138
|
+
margin-bottom: 20px;
|
|
139
|
+
padding-bottom: 12px;
|
|
140
|
+
border-bottom: 2px solid #e5e7eb;
|
|
141
|
+
flex-shrink: 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.customization-group {
|
|
145
|
+
margin-bottom: 24px;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.customization-group h3 {
|
|
149
|
+
font-size: 16px;
|
|
150
|
+
font-weight: 600;
|
|
151
|
+
color: #374151;
|
|
152
|
+
margin-bottom: 12px;
|
|
153
|
+
display: flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
gap: 8px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.customization-group h3::before {
|
|
159
|
+
content: '';
|
|
160
|
+
width: 4px;
|
|
161
|
+
height: 16px;
|
|
162
|
+
background: #667eea;
|
|
163
|
+
border-radius: 2px;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.control-item {
|
|
167
|
+
margin-bottom: 16px;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.control-item label {
|
|
171
|
+
display: block;
|
|
172
|
+
font-size: 14px;
|
|
173
|
+
font-weight: 500;
|
|
174
|
+
color: #6b7280;
|
|
175
|
+
margin-bottom: 6px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.control-item input[type="text"],
|
|
179
|
+
.control-item input[type="number"],
|
|
180
|
+
.control-item select {
|
|
181
|
+
width: 100%;
|
|
182
|
+
padding: 8px 12px;
|
|
183
|
+
border: 1px solid #d1d5db;
|
|
184
|
+
border-radius: 8px;
|
|
185
|
+
font-size: 14px;
|
|
186
|
+
transition: border-color 0.2s;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.control-item input[type="text"]:focus,
|
|
190
|
+
.control-item input[type="number"]:focus,
|
|
191
|
+
.control-item select:focus {
|
|
192
|
+
outline: none;
|
|
193
|
+
border-color: #667eea;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.control-item input[type="color"] {
|
|
197
|
+
width: 100%;
|
|
198
|
+
height: 40px;
|
|
199
|
+
border: 1px solid #d1d5db;
|
|
200
|
+
border-radius: 8px;
|
|
201
|
+
cursor: pointer;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.control-item input[type="checkbox"] {
|
|
205
|
+
margin-right: 8px;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.button-group {
|
|
209
|
+
display: flex;
|
|
210
|
+
gap: 12px;
|
|
211
|
+
margin-top: 24px;
|
|
212
|
+
flex-shrink: 0;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.btn {
|
|
216
|
+
flex: 1;
|
|
217
|
+
padding: 12px 24px;
|
|
218
|
+
border: none;
|
|
219
|
+
border-radius: 8px;
|
|
220
|
+
font-size: 14px;
|
|
221
|
+
font-weight: 600;
|
|
222
|
+
cursor: pointer;
|
|
223
|
+
transition: all 0.2s;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.btn-primary {
|
|
227
|
+
background: #667eea;
|
|
228
|
+
color: white;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.btn-primary:hover {
|
|
232
|
+
background: #5568d3;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.btn-secondary {
|
|
236
|
+
background: #e5e7eb;
|
|
237
|
+
color: #374151;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.btn-secondary:hover {
|
|
241
|
+
background: #d1d5db;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.info-box {
|
|
245
|
+
background: #eff6ff;
|
|
246
|
+
border-left: 4px solid #3b82f6;
|
|
247
|
+
padding: 16px;
|
|
248
|
+
border-radius: 8px;
|
|
249
|
+
margin-bottom: 24px;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.info-box p {
|
|
253
|
+
font-size: 14px;
|
|
254
|
+
color: #1e40af;
|
|
255
|
+
line-height: 1.6;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.code-output {
|
|
259
|
+
background: #1f2937;
|
|
260
|
+
color: #f9fafb;
|
|
261
|
+
padding: 20px;
|
|
262
|
+
border-radius: 8px;
|
|
263
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
264
|
+
font-size: 13px;
|
|
265
|
+
overflow-x: auto;
|
|
266
|
+
overflow-y: visible;
|
|
267
|
+
flex-shrink: 0;
|
|
268
|
+
position: relative;
|
|
269
|
+
border: 2px solid #374151;
|
|
270
|
+
min-height: 400px;
|
|
271
|
+
height: auto;
|
|
272
|
+
max-height: none;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.code-output pre {
|
|
276
|
+
white-space: pre-wrap;
|
|
277
|
+
word-wrap: break-word;
|
|
278
|
+
margin: 0;
|
|
279
|
+
padding: 0;
|
|
280
|
+
line-height: 1.6;
|
|
281
|
+
font-size: 13px;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.code-output h3 {
|
|
285
|
+
margin-bottom: 16px;
|
|
286
|
+
font-size: 16px;
|
|
287
|
+
font-weight: 700;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.code-output::-webkit-scrollbar {
|
|
291
|
+
width: 8px;
|
|
292
|
+
height: 8px;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.code-output::-webkit-scrollbar-track {
|
|
296
|
+
background: #374151;
|
|
297
|
+
border-radius: 4px;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.code-output::-webkit-scrollbar-thumb {
|
|
301
|
+
background: #6b7280;
|
|
302
|
+
border-radius: 4px;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.code-output::-webkit-scrollbar-thumb:hover {
|
|
306
|
+
background: #9ca3af;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.code-output pre {
|
|
310
|
+
margin: 0;
|
|
311
|
+
white-space: pre-wrap;
|
|
312
|
+
word-wrap: break-word;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.element-highlight {
|
|
316
|
+
outline: 2px solid #667eea;
|
|
317
|
+
outline-offset: 2px;
|
|
318
|
+
cursor: pointer;
|
|
319
|
+
transition: outline-color 0.2s;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.element-highlight:hover {
|
|
323
|
+
outline-color: #ef4444;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.mock-widget {
|
|
327
|
+
position: absolute;
|
|
328
|
+
/* Position is set dynamically via updateMockWidgetPosition() */
|
|
329
|
+
z-index: 1000;
|
|
330
|
+
/* Ensure widget doesn't get clipped */
|
|
331
|
+
min-width: 360px;
|
|
332
|
+
/* Ensure widget is fully visible - adjust position if needed */
|
|
333
|
+
transform: translateZ(0);
|
|
334
|
+
cursor: pointer;
|
|
335
|
+
padding: 4px;
|
|
336
|
+
border-radius: 8px;
|
|
337
|
+
transition: background 0.2s;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.mock-widget:hover {
|
|
341
|
+
background: rgba(102, 126, 234, 0.05);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.mock-widget.element-highlight {
|
|
345
|
+
background: rgba(102, 126, 234, 0.1);
|
|
346
|
+
outline: 2px solid #667eea;
|
|
347
|
+
outline-offset: 4px;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* Ensure panel has enough space and is fully visible */
|
|
351
|
+
.mock-widget-panel {
|
|
352
|
+
position: relative;
|
|
353
|
+
margin-bottom: 100px; /* Extra space for button below */
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/* Ensure preview section doesn't clip content */
|
|
357
|
+
.preview-section {
|
|
358
|
+
overflow-x: visible;
|
|
359
|
+
overflow-y: visible;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/* Custom scrollbar styling for preview area */
|
|
363
|
+
.preview-area::-webkit-scrollbar {
|
|
364
|
+
width: 14px;
|
|
365
|
+
height: 14px;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.preview-area::-webkit-scrollbar-track {
|
|
369
|
+
background: #e5e7eb;
|
|
370
|
+
border-radius: 7px;
|
|
371
|
+
margin: 4px;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.preview-area::-webkit-scrollbar-thumb {
|
|
375
|
+
background: #6b7280;
|
|
376
|
+
border-radius: 7px;
|
|
377
|
+
border: 2px solid #e5e7eb;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.preview-area::-webkit-scrollbar-thumb:hover {
|
|
381
|
+
background: #4b5563;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.preview-area::-webkit-scrollbar-corner {
|
|
385
|
+
background: #e5e7eb;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/* Firefox scrollbar */
|
|
389
|
+
.preview-area {
|
|
390
|
+
scrollbar-width: auto;
|
|
391
|
+
scrollbar-color: #6b7280 #e5e7eb;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
@media (max-width: 1400px) {
|
|
395
|
+
.container {
|
|
396
|
+
grid-template-columns: 1fr 380px;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.preview-area {
|
|
400
|
+
padding: 20px;
|
|
401
|
+
min-width: 450px;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* Position is set dynamically via updateMockWidgetPosition() */
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
@media (max-width: 1200px) {
|
|
408
|
+
.container {
|
|
409
|
+
grid-template-columns: 1fr;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.customization-panel {
|
|
413
|
+
max-height: none;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.preview-area {
|
|
417
|
+
min-height: 500px;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/* Mock widget styles - will be dynamically generated */
|
|
422
|
+
.mock-button-container {
|
|
423
|
+
position: relative;
|
|
424
|
+
display: inline-flex;
|
|
425
|
+
align-items: center;
|
|
426
|
+
justify-content: center;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.mock-widget-button {
|
|
430
|
+
position: relative;
|
|
431
|
+
width: 60px;
|
|
432
|
+
height: 60px;
|
|
433
|
+
border-radius: 50%;
|
|
434
|
+
background: #FFFFFF;
|
|
435
|
+
border: none;
|
|
436
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
437
|
+
cursor: pointer;
|
|
438
|
+
display: flex;
|
|
439
|
+
align-items: center;
|
|
440
|
+
justify-content: center;
|
|
441
|
+
font-size: 24px;
|
|
442
|
+
transition: all 0.3s;
|
|
443
|
+
margin: 0;
|
|
444
|
+
flex-shrink: 0;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.mock-widget-button:hover {
|
|
448
|
+
transform: scale(1.1);
|
|
449
|
+
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/* Prompt bubble styles */
|
|
453
|
+
.mock-prompt-bubble {
|
|
454
|
+
position: absolute;
|
|
455
|
+
z-index: 10002;
|
|
456
|
+
pointer-events: none;
|
|
457
|
+
white-space: nowrap;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.mock-prompt-bubble.top {
|
|
461
|
+
bottom: calc(100% + 18px);
|
|
462
|
+
left: 50%;
|
|
463
|
+
transform: translateX(-50%);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.mock-prompt-bubble.left {
|
|
467
|
+
right: calc(100% + 12px);
|
|
468
|
+
top: 50%;
|
|
469
|
+
transform: translateY(-50%);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.mock-prompt-bubble.right {
|
|
473
|
+
left: calc(100% + 12px);
|
|
474
|
+
top: 50%;
|
|
475
|
+
transform: translateY(-50%);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.mock-prompt-bubble-content {
|
|
479
|
+
position: relative;
|
|
480
|
+
padding: 8px 16px;
|
|
481
|
+
border-radius: 20px;
|
|
482
|
+
font-weight: 500;
|
|
483
|
+
font-size: 14px;
|
|
484
|
+
box-shadow: 0 4px 15px rgba(124, 58, 237, 0.3);
|
|
485
|
+
overflow: hidden;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.mock-prompt-bubble-shimmer {
|
|
489
|
+
position: absolute;
|
|
490
|
+
inset: 0;
|
|
491
|
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent);
|
|
492
|
+
animation: mock-shimmer 2s infinite;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
@keyframes mock-shimmer {
|
|
496
|
+
0% { transform: translateX(-100%); }
|
|
497
|
+
100% { transform: translateX(200%); }
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
@keyframes mock-bounce {
|
|
501
|
+
0%, 100% { transform: translateX(-50%) translateY(0); }
|
|
502
|
+
50% { transform: translateX(-50%) translateY(-10px); }
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
@keyframes mock-float {
|
|
506
|
+
0%, 100% { transform: translateX(-50%) translateY(0); }
|
|
507
|
+
50% { transform: translateX(-50%) translateY(-8px); }
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
@keyframes mock-pulse-ring {
|
|
511
|
+
0% { transform: scale(1); opacity: 0.4; }
|
|
512
|
+
100% { transform: scale(1.8); opacity: 0; }
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.mock-prompt-bubble.animation-bounce {
|
|
516
|
+
animation: mock-bounce 1s ease-in-out infinite;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.mock-prompt-bubble.animation-pulse {
|
|
520
|
+
animation: pulse 2s ease-in-out infinite;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.mock-prompt-bubble.animation-float {
|
|
524
|
+
animation: mock-float 2s ease-in-out infinite;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/* Ensure top-positioned bubbles maintain horizontal centering during animations */
|
|
528
|
+
.mock-prompt-bubble.top.animation-bounce,
|
|
529
|
+
.mock-prompt-bubble.top.animation-float,
|
|
530
|
+
.mock-prompt-bubble.top.animation-pulse {
|
|
531
|
+
/* Animation keyframes already include translateX(-50%) for centering */
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.mock-prompt-bubble-arrow {
|
|
535
|
+
position: absolute;
|
|
536
|
+
width: 0;
|
|
537
|
+
height: 0;
|
|
538
|
+
border: 8px solid transparent;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.mock-prompt-bubble.top .mock-prompt-bubble-arrow {
|
|
542
|
+
top: 100%;
|
|
543
|
+
left: 50%;
|
|
544
|
+
transform: translateX(-50%);
|
|
545
|
+
border-top-color: var(--mock-prompt-bg-color, #7c3aed);
|
|
546
|
+
border-bottom: none;
|
|
547
|
+
margin-left: 0;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.mock-prompt-bubble.left .mock-prompt-bubble-arrow {
|
|
551
|
+
left: 100%;
|
|
552
|
+
top: 50%;
|
|
553
|
+
transform: translateY(-50%);
|
|
554
|
+
border-left-color: var(--mock-prompt-bg-color, #7c3aed);
|
|
555
|
+
border-right: none;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.mock-prompt-bubble.right .mock-prompt-bubble-arrow {
|
|
559
|
+
right: 100%;
|
|
560
|
+
top: 50%;
|
|
561
|
+
transform: translateY(-50%);
|
|
562
|
+
border-right-color: var(--mock-prompt-bg-color, #7c3aed);
|
|
563
|
+
border-left: none;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.mock-pulse-rings {
|
|
567
|
+
position: absolute;
|
|
568
|
+
inset: 0;
|
|
569
|
+
border-radius: 50%;
|
|
570
|
+
pointer-events: none;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.mock-pulse-ring {
|
|
574
|
+
position: absolute;
|
|
575
|
+
inset: 0;
|
|
576
|
+
border-radius: 50%;
|
|
577
|
+
animation: mock-pulse-ring 2s ease-out infinite;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.mock-pulse-ring:nth-child(2) {
|
|
581
|
+
animation-delay: 0.5s;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.mock-widget-panel {
|
|
585
|
+
position: relative;
|
|
586
|
+
/* Position is controlled by parent .mock-widget */
|
|
587
|
+
width: 360px;
|
|
588
|
+
height: 550px;
|
|
589
|
+
background: white;
|
|
590
|
+
border-radius: 24px;
|
|
591
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
592
|
+
display: none;
|
|
593
|
+
overflow: hidden;
|
|
594
|
+
flex-direction: column;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.mock-widget-panel.open {
|
|
598
|
+
display: flex;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
.mock-panel-header {
|
|
602
|
+
background: #7C3AED;
|
|
603
|
+
color: white;
|
|
604
|
+
padding: 16px 20px;
|
|
605
|
+
display: flex;
|
|
606
|
+
align-items: center;
|
|
607
|
+
justify-content: space-between;
|
|
608
|
+
font-weight: 600;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.mock-panel-close {
|
|
612
|
+
background: none;
|
|
613
|
+
border: none;
|
|
614
|
+
color: white;
|
|
615
|
+
font-size: 24px;
|
|
616
|
+
cursor: pointer;
|
|
617
|
+
padding: 0;
|
|
618
|
+
width: 32px;
|
|
619
|
+
height: 32px;
|
|
620
|
+
display: flex;
|
|
621
|
+
align-items: center;
|
|
622
|
+
justify-content: center;
|
|
623
|
+
border-radius: 4px;
|
|
624
|
+
transition: background 0.2s;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.mock-panel-close:hover {
|
|
628
|
+
background: rgba(255, 255, 255, 0.2);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.mock-panel-content {
|
|
632
|
+
flex: 1;
|
|
633
|
+
overflow-y: auto;
|
|
634
|
+
padding: 0; /* Remove padding so landing screen fills entire area */
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.mock-landing-screen {
|
|
638
|
+
display: flex;
|
|
639
|
+
flex-direction: column;
|
|
640
|
+
align-items: center;
|
|
641
|
+
justify-content: flex-start;
|
|
642
|
+
flex: 1;
|
|
643
|
+
padding: 32px 24px;
|
|
644
|
+
text-align: center;
|
|
645
|
+
overflow-y: auto;
|
|
646
|
+
min-height: 0;
|
|
647
|
+
width: 100%;
|
|
648
|
+
/* Background will be set inline via style attribute to match widget config */
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
.mock-landing-logo {
|
|
652
|
+
width: 88px;
|
|
653
|
+
height: 88px;
|
|
654
|
+
border-radius: 22px;
|
|
655
|
+
margin-bottom: 20px;
|
|
656
|
+
display: flex;
|
|
657
|
+
align-items: center;
|
|
658
|
+
justify-content: center;
|
|
659
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
660
|
+
box-shadow: 0 8px 28px rgba(102, 126, 234, 0.35);
|
|
661
|
+
border: none; /* No border by default - will be overridden by inline styles */
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.mock-landing-logo img {
|
|
665
|
+
display: block;
|
|
666
|
+
border: none !important;
|
|
667
|
+
outline: none !important;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
.mock-landing-title {
|
|
671
|
+
font-size: 20px;
|
|
672
|
+
font-weight: 600;
|
|
673
|
+
color: #1e1b4b;
|
|
674
|
+
margin-bottom: 6px;
|
|
675
|
+
line-height: 1.3;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
.mock-landing-subtitle {
|
|
679
|
+
font-size: 14px;
|
|
680
|
+
color: #64748b;
|
|
681
|
+
margin-bottom: 28px;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.mock-mode-cards {
|
|
685
|
+
display: flex;
|
|
686
|
+
gap: 12px;
|
|
687
|
+
width: 100%;
|
|
688
|
+
justify-content: center;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
.mock-mode-card {
|
|
692
|
+
flex: 1;
|
|
693
|
+
max-width: 160px;
|
|
694
|
+
background: white;
|
|
695
|
+
border: 1px solid rgba(0, 0, 0, 0.06);
|
|
696
|
+
border-radius: 18px;
|
|
697
|
+
padding: 20px 16px;
|
|
698
|
+
cursor: pointer;
|
|
699
|
+
display: flex;
|
|
700
|
+
flex-direction: column;
|
|
701
|
+
align-items: center;
|
|
702
|
+
gap: 12px;
|
|
703
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
704
|
+
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
.mock-mode-card:hover {
|
|
708
|
+
transform: translateY(-4px);
|
|
709
|
+
box-shadow: 0 8px 24px rgba(124, 58, 237, 0.2);
|
|
710
|
+
border-color: rgba(124, 58, 237, 0.3);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.mock-mode-icon {
|
|
714
|
+
width: 56px;
|
|
715
|
+
height: 56px;
|
|
716
|
+
display: flex;
|
|
717
|
+
align-items: center;
|
|
718
|
+
justify-content: center;
|
|
719
|
+
border-radius: 16px;
|
|
720
|
+
background: #7C3AED;
|
|
721
|
+
color: #fff;
|
|
722
|
+
box-shadow: 0 4px 14px rgba(124, 58, 237, 0.35);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
.mock-mode-icon svg {
|
|
726
|
+
stroke: white;
|
|
727
|
+
fill: none;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.mock-mode-title {
|
|
731
|
+
font-size: 14px;
|
|
732
|
+
font-weight: 600;
|
|
733
|
+
color: #1e1b4b;
|
|
734
|
+
text-align: center;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
.mock-mode-desc {
|
|
738
|
+
display: none; /* Hide description to match real widget */
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
@keyframes pulse {
|
|
742
|
+
0%, 100% { transform: scale(1); opacity: 1; }
|
|
743
|
+
50% { transform: scale(1.15); opacity: 0.8; }
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.mock-text-interface {
|
|
747
|
+
display: flex;
|
|
748
|
+
flex-direction: column;
|
|
749
|
+
height: 100%;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
.mock-messages {
|
|
753
|
+
flex: 1;
|
|
754
|
+
overflow-y: auto;
|
|
755
|
+
padding: 20px;
|
|
756
|
+
display: flex;
|
|
757
|
+
flex-direction: column;
|
|
758
|
+
gap: 12px;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
.mock-message {
|
|
762
|
+
display: flex;
|
|
763
|
+
gap: 8px;
|
|
764
|
+
align-items: center;
|
|
765
|
+
max-width: 75%;
|
|
766
|
+
padding: 4px 0;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
.mock-message.user {
|
|
770
|
+
align-self: flex-end;
|
|
771
|
+
flex-direction: row-reverse; /* Avatar on right for LTR */
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
.mock-message.agent {
|
|
775
|
+
align-self: flex-start;
|
|
776
|
+
flex-direction: row; /* Avatar on left for LTR */
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/* RTL support - reverse flex direction */
|
|
780
|
+
.mock-text-interface[style*="direction: rtl"] .mock-message.user {
|
|
781
|
+
flex-direction: row; /* Avatar on left for RTL */
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
.mock-text-interface[style*="direction: rtl"] .mock-message.agent {
|
|
785
|
+
flex-direction: row-reverse; /* Avatar on right for RTL */
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.mock-message-avatar {
|
|
789
|
+
width: 20px;
|
|
790
|
+
height: 20px;
|
|
791
|
+
display: flex;
|
|
792
|
+
align-items: center;
|
|
793
|
+
justify-content: center;
|
|
794
|
+
flex-shrink: 0;
|
|
795
|
+
font-size: 18px;
|
|
796
|
+
line-height: 1;
|
|
797
|
+
background: transparent;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
.mock-message-bubble {
|
|
801
|
+
padding: 12px 16px;
|
|
802
|
+
border-radius: 16px;
|
|
803
|
+
font-size: 14px;
|
|
804
|
+
line-height: 1.5;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
.mock-message.user .mock-message-bubble {
|
|
808
|
+
background: #E5E7EB;
|
|
809
|
+
color: #1F2937;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
.mock-message.agent .mock-message-bubble {
|
|
813
|
+
background: #F3F4F6;
|
|
814
|
+
color: #1F2937;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
.mock-input-area {
|
|
818
|
+
padding: 16px;
|
|
819
|
+
border-top: 1px solid #e5e7eb;
|
|
820
|
+
display: flex;
|
|
821
|
+
gap: 8px;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
.mock-input {
|
|
825
|
+
flex: 1;
|
|
826
|
+
padding: 12px 16px;
|
|
827
|
+
border: 1px solid #d1d5db;
|
|
828
|
+
border-radius: 24px;
|
|
829
|
+
font-size: 14px;
|
|
830
|
+
outline: none;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
.mock-input:focus {
|
|
834
|
+
border-color: #667eea;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
.mock-send-button {
|
|
838
|
+
width: 40px;
|
|
839
|
+
height: 40px;
|
|
840
|
+
border-radius: 50%;
|
|
841
|
+
background: #7C3AED;
|
|
842
|
+
border: none;
|
|
843
|
+
color: white;
|
|
844
|
+
cursor: pointer;
|
|
845
|
+
display: flex;
|
|
846
|
+
align-items: center;
|
|
847
|
+
justify-content: center;
|
|
848
|
+
font-size: 18px;
|
|
849
|
+
transition: background 0.2s;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
.mock-send-button:hover {
|
|
853
|
+
background: #6D28D9;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
.mock-voice-interface {
|
|
857
|
+
display: flex;
|
|
858
|
+
flex-direction: column;
|
|
859
|
+
height: 100%;
|
|
860
|
+
overflow-y: auto;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
.mock-voice-section {
|
|
864
|
+
padding: 24px;
|
|
865
|
+
border-bottom: 1px solid #e5e7eb;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
.mock-voice-timer {
|
|
869
|
+
display: flex;
|
|
870
|
+
align-items: center;
|
|
871
|
+
gap: 8px;
|
|
872
|
+
margin-bottom: 16px;
|
|
873
|
+
font-size: 14px;
|
|
874
|
+
color: #64748b;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
.mock-timer-dot {
|
|
878
|
+
width: 8px;
|
|
879
|
+
height: 8px;
|
|
880
|
+
background: #ef4444;
|
|
881
|
+
border-radius: 50%;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
.mock-waveform {
|
|
885
|
+
display: flex;
|
|
886
|
+
align-items: center;
|
|
887
|
+
justify-content: center;
|
|
888
|
+
gap: 3px;
|
|
889
|
+
height: 60px;
|
|
890
|
+
margin-bottom: 16px;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
.mock-waveform-bar {
|
|
894
|
+
width: 3px;
|
|
895
|
+
background: #7C3AED;
|
|
896
|
+
border-radius: 2px;
|
|
897
|
+
animation: waveformAnimation 0.8s ease-in-out infinite;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/* Match the real widget waveform heights and delays */
|
|
901
|
+
.mock-waveform-bar:nth-child(1) { height: 12px; animation-delay: 0s; }
|
|
902
|
+
.mock-waveform-bar:nth-child(2) { height: 20px; animation-delay: 0.05s; }
|
|
903
|
+
.mock-waveform-bar:nth-child(3) { height: 28px; animation-delay: 0.1s; }
|
|
904
|
+
.mock-waveform-bar:nth-child(4) { height: 36px; animation-delay: 0.15s; }
|
|
905
|
+
.mock-waveform-bar:nth-child(5) { height: 44px; animation-delay: 0.2s; }
|
|
906
|
+
.mock-waveform-bar:nth-child(6) { height: 50px; animation-delay: 0.25s; }
|
|
907
|
+
.mock-waveform-bar:nth-child(7) { height: 54px; animation-delay: 0.3s; }
|
|
908
|
+
.mock-waveform-bar:nth-child(8) { height: 56px; animation-delay: 0.35s; }
|
|
909
|
+
.mock-waveform-bar:nth-child(9) { height: 54px; animation-delay: 0.4s; }
|
|
910
|
+
.mock-waveform-bar:nth-child(10) { height: 50px; animation-delay: 0.45s; }
|
|
911
|
+
.mock-waveform-bar:nth-child(11) { height: 44px; animation-delay: 0.5s; }
|
|
912
|
+
.mock-waveform-bar:nth-child(12) { height: 36px; animation-delay: 0.55s; }
|
|
913
|
+
.mock-waveform-bar:nth-child(13) { height: 28px; animation-delay: 0.6s; }
|
|
914
|
+
.mock-waveform-bar:nth-child(14) { height: 20px; animation-delay: 0.65s; }
|
|
915
|
+
.mock-waveform-bar:nth-child(15) { height: 12px; animation-delay: 0.7s; }
|
|
916
|
+
|
|
917
|
+
@keyframes waveformAnimation {
|
|
918
|
+
0%, 100% {
|
|
919
|
+
transform: scaleY(0.3);
|
|
920
|
+
opacity: 0.7;
|
|
921
|
+
}
|
|
922
|
+
50% {
|
|
923
|
+
transform: scaleY(1);
|
|
924
|
+
opacity: 1;
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
.mock-voice-status {
|
|
929
|
+
display: flex;
|
|
930
|
+
align-items: center;
|
|
931
|
+
gap: 8px;
|
|
932
|
+
margin-bottom: 24px;
|
|
933
|
+
font-size: 14px;
|
|
934
|
+
color: #64748b;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
.mock-status-dot {
|
|
938
|
+
width: 8px;
|
|
939
|
+
height: 8px;
|
|
940
|
+
background: #10b981;
|
|
941
|
+
border-radius: 50%;
|
|
942
|
+
animation: pulse-dot 2s infinite;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
@keyframes pulse-dot {
|
|
946
|
+
0%, 100% { opacity: 1; }
|
|
947
|
+
50% { opacity: 0.5; }
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
.mock-voice-controls {
|
|
951
|
+
display: flex;
|
|
952
|
+
justify-content: center;
|
|
953
|
+
gap: 12px;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/* Compact single-row layout when history is expanded */
|
|
957
|
+
.mock-voice-section-compact {
|
|
958
|
+
padding: 16px 24px;
|
|
959
|
+
border-bottom: 1px solid #e5e7eb;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
.mock-compact-row {
|
|
963
|
+
display: flex;
|
|
964
|
+
align-items: center;
|
|
965
|
+
gap: 16px;
|
|
966
|
+
flex-wrap: nowrap;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
.mock-compact-waveform {
|
|
970
|
+
display: flex;
|
|
971
|
+
align-items: center;
|
|
972
|
+
gap: 2px;
|
|
973
|
+
height: 32px;
|
|
974
|
+
flex-shrink: 0;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
.mock-compact-waveform .mock-waveform-bar {
|
|
978
|
+
width: 2px;
|
|
979
|
+
background: #7C3AED;
|
|
980
|
+
border-radius: 1px;
|
|
981
|
+
animation: waveformAnimation 0.8s ease-in-out infinite;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(1) { height: 8px; animation-delay: 0s; }
|
|
985
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(2) { height: 12px; animation-delay: 0.05s; }
|
|
986
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(3) { height: 16px; animation-delay: 0.1s; }
|
|
987
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(4) { height: 20px; animation-delay: 0.15s; }
|
|
988
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(5) { height: 24px; animation-delay: 0.2s; }
|
|
989
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(6) { height: 28px; animation-delay: 0.25s; }
|
|
990
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(7) { height: 30px; animation-delay: 0.3s; }
|
|
991
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(8) { height: 32px; animation-delay: 0.35s; }
|
|
992
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(9) { height: 30px; animation-delay: 0.4s; }
|
|
993
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(10) { height: 28px; animation-delay: 0.45s; }
|
|
994
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(11) { height: 24px; animation-delay: 0.5s; }
|
|
995
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(12) { height: 20px; animation-delay: 0.55s; }
|
|
996
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(13) { height: 16px; animation-delay: 0.6s; }
|
|
997
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(14) { height: 12px; animation-delay: 0.65s; }
|
|
998
|
+
.mock-compact-waveform .mock-waveform-bar:nth-child(15) { height: 8px; animation-delay: 0.7s; }
|
|
999
|
+
|
|
1000
|
+
.mock-compact-timer {
|
|
1001
|
+
display: flex;
|
|
1002
|
+
align-items: center;
|
|
1003
|
+
gap: 6px;
|
|
1004
|
+
font-size: 14px;
|
|
1005
|
+
color: #64748b;
|
|
1006
|
+
flex-shrink: 0;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
.mock-compact-timer .mock-timer-dot {
|
|
1010
|
+
width: 6px;
|
|
1011
|
+
height: 6px;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
.mock-compact-status {
|
|
1015
|
+
display: flex;
|
|
1016
|
+
align-items: center;
|
|
1017
|
+
gap: 6px;
|
|
1018
|
+
font-size: 14px;
|
|
1019
|
+
color: #10b981;
|
|
1020
|
+
flex-shrink: 0;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
.mock-compact-status .mock-status-dot {
|
|
1024
|
+
width: 6px;
|
|
1025
|
+
height: 6px;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
.mock-compact-controls {
|
|
1029
|
+
display: flex;
|
|
1030
|
+
align-items: center;
|
|
1031
|
+
gap: 8px;
|
|
1032
|
+
margin-left: auto;
|
|
1033
|
+
flex-shrink: 0;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
.mock-compact-controls .mock-control-btn {
|
|
1037
|
+
width: 40px;
|
|
1038
|
+
height: 40px;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
.mock-control-btn {
|
|
1042
|
+
width: 48px;
|
|
1043
|
+
height: 48px;
|
|
1044
|
+
border-radius: 50%;
|
|
1045
|
+
border: none;
|
|
1046
|
+
cursor: pointer;
|
|
1047
|
+
display: flex;
|
|
1048
|
+
align-items: center;
|
|
1049
|
+
justify-content: center;
|
|
1050
|
+
transition: all 0.2s;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
.mock-control-btn.secondary {
|
|
1054
|
+
background: white;
|
|
1055
|
+
color: #374151;
|
|
1056
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
.mock-control-btn.danger {
|
|
1060
|
+
background: #ef4444;
|
|
1061
|
+
color: white;
|
|
1062
|
+
width: 56px;
|
|
1063
|
+
height: 56px;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
.mock-control-btn svg {
|
|
1067
|
+
width: 20px;
|
|
1068
|
+
height: 20px;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
.mock-conversation-section {
|
|
1072
|
+
flex: 1;
|
|
1073
|
+
display: flex;
|
|
1074
|
+
flex-direction: column;
|
|
1075
|
+
overflow-y: auto;
|
|
1076
|
+
padding: 16px;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
.mock-conversation-header {
|
|
1080
|
+
display: flex;
|
|
1081
|
+
justify-content: space-between;
|
|
1082
|
+
align-items: center;
|
|
1083
|
+
margin-bottom: 12px;
|
|
1084
|
+
font-size: 12px;
|
|
1085
|
+
font-weight: 600;
|
|
1086
|
+
color: #6b7280;
|
|
1087
|
+
text-transform: uppercase;
|
|
1088
|
+
letter-spacing: 0.5px;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
.mock-conversation-toggle {
|
|
1092
|
+
display: flex;
|
|
1093
|
+
align-items: center;
|
|
1094
|
+
gap: 4px;
|
|
1095
|
+
cursor: pointer;
|
|
1096
|
+
color: #6b7280;
|
|
1097
|
+
font-size: 12px;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
.mock-live-indicator {
|
|
1101
|
+
display: flex;
|
|
1102
|
+
align-items: center;
|
|
1103
|
+
gap: 6px;
|
|
1104
|
+
margin-bottom: 12px;
|
|
1105
|
+
font-size: 12px;
|
|
1106
|
+
font-weight: 600;
|
|
1107
|
+
color: #10b981;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
.mock-live-dot {
|
|
1111
|
+
width: 6px;
|
|
1112
|
+
height: 6px;
|
|
1113
|
+
background: #10b981;
|
|
1114
|
+
border-radius: 50%;
|
|
1115
|
+
animation: pulse-dot 2s infinite;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
.mock-live-transcript-collapsed {
|
|
1119
|
+
padding: 0;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
.mock-live-text-collapsed {
|
|
1123
|
+
color: #64748b;
|
|
1124
|
+
font-size: 14px;
|
|
1125
|
+
line-height: 1.6;
|
|
1126
|
+
min-height: 44px;
|
|
1127
|
+
display: -webkit-box;
|
|
1128
|
+
-webkit-line-clamp: 2;
|
|
1129
|
+
-webkit-box-orient: vertical;
|
|
1130
|
+
overflow: hidden;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/* Expanded history styles */
|
|
1134
|
+
.mock-conversation-history {
|
|
1135
|
+
display: none;
|
|
1136
|
+
flex-direction: column;
|
|
1137
|
+
gap: 16px;
|
|
1138
|
+
max-height: 300px;
|
|
1139
|
+
overflow-y: auto;
|
|
1140
|
+
padding-right: 8px;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
.mock-conversation-history.expanded {
|
|
1144
|
+
display: flex !important;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
.mock-history-message {
|
|
1148
|
+
display: flex;
|
|
1149
|
+
gap: 12px;
|
|
1150
|
+
align-items: flex-start;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
.mock-history-avatar {
|
|
1154
|
+
width: 32px;
|
|
1155
|
+
height: 32px;
|
|
1156
|
+
border-radius: 50%;
|
|
1157
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
1158
|
+
display: flex;
|
|
1159
|
+
align-items: center;
|
|
1160
|
+
justify-content: center;
|
|
1161
|
+
flex-shrink: 0;
|
|
1162
|
+
font-size: 14px;
|
|
1163
|
+
color: white;
|
|
1164
|
+
overflow: hidden;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
.mock-history-avatar img {
|
|
1168
|
+
width: 100%;
|
|
1169
|
+
height: 100%;
|
|
1170
|
+
object-fit: cover;
|
|
1171
|
+
border-radius: 50%;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
.mock-history-bubble {
|
|
1175
|
+
background: #f3f4f6;
|
|
1176
|
+
padding: 12px 16px;
|
|
1177
|
+
border-radius: 16px;
|
|
1178
|
+
font-size: 14px;
|
|
1179
|
+
color: #1f2937;
|
|
1180
|
+
line-height: 1.5;
|
|
1181
|
+
max-width: calc(100% - 44px);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
.mock-live-message-row {
|
|
1185
|
+
display: flex;
|
|
1186
|
+
gap: 12px;
|
|
1187
|
+
align-items: flex-start;
|
|
1188
|
+
margin-top: 8px;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
.mock-live-badge {
|
|
1192
|
+
display: inline-block;
|
|
1193
|
+
background: #10b981;
|
|
1194
|
+
color: white;
|
|
1195
|
+
font-size: 10px;
|
|
1196
|
+
font-weight: 600;
|
|
1197
|
+
padding: 2px 6px;
|
|
1198
|
+
border-radius: 4px;
|
|
1199
|
+
margin-right: 8px;
|
|
1200
|
+
text-transform: uppercase;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
.mock-voice-input-area {
|
|
1204
|
+
padding: 16px;
|
|
1205
|
+
border-top: 1px solid #e5e7eb;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
.mock-voice-input-wrapper {
|
|
1209
|
+
display: flex;
|
|
1210
|
+
gap: 8px;
|
|
1211
|
+
align-items: center;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
.mock-voice-text-input {
|
|
1215
|
+
flex: 1;
|
|
1216
|
+
padding: 12px 16px;
|
|
1217
|
+
border: 1px solid #d1d5db;
|
|
1218
|
+
border-radius: 24px;
|
|
1219
|
+
font-size: 14px;
|
|
1220
|
+
outline: none;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
.mock-voice-text-input:focus {
|
|
1224
|
+
border-color: #7C3AED;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
.mock-voice-send-btn {
|
|
1228
|
+
width: 40px;
|
|
1229
|
+
height: 40px;
|
|
1230
|
+
border-radius: 8px;
|
|
1231
|
+
background: #7C3AED;
|
|
1232
|
+
border: none;
|
|
1233
|
+
color: white;
|
|
1234
|
+
cursor: pointer;
|
|
1235
|
+
display: flex;
|
|
1236
|
+
align-items: center;
|
|
1237
|
+
justify-content: center;
|
|
1238
|
+
transition: background 0.2s;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
.mock-voice-send-btn:hover {
|
|
1242
|
+
background: #6D28D9;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
.mock-powered-by {
|
|
1246
|
+
padding: 12px 16px;
|
|
1247
|
+
border-top: 1px solid #e5e7eb;
|
|
1248
|
+
font-size: 11px;
|
|
1249
|
+
color: #9ca3af;
|
|
1250
|
+
display: flex;
|
|
1251
|
+
align-items: center;
|
|
1252
|
+
gap: 6px;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
.mock-powered-by strong {
|
|
1256
|
+
color: #7C3AED;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
@keyframes pulse {
|
|
1260
|
+
0%, 100% {
|
|
1261
|
+
transform: scale(1);
|
|
1262
|
+
}
|
|
1263
|
+
50% {
|
|
1264
|
+
transform: scale(1.1);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
</style>
|
|
1268
|
+
</head>
|
|
1269
|
+
<body>
|
|
1270
|
+
<div class="header">
|
|
1271
|
+
<a href="test-index.html" class="back-link">← Back to Demos</a>
|
|
1272
|
+
<h1>🎨 Widget Live Customization</h1>
|
|
1273
|
+
<div style="margin-top: 16px; display: flex; align-items: center; justify-content: center; gap: 24px; flex-wrap: wrap;">
|
|
1274
|
+
<div id="modeInstructions" style="background: rgba(255,255,255,0.2); padding: 12px 20px; border-radius: 12px; font-size: 15px; font-weight: 500;">
|
|
1275
|
+
<span id="instructionText">✏️ <strong>Single click</strong> to customize • <strong>Double click</strong> to interact</span>
|
|
1276
|
+
</div>
|
|
1277
|
+
</div>
|
|
1278
|
+
</div>
|
|
1279
|
+
|
|
1280
|
+
<div class="container">
|
|
1281
|
+
<div class="preview-section">
|
|
1282
|
+
<h2>Preview</h2>
|
|
1283
|
+
<div class="preview-area" id="previewArea">
|
|
1284
|
+
<!-- Container to ensure proper scrolling - must be taller than preview area -->
|
|
1285
|
+
<div style="position: relative; min-height: 800px; width: 100%; padding-bottom: 200px;">
|
|
1286
|
+
<div class="mock-widget" id="mockWidget" data-element-type="position">
|
|
1287
|
+
<div class="mock-widget-panel" id="mockPanel">
|
|
1288
|
+
<!-- Panel content will be dynamically generated -->
|
|
1289
|
+
</div>
|
|
1290
|
+
<div class="mock-button-container" id="mockButtonContainer">
|
|
1291
|
+
<div class="mock-prompt-bubble" id="mockPromptBubble" style="display: none;"></div>
|
|
1292
|
+
<div class="mock-pulse-rings" id="mockPulseRings" style="display: none;"></div>
|
|
1293
|
+
<button class="mock-widget-button" id="mockButton">🎤</button>
|
|
1294
|
+
</div>
|
|
1295
|
+
</div>
|
|
1296
|
+
</div>
|
|
1297
|
+
</div>
|
|
1298
|
+
</div>
|
|
1299
|
+
|
|
1300
|
+
<div class="customization-panel">
|
|
1301
|
+
<h2>Customization</h2>
|
|
1302
|
+
|
|
1303
|
+
<div class="info-box" id="instructionsBox">
|
|
1304
|
+
<p id="instructionsText">
|
|
1305
|
+
<strong>How to use:</strong><br>
|
|
1306
|
+
<span style="display: block; margin-top: 8px;">
|
|
1307
|
+
<strong>Single Click:</strong> Select any widget element to customize its appearance. Changes apply instantly.<br>
|
|
1308
|
+
<strong>Double Click:</strong> Interact with elements normally (open widget, switch views, etc.)
|
|
1309
|
+
</span>
|
|
1310
|
+
<span style="display: block; margin-top: 12px; font-size: 13px; color: #1e40af;">
|
|
1311
|
+
💡 <strong>Tip:</strong> Single click any element to see customization options. Double click to use it normally.
|
|
1312
|
+
</span>
|
|
1313
|
+
</p>
|
|
1314
|
+
</div>
|
|
1315
|
+
|
|
1316
|
+
<div id="customizationControls" style="flex: 0 0 auto; overflow: visible; margin-bottom: 16px;">
|
|
1317
|
+
<!-- Controls will be dynamically generated based on selected element -->
|
|
1318
|
+
<div class="customization-group">
|
|
1319
|
+
<h3>Select an Element</h3>
|
|
1320
|
+
<p style="color: #6b7280; font-size: 14px; margin-top: 8px;">
|
|
1321
|
+
Click on any widget element in the preview to start customizing.
|
|
1322
|
+
</p>
|
|
1323
|
+
</div>
|
|
1324
|
+
</div>
|
|
1325
|
+
|
|
1326
|
+
<div style="flex-shrink: 0; display: flex; flex-direction: column; gap: 16px; margin-top: auto;">
|
|
1327
|
+
<div class="button-group" style="flex-shrink: 0;">
|
|
1328
|
+
<button class="btn btn-primary" id="resetBtn">Reset to Defaults</button>
|
|
1329
|
+
<button class="btn btn-secondary" id="togglePanelBtn">Toggle Panel</button>
|
|
1330
|
+
</div>
|
|
1331
|
+
|
|
1332
|
+
<div class="code-output" style="flex-shrink: 0; height: auto; min-height: 400px; max-height: none; overflow-y: visible; overflow-x: auto; margin-bottom: 0;">
|
|
1333
|
+
<h3 style="color: #f9fafb; margin-bottom: 12px; font-size: 16px; font-weight: 700;">Configuration Code:</h3>
|
|
1334
|
+
<pre id="configCode" style="margin: 0; white-space: pre-wrap; word-wrap: break-word; font-size: 13px; line-height: 1.6;">// Select an element to see its configuration</pre>
|
|
1335
|
+
</div>
|
|
1336
|
+
</div>
|
|
1337
|
+
</div>
|
|
1338
|
+
</div>
|
|
1339
|
+
|
|
1340
|
+
<script>
|
|
1341
|
+
// Mock widget configuration
|
|
1342
|
+
let widgetConfig = {
|
|
1343
|
+
button: {
|
|
1344
|
+
size: 'medium',
|
|
1345
|
+
shape: 'circle',
|
|
1346
|
+
backgroundColor: '#FFFFFF',
|
|
1347
|
+
hoverColor: '#D3D3D3', // SDK default: light gray
|
|
1348
|
+
shadow: true,
|
|
1349
|
+
shadowColor: 'rgba(0,0,0,0.15)'
|
|
1350
|
+
},
|
|
1351
|
+
icon: {
|
|
1352
|
+
type: 'custom',
|
|
1353
|
+
customImage: 'https://talktopc.com/logo192.png',
|
|
1354
|
+
size: 'medium',
|
|
1355
|
+
backgroundColor: '#FFFFFF'
|
|
1356
|
+
},
|
|
1357
|
+
panel: {
|
|
1358
|
+
width: 360,
|
|
1359
|
+
height: 550, // Updated from SDK default 500
|
|
1360
|
+
borderRadius: 24,
|
|
1361
|
+
backgroundColor: '#FFFFFF',
|
|
1362
|
+
border: '1px solid #E5E7EB'
|
|
1363
|
+
},
|
|
1364
|
+
direction: 'ltr', // 'ltr' or 'rtl'
|
|
1365
|
+
position: {
|
|
1366
|
+
vertical: 'bottom', // 'top' or 'bottom'
|
|
1367
|
+
horizontal: 'right', // 'left' or 'right'
|
|
1368
|
+
offset: { x: 20, y: 20 }
|
|
1369
|
+
},
|
|
1370
|
+
header: {
|
|
1371
|
+
title: 'Chat Assistant',
|
|
1372
|
+
backgroundColor: '#7C3AED', // Default purple
|
|
1373
|
+
textColor: '#FFFFFF',
|
|
1374
|
+
showCloseButton: true,
|
|
1375
|
+
onlineIndicatorText: 'Online',
|
|
1376
|
+
onlineIndicatorColor: '#FFFFFF',
|
|
1377
|
+
onlineIndicatorDotColor: '#10b981'
|
|
1378
|
+
},
|
|
1379
|
+
messages: {
|
|
1380
|
+
userBackgroundColor: '#E5E7EB',
|
|
1381
|
+
agentBackgroundColor: '#F3F4F6',
|
|
1382
|
+
textColor: '#1F2937', // Fallback for backward compatibility
|
|
1383
|
+
userTextColor: '#1F2937',
|
|
1384
|
+
agentTextColor: '#1F2937',
|
|
1385
|
+
userAvatarIcon: '👤',
|
|
1386
|
+
agentAvatarIcon: '🤖',
|
|
1387
|
+
fontSize: '14px',
|
|
1388
|
+
borderRadius: 16
|
|
1389
|
+
},
|
|
1390
|
+
text: {
|
|
1391
|
+
sendButtonText: '→',
|
|
1392
|
+
sendButtonColor: '#7C3AED',
|
|
1393
|
+
sendButtonHoverColor: '#6D28D9',
|
|
1394
|
+
inputPlaceholder: 'Type your message...',
|
|
1395
|
+
inputFocusColor: '#7C3AED'
|
|
1396
|
+
},
|
|
1397
|
+
voice: {
|
|
1398
|
+
micButtonColor: '#7C3AED',
|
|
1399
|
+
micButtonActiveColor: '#EF4444',
|
|
1400
|
+
speakerButtonColor: '#FFFFFF',
|
|
1401
|
+
endCallButtonColor: '#ef4444',
|
|
1402
|
+
avatarBackgroundColor: '#667eea',
|
|
1403
|
+
avatarType: 'icon', // 'icon' or 'image'
|
|
1404
|
+
avatarIcon: '🤖',
|
|
1405
|
+
avatarImageUrl: '',
|
|
1406
|
+
startCallButtonText: 'Start Call',
|
|
1407
|
+
startCallButtonColor: '#667eea',
|
|
1408
|
+
startCallButtonTextColor: '#FFFFFF',
|
|
1409
|
+
statusTitleColor: '#1e293b',
|
|
1410
|
+
statusSubtitleColor: '#64748b',
|
|
1411
|
+
statusDotColor: '#10b981',
|
|
1412
|
+
statusText: 'Listening...',
|
|
1413
|
+
liveTranscriptTextColor: '#64748b',
|
|
1414
|
+
liveTranscriptFontSize: '14px',
|
|
1415
|
+
liveIndicatorDotColor: '#10b981',
|
|
1416
|
+
liveIndicatorTextColor: '#10b981',
|
|
1417
|
+
waveformType: 'waveform', // 'waveform', 'icon', or 'image'
|
|
1418
|
+
waveformIcon: '🎤',
|
|
1419
|
+
waveformImageUrl: ''
|
|
1420
|
+
},
|
|
1421
|
+
landing: {
|
|
1422
|
+
backgroundColor: 'linear-gradient(180deg, #ffffff 0%, rgba(168, 85, 247, 0.03) 100%)', // Default gradient matching real widget
|
|
1423
|
+
logo: '🤖',
|
|
1424
|
+
logoType: 'icon', // 'icon' or 'image'
|
|
1425
|
+
logoIcon: '🤖',
|
|
1426
|
+
logoImageUrl: '',
|
|
1427
|
+
logoBackgroundColor: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
1428
|
+
logoBackgroundEnabled: true,
|
|
1429
|
+
title: 'Welcome to AI Assistant',
|
|
1430
|
+
subtitle: 'Choose how you\'d like to interact',
|
|
1431
|
+
voiceCardTitle: 'Voice Call',
|
|
1432
|
+
textCardTitle: 'Text Chat',
|
|
1433
|
+
titleColor: '#1e293b',
|
|
1434
|
+
subtitleColor: '#64748b',
|
|
1435
|
+
modeCardBackgroundColor: '#FFFFFF'
|
|
1436
|
+
},
|
|
1437
|
+
position: {
|
|
1438
|
+
vertical: 'bottom',
|
|
1439
|
+
horizontal: 'left',
|
|
1440
|
+
offset: { x: 20, y: 20 }
|
|
1441
|
+
},
|
|
1442
|
+
promptAnimation: {
|
|
1443
|
+
enabled: false,
|
|
1444
|
+
text: 'Try me!',
|
|
1445
|
+
backgroundColor: 'linear-gradient(135deg, #7c3aed, #4f46e5)',
|
|
1446
|
+
textColor: '#ffffff',
|
|
1447
|
+
animationType: 'bounce',
|
|
1448
|
+
showShimmer: true,
|
|
1449
|
+
showPulseRings: true,
|
|
1450
|
+
hideAfterClick: true,
|
|
1451
|
+
hideAfterSeconds: null,
|
|
1452
|
+
position: 'top'
|
|
1453
|
+
}
|
|
1454
|
+
};
|
|
1455
|
+
|
|
1456
|
+
let selectedElement = null;
|
|
1457
|
+
let currentView = 'landing'; // 'landing', 'text', 'voice'
|
|
1458
|
+
let panelOpen = true; // Start with panel open to show defaults
|
|
1459
|
+
let clickTimeout = null; // Track single vs double click
|
|
1460
|
+
let historyExpanded = false; // Track conversation history state
|
|
1461
|
+
let isInitializing = true; // Track if we're in the initial load phase
|
|
1462
|
+
let lastClickTime = 0; // Track clicks for double-click detection
|
|
1463
|
+
let lastClickElement = null;
|
|
1464
|
+
let lastClickType = null;
|
|
1465
|
+
|
|
1466
|
+
// Initialize mock widget
|
|
1467
|
+
function initMockWidget() {
|
|
1468
|
+
const mockButton = document.getElementById('mockButton');
|
|
1469
|
+
const mockPanel = document.getElementById('mockPanel');
|
|
1470
|
+
|
|
1471
|
+
// Apply button styles (this will also update prompt bubble)
|
|
1472
|
+
applyButtonStyles(mockButton);
|
|
1473
|
+
|
|
1474
|
+
// Open panel by default to show defaults
|
|
1475
|
+
mockPanel.classList.add('open');
|
|
1476
|
+
|
|
1477
|
+
// Setup button click handler
|
|
1478
|
+
mockButton.addEventListener('click', () => {
|
|
1479
|
+
panelOpen = !panelOpen;
|
|
1480
|
+
mockPanel.classList.toggle('open');
|
|
1481
|
+
if (panelOpen) {
|
|
1482
|
+
renderPanelContent();
|
|
1483
|
+
}
|
|
1484
|
+
// Update prompt bubble visibility based on panel state
|
|
1485
|
+
updatePromptBubble(mockButton);
|
|
1486
|
+
// Sync actual widget state with mock panel
|
|
1487
|
+
syncWidgetWithMockPanel();
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
// Render initial panel content with defaults
|
|
1491
|
+
renderPanelContent();
|
|
1492
|
+
|
|
1493
|
+
// Update prompt bubble visibility (should be hidden since panel starts open)
|
|
1494
|
+
updatePromptBubble(mockButton);
|
|
1495
|
+
|
|
1496
|
+
// Show default customization controls
|
|
1497
|
+
showCustomizationControls('default');
|
|
1498
|
+
|
|
1499
|
+
// Initialize config code to show only appId and agentId
|
|
1500
|
+
updateConfigCode();
|
|
1501
|
+
|
|
1502
|
+
// Apply initial position after a brief delay to ensure DOM is ready
|
|
1503
|
+
setTimeout(() => {
|
|
1504
|
+
updateMockWidgetPosition();
|
|
1505
|
+
}, 0);
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
function applyButtonStyles(button) {
|
|
1509
|
+
const config = widgetConfig.button;
|
|
1510
|
+
button.style.width = getSizeValue(config.size) + 'px';
|
|
1511
|
+
button.style.height = getSizeValue(config.size) + 'px';
|
|
1512
|
+
button.style.backgroundColor = config.backgroundColor;
|
|
1513
|
+
button.style.borderRadius = config.shape === 'circle' ? '50%' : config.shape === 'rounded' ? '12px' : '0';
|
|
1514
|
+
|
|
1515
|
+
if (config.shadow) {
|
|
1516
|
+
button.style.boxShadow = `0 4px 12px ${config.shadowColor}`;
|
|
1517
|
+
} else {
|
|
1518
|
+
button.style.boxShadow = 'none';
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
// Apply icon
|
|
1522
|
+
const iconConfig = widgetConfig.icon;
|
|
1523
|
+
button.innerHTML = ''; // Clear previous content
|
|
1524
|
+
|
|
1525
|
+
if (iconConfig.type === 'custom' && iconConfig.customImage) {
|
|
1526
|
+
const img = document.createElement('img');
|
|
1527
|
+
img.src = iconConfig.customImage;
|
|
1528
|
+
img.alt = 'Chat Assistant';
|
|
1529
|
+
const iconSize = Math.floor(getSizeValue(config.size) * 0.6);
|
|
1530
|
+
img.style.width = iconSize + 'px';
|
|
1531
|
+
img.style.height = iconSize + 'px';
|
|
1532
|
+
img.style.objectFit = 'contain';
|
|
1533
|
+
button.appendChild(img);
|
|
1534
|
+
} else if (iconConfig.type === 'emoji') {
|
|
1535
|
+
button.textContent = iconConfig.emoji;
|
|
1536
|
+
} else if (iconConfig.type === 'text') {
|
|
1537
|
+
button.textContent = iconConfig.text;
|
|
1538
|
+
} else if (iconConfig.type === 'microphone') {
|
|
1539
|
+
// Default microphone SVG
|
|
1540
|
+
const iconSize = Math.floor(getSizeValue(config.size) * 0.5);
|
|
1541
|
+
button.innerHTML = `<svg viewBox="0 0 24 24" style="width: ${iconSize}px; height: ${iconSize}px; fill: #7C3AED;">
|
|
1542
|
+
<path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z"/>
|
|
1543
|
+
<path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z"/>
|
|
1544
|
+
</svg>`;
|
|
1545
|
+
} else {
|
|
1546
|
+
// Fallback to custom image if type is custom but no image specified
|
|
1547
|
+
const img = document.createElement('img');
|
|
1548
|
+
img.src = 'https://talktopc.com/logo192.png';
|
|
1549
|
+
img.alt = 'Chat Assistant';
|
|
1550
|
+
const iconSize = Math.floor(getSizeValue(config.size) * 0.6);
|
|
1551
|
+
img.style.width = iconSize + 'px';
|
|
1552
|
+
img.style.height = iconSize + 'px';
|
|
1553
|
+
img.style.objectFit = 'contain';
|
|
1554
|
+
button.appendChild(img);
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
// Make button highlightable
|
|
1558
|
+
button.dataset.elementType = 'button';
|
|
1559
|
+
|
|
1560
|
+
// Update prompt bubble if enabled
|
|
1561
|
+
updatePromptBubble(button);
|
|
1562
|
+
|
|
1563
|
+
// Remove old event listeners by replacing the button
|
|
1564
|
+
const oldButton = button;
|
|
1565
|
+
const newButton = oldButton.cloneNode(true);
|
|
1566
|
+
oldButton.parentNode.replaceChild(newButton, oldButton);
|
|
1567
|
+
newButton.id = 'mockButton';
|
|
1568
|
+
|
|
1569
|
+
newButton.addEventListener('click', (e) => {
|
|
1570
|
+
e.preventDefault();
|
|
1571
|
+
e.stopPropagation();
|
|
1572
|
+
|
|
1573
|
+
// For button, single click should toggle panel immediately
|
|
1574
|
+
// Double click will still select for editing (without double-toggling)
|
|
1575
|
+
const currentTime = Date.now();
|
|
1576
|
+
const timeSinceLastClick = currentTime - lastClickTime;
|
|
1577
|
+
|
|
1578
|
+
// Check if this is a double click (within 400ms and same element)
|
|
1579
|
+
if (timeSinceLastClick < 400 && lastClickElement === newButton && lastClickType === 'button') {
|
|
1580
|
+
// Double click - just select for editing (don't toggle panel again)
|
|
1581
|
+
lastClickTime = 0;
|
|
1582
|
+
lastClickElement = null;
|
|
1583
|
+
lastClickType = null;
|
|
1584
|
+
selectElement('button', newButton, e);
|
|
1585
|
+
} else {
|
|
1586
|
+
// Single click - toggle panel immediately and select button
|
|
1587
|
+
lastClickTime = currentTime;
|
|
1588
|
+
lastClickElement = newButton;
|
|
1589
|
+
lastClickType = 'button';
|
|
1590
|
+
|
|
1591
|
+
// Toggle panel
|
|
1592
|
+
panelOpen = !panelOpen;
|
|
1593
|
+
const panel = document.getElementById('mockPanel');
|
|
1594
|
+
panel.classList.toggle('open');
|
|
1595
|
+
if (panelOpen) {
|
|
1596
|
+
renderPanelContent();
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
// Update prompt bubble visibility based on panel state
|
|
1600
|
+
updatePromptBubble(newButton);
|
|
1601
|
+
// Sync actual widget state with mock panel
|
|
1602
|
+
syncWidgetWithMockPanel();
|
|
1603
|
+
|
|
1604
|
+
// Select the button for editing (so user can customize it)
|
|
1605
|
+
selectElement('button', newButton, e);
|
|
1606
|
+
}
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
function updatePromptBubble(button) {
|
|
1611
|
+
const promptConfig = widgetConfig.promptAnimation || {};
|
|
1612
|
+
const promptBubble = document.getElementById('mockPromptBubble');
|
|
1613
|
+
const pulseRings = document.getElementById('mockPulseRings');
|
|
1614
|
+
|
|
1615
|
+
if (!promptBubble || !pulseRings) return;
|
|
1616
|
+
|
|
1617
|
+
// Show/hide prompt bubble - hide when mock panel is open, show when closed
|
|
1618
|
+
if (promptConfig.enabled === true && !panelOpen) {
|
|
1619
|
+
promptBubble.style.display = 'block';
|
|
1620
|
+
|
|
1621
|
+
// Extract solid color from gradient for arrow
|
|
1622
|
+
let arrowColor = '#7c3aed';
|
|
1623
|
+
if (promptConfig.backgroundColor && promptConfig.backgroundColor.includes('gradient')) {
|
|
1624
|
+
const match = promptConfig.backgroundColor.match(/#[0-9a-fA-F]{6}/);
|
|
1625
|
+
if (match) arrowColor = match[0];
|
|
1626
|
+
} else if (promptConfig.backgroundColor) {
|
|
1627
|
+
arrowColor = promptConfig.backgroundColor;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
// Set CSS variable for arrow color
|
|
1631
|
+
promptBubble.style.setProperty('--mock-prompt-bg-color', arrowColor);
|
|
1632
|
+
|
|
1633
|
+
// Build prompt bubble HTML
|
|
1634
|
+
// Convert 'pulse' to 'bounce' if pulse is selected (pulse option removed)
|
|
1635
|
+
const animationType = promptConfig.animationType === 'pulse' ? 'bounce' : promptConfig.animationType;
|
|
1636
|
+
const animationClass = animationType === 'none' ? '' : `animation-${animationType}`;
|
|
1637
|
+
const shimmerHTML = promptConfig.showShimmer !== false ? '<div class="mock-prompt-bubble-shimmer"></div>' : '';
|
|
1638
|
+
const position = promptConfig.position || 'top';
|
|
1639
|
+
|
|
1640
|
+
promptBubble.className = `mock-prompt-bubble ${position} ${animationClass}`;
|
|
1641
|
+
promptBubble.innerHTML = `
|
|
1642
|
+
<div class="mock-prompt-bubble-content" style="background: ${promptConfig.backgroundColor || 'linear-gradient(135deg, #7c3aed, #4f46e5)'}; color: ${promptConfig.textColor || '#ffffff'};">
|
|
1643
|
+
${shimmerHTML}
|
|
1644
|
+
<span style="position: relative; z-index: 1;">${promptConfig.text || 'Try me!'}</span>
|
|
1645
|
+
</div>
|
|
1646
|
+
<div class="mock-prompt-bubble-arrow"></div>
|
|
1647
|
+
`;
|
|
1648
|
+
} else {
|
|
1649
|
+
promptBubble.style.display = 'none';
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
// Show/hide pulse rings - hide when mock panel is open, show when closed
|
|
1653
|
+
if (promptConfig.enabled === true && promptConfig.showPulseRings !== false && !panelOpen) {
|
|
1654
|
+
pulseRings.style.display = 'block';
|
|
1655
|
+
|
|
1656
|
+
// Extract solid color from gradient for rings
|
|
1657
|
+
let ringColor = '#7c3aed';
|
|
1658
|
+
if (promptConfig.backgroundColor && promptConfig.backgroundColor.includes('gradient')) {
|
|
1659
|
+
const match = promptConfig.backgroundColor.match(/#[0-9a-fA-F]{6}/);
|
|
1660
|
+
if (match) ringColor = match[0];
|
|
1661
|
+
} else if (promptConfig.backgroundColor) {
|
|
1662
|
+
ringColor = promptConfig.backgroundColor;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
pulseRings.innerHTML = `
|
|
1666
|
+
<div class="mock-pulse-ring" style="background-color: ${ringColor}33;"></div>
|
|
1667
|
+
<div class="mock-pulse-ring" style="background-color: ${ringColor}1a;"></div>
|
|
1668
|
+
`;
|
|
1669
|
+
} else {
|
|
1670
|
+
pulseRings.style.display = 'none';
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
function getSizeValue(size) {
|
|
1675
|
+
const sizes = { small: 48, medium: 60, large: 72, xl: 84 };
|
|
1676
|
+
return sizes[size] || 60;
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
function updateMockWidgetPosition() {
|
|
1680
|
+
const mockWidget = document.getElementById('mockWidget');
|
|
1681
|
+
if (!mockWidget) {
|
|
1682
|
+
console.warn('Mock widget not found, retrying...');
|
|
1683
|
+
setTimeout(updateMockWidgetPosition, 100);
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
const pos = widgetConfig.position;
|
|
1688
|
+
const offset = pos.offset || { x: 20, y: 20 };
|
|
1689
|
+
|
|
1690
|
+
// Clear any existing positioning
|
|
1691
|
+
mockWidget.style.top = '';
|
|
1692
|
+
mockWidget.style.bottom = '';
|
|
1693
|
+
mockWidget.style.left = '';
|
|
1694
|
+
mockWidget.style.right = '';
|
|
1695
|
+
|
|
1696
|
+
// Set position
|
|
1697
|
+
mockWidget.style.position = 'absolute';
|
|
1698
|
+
|
|
1699
|
+
// Vertical positioning - use config value
|
|
1700
|
+
if (pos.vertical === 'top') {
|
|
1701
|
+
mockWidget.style.setProperty('top', `${offset.y}px`, 'important');
|
|
1702
|
+
mockWidget.style.setProperty('bottom', 'auto', 'important');
|
|
1703
|
+
} else if (pos.vertical === 'bottom') {
|
|
1704
|
+
mockWidget.style.setProperty('bottom', `${offset.y}px`, 'important');
|
|
1705
|
+
mockWidget.style.setProperty('top', 'auto', 'important');
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
// Horizontal positioning - ALWAYS keep mock widget on the right side
|
|
1709
|
+
mockWidget.style.setProperty('right', `${offset.x}px`, 'important');
|
|
1710
|
+
mockWidget.style.setProperty('left', 'auto', 'important');
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
function renderPanelContent() {
|
|
1714
|
+
const mockPanel = document.getElementById('mockPanel');
|
|
1715
|
+
|
|
1716
|
+
// Apply panel styles
|
|
1717
|
+
mockPanel.style.width = widgetConfig.panel.width + 'px';
|
|
1718
|
+
mockPanel.style.height = widgetConfig.panel.height + 'px';
|
|
1719
|
+
mockPanel.style.borderRadius = widgetConfig.panel.borderRadius + 'px';
|
|
1720
|
+
// Panel background should match landing background to avoid double-frame effect
|
|
1721
|
+
const landingBg = widgetConfig.landing.backgroundColor || 'linear-gradient(180deg, #ffffff 0%, rgba(168, 85, 247, 0.03) 100%)';
|
|
1722
|
+
mockPanel.style.backgroundColor = landingBg;
|
|
1723
|
+
mockPanel.style.border = widgetConfig.panel.border;
|
|
1724
|
+
// Apply direction (LTR/RTL)
|
|
1725
|
+
mockPanel.style.direction = widgetConfig.direction || 'ltr';
|
|
1726
|
+
|
|
1727
|
+
// Render based on current view
|
|
1728
|
+
if (currentView === 'landing') {
|
|
1729
|
+
renderLandingScreen(mockPanel);
|
|
1730
|
+
} else if (currentView === 'text') {
|
|
1731
|
+
renderTextInterface(mockPanel);
|
|
1732
|
+
} else if (currentView === 'voice') {
|
|
1733
|
+
renderVoiceInterface(mockPanel);
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
// Add panel selector after rendering
|
|
1737
|
+
addPanelSelector();
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
function renderLandingScreen(panel) {
|
|
1741
|
+
const config = widgetConfig.landing;
|
|
1742
|
+
const iconBgColor = config.modeCardIconBackgroundColor || '#7C3AED';
|
|
1743
|
+
// Determine logo background - use logoBackgroundColor if enabled, otherwise transparent
|
|
1744
|
+
let logoBg = 'transparent';
|
|
1745
|
+
if (config.logoType === 'image' && config.logoBackgroundEnabled !== false) {
|
|
1746
|
+
logoBg = config.logoBackgroundColor || 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
|
|
1747
|
+
} else if (config.logoType === 'icon') {
|
|
1748
|
+
// For icon type, use the default avatar background
|
|
1749
|
+
logoBg = config.avatarBackground || 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
panel.innerHTML = `
|
|
1753
|
+
<div class="mock-panel-header" style="background: ${widgetConfig.header.backgroundColor}; color: ${widgetConfig.header.textColor}; direction: ${widgetConfig.direction || 'ltr'};" data-element-type="header">
|
|
1754
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
1755
|
+
<span>${widgetConfig.header.title}</span>
|
|
1756
|
+
<div style="display: flex; align-items: center; gap: 6px; margin-left: 8px;" data-element-type="onlineIndicator">
|
|
1757
|
+
<div style="width: 6px; height: 6px; background: ${widgetConfig.header.onlineIndicatorDotColor || '#10b981'}; border-radius: 50%; animation: pulse 2s ease-in-out infinite;"></div>
|
|
1758
|
+
<span style="font-size: 12px; opacity: 0.9; color: ${widgetConfig.header.onlineIndicatorColor || widgetConfig.header.textColor};">${widgetConfig.header.onlineIndicatorText || 'Online'}</span>
|
|
1759
|
+
</div>
|
|
1760
|
+
</div>
|
|
1761
|
+
${widgetConfig.header.showCloseButton ? '<button class="mock-panel-close" data-element-type="closeButton">×</button>' : ''}
|
|
1762
|
+
</div>
|
|
1763
|
+
<div class="mock-panel-content" style="direction: ${widgetConfig.direction || 'ltr'};">
|
|
1764
|
+
<div class="mock-landing-screen" style="background: ${config.backgroundColor || 'linear-gradient(180deg, #ffffff 0%, rgba(168, 85, 247, 0.03) 100%)'}; height: 100%;" data-element-type="landingBackground">
|
|
1765
|
+
<div class="mock-landing-logo" style="background: ${logoBg}; ${logoBg !== 'transparent' ? 'box-shadow: 0 8px 28px rgba(102, 126, 234, 0.35); border: none !important;' : 'box-shadow: none !important; border: none !important; outline: none !important;'}" data-element-type="landingLogo">
|
|
1766
|
+
${config.logoType === 'image' && config.logoImageUrl ? `
|
|
1767
|
+
<img src="${config.logoImageUrl}" alt="Logo" style="max-width: 44px; max-height: 44px; object-fit: contain;">
|
|
1768
|
+
` : `
|
|
1769
|
+
<span style="font-size: 44px; line-height: 1;">${config.logoIcon || config.logo || '🤖'}</span>
|
|
1770
|
+
`}
|
|
1771
|
+
</div>
|
|
1772
|
+
<div class="mock-landing-title" style="color: ${config.titleColor || '#1e1b4b'};" data-element-type="landingTitle">${config.title || 'Welcome to AI Assistant'}</div>
|
|
1773
|
+
<div class="mock-landing-subtitle" style="color: ${config.subtitleColor || '#64748b'};" data-element-type="landingSubtitle">${config.subtitle || 'Choose how you\'d like to interact'}</div>
|
|
1774
|
+
<div class="mock-mode-cards">
|
|
1775
|
+
<div class="mock-mode-card" style="background: ${config.modeCardBackgroundColor || '#ffffff'}; border: 1px solid ${config.modeCardBorderColor || 'rgba(0, 0, 0, 0.06)'};" data-element-type="modeCard" data-mode="voice">
|
|
1776
|
+
<div class="mock-mode-icon" style="background: ${iconBgColor}; color: #fff; box-shadow: 0 4px 14px rgba(124, 58, 237, 0.35);">
|
|
1777
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width: 26px; height: 26px;">
|
|
1778
|
+
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
|
|
1779
|
+
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
|
|
1780
|
+
<line x1="12" y1="19" x2="12" y2="23"/>
|
|
1781
|
+
<line x1="8" y1="23" x2="16" y2="23"/>
|
|
1782
|
+
</svg>
|
|
1783
|
+
</div>
|
|
1784
|
+
<div class="mock-mode-title" style="color: ${config.modeCardTitleColor || '#1e1b4b'};">${config.voiceCardTitle || 'Voice Call'}</div>
|
|
1785
|
+
</div>
|
|
1786
|
+
<div class="mock-mode-card" style="background: ${config.modeCardBackgroundColor || '#ffffff'}; border: 1px solid ${config.modeCardBorderColor || 'rgba(0, 0, 0, 0.06)'};" data-element-type="modeCard" data-mode="text">
|
|
1787
|
+
<div class="mock-mode-icon" style="background: ${iconBgColor}; color: #fff; box-shadow: 0 4px 14px rgba(124, 58, 237, 0.35);">
|
|
1788
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width: 26px; height: 26px;">
|
|
1789
|
+
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>
|
|
1790
|
+
</svg>
|
|
1791
|
+
</div>
|
|
1792
|
+
<div class="mock-mode-title" style="color: ${config.modeCardTitleColor || '#1e1b4b'};">${config.textCardTitle || 'Text Chat'}</div>
|
|
1793
|
+
</div>
|
|
1794
|
+
</div>
|
|
1795
|
+
</div>
|
|
1796
|
+
</div>
|
|
1797
|
+
<div class="mock-footer" style="padding: 10px 16px; text-align: center; border-top: 1px solid rgba(0,0,0,0.06); font-size: 11px; color: #64748b; background: #fff; flex-shrink: 0;">
|
|
1798
|
+
<span style="display: inline-flex; align-items: center; gap: 4px;">
|
|
1799
|
+
<span>⚡</span>
|
|
1800
|
+
<span>Powered by TalkToPC</span>
|
|
1801
|
+
</span>
|
|
1802
|
+
</div>
|
|
1803
|
+
`;
|
|
1804
|
+
|
|
1805
|
+
// Make elements selectable
|
|
1806
|
+
setupElementListeners(panel);
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
function renderTextInterface(panel) {
|
|
1810
|
+
const config = widgetConfig.text;
|
|
1811
|
+
panel.innerHTML = `
|
|
1812
|
+
<div class="mock-panel-header" style="background: ${widgetConfig.header.backgroundColor}; color: ${widgetConfig.header.textColor}; direction: ${widgetConfig.direction || 'ltr'};" data-element-type="header">
|
|
1813
|
+
<span>${widgetConfig.header.title}</span>
|
|
1814
|
+
${widgetConfig.header.showCloseButton ? '<button class="mock-panel-close" data-element-type="closeButton" onclick="switchView(\'landing\')">×</button>' : ''}
|
|
1815
|
+
</div>
|
|
1816
|
+
<div class="mock-text-interface" style="direction: ${widgetConfig.direction || 'ltr'};">
|
|
1817
|
+
<div class="mock-messages">
|
|
1818
|
+
<div class="mock-message user" data-element-type="userMessage">
|
|
1819
|
+
<div class="mock-message-avatar">${widgetConfig.messages.userAvatarIcon || '👤'}</div>
|
|
1820
|
+
<div class="mock-message-bubble" style="background: ${widgetConfig.messages.userBackgroundColor}; color: ${widgetConfig.messages.userTextColor || widgetConfig.messages.textColor || '#1F2937'}; border-radius: ${widgetConfig.messages.borderRadius}px; font-size: ${widgetConfig.messages.fontSize};">
|
|
1821
|
+
Hello! How can I help you?
|
|
1822
|
+
</div>
|
|
1823
|
+
</div>
|
|
1824
|
+
<div class="mock-message agent" data-element-type="agentMessage">
|
|
1825
|
+
<div class="mock-message-avatar">${widgetConfig.messages.agentAvatarIcon || '🤖'}</div>
|
|
1826
|
+
<div class="mock-message-bubble" style="background: ${widgetConfig.messages.agentBackgroundColor}; color: ${widgetConfig.messages.agentTextColor || widgetConfig.messages.textColor || '#1F2937'}; border-radius: ${widgetConfig.messages.borderRadius}px; font-size: ${widgetConfig.messages.fontSize};">
|
|
1827
|
+
Hi! I'm here to assist you. What would you like to know?
|
|
1828
|
+
</div>
|
|
1829
|
+
</div>
|
|
1830
|
+
<div class="mock-message user" data-element-type="userMessage">
|
|
1831
|
+
<div class="mock-message-avatar">${widgetConfig.messages.userAvatarIcon || '👤'}</div>
|
|
1832
|
+
<div class="mock-message-bubble" style="background: ${widgetConfig.messages.userBackgroundColor}; color: ${widgetConfig.messages.userTextColor || widgetConfig.messages.textColor || '#1F2937'}; border-radius: ${widgetConfig.messages.borderRadius}px; font-size: ${widgetConfig.messages.fontSize};">
|
|
1833
|
+
Can you tell me about your features?
|
|
1834
|
+
</div>
|
|
1835
|
+
</div>
|
|
1836
|
+
</div>
|
|
1837
|
+
<div class="mock-input-area">
|
|
1838
|
+
<input type="text" class="mock-input" placeholder="${config.inputPlaceholder}" style="border-color: ${config.inputFocusColor};" data-element-type="input">
|
|
1839
|
+
<button class="mock-send-button" style="background: ${config.sendButtonColor};" data-element-type="sendButton">${config.sendButtonText || '→'}</button>
|
|
1840
|
+
</div>
|
|
1841
|
+
</div>
|
|
1842
|
+
`;
|
|
1843
|
+
|
|
1844
|
+
// Make elements selectable
|
|
1845
|
+
setupElementListeners(panel);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
function renderVoiceInterface(panel) {
|
|
1849
|
+
const config = widgetConfig.voice;
|
|
1850
|
+
// Show active call state matching real widget structure
|
|
1851
|
+
panel.innerHTML = `
|
|
1852
|
+
<div class="mock-panel-header" style="background: ${widgetConfig.header.backgroundColor}; color: ${widgetConfig.header.textColor}; direction: ${widgetConfig.direction || 'ltr'};" data-element-type="header">
|
|
1853
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
1854
|
+
<span>${widgetConfig.header.title}</span>
|
|
1855
|
+
<div style="display: flex; align-items: center; gap: 6px; margin-left: 8px;" data-element-type="onlineIndicator">
|
|
1856
|
+
<div style="width: 6px; height: 6px; background: ${widgetConfig.header.onlineIndicatorDotColor || '#10b981'}; border-radius: 50%;"></div>
|
|
1857
|
+
<span style="font-size: 12px; opacity: 0.9; color: ${widgetConfig.header.onlineIndicatorColor || widgetConfig.header.textColor};">${widgetConfig.header.onlineIndicatorText || 'Online'}</span>
|
|
1858
|
+
</div>
|
|
1859
|
+
</div>
|
|
1860
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
1861
|
+
<button style="background: none; border: none; color: ${widgetConfig.header.textColor}; cursor: pointer; padding: 4px; display: flex; align-items: center;" data-element-type="backButton">←</button>
|
|
1862
|
+
${widgetConfig.header.showCloseButton ? '<button class="mock-panel-close" data-element-type="closeButton">×</button>' : ''}
|
|
1863
|
+
</div>
|
|
1864
|
+
</div>
|
|
1865
|
+
<div class="mock-voice-interface" style="direction: ${widgetConfig.direction || 'ltr'};">
|
|
1866
|
+
<!-- Voice Section -->
|
|
1867
|
+
<!-- Multi-row layout when history is collapsed -->
|
|
1868
|
+
<div class="mock-voice-section" id="mockVoiceSectionExpanded">
|
|
1869
|
+
<div class="mock-voice-timer" data-element-type="timer">
|
|
1870
|
+
<div class="mock-timer-dot"></div>
|
|
1871
|
+
<span>00:11</span>
|
|
1872
|
+
</div>
|
|
1873
|
+
<div class="mock-waveform" data-element-type="waveform">
|
|
1874
|
+
${widgetConfig.voice.waveformType === 'waveform' ? `
|
|
1875
|
+
<div class="mock-waveform-bar"></div>
|
|
1876
|
+
<div class="mock-waveform-bar"></div>
|
|
1877
|
+
<div class="mock-waveform-bar"></div>
|
|
1878
|
+
<div class="mock-waveform-bar"></div>
|
|
1879
|
+
<div class="mock-waveform-bar"></div>
|
|
1880
|
+
<div class="mock-waveform-bar"></div>
|
|
1881
|
+
<div class="mock-waveform-bar"></div>
|
|
1882
|
+
<div class="mock-waveform-bar"></div>
|
|
1883
|
+
<div class="mock-waveform-bar"></div>
|
|
1884
|
+
<div class="mock-waveform-bar"></div>
|
|
1885
|
+
<div class="mock-waveform-bar"></div>
|
|
1886
|
+
<div class="mock-waveform-bar"></div>
|
|
1887
|
+
<div class="mock-waveform-bar"></div>
|
|
1888
|
+
<div class="mock-waveform-bar"></div>
|
|
1889
|
+
<div class="mock-waveform-bar"></div>
|
|
1890
|
+
` : widgetConfig.voice.waveformType === 'icon' ? `
|
|
1891
|
+
<div class="mock-waveform-icon" style="font-size: 48px; line-height: 1;">${widgetConfig.voice.waveformIcon || '🎤'}</div>
|
|
1892
|
+
` : widgetConfig.voice.waveformType === 'image' && widgetConfig.voice.waveformImageUrl ? `
|
|
1893
|
+
<img src="${widgetConfig.voice.waveformImageUrl}" alt="Waveform" class="mock-waveform-image" style="max-width: 60px; max-height: 60px; object-fit: contain;">
|
|
1894
|
+
` : `
|
|
1895
|
+
<div class="mock-waveform-bar"></div>
|
|
1896
|
+
<div class="mock-waveform-bar"></div>
|
|
1897
|
+
<div class="mock-waveform-bar"></div>
|
|
1898
|
+
<div class="mock-waveform-bar"></div>
|
|
1899
|
+
<div class="mock-waveform-bar"></div>
|
|
1900
|
+
<div class="mock-waveform-bar"></div>
|
|
1901
|
+
<div class="mock-waveform-bar"></div>
|
|
1902
|
+
<div class="mock-waveform-bar"></div>
|
|
1903
|
+
<div class="mock-waveform-bar"></div>
|
|
1904
|
+
<div class="mock-waveform-bar"></div>
|
|
1905
|
+
<div class="mock-waveform-bar"></div>
|
|
1906
|
+
<div class="mock-waveform-bar"></div>
|
|
1907
|
+
<div class="mock-waveform-bar"></div>
|
|
1908
|
+
<div class="mock-waveform-bar"></div>
|
|
1909
|
+
<div class="mock-waveform-bar"></div>
|
|
1910
|
+
`}
|
|
1911
|
+
</div>
|
|
1912
|
+
<div class="mock-voice-status" data-element-type="statusTitle">
|
|
1913
|
+
<div class="mock-status-dot"></div>
|
|
1914
|
+
<span>Listening...</span>
|
|
1915
|
+
</div>
|
|
1916
|
+
<div class="mock-voice-controls">
|
|
1917
|
+
<button class="mock-control-btn secondary" data-element-type="micButton" title="Mute" style="background: ${config.micButtonColor || '#FFFFFF'};">
|
|
1918
|
+
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
1919
|
+
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
|
|
1920
|
+
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
|
|
1921
|
+
<line x1="12" y1="19" x2="12" y2="23"/>
|
|
1922
|
+
</svg>
|
|
1923
|
+
</button>
|
|
1924
|
+
<button class="mock-control-btn danger" data-element-type="endCallButton" title="End Call" style="background: ${config.endCallButtonColor || '#ef4444'};">
|
|
1925
|
+
<svg fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24">
|
|
1926
|
+
<path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6 19.79 19.79 0 01-3.07-8.67A2 2 0 014.11 2h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z" transform="rotate(135 12 12)"/>
|
|
1927
|
+
</svg>
|
|
1928
|
+
</button>
|
|
1929
|
+
<button class="mock-control-btn secondary" data-element-type="speakerButton" title="Speaker" style="background: ${config.speakerButtonColor || '#FFFFFF'};">
|
|
1930
|
+
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
1931
|
+
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
|
|
1932
|
+
<path d="M19.07 4.93a10 10 0 010 14.14M15.54 8.46a5 5 0 010 7.07"/>
|
|
1933
|
+
</svg>
|
|
1934
|
+
</button>
|
|
1935
|
+
</div>
|
|
1936
|
+
</div>
|
|
1937
|
+
|
|
1938
|
+
<!-- Single-row compact layout when history is expanded -->
|
|
1939
|
+
<div class="mock-voice-section-compact" id="mockVoiceSectionCompact" style="display: none;">
|
|
1940
|
+
<div class="mock-compact-row">
|
|
1941
|
+
<div class="mock-compact-waveform" data-element-type="waveform">
|
|
1942
|
+
${widgetConfig.voice.waveformType === 'waveform' ? `
|
|
1943
|
+
<div class="mock-waveform-bar"></div>
|
|
1944
|
+
<div class="mock-waveform-bar"></div>
|
|
1945
|
+
<div class="mock-waveform-bar"></div>
|
|
1946
|
+
<div class="mock-waveform-bar"></div>
|
|
1947
|
+
<div class="mock-waveform-bar"></div>
|
|
1948
|
+
<div class="mock-waveform-bar"></div>
|
|
1949
|
+
<div class="mock-waveform-bar"></div>
|
|
1950
|
+
<div class="mock-waveform-bar"></div>
|
|
1951
|
+
<div class="mock-waveform-bar"></div>
|
|
1952
|
+
<div class="mock-waveform-bar"></div>
|
|
1953
|
+
<div class="mock-waveform-bar"></div>
|
|
1954
|
+
<div class="mock-waveform-bar"></div>
|
|
1955
|
+
<div class="mock-waveform-bar"></div>
|
|
1956
|
+
<div class="mock-waveform-bar"></div>
|
|
1957
|
+
<div class="mock-waveform-bar"></div>
|
|
1958
|
+
` : widgetConfig.voice.waveformType === 'icon' ? `
|
|
1959
|
+
<div class="mock-waveform-icon" style="font-size: 24px; line-height: 1;">${widgetConfig.voice.waveformIcon || '🎤'}</div>
|
|
1960
|
+
` : widgetConfig.voice.waveformType === 'image' && widgetConfig.voice.waveformImageUrl ? `
|
|
1961
|
+
<img src="${widgetConfig.voice.waveformImageUrl}" alt="Waveform" class="mock-waveform-image" style="max-width: 32px; max-height: 32px; object-fit: contain;">
|
|
1962
|
+
` : `
|
|
1963
|
+
<div class="mock-waveform-bar"></div>
|
|
1964
|
+
<div class="mock-waveform-bar"></div>
|
|
1965
|
+
<div class="mock-waveform-bar"></div>
|
|
1966
|
+
<div class="mock-waveform-bar"></div>
|
|
1967
|
+
<div class="mock-waveform-bar"></div>
|
|
1968
|
+
<div class="mock-waveform-bar"></div>
|
|
1969
|
+
<div class="mock-waveform-bar"></div>
|
|
1970
|
+
<div class="mock-waveform-bar"></div>
|
|
1971
|
+
<div class="mock-waveform-bar"></div>
|
|
1972
|
+
<div class="mock-waveform-bar"></div>
|
|
1973
|
+
<div class="mock-waveform-bar"></div>
|
|
1974
|
+
<div class="mock-waveform-bar"></div>
|
|
1975
|
+
<div class="mock-waveform-bar"></div>
|
|
1976
|
+
<div class="mock-waveform-bar"></div>
|
|
1977
|
+
<div class="mock-waveform-bar"></div>
|
|
1978
|
+
`}
|
|
1979
|
+
</div>
|
|
1980
|
+
<div class="mock-compact-timer" data-element-type="timer">
|
|
1981
|
+
<div class="mock-timer-dot"></div>
|
|
1982
|
+
<span>00:11</span>
|
|
1983
|
+
</div>
|
|
1984
|
+
<div class="mock-compact-status" data-element-type="statusTitle">
|
|
1985
|
+
<div class="mock-status-dot"></div>
|
|
1986
|
+
<span>Listening...</span>
|
|
1987
|
+
</div>
|
|
1988
|
+
<div class="mock-compact-controls">
|
|
1989
|
+
<button class="mock-control-btn secondary" data-element-type="micButton" title="Mute" style="background: ${config.micButtonColor || '#FFFFFF'};">
|
|
1990
|
+
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
1991
|
+
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
|
|
1992
|
+
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
|
|
1993
|
+
<line x1="12" y1="19" x2="12" y2="23"/>
|
|
1994
|
+
</svg>
|
|
1995
|
+
</button>
|
|
1996
|
+
<button class="mock-control-btn danger" data-element-type="endCallButton" title="End Call">
|
|
1997
|
+
<svg fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24">
|
|
1998
|
+
<path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6 19.79 19.79 0 01-3.07-8.67A2 2 0 014.11 2h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z" transform="rotate(135 12 12)"/>
|
|
1999
|
+
</svg>
|
|
2000
|
+
</button>
|
|
2001
|
+
<button class="mock-control-btn secondary" data-element-type="speakerButton" title="Speaker">
|
|
2002
|
+
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
2003
|
+
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
|
|
2004
|
+
<path d="M19.07 4.93a10 10 0 010 14.14M15.54 8.46a5 5 0 010 7.07"/>
|
|
2005
|
+
</svg>
|
|
2006
|
+
</button>
|
|
2007
|
+
</div>
|
|
2008
|
+
</div>
|
|
2009
|
+
</div>
|
|
2010
|
+
|
|
2011
|
+
<!-- Conversation Section -->
|
|
2012
|
+
<div class="mock-conversation-section">
|
|
2013
|
+
<div class="mock-conversation-header">
|
|
2014
|
+
<span>CONVERSATION</span>
|
|
2015
|
+
<div class="mock-conversation-toggle" data-element-type="conversationToggle">
|
|
2016
|
+
<span id="historyToggleText">Show history</span>
|
|
2017
|
+
<svg id="historyToggleIcon" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
2018
|
+
<path d="M6 9l6 6 6-6"/>
|
|
2019
|
+
</svg>
|
|
2020
|
+
</div>
|
|
2021
|
+
</div>
|
|
2022
|
+
|
|
2023
|
+
<!-- Collapsed: Live transcript only (no bubbles) -->
|
|
2024
|
+
<div class="mock-live-transcript-collapsed" id="collapsedTranscript" data-element-type="liveTranscript">
|
|
2025
|
+
<div class="mock-live-indicator" data-element-type="liveIndicator" style="color: ${config.liveIndicatorTextColor || '#10b981'};">
|
|
2026
|
+
<div class="mock-live-dot" style="background: ${config.liveIndicatorDotColor || '#10b981'};"></div>
|
|
2027
|
+
<span>LIVE</span>
|
|
2028
|
+
</div>
|
|
2029
|
+
<div class="mock-live-text-collapsed" data-element-type="liveTranscriptText" style="color: ${config.liveTranscriptTextColor || '#64748b'}; font-size: ${config.liveTranscriptFontSize || '14px'}; line-height: 1.6; margin-top: 8px;">
|
|
2030
|
+
Hello, I'm Sasha from Bridgewise, How can I help you today?
|
|
2031
|
+
</div>
|
|
2032
|
+
</div>
|
|
2033
|
+
|
|
2034
|
+
<!-- Expanded: Full conversation history -->
|
|
2035
|
+
<div class="mock-conversation-history" id="expandedHistory">
|
|
2036
|
+
<div class="mock-history-message">
|
|
2037
|
+
<div class="mock-history-avatar" data-element-type="agentAvatar" style="cursor: pointer;">
|
|
2038
|
+
${widgetConfig.voice.avatarType === 'image' && widgetConfig.voice.avatarImageUrl ? `
|
|
2039
|
+
<img src="${widgetConfig.voice.avatarImageUrl}" alt="Agent" style="width: 100%; height: 100%; object-fit: cover; border-radius: 50%; pointer-events: none;">
|
|
2040
|
+
` : `
|
|
2041
|
+
<span style="pointer-events: none;">${widgetConfig.voice.avatarIcon || '🤖'}</span>
|
|
2042
|
+
`}
|
|
2043
|
+
</div>
|
|
2044
|
+
<div class="mock-history-bubble">I help you today?</div>
|
|
2045
|
+
</div>
|
|
2046
|
+
<div class="mock-history-message">
|
|
2047
|
+
<div class="mock-history-avatar" data-element-type="agentAvatar" style="cursor: pointer;">
|
|
2048
|
+
${widgetConfig.voice.avatarType === 'image' && widgetConfig.voice.avatarImageUrl ? `
|
|
2049
|
+
<img src="${widgetConfig.voice.avatarImageUrl}" alt="Agent" style="width: 100%; height: 100%; object-fit: cover; border-radius: 50%; pointer-events: none;">
|
|
2050
|
+
` : `
|
|
2051
|
+
<span style="pointer-events: none;">${widgetConfig.voice.avatarIcon || '🤖'}</span>
|
|
2052
|
+
`}
|
|
2053
|
+
</div>
|
|
2054
|
+
<div class="mock-history-bubble">I am doing well, thank you for asking.</div>
|
|
2055
|
+
</div>
|
|
2056
|
+
<div class="mock-live-message-row">
|
|
2057
|
+
<div class="mock-history-avatar" data-element-type="agentAvatar" style="cursor: pointer;">
|
|
2058
|
+
${widgetConfig.voice.avatarType === 'image' && widgetConfig.voice.avatarImageUrl ? `
|
|
2059
|
+
<img src="${widgetConfig.voice.avatarImageUrl}" alt="Agent" style="width: 100%; height: 100%; object-fit: cover; border-radius: 50%; pointer-events: none;">
|
|
2060
|
+
` : `
|
|
2061
|
+
<span style="pointer-events: none;">${widgetConfig.voice.avatarIcon || '🤖'}</span>
|
|
2062
|
+
`}
|
|
2063
|
+
</div>
|
|
2064
|
+
<div class="mock-history-bubble">
|
|
2065
|
+
<span class="mock-live-badge">LIVE</span>
|
|
2066
|
+
How may I assist you with the website today?
|
|
2067
|
+
</div>
|
|
2068
|
+
</div>
|
|
2069
|
+
</div>
|
|
2070
|
+
</div>
|
|
2071
|
+
|
|
2072
|
+
<!-- Text Input Area -->
|
|
2073
|
+
<div class="mock-voice-input-area">
|
|
2074
|
+
<div class="mock-voice-input-wrapper">
|
|
2075
|
+
<input type="text" class="mock-voice-text-input" placeholder="${widgetConfig.text.inputPlaceholder || 'Type your message...'}" data-element-type="voiceInput">
|
|
2076
|
+
<button class="mock-voice-send-btn" style="background: ${widgetConfig.text.sendButtonColor};" data-element-type="voiceSendButton">
|
|
2077
|
+
<svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
2078
|
+
<line x1="22" y1="2" x2="11" y2="13"/>
|
|
2079
|
+
<polygon points="22 2 15 22 11 13 2 9 22 2"/>
|
|
2080
|
+
</svg>
|
|
2081
|
+
</button>
|
|
2082
|
+
</div>
|
|
2083
|
+
</div>
|
|
2084
|
+
|
|
2085
|
+
<!-- Powered By -->
|
|
2086
|
+
<div class="mock-powered-by">
|
|
2087
|
+
<span>⚡</span>
|
|
2088
|
+
<span>Powered by <strong>TalkToPC</strong></span>
|
|
2089
|
+
</div>
|
|
2090
|
+
</div>
|
|
2091
|
+
`;
|
|
2092
|
+
|
|
2093
|
+
// Make elements selectable
|
|
2094
|
+
setupElementListeners(panel);
|
|
2095
|
+
|
|
2096
|
+
// Initialize history view state
|
|
2097
|
+
setTimeout(() => {
|
|
2098
|
+
updateHistoryView();
|
|
2099
|
+
}, 0);
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
function setupElementListeners(panel) {
|
|
2103
|
+
// Get all elements with data-element-type, sorted by depth (deepest first)
|
|
2104
|
+
const elements = Array.from(panel.querySelectorAll('[data-element-type]'));
|
|
2105
|
+
// Sort by depth - deeper elements first so we handle children before parents
|
|
2106
|
+
elements.sort((a, b) => {
|
|
2107
|
+
const depthA = (a.parentElement.closest('[data-element-type]') ? 1 : 0) + (a.querySelectorAll('[data-element-type]').length);
|
|
2108
|
+
const depthB = (b.parentElement.closest('[data-element-type]') ? 1 : 0) + (b.querySelectorAll('[data-element-type]').length);
|
|
2109
|
+
return depthB - depthA;
|
|
2110
|
+
});
|
|
2111
|
+
|
|
2112
|
+
elements.forEach(el => {
|
|
2113
|
+
// Check if this element is inside another element with data-element-type (child elements)
|
|
2114
|
+
const parentWithType = el.parentElement.closest('[data-element-type]');
|
|
2115
|
+
const isChild = parentWithType && parentWithType !== panel;
|
|
2116
|
+
|
|
2117
|
+
if (isChild) {
|
|
2118
|
+
// This is a child element (e.g., modeCard inside landingBackground)
|
|
2119
|
+
// Attach listener directly to the element
|
|
2120
|
+
el.addEventListener('click', (e) => {
|
|
2121
|
+
// Find the actual element with data-element-type (in case click is on child like icon/title)
|
|
2122
|
+
let targetElement = e.target;
|
|
2123
|
+
let foundElement = null;
|
|
2124
|
+
|
|
2125
|
+
// Walk up the DOM tree to find the element with data-element-type
|
|
2126
|
+
while (targetElement && targetElement !== panel && targetElement !== document.body) {
|
|
2127
|
+
if (targetElement.hasAttribute && targetElement.hasAttribute('data-element-type')) {
|
|
2128
|
+
foundElement = targetElement;
|
|
2129
|
+
break;
|
|
2130
|
+
}
|
|
2131
|
+
targetElement = targetElement.parentElement;
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
// Use found element or fallback to the element we're attaching to
|
|
2135
|
+
const elementToUse = foundElement || el;
|
|
2136
|
+
const elementType = elementToUse.getAttribute('data-element-type');
|
|
2137
|
+
|
|
2138
|
+
console.log('✅ Child element clicked:', elementType, elementToUse.getAttribute('data-mode'), 'clicked on:', e.target.tagName, e.target.className);
|
|
2139
|
+
|
|
2140
|
+
// Don't stop propagation immediately - let handleElementClick process it
|
|
2141
|
+
handleElementClick(elementType, elementToUse, e);
|
|
2142
|
+
|
|
2143
|
+
// Only prevent default and stop propagation after handling
|
|
2144
|
+
e.preventDefault();
|
|
2145
|
+
e.stopPropagation();
|
|
2146
|
+
}, true); // Use capture phase - runs before bubbling phase, so it runs BEFORE parent handlers
|
|
2147
|
+
} else {
|
|
2148
|
+
// This is a top-level element (e.g., landingBackground)
|
|
2149
|
+
el.addEventListener('click', (e) => {
|
|
2150
|
+
// IMPORTANT: Check if click is inside ANY child element with data-element-type
|
|
2151
|
+
// Walk up from the target to see if we hit a child element with data-element-type
|
|
2152
|
+
let current = e.target;
|
|
2153
|
+
while (current && current !== el && current !== panel && current !== document.body) {
|
|
2154
|
+
// Check if this element has data-element-type and is a child of el
|
|
2155
|
+
if (current.hasAttribute && current.hasAttribute('data-element-type')) {
|
|
2156
|
+
if (current !== el && el.contains(current)) {
|
|
2157
|
+
// Found a child element with data-element-type - let it handle the click
|
|
2158
|
+
console.log('⏭️ Parent ignoring click - child element found:', current.dataset.elementType);
|
|
2159
|
+
return; // Don't handle - let the child handle it
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
current = current.parentElement;
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
// Also check: if the target itself is inside a child element with data-element-type
|
|
2166
|
+
const closestChild = Array.from(el.querySelectorAll('[data-element-type]')).find(child => {
|
|
2167
|
+
return child !== el && child.contains(e.target);
|
|
2168
|
+
});
|
|
2169
|
+
if (closestChild) {
|
|
2170
|
+
console.log('⏭️ Parent ignoring click - target is inside child:', closestChild.dataset.elementType);
|
|
2171
|
+
return;
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
// Only handle clicks directly on this element (not on any child elements)
|
|
2175
|
+
console.log('✅ Parent element clicked directly:', el.dataset.elementType, 'target:', e.target);
|
|
2176
|
+
e.preventDefault();
|
|
2177
|
+
e.stopPropagation();
|
|
2178
|
+
handleElementClick(el.dataset.elementType, el, e);
|
|
2179
|
+
}, false); // Use bubbling phase so child handlers (capture phase) run first
|
|
2180
|
+
}
|
|
2181
|
+
});
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
function switchView(view) {
|
|
2185
|
+
currentView = view;
|
|
2186
|
+
renderPanelContent();
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
// Make switchView and selectElement available globally
|
|
2190
|
+
window.switchView = switchView;
|
|
2191
|
+
window.selectElement = selectElement;
|
|
2192
|
+
|
|
2193
|
+
function selectElement(elementType, element, event) {
|
|
2194
|
+
// Prevent default action
|
|
2195
|
+
if (event) {
|
|
2196
|
+
event.preventDefault();
|
|
2197
|
+
event.stopPropagation();
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
// Remove previous selection
|
|
2201
|
+
document.querySelectorAll('.element-highlight').forEach(el => {
|
|
2202
|
+
el.classList.remove('element-highlight');
|
|
2203
|
+
});
|
|
2204
|
+
|
|
2205
|
+
// Highlight selected element
|
|
2206
|
+
element.classList.add('element-highlight');
|
|
2207
|
+
selectedElement = { type: elementType, element };
|
|
2208
|
+
|
|
2209
|
+
// Show customization controls
|
|
2210
|
+
showCustomizationControls(elementType);
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
function handleElementClick(elementType, element, event) {
|
|
2214
|
+
// If event was prevented/stopped, don't process
|
|
2215
|
+
if (event && event.defaultPrevented) {
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
const currentTime = Date.now();
|
|
2220
|
+
const timeSinceLastClick = currentTime - lastClickTime;
|
|
2221
|
+
|
|
2222
|
+
// Check if this is a double click (within 400ms and same element type)
|
|
2223
|
+
// Compare by element reference, not just type, to ensure it's the same element
|
|
2224
|
+
if (timeSinceLastClick < 400 && lastClickElement && elementType === lastClickType &&
|
|
2225
|
+
lastClickElement === element) {
|
|
2226
|
+
// Double click detected - perform normal interaction
|
|
2227
|
+
console.log('🖱️ Double click detected on:', elementType);
|
|
2228
|
+
lastClickTime = 0;
|
|
2229
|
+
lastClickElement = null;
|
|
2230
|
+
lastClickType = null;
|
|
2231
|
+
performNormalInteraction(elementType, element, event);
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
// Single click - wait to see if there's a second click
|
|
2236
|
+
console.log('🖱️ Single click detected on:', elementType, 'waiting for potential double click...');
|
|
2237
|
+
lastClickTime = currentTime;
|
|
2238
|
+
lastClickElement = element;
|
|
2239
|
+
lastClickType = elementType;
|
|
2240
|
+
|
|
2241
|
+
setTimeout(() => {
|
|
2242
|
+
// If no second click happened, treat as single click
|
|
2243
|
+
if (lastClickTime === currentTime && lastClickElement === element) {
|
|
2244
|
+
console.log('🖱️ Single click confirmed on:', elementType);
|
|
2245
|
+
lastClickTime = 0;
|
|
2246
|
+
lastClickElement = null;
|
|
2247
|
+
lastClickType = null;
|
|
2248
|
+
selectElement(elementType, element, event);
|
|
2249
|
+
}
|
|
2250
|
+
}, 400);
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
function performNormalInteraction(elementType, element, event) {
|
|
2254
|
+
if (elementType === 'closeButton' || elementType === 'endCallButton' || elementType === 'backButton') {
|
|
2255
|
+
// End call or close should go back to landing screen
|
|
2256
|
+
switchView('landing');
|
|
2257
|
+
} else if (elementType === 'modeCard') {
|
|
2258
|
+
const mode = element.getAttribute('data-mode');
|
|
2259
|
+
if (mode === 'voice') {
|
|
2260
|
+
switchView('voice');
|
|
2261
|
+
} else if (mode === 'text') {
|
|
2262
|
+
switchView('text');
|
|
2263
|
+
}
|
|
2264
|
+
} else if (elementType === 'button') {
|
|
2265
|
+
// Toggle panel
|
|
2266
|
+
panelOpen = !panelOpen;
|
|
2267
|
+
const panel = document.getElementById('mockPanel');
|
|
2268
|
+
panel.classList.toggle('open');
|
|
2269
|
+
if (panelOpen) {
|
|
2270
|
+
renderPanelContent();
|
|
2271
|
+
}
|
|
2272
|
+
// Update prompt bubble visibility based on panel state
|
|
2273
|
+
const mockButton = document.getElementById('mockButton');
|
|
2274
|
+
if (mockButton) {
|
|
2275
|
+
updatePromptBubble(mockButton);
|
|
2276
|
+
}
|
|
2277
|
+
// Sync actual widget state with mock panel
|
|
2278
|
+
syncWidgetWithMockPanel();
|
|
2279
|
+
} else if (elementType === 'conversationToggle') {
|
|
2280
|
+
// Toggle conversation history
|
|
2281
|
+
toggleConversationHistory();
|
|
2282
|
+
}
|
|
2283
|
+
// For other elements, allow normal behavior
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
function updateHistoryView() {
|
|
2287
|
+
const collapsed = document.getElementById('collapsedTranscript');
|
|
2288
|
+
const expanded = document.getElementById('expandedHistory');
|
|
2289
|
+
const toggleText = document.getElementById('historyToggleText');
|
|
2290
|
+
const toggleIcon = document.getElementById('historyToggleIcon');
|
|
2291
|
+
const expandedSection = document.getElementById('mockVoiceSectionExpanded');
|
|
2292
|
+
const compactSection = document.getElementById('mockVoiceSectionCompact');
|
|
2293
|
+
|
|
2294
|
+
console.log('updateHistoryView called, historyExpanded:', historyExpanded);
|
|
2295
|
+
console.log('Elements found:', { collapsed: !!collapsed, expanded: !!expanded, toggleText: !!toggleText, toggleIcon: !!toggleIcon, expandedSection: !!expandedSection, compactSection: !!compactSection });
|
|
2296
|
+
|
|
2297
|
+
if (!collapsed || !expanded || !toggleText || !toggleIcon) {
|
|
2298
|
+
// Elements not found yet, try again after a short delay
|
|
2299
|
+
console.log('Elements not found, retrying...');
|
|
2300
|
+
setTimeout(updateHistoryView, 100);
|
|
2301
|
+
return;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
if (historyExpanded) {
|
|
2305
|
+
console.log('Expanding history...');
|
|
2306
|
+
// Hide collapsed transcript
|
|
2307
|
+
collapsed.style.display = 'none';
|
|
2308
|
+
// Show expanded history
|
|
2309
|
+
expanded.style.display = 'flex';
|
|
2310
|
+
expanded.classList.add('expanded');
|
|
2311
|
+
// Switch to compact single-row layout
|
|
2312
|
+
if (expandedSection) expandedSection.style.display = 'none';
|
|
2313
|
+
if (compactSection) compactSection.style.display = 'block';
|
|
2314
|
+
// Update toggle text and icon
|
|
2315
|
+
toggleText.textContent = 'Hide history';
|
|
2316
|
+
const path = toggleIcon.querySelector('path');
|
|
2317
|
+
if (path) {
|
|
2318
|
+
path.setAttribute('d', 'M18 15l-6-6-6 6');
|
|
2319
|
+
} else {
|
|
2320
|
+
toggleIcon.innerHTML = '<path d="M18 15l-6-6-6 6" stroke="currentColor" stroke-width="2" fill="none"/>';
|
|
2321
|
+
}
|
|
2322
|
+
} else {
|
|
2323
|
+
console.log('Collapsing history...');
|
|
2324
|
+
// Show collapsed transcript
|
|
2325
|
+
collapsed.style.display = 'block';
|
|
2326
|
+
// Hide expanded history
|
|
2327
|
+
expanded.style.display = 'none';
|
|
2328
|
+
expanded.classList.remove('expanded');
|
|
2329
|
+
// Switch to multi-row expanded layout
|
|
2330
|
+
if (expandedSection) expandedSection.style.display = 'block';
|
|
2331
|
+
if (compactSection) compactSection.style.display = 'none';
|
|
2332
|
+
// Update toggle text and icon
|
|
2333
|
+
toggleText.textContent = 'Show history';
|
|
2334
|
+
const path = toggleIcon.querySelector('path');
|
|
2335
|
+
if (path) {
|
|
2336
|
+
path.setAttribute('d', 'M6 9l6 6 6-6');
|
|
2337
|
+
} else {
|
|
2338
|
+
toggleIcon.innerHTML = '<path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" fill="none"/>';
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
function toggleConversationHistory() {
|
|
2344
|
+
historyExpanded = !historyExpanded;
|
|
2345
|
+
console.log('toggleConversationHistory called, new state:', historyExpanded);
|
|
2346
|
+
updateHistoryView();
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
function showCustomizationControls(elementType) {
|
|
2350
|
+
const controlsDiv = document.getElementById('customizationControls');
|
|
2351
|
+
let controlsHTML = '';
|
|
2352
|
+
|
|
2353
|
+
switch(elementType) {
|
|
2354
|
+
case 'button':
|
|
2355
|
+
controlsHTML = `
|
|
2356
|
+
<div class="customization-group">
|
|
2357
|
+
<h3>Floating Button</h3>
|
|
2358
|
+
<div class="control-item">
|
|
2359
|
+
<label>Size</label>
|
|
2360
|
+
<select id="btnSize">
|
|
2361
|
+
<option value="small">Small</option>
|
|
2362
|
+
<option value="medium" selected>Medium</option>
|
|
2363
|
+
<option value="large">Large</option>
|
|
2364
|
+
<option value="xl">Extra Large</option>
|
|
2365
|
+
</select>
|
|
2366
|
+
</div>
|
|
2367
|
+
<div class="control-item">
|
|
2368
|
+
<label>Shape</label>
|
|
2369
|
+
<select id="btnShape">
|
|
2370
|
+
<option value="circle" selected>Circle</option>
|
|
2371
|
+
<option value="rounded">Rounded</option>
|
|
2372
|
+
<option value="square">Square</option>
|
|
2373
|
+
</select>
|
|
2374
|
+
</div>
|
|
2375
|
+
<div class="control-item">
|
|
2376
|
+
<label>Background Color</label>
|
|
2377
|
+
<input type="color" id="btnBgColor" value="${widgetConfig.button.backgroundColor}">
|
|
2378
|
+
</div>
|
|
2379
|
+
<div class="control-item">
|
|
2380
|
+
<label>Hover Color</label>
|
|
2381
|
+
<input type="color" id="btnHoverColor" value="${widgetConfig.button.hoverColor}">
|
|
2382
|
+
</div>
|
|
2383
|
+
</div>
|
|
2384
|
+
<div class="customization-group">
|
|
2385
|
+
<h3>"Try Me" Prompt Animation</h3>
|
|
2386
|
+
<div class="control-item">
|
|
2387
|
+
<label>
|
|
2388
|
+
<input type="checkbox" id="promptEnabled" ${widgetConfig.promptAnimation?.enabled === true ? 'checked' : ''}>
|
|
2389
|
+
Enable Prompt Animation
|
|
2390
|
+
</label>
|
|
2391
|
+
</div>
|
|
2392
|
+
<div class="control-item" id="promptControls" style="display: ${widgetConfig.promptAnimation?.enabled === true ? 'block' : 'none'};">
|
|
2393
|
+
<label>Prompt Text</label>
|
|
2394
|
+
<input type="text" id="promptText" value="${widgetConfig.promptAnimation?.text || 'Try me!'}" placeholder="Try me!">
|
|
2395
|
+
</div>
|
|
2396
|
+
<div class="control-item" id="promptBgColorControl" style="display: ${widgetConfig.promptAnimation?.enabled === true ? 'block' : 'none'};">
|
|
2397
|
+
<label>Background Color/Gradient</label>
|
|
2398
|
+
<input type="text" id="promptBgColor" value="${widgetConfig.promptAnimation?.backgroundColor || 'linear-gradient(135deg, #7c3aed, #4f46e5)'}" placeholder="linear-gradient(135deg, #7c3aed, #4f46e5)">
|
|
2399
|
+
<p style="color: #6b7280; font-size: 11px; margin-top: 4px;">Supports solid colors (e.g., #7c3aed) or gradients</p>
|
|
2400
|
+
</div>
|
|
2401
|
+
<div class="control-item" id="promptTextColorControl" style="display: ${widgetConfig.promptAnimation?.enabled === true ? 'block' : 'none'};">
|
|
2402
|
+
<label>Text Color</label>
|
|
2403
|
+
<input type="color" id="promptTextColor" value="${widgetConfig.promptAnimation?.textColor || '#ffffff'}">
|
|
2404
|
+
</div>
|
|
2405
|
+
<div class="control-item" id="promptAnimationTypeControl" style="display: ${widgetConfig.promptAnimation?.enabled === true ? 'block' : 'none'};">
|
|
2406
|
+
<label>Animation Type</label>
|
|
2407
|
+
<select id="promptAnimationType">
|
|
2408
|
+
<option value="bounce" ${widgetConfig.promptAnimation?.animationType === 'bounce' ? 'selected' : ''}>Bounce</option>
|
|
2409
|
+
<option value="float" ${widgetConfig.promptAnimation?.animationType === 'float' ? 'selected' : ''}>Float</option>
|
|
2410
|
+
<option value="none" ${widgetConfig.promptAnimation?.animationType === 'none' ? 'selected' : ''}>None</option>
|
|
2411
|
+
</select>
|
|
2412
|
+
</div>
|
|
2413
|
+
<div class="control-item" id="promptHideAfterSecondsControl" style="display: ${widgetConfig.promptAnimation?.enabled === true ? 'block' : 'none'};">
|
|
2414
|
+
<label>Auto-Hide After (seconds)</label>
|
|
2415
|
+
<input type="number" id="promptHideAfterSeconds" value="${widgetConfig.promptAnimation?.hideAfterSeconds || ''}" placeholder="Leave empty for never" min="0" step="1">
|
|
2416
|
+
<p style="color: #6b7280; font-size: 11px; margin-top: 4px;">Leave empty to never auto-hide</p>
|
|
2417
|
+
</div>
|
|
2418
|
+
<div class="control-item" id="promptPositionControl" style="display: ${widgetConfig.promptAnimation?.enabled === true ? 'block' : 'none'};">
|
|
2419
|
+
<label>Position</label>
|
|
2420
|
+
<select id="promptPosition">
|
|
2421
|
+
<option value="top" ${widgetConfig.promptAnimation?.position === 'top' ? 'selected' : ''}>Top</option>
|
|
2422
|
+
<option value="left" ${widgetConfig.promptAnimation?.position === 'left' ? 'selected' : ''}>Left</option>
|
|
2423
|
+
<option value="right" ${widgetConfig.promptAnimation?.position === 'right' ? 'selected' : ''}>Right</option>
|
|
2424
|
+
</select>
|
|
2425
|
+
</div>
|
|
2426
|
+
</div>
|
|
2427
|
+
<div class="customization-group">
|
|
2428
|
+
<h3>Icon</h3>
|
|
2429
|
+
<div class="control-item">
|
|
2430
|
+
<label>Type</label>
|
|
2431
|
+
<select id="iconType">
|
|
2432
|
+
<option value="custom" ${widgetConfig.icon.type === 'custom' ? 'selected' : ''}>Custom Image</option>
|
|
2433
|
+
<option value="microphone" ${widgetConfig.icon.type === 'microphone' ? 'selected' : ''}>Microphone</option>
|
|
2434
|
+
<option value="emoji" ${widgetConfig.icon.type === 'emoji' ? 'selected' : ''}>Emoji</option>
|
|
2435
|
+
<option value="text" ${widgetConfig.icon.type === 'text' ? 'selected' : ''}>Text</option>
|
|
2436
|
+
</select>
|
|
2437
|
+
</div>
|
|
2438
|
+
<div class="control-item" id="iconCustomImageControl" style="display: ${widgetConfig.icon.type === 'custom' ? 'block' : 'none'};">
|
|
2439
|
+
<label>Image URL</label>
|
|
2440
|
+
<input type="text" id="iconCustomImage" value="${widgetConfig.icon.customImage || 'https://talktopc.com/logo192.png'}" placeholder="https://talktopc.com/logo192.png">
|
|
2441
|
+
</div>
|
|
2442
|
+
<div class="control-item" id="iconEmojiControl" style="display: ${widgetConfig.icon.type === 'emoji' ? 'block' : 'none'};">
|
|
2443
|
+
<label>Emoji</label>
|
|
2444
|
+
<input type="text" id="iconEmoji" value="${widgetConfig.icon.emoji || '🎤'}" placeholder="🎤">
|
|
2445
|
+
</div>
|
|
2446
|
+
<div class="control-item" id="iconTextControl" style="display: ${widgetConfig.icon.type === 'text' ? 'block' : 'none'};">
|
|
2447
|
+
<label>Text</label>
|
|
2448
|
+
<input type="text" id="iconText" value="${widgetConfig.icon.text || 'AI'}" placeholder="AI">
|
|
2449
|
+
</div>
|
|
2450
|
+
</div>
|
|
2451
|
+
`;
|
|
2452
|
+
break;
|
|
2453
|
+
case 'onlineIndicator':
|
|
2454
|
+
case 'header':
|
|
2455
|
+
controlsHTML = `
|
|
2456
|
+
<div class="customization-group">
|
|
2457
|
+
<h3>Header</h3>
|
|
2458
|
+
<div class="control-item">
|
|
2459
|
+
<label>Title Text</label>
|
|
2460
|
+
<input type="text" id="headerTitle" value="${widgetConfig.header.title}" placeholder="Chat Assistant">
|
|
2461
|
+
</div>
|
|
2462
|
+
<div class="control-item">
|
|
2463
|
+
<label>Background Color</label>
|
|
2464
|
+
<input type="color" id="headerBgColor" value="${widgetConfig.header.backgroundColor}">
|
|
2465
|
+
</div>
|
|
2466
|
+
<div class="control-item">
|
|
2467
|
+
<label>Text Color</label>
|
|
2468
|
+
<input type="color" id="headerTextColor" value="${widgetConfig.header.textColor}">
|
|
2469
|
+
</div>
|
|
2470
|
+
<div class="control-item">
|
|
2471
|
+
<label>
|
|
2472
|
+
<input type="checkbox" id="headerShowClose" ${widgetConfig.header.showCloseButton ? 'checked' : ''}>
|
|
2473
|
+
Show Close Button
|
|
2474
|
+
</label>
|
|
2475
|
+
</div>
|
|
2476
|
+
<div class="control-item" style="margin-top: 16px; padding-top: 16px; border-top: 1px solid #374151;">
|
|
2477
|
+
<label style="font-weight: 600; color: #f9fafb; margin-bottom: 8px; display: block;">Online Indicator</label>
|
|
2478
|
+
<div class="control-item">
|
|
2479
|
+
<label>Indicator Text</label>
|
|
2480
|
+
<input type="text" id="onlineIndicatorText" value="${widgetConfig.header.onlineIndicatorText || 'Online'}" placeholder="Online">
|
|
2481
|
+
</div>
|
|
2482
|
+
<div class="control-item">
|
|
2483
|
+
<label>Indicator Text Color</label>
|
|
2484
|
+
<input type="color" id="onlineIndicatorColor" value="${widgetConfig.header.onlineIndicatorColor || widgetConfig.header.textColor}">
|
|
2485
|
+
</div>
|
|
2486
|
+
<div class="control-item">
|
|
2487
|
+
<label>Indicator Dot Color</label>
|
|
2488
|
+
<input type="color" id="onlineIndicatorDotColor" value="${widgetConfig.header.onlineIndicatorDotColor || '#10b981'}">
|
|
2489
|
+
</div>
|
|
2490
|
+
</div>
|
|
2491
|
+
</div>
|
|
2492
|
+
`;
|
|
2493
|
+
break;
|
|
2494
|
+
case 'panel':
|
|
2495
|
+
controlsHTML = `
|
|
2496
|
+
<div class="customization-group">
|
|
2497
|
+
<h3>Panel</h3>
|
|
2498
|
+
<div class="control-item">
|
|
2499
|
+
<label>Width (px)</label>
|
|
2500
|
+
<input type="number" id="panelWidth" value="${widgetConfig.panel.width}">
|
|
2501
|
+
</div>
|
|
2502
|
+
<div class="control-item">
|
|
2503
|
+
<label>Height (px)</label>
|
|
2504
|
+
<input type="number" id="panelHeight" value="${widgetConfig.panel.height}">
|
|
2505
|
+
</div>
|
|
2506
|
+
<div class="control-item">
|
|
2507
|
+
<label>Border Radius (px)</label>
|
|
2508
|
+
<input type="number" id="panelRadius" value="${widgetConfig.panel.borderRadius}">
|
|
2509
|
+
</div>
|
|
2510
|
+
</div>
|
|
2511
|
+
<div class="customization-group">
|
|
2512
|
+
<h3>Widget Position</h3>
|
|
2513
|
+
<div class="control-item">
|
|
2514
|
+
<label>Vertical Position</label>
|
|
2515
|
+
<select id="positionVertical">
|
|
2516
|
+
<option value="bottom" ${widgetConfig.position.vertical === 'bottom' ? 'selected' : ''}>Bottom</option>
|
|
2517
|
+
<option value="top" ${widgetConfig.position.vertical === 'top' ? 'selected' : ''}>Top</option>
|
|
2518
|
+
</select>
|
|
2519
|
+
</div>
|
|
2520
|
+
<div class="control-item">
|
|
2521
|
+
<label>Horizontal Position</label>
|
|
2522
|
+
<select id="positionHorizontal">
|
|
2523
|
+
<option value="right" ${widgetConfig.position.horizontal === 'right' ? 'selected' : ''}>Right</option>
|
|
2524
|
+
<option value="left" ${widgetConfig.position.horizontal === 'left' ? 'selected' : ''}>Left</option>
|
|
2525
|
+
</select>
|
|
2526
|
+
</div>
|
|
2527
|
+
<div class="control-item">
|
|
2528
|
+
<label>Offset X (px)</label>
|
|
2529
|
+
<input type="number" id="positionOffsetX" value="${widgetConfig.position.offset.x}">
|
|
2530
|
+
</div>
|
|
2531
|
+
<div class="control-item">
|
|
2532
|
+
<label>Offset Y (px)</label>
|
|
2533
|
+
<input type="number" id="positionOffsetY" value="${widgetConfig.position.offset.y}">
|
|
2534
|
+
</div>
|
|
2535
|
+
<p style="color: #6b7280; font-size: 12px; margin-top: 8px;">
|
|
2536
|
+
Position of the floating button and widget on the page.
|
|
2537
|
+
</p>
|
|
2538
|
+
</div>
|
|
2539
|
+
<div class="customization-group">
|
|
2540
|
+
<h3>Text Direction</h3>
|
|
2541
|
+
<div class="control-item">
|
|
2542
|
+
<label>Direction</label>
|
|
2543
|
+
<select id="direction">
|
|
2544
|
+
<option value="ltr" ${widgetConfig.direction === 'ltr' ? 'selected' : ''}>Left to Right (LTR)</option>
|
|
2545
|
+
<option value="rtl" ${widgetConfig.direction === 'rtl' ? 'selected' : ''}>Right to Left (RTL)</option>
|
|
2546
|
+
</select>
|
|
2547
|
+
</div>
|
|
2548
|
+
<p style="color: #6b7280; font-size: 12px; margin-top: 8px;">
|
|
2549
|
+
Text direction for the widget. Use RTL for languages like Arabic or Hebrew.
|
|
2550
|
+
</p>
|
|
2551
|
+
</div>
|
|
2552
|
+
`;
|
|
2553
|
+
break;
|
|
2554
|
+
case 'position':
|
|
2555
|
+
controlsHTML = `
|
|
2556
|
+
<div class="customization-group">
|
|
2557
|
+
<h3>Widget Position</h3>
|
|
2558
|
+
<div class="control-item">
|
|
2559
|
+
<label>Vertical Position</label>
|
|
2560
|
+
<select id="positionVertical">
|
|
2561
|
+
<option value="bottom" ${widgetConfig.position.vertical === 'bottom' ? 'selected' : ''}>Bottom</option>
|
|
2562
|
+
<option value="top" ${widgetConfig.position.vertical === 'top' ? 'selected' : ''}>Top</option>
|
|
2563
|
+
</select>
|
|
2564
|
+
</div>
|
|
2565
|
+
<div class="control-item">
|
|
2566
|
+
<label>Horizontal Position</label>
|
|
2567
|
+
<select id="positionHorizontal">
|
|
2568
|
+
<option value="right" ${widgetConfig.position.horizontal === 'right' ? 'selected' : ''}>Right</option>
|
|
2569
|
+
<option value="left" ${widgetConfig.position.horizontal === 'left' ? 'selected' : ''}>Left</option>
|
|
2570
|
+
</select>
|
|
2571
|
+
</div>
|
|
2572
|
+
<div class="control-item">
|
|
2573
|
+
<label>Offset X (px)</label>
|
|
2574
|
+
<input type="number" id="positionOffsetX" value="${widgetConfig.position.offset.x}">
|
|
2575
|
+
</div>
|
|
2576
|
+
<div class="control-item">
|
|
2577
|
+
<label>Offset Y (px)</label>
|
|
2578
|
+
<input type="number" id="positionOffsetY" value="${widgetConfig.position.offset.y}">
|
|
2579
|
+
</div>
|
|
2580
|
+
<p style="color: #6b7280; font-size: 12px; margin-top: 8px;">
|
|
2581
|
+
Position of the floating button and widget on the page.
|
|
2582
|
+
</p>
|
|
2583
|
+
</div>
|
|
2584
|
+
`;
|
|
2585
|
+
break;
|
|
2586
|
+
case 'direction':
|
|
2587
|
+
controlsHTML = `
|
|
2588
|
+
<div class="customization-group">
|
|
2589
|
+
<h3>Text Direction</h3>
|
|
2590
|
+
<div class="control-item">
|
|
2591
|
+
<label>Direction</label>
|
|
2592
|
+
<select id="direction">
|
|
2593
|
+
<option value="ltr" ${widgetConfig.direction === 'ltr' ? 'selected' : ''}>Left to Right (LTR)</option>
|
|
2594
|
+
<option value="rtl" ${widgetConfig.direction === 'rtl' ? 'selected' : ''}>Right to Left (RTL)</option>
|
|
2595
|
+
</select>
|
|
2596
|
+
</div>
|
|
2597
|
+
<p style="color: #6b7280; font-size: 12px; margin-top: 8px;">
|
|
2598
|
+
Text direction for the widget. Use RTL for languages like Arabic or Hebrew.
|
|
2599
|
+
</p>
|
|
2600
|
+
</div>
|
|
2601
|
+
`;
|
|
2602
|
+
break;
|
|
2603
|
+
case 'userMessage':
|
|
2604
|
+
case 'agentMessage':
|
|
2605
|
+
controlsHTML = `
|
|
2606
|
+
<div class="customization-group">
|
|
2607
|
+
<h3>Messages</h3>
|
|
2608
|
+
<div class="control-item">
|
|
2609
|
+
<label>User Message Background</label>
|
|
2610
|
+
<input type="color" id="msgUserBg" value="${widgetConfig.messages.userBackgroundColor}">
|
|
2611
|
+
</div>
|
|
2612
|
+
<div class="control-item">
|
|
2613
|
+
<label>Agent Message Background</label>
|
|
2614
|
+
<input type="color" id="msgAgentBg" value="${widgetConfig.messages.agentBackgroundColor}">
|
|
2615
|
+
</div>
|
|
2616
|
+
<div class="control-item">
|
|
2617
|
+
<label>User Message Text Color</label>
|
|
2618
|
+
<input type="color" id="msgUserTextColor" value="${widgetConfig.messages.userTextColor || widgetConfig.messages.textColor || '#1F2937'}">
|
|
2619
|
+
</div>
|
|
2620
|
+
<div class="control-item">
|
|
2621
|
+
<label>Agent Message Text Color</label>
|
|
2622
|
+
<input type="color" id="msgAgentTextColor" value="${widgetConfig.messages.agentTextColor || widgetConfig.messages.textColor || '#1F2937'}">
|
|
2623
|
+
</div>
|
|
2624
|
+
<div class="control-item" style="margin-top: 16px; padding-top: 16px; border-top: 1px solid #374151;">
|
|
2625
|
+
<label style="font-weight: 600; color: #f9fafb; margin-bottom: 8px; display: block;">Message Avatars</label>
|
|
2626
|
+
<div class="control-item">
|
|
2627
|
+
<label>User Avatar Icon</label>
|
|
2628
|
+
<input type="text" id="msgUserAvatarIcon" value="${widgetConfig.messages.userAvatarIcon || '👤'}" placeholder="👤">
|
|
2629
|
+
<p style="color: #6b7280; font-size: 11px; margin-top: 4px;">Emoji or text to display next to user messages</p>
|
|
2630
|
+
</div>
|
|
2631
|
+
<div class="control-item">
|
|
2632
|
+
<label>Agent Avatar Icon</label>
|
|
2633
|
+
<input type="text" id="msgAgentAvatarIcon" value="${widgetConfig.messages.agentAvatarIcon || '🤖'}" placeholder="🤖">
|
|
2634
|
+
<p style="color: #6b7280; font-size: 11px; margin-top: 4px;">Emoji or text to display next to agent messages</p>
|
|
2635
|
+
</div>
|
|
2636
|
+
</div>
|
|
2637
|
+
<div class="control-item">
|
|
2638
|
+
<label>Font Size</label>
|
|
2639
|
+
<input type="text" id="msgFontSize" value="${widgetConfig.messages.fontSize}" placeholder="14px">
|
|
2640
|
+
</div>
|
|
2641
|
+
<div class="control-item">
|
|
2642
|
+
<label>Border Radius (px)</label>
|
|
2643
|
+
<input type="number" id="msgRadius" value="${widgetConfig.messages.borderRadius}">
|
|
2644
|
+
</div>
|
|
2645
|
+
<p style="color: #6b7280; font-size: 12px; margin-top: 8px;">
|
|
2646
|
+
Note: Message text content is controlled by your agent, not the widget configuration.
|
|
2647
|
+
</p>
|
|
2648
|
+
</div>
|
|
2649
|
+
`;
|
|
2650
|
+
break;
|
|
2651
|
+
case 'sendButton':
|
|
2652
|
+
controlsHTML = `
|
|
2653
|
+
<div class="customization-group">
|
|
2654
|
+
<h3>Send Button</h3>
|
|
2655
|
+
<div class="control-item">
|
|
2656
|
+
<label>Button Text/Icon</label>
|
|
2657
|
+
<input type="text" id="sendButtonText" value="→" placeholder="→ or Send">
|
|
2658
|
+
</div>
|
|
2659
|
+
<div class="control-item">
|
|
2660
|
+
<label>Color</label>
|
|
2661
|
+
<input type="color" id="sendBtnColor" value="${widgetConfig.text.sendButtonColor}">
|
|
2662
|
+
</div>
|
|
2663
|
+
<div class="control-item">
|
|
2664
|
+
<label>Hover Color</label>
|
|
2665
|
+
<input type="color" id="sendBtnHover" value="${widgetConfig.text.sendButtonHoverColor}">
|
|
2666
|
+
</div>
|
|
2667
|
+
</div>
|
|
2668
|
+
<div class="customization-group">
|
|
2669
|
+
<h3>Input Field</h3>
|
|
2670
|
+
<div class="control-item">
|
|
2671
|
+
<label>Placeholder Text</label>
|
|
2672
|
+
<input type="text" id="inputPlaceholder" value="${widgetConfig.text.inputPlaceholder}" placeholder="Type your message...">
|
|
2673
|
+
</div>
|
|
2674
|
+
<div class="control-item">
|
|
2675
|
+
<label>Focus Color</label>
|
|
2676
|
+
<input type="color" id="inputFocusColor" value="${widgetConfig.text.inputFocusColor}">
|
|
2677
|
+
</div>
|
|
2678
|
+
</div>
|
|
2679
|
+
`;
|
|
2680
|
+
break;
|
|
2681
|
+
case 'startCallButton':
|
|
2682
|
+
controlsHTML = `
|
|
2683
|
+
<div class="customization-group">
|
|
2684
|
+
<h3>Start Call Button</h3>
|
|
2685
|
+
<div class="control-item">
|
|
2686
|
+
<label>Button Text</label>
|
|
2687
|
+
<input type="text" id="startCallBtnText" value="Start Call" placeholder="Start Call">
|
|
2688
|
+
</div>
|
|
2689
|
+
<div class="control-item">
|
|
2690
|
+
<label>Background Color</label>
|
|
2691
|
+
<input type="color" id="startCallBtnColor" value="${widgetConfig.voice.startCallButtonColor}">
|
|
2692
|
+
</div>
|
|
2693
|
+
<div class="control-item">
|
|
2694
|
+
<label>Text Color</label>
|
|
2695
|
+
<input type="color" id="startCallBtnTextColor" value="${widgetConfig.voice.startCallButtonTextColor}">
|
|
2696
|
+
</div>
|
|
2697
|
+
</div>
|
|
2698
|
+
`;
|
|
2699
|
+
break;
|
|
2700
|
+
case 'micButton':
|
|
2701
|
+
controlsHTML = `
|
|
2702
|
+
<div class="customization-group">
|
|
2703
|
+
<h3>Microphone Button</h3>
|
|
2704
|
+
<div class="control-item">
|
|
2705
|
+
<label>Background Color</label>
|
|
2706
|
+
<input type="color" id="micBtnColor" value="${widgetConfig.voice.micButtonColor}">
|
|
2707
|
+
</div>
|
|
2708
|
+
</div>
|
|
2709
|
+
`;
|
|
2710
|
+
break;
|
|
2711
|
+
case 'endCallButton':
|
|
2712
|
+
controlsHTML = `
|
|
2713
|
+
<div class="customization-group">
|
|
2714
|
+
<h3>End Call Button</h3>
|
|
2715
|
+
<div class="control-item">
|
|
2716
|
+
<label>Background Color</label>
|
|
2717
|
+
<input type="color" id="endCallBtnColor" value="${widgetConfig.voice.endCallButtonColor || '#ef4444'}">
|
|
2718
|
+
</div>
|
|
2719
|
+
</div>
|
|
2720
|
+
`;
|
|
2721
|
+
break;
|
|
2722
|
+
case 'speakerButton':
|
|
2723
|
+
controlsHTML = `
|
|
2724
|
+
<div class="customization-group">
|
|
2725
|
+
<h3>Speaker Button</h3>
|
|
2726
|
+
<div class="control-item">
|
|
2727
|
+
<label>Background Color</label>
|
|
2728
|
+
<input type="color" id="speakerBtnColor" value="${widgetConfig.voice.speakerButtonColor || '#FFFFFF'}">
|
|
2729
|
+
</div>
|
|
2730
|
+
</div>
|
|
2731
|
+
`;
|
|
2732
|
+
break;
|
|
2733
|
+
case 'timer':
|
|
2734
|
+
controlsHTML = `
|
|
2735
|
+
<div class="customization-group">
|
|
2736
|
+
<h3>Call Timer</h3>
|
|
2737
|
+
<div class="control-item">
|
|
2738
|
+
<label>Timer Dot Color</label>
|
|
2739
|
+
<input type="color" id="timerDotColor" value="#ef4444">
|
|
2740
|
+
</div>
|
|
2741
|
+
<div class="control-item">
|
|
2742
|
+
<label>Timer Text Color</label>
|
|
2743
|
+
<input type="color" id="timerTextColor" value="#64748b">
|
|
2744
|
+
</div>
|
|
2745
|
+
</div>
|
|
2746
|
+
`;
|
|
2747
|
+
break;
|
|
2748
|
+
case 'waveform':
|
|
2749
|
+
controlsHTML = `
|
|
2750
|
+
<div class="customization-group">
|
|
2751
|
+
<h3>Waveform Visualizer</h3>
|
|
2752
|
+
<div class="control-item">
|
|
2753
|
+
<label>Display Type</label>
|
|
2754
|
+
<select id="waveformType">
|
|
2755
|
+
<option value="waveform" ${widgetConfig.voice.waveformType === 'waveform' ? 'selected' : ''}>Waveform</option>
|
|
2756
|
+
<option value="icon" ${widgetConfig.voice.waveformType === 'icon' ? 'selected' : ''}>Icon</option>
|
|
2757
|
+
<option value="image" ${widgetConfig.voice.waveformType === 'image' ? 'selected' : ''}>Image URL</option>
|
|
2758
|
+
</select>
|
|
2759
|
+
</div>
|
|
2760
|
+
<div class="control-item" id="waveformColorControl">
|
|
2761
|
+
<label>Waveform Color</label>
|
|
2762
|
+
<input type="color" id="waveformColor" value="${widgetConfig.voice.micButtonColor}">
|
|
2763
|
+
</div>
|
|
2764
|
+
<div class="control-item" id="waveformIconControl" style="display: ${widgetConfig.voice.waveformType === 'icon' ? 'block' : 'none'};">
|
|
2765
|
+
<label>Icon (Emoji or Text)</label>
|
|
2766
|
+
<input type="text" id="waveformIcon" value="${widgetConfig.voice.waveformIcon || '🎤'}" placeholder="🎤">
|
|
2767
|
+
</div>
|
|
2768
|
+
<div class="control-item" id="waveformImageControl" style="display: ${widgetConfig.voice.waveformType === 'image' ? 'block' : 'none'};">
|
|
2769
|
+
<label>Image URL</label>
|
|
2770
|
+
<input type="text" id="waveformImageUrl" value="${widgetConfig.voice.waveformImageUrl || ''}" placeholder="https://example.com/image.png">
|
|
2771
|
+
</div>
|
|
2772
|
+
</div>
|
|
2773
|
+
`;
|
|
2774
|
+
break;
|
|
2775
|
+
case 'statusTitle':
|
|
2776
|
+
controlsHTML = `
|
|
2777
|
+
<div class="customization-group">
|
|
2778
|
+
<h3>Status Text</h3>
|
|
2779
|
+
<div class="control-item">
|
|
2780
|
+
<label>Status Text</label>
|
|
2781
|
+
<input type="text" id="statusText" value="${widgetConfig.voice.statusText || 'Listening...'}" placeholder="Listening...">
|
|
2782
|
+
</div>
|
|
2783
|
+
<div class="control-item">
|
|
2784
|
+
<label>Text Color</label>
|
|
2785
|
+
<input type="color" id="statusTitleColor" value="${widgetConfig.voice.statusTitleColor || '#1e293b'}">
|
|
2786
|
+
</div>
|
|
2787
|
+
<div class="control-item">
|
|
2788
|
+
<label>Status Dot Color</label>
|
|
2789
|
+
<input type="color" id="statusDotColor" value="${widgetConfig.voice.statusDotColor || '#10b981'}">
|
|
2790
|
+
</div>
|
|
2791
|
+
</div>
|
|
2792
|
+
`;
|
|
2793
|
+
break;
|
|
2794
|
+
case 'liveTranscript':
|
|
2795
|
+
case 'liveTranscriptText':
|
|
2796
|
+
controlsHTML = `
|
|
2797
|
+
<div class="customization-group">
|
|
2798
|
+
<h3>Live Transcript (Collapsed View)</h3>
|
|
2799
|
+
<div class="control-item">
|
|
2800
|
+
<label>Transcript Text Color</label>
|
|
2801
|
+
<input type="color" id="liveTranscriptColor" value="${widgetConfig.voice.liveTranscriptTextColor || '#64748b'}">
|
|
2802
|
+
</div>
|
|
2803
|
+
<div class="control-item">
|
|
2804
|
+
<label>Font Size</label>
|
|
2805
|
+
<input type="text" id="liveTranscriptFontSize" value="${widgetConfig.voice.liveTranscriptFontSize || '14px'}" placeholder="14px">
|
|
2806
|
+
</div>
|
|
2807
|
+
<p style="color: #6b7280; font-size: 12px; margin-top: 8px;">
|
|
2808
|
+
This is the live transcript shown when history is collapsed. It displays only the current spoken text (max 2 lines).
|
|
2809
|
+
</p>
|
|
2810
|
+
</div>
|
|
2811
|
+
`;
|
|
2812
|
+
break;
|
|
2813
|
+
case 'voiceInput':
|
|
2814
|
+
case 'voiceSendButton':
|
|
2815
|
+
controlsHTML = `
|
|
2816
|
+
<div class="customization-group">
|
|
2817
|
+
<h3>Voice Text Input</h3>
|
|
2818
|
+
<div class="control-item">
|
|
2819
|
+
<label>Placeholder Text</label>
|
|
2820
|
+
<input type="text" id="voiceInputPlaceholder" value="${widgetConfig.text.inputPlaceholder}" placeholder="Type your message...">
|
|
2821
|
+
</div>
|
|
2822
|
+
<div class="control-item">
|
|
2823
|
+
<label>Send Button Color</label>
|
|
2824
|
+
<input type="color" id="voiceSendBtnColor" value="${widgetConfig.text.sendButtonColor}">
|
|
2825
|
+
</div>
|
|
2826
|
+
</div>
|
|
2827
|
+
`;
|
|
2828
|
+
break;
|
|
2829
|
+
case 'voiceAvatar':
|
|
2830
|
+
controlsHTML = `
|
|
2831
|
+
<div class="customization-group">
|
|
2832
|
+
<h3>Voice Avatar</h3>
|
|
2833
|
+
<div class="control-item">
|
|
2834
|
+
<label>Background Color</label>
|
|
2835
|
+
<input type="color" id="avatarBg" value="${widgetConfig.voice.avatarBackgroundColor}">
|
|
2836
|
+
</div>
|
|
2837
|
+
</div>
|
|
2838
|
+
`;
|
|
2839
|
+
break;
|
|
2840
|
+
case 'conversationHistory':
|
|
2841
|
+
controlsHTML = `
|
|
2842
|
+
<div class="customization-group">
|
|
2843
|
+
<h3>Conversation History</h3>
|
|
2844
|
+
<p style="color: #6b7280; font-size: 14px; margin-top: 8px;">
|
|
2845
|
+
This is the expanded conversation history view showing all messages.
|
|
2846
|
+
</p>
|
|
2847
|
+
</div>
|
|
2848
|
+
`;
|
|
2849
|
+
break;
|
|
2850
|
+
case 'agentAvatar':
|
|
2851
|
+
controlsHTML = `
|
|
2852
|
+
<div class="customization-group">
|
|
2853
|
+
<h3>Agent Avatar (Next to Messages)</h3>
|
|
2854
|
+
<div class="control-item">
|
|
2855
|
+
<label>Display Type</label>
|
|
2856
|
+
<select id="agentAvatarType">
|
|
2857
|
+
<option value="icon" ${widgetConfig.voice.avatarType === 'icon' || !widgetConfig.voice.avatarType ? 'selected' : ''}>Icon</option>
|
|
2858
|
+
<option value="image" ${widgetConfig.voice.avatarType === 'image' ? 'selected' : ''}>Image URL</option>
|
|
2859
|
+
</select>
|
|
2860
|
+
</div>
|
|
2861
|
+
<div class="control-item" id="agentAvatarIconControl" style="display: ${widgetConfig.voice.avatarType === 'image' ? 'none' : 'block'};">
|
|
2862
|
+
<label>Icon (Emoji or Text)</label>
|
|
2863
|
+
<input type="text" id="agentAvatarIcon" value="${widgetConfig.voice.avatarIcon || '🤖'}" placeholder="🤖">
|
|
2864
|
+
</div>
|
|
2865
|
+
<div class="control-item" id="agentAvatarImageControl" style="display: ${widgetConfig.voice.avatarType === 'image' ? 'block' : 'none'};">
|
|
2866
|
+
<label>Image URL</label>
|
|
2867
|
+
<input type="text" id="agentAvatarImageUrl" value="${widgetConfig.voice.avatarImageUrl || ''}" placeholder="https://example.com/avatar.png">
|
|
2868
|
+
</div>
|
|
2869
|
+
<div class="control-item">
|
|
2870
|
+
<label>Background Color</label>
|
|
2871
|
+
<input type="color" id="agentAvatarBg" value="${widgetConfig.voice.avatarBackgroundColor}">
|
|
2872
|
+
</div>
|
|
2873
|
+
<p style="color: #6b7280; font-size: 12px; margin-top: 8px;">
|
|
2874
|
+
This icon appears next to agent messages in the conversation history.
|
|
2875
|
+
</p>
|
|
2876
|
+
</div>
|
|
2877
|
+
`;
|
|
2878
|
+
break;
|
|
2879
|
+
case 'liveIndicator':
|
|
2880
|
+
controlsHTML = `
|
|
2881
|
+
<div class="customization-group">
|
|
2882
|
+
<h3>Live Indicator</h3>
|
|
2883
|
+
<div class="control-item">
|
|
2884
|
+
<label>Live Dot Color</label>
|
|
2885
|
+
<input type="color" id="liveDotColor" value="${widgetConfig.voice.liveIndicatorDotColor || '#10b981'}">
|
|
2886
|
+
</div>
|
|
2887
|
+
<div class="control-item">
|
|
2888
|
+
<label>Live Text Color</label>
|
|
2889
|
+
<input type="color" id="liveTextColor" value="${widgetConfig.voice.liveIndicatorTextColor || '#10b981'}">
|
|
2890
|
+
</div>
|
|
2891
|
+
</div>
|
|
2892
|
+
`;
|
|
2893
|
+
break;
|
|
2894
|
+
case 'landingLogo':
|
|
2895
|
+
controlsHTML = `
|
|
2896
|
+
<div class="customization-group">
|
|
2897
|
+
<h3>Landing Screen - Logo</h3>
|
|
2898
|
+
<div class="control-item">
|
|
2899
|
+
<label>Display Type</label>
|
|
2900
|
+
<select id="logoType">
|
|
2901
|
+
<option value="icon" ${widgetConfig.landing.logoType === 'icon' || !widgetConfig.landing.logoType ? 'selected' : ''}>Icon</option>
|
|
2902
|
+
<option value="image" ${widgetConfig.landing.logoType === 'image' ? 'selected' : ''}>Image URL</option>
|
|
2903
|
+
</select>
|
|
2904
|
+
</div>
|
|
2905
|
+
<div class="control-item" id="logoIconControl" style="display: ${widgetConfig.landing.logoType === 'image' ? 'none' : 'block'};">
|
|
2906
|
+
<label>Icon (Emoji or Text)</label>
|
|
2907
|
+
<input type="text" id="landingLogoIcon" value="${widgetConfig.landing.logoIcon || widgetConfig.landing.logo || '🤖'}" placeholder="🤖">
|
|
2908
|
+
</div>
|
|
2909
|
+
<div class="control-item" id="logoImageControl" style="display: ${widgetConfig.landing.logoType === 'image' ? 'block' : 'none'};">
|
|
2910
|
+
<label>Image URL</label>
|
|
2911
|
+
<input type="text" id="landingLogoImageUrl" value="${widgetConfig.landing.logoImageUrl || ''}" placeholder="https://example.com/logo.png">
|
|
2912
|
+
</div>
|
|
2913
|
+
<div class="control-item" id="logoBackgroundControl" style="display: ${widgetConfig.landing.logoType === 'image' ? 'block' : 'none'}; margin-top: 16px; padding-top: 16px; border-top: 1px solid #374151;">
|
|
2914
|
+
<label style="font-weight: 600; color: #f9fafb; margin-bottom: 8px; display: block;">Background</label>
|
|
2915
|
+
<div class="control-item">
|
|
2916
|
+
<label>
|
|
2917
|
+
<input type="checkbox" id="logoBackgroundEnabled" ${widgetConfig.landing.logoBackgroundEnabled !== false ? 'checked' : ''}>
|
|
2918
|
+
Enable Background
|
|
2919
|
+
</label>
|
|
2920
|
+
</div>
|
|
2921
|
+
<div class="control-item" id="logoBackgroundColorControl" style="display: ${widgetConfig.landing.logoBackgroundEnabled !== false ? 'block' : 'none'};">
|
|
2922
|
+
<label>Background Color</label>
|
|
2923
|
+
<input type="color" id="logoBackgroundColor" value="${widgetConfig.landing.logoBackgroundColor && widgetConfig.landing.logoBackgroundColor.startsWith('#') ? widgetConfig.landing.logoBackgroundColor : '#667eea'}">
|
|
2924
|
+
<p style="color: #6b7280; font-size: 11px; margin-top: 4px;">
|
|
2925
|
+
Background color for the logo container. Uncheck "Enable Background" to make it transparent.
|
|
2926
|
+
</p>
|
|
2927
|
+
</div>
|
|
2928
|
+
</div>
|
|
2929
|
+
</div>
|
|
2930
|
+
`;
|
|
2931
|
+
break;
|
|
2932
|
+
case 'landingTitle':
|
|
2933
|
+
controlsHTML = `
|
|
2934
|
+
<div class="customization-group">
|
|
2935
|
+
<h3>Landing Screen - Title</h3>
|
|
2936
|
+
<div class="control-item">
|
|
2937
|
+
<label>Title Text</label>
|
|
2938
|
+
<input type="text" id="landingTitleText" value="${widgetConfig.landing.title || 'Welcome to AI Assistant'}" placeholder="Welcome to AI Assistant">
|
|
2939
|
+
</div>
|
|
2940
|
+
<div class="control-item">
|
|
2941
|
+
<label>Title Color</label>
|
|
2942
|
+
<input type="color" id="landingTitleColor" value="${widgetConfig.landing.titleColor}">
|
|
2943
|
+
</div>
|
|
2944
|
+
</div>
|
|
2945
|
+
`;
|
|
2946
|
+
break;
|
|
2947
|
+
case 'landingSubtitle':
|
|
2948
|
+
controlsHTML = `
|
|
2949
|
+
<div class="customization-group">
|
|
2950
|
+
<h3>Landing Screen - Subtitle</h3>
|
|
2951
|
+
<div class="control-item">
|
|
2952
|
+
<label>Subtitle Text</label>
|
|
2953
|
+
<input type="text" id="landingSubtitleText" value="${widgetConfig.landing.subtitle || 'Choose how you\'d like to interact'}" placeholder="Choose how you'd like to interact">
|
|
2954
|
+
</div>
|
|
2955
|
+
<div class="control-item">
|
|
2956
|
+
<label>Subtitle Color</label>
|
|
2957
|
+
<input type="color" id="landingSubtitleColor" value="${widgetConfig.landing.subtitleColor}">
|
|
2958
|
+
</div>
|
|
2959
|
+
</div>
|
|
2960
|
+
`;
|
|
2961
|
+
break;
|
|
2962
|
+
case 'landingBackground':
|
|
2963
|
+
// Extract color from gradient or use default
|
|
2964
|
+
const currentBg = widgetConfig.landing.backgroundColor || 'linear-gradient(180deg, #ffffff 0%, rgba(168, 85, 247, 0.03) 100%)';
|
|
2965
|
+
let defaultColor = '#ffffff';
|
|
2966
|
+
if (currentBg.startsWith('#')) {
|
|
2967
|
+
defaultColor = currentBg;
|
|
2968
|
+
} else if (currentBg.includes('#')) {
|
|
2969
|
+
// Extract the first hex color from gradient (usually the main color)
|
|
2970
|
+
const hexMatch = currentBg.match(/#[0-9a-fA-F]{6}/);
|
|
2971
|
+
if (hexMatch) defaultColor = hexMatch[0];
|
|
2972
|
+
}
|
|
2973
|
+
controlsHTML = `
|
|
2974
|
+
<div class="customization-group">
|
|
2975
|
+
<h3>Landing Screen - Background</h3>
|
|
2976
|
+
<div class="control-item">
|
|
2977
|
+
<label>Background Color</label>
|
|
2978
|
+
<input type="color" id="landingBgColor" value="${defaultColor}">
|
|
2979
|
+
<p style="color: #6b7280; font-size: 11px; margin-top: 4px;">
|
|
2980
|
+
Pick a solid color for the background. Default uses a subtle gradient.
|
|
2981
|
+
</p>
|
|
2982
|
+
</div>
|
|
2983
|
+
</div>
|
|
2984
|
+
`;
|
|
2985
|
+
break;
|
|
2986
|
+
case 'modeCard':
|
|
2987
|
+
controlsHTML = `
|
|
2988
|
+
<div class="customization-group">
|
|
2989
|
+
<h3>Mode Cards (Voice/Text Buttons)</h3>
|
|
2990
|
+
<div class="control-item">
|
|
2991
|
+
<label>Voice Card Title</label>
|
|
2992
|
+
<input type="text" id="voiceCardTitle" value="Voice Call" placeholder="Voice Call">
|
|
2993
|
+
</div>
|
|
2994
|
+
<div class="control-item">
|
|
2995
|
+
<label>Text Card Title</label>
|
|
2996
|
+
<input type="text" id="textCardTitle" value="Text Chat" placeholder="Text Chat">
|
|
2997
|
+
</div>
|
|
2998
|
+
<div class="control-item">
|
|
2999
|
+
<label>Background Color</label>
|
|
3000
|
+
<input type="color" id="modeCardBg" value="${widgetConfig.landing.modeCardBackgroundColor}">
|
|
3001
|
+
</div>
|
|
3002
|
+
<p style="color: #6b7280; font-size: 12px; margin-top: 8px;">
|
|
3003
|
+
These are the buttons on the landing screen that let users choose between voice and text chat.
|
|
3004
|
+
</p>
|
|
3005
|
+
</div>
|
|
3006
|
+
`;
|
|
3007
|
+
break;
|
|
3008
|
+
default:
|
|
3009
|
+
controlsHTML = `
|
|
3010
|
+
<div class="customization-group">
|
|
3011
|
+
<h3>Select an Element</h3>
|
|
3012
|
+
<p style="color: #6b7280; font-size: 14px; margin-top: 8px;">
|
|
3013
|
+
Click on any widget element in the preview to start customizing.
|
|
3014
|
+
</p>
|
|
3015
|
+
<p style="color: #6b7280; font-size: 13px; margin-top: 12px;">
|
|
3016
|
+
💡 <strong>Tip:</strong> Click the ⚙️ settings icon on the widget panel to configure panel size, position, and text direction.
|
|
3017
|
+
</p>
|
|
3018
|
+
</div>
|
|
3019
|
+
`;
|
|
3020
|
+
}
|
|
3021
|
+
|
|
3022
|
+
controlsDiv.innerHTML = controlsHTML;
|
|
3023
|
+
|
|
3024
|
+
// Scroll controls to top so user can see them
|
|
3025
|
+
controlsDiv.scrollTop = 0;
|
|
3026
|
+
|
|
3027
|
+
// Attach event listeners based on element type
|
|
3028
|
+
attachControlListeners(elementType);
|
|
3029
|
+
|
|
3030
|
+
// Update code output
|
|
3031
|
+
updateConfigCode();
|
|
3032
|
+
|
|
3033
|
+
// Ensure controls are visible by scrolling the panel if needed
|
|
3034
|
+
setTimeout(() => {
|
|
3035
|
+
const firstControl = controlsDiv.querySelector('.control-item');
|
|
3036
|
+
if (firstControl) {
|
|
3037
|
+
firstControl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
3038
|
+
}
|
|
3039
|
+
}, 100);
|
|
3040
|
+
}
|
|
3041
|
+
|
|
3042
|
+
function attachControlListeners(elementType) {
|
|
3043
|
+
// Get controls div reference (it was already updated in showCustomizationControls)
|
|
3044
|
+
const controlsDiv = document.getElementById('customizationControls');
|
|
3045
|
+
if (!controlsDiv) return;
|
|
3046
|
+
|
|
3047
|
+
// Button controls
|
|
3048
|
+
if (elementType === 'button') {
|
|
3049
|
+
document.getElementById('btnSize')?.addEventListener('change', (e) => {
|
|
3050
|
+
widgetConfig.button.size = e.target.value;
|
|
3051
|
+
applyButtonStyles(document.getElementById('mockButton'));
|
|
3052
|
+
updateConfigCode();
|
|
3053
|
+
});
|
|
3054
|
+
document.getElementById('btnShape')?.addEventListener('change', (e) => {
|
|
3055
|
+
widgetConfig.button.shape = e.target.value;
|
|
3056
|
+
applyButtonStyles(document.getElementById('mockButton'));
|
|
3057
|
+
updateConfigCode();
|
|
3058
|
+
});
|
|
3059
|
+
document.getElementById('btnBgColor')?.addEventListener('input', (e) => {
|
|
3060
|
+
widgetConfig.button.backgroundColor = e.target.value;
|
|
3061
|
+
applyButtonStyles(document.getElementById('mockButton'));
|
|
3062
|
+
updateConfigCode();
|
|
3063
|
+
});
|
|
3064
|
+
document.getElementById('iconType')?.addEventListener('change', (e) => {
|
|
3065
|
+
widgetConfig.icon.type = e.target.value;
|
|
3066
|
+
// Show/hide relevant controls
|
|
3067
|
+
const customImageControl = document.getElementById('iconCustomImageControl');
|
|
3068
|
+
const emojiControl = document.getElementById('iconEmojiControl');
|
|
3069
|
+
const textControl = document.getElementById('iconTextControl');
|
|
3070
|
+
if (customImageControl) customImageControl.style.display = e.target.value === 'custom' ? 'block' : 'none';
|
|
3071
|
+
if (emojiControl) emojiControl.style.display = e.target.value === 'emoji' ? 'block' : 'none';
|
|
3072
|
+
if (textControl) textControl.style.display = e.target.value === 'text' ? 'block' : 'none';
|
|
3073
|
+
applyButtonStyles(document.getElementById('mockButton'));
|
|
3074
|
+
updateConfigCode();
|
|
3075
|
+
});
|
|
3076
|
+
|
|
3077
|
+
// Prompt Animation controls
|
|
3078
|
+
document.getElementById('promptEnabled')?.addEventListener('change', (e) => {
|
|
3079
|
+
if (!widgetConfig.promptAnimation) widgetConfig.promptAnimation = {};
|
|
3080
|
+
widgetConfig.promptAnimation.enabled = e.target.checked;
|
|
3081
|
+
const promptControls = document.getElementById('promptControls');
|
|
3082
|
+
const allControls = ['promptControls', 'promptBgColorControl', 'promptTextColorControl', 'promptAnimationTypeControl', 'promptHideAfterSecondsControl', 'promptPositionControl'];
|
|
3083
|
+
allControls.forEach(id => {
|
|
3084
|
+
const el = document.getElementById(id);
|
|
3085
|
+
if (el) el.style.display = e.target.checked ? 'block' : 'none';
|
|
3086
|
+
});
|
|
3087
|
+
applyButtonStyles(document.getElementById('mockButton'));
|
|
3088
|
+
updateConfigCode();
|
|
3089
|
+
});
|
|
3090
|
+
|
|
3091
|
+
document.getElementById('promptText')?.addEventListener('input', (e) => {
|
|
3092
|
+
if (!widgetConfig.promptAnimation) widgetConfig.promptAnimation = {};
|
|
3093
|
+
widgetConfig.promptAnimation.text = e.target.value;
|
|
3094
|
+
applyButtonStyles(document.getElementById('mockButton'));
|
|
3095
|
+
updateConfigCode();
|
|
3096
|
+
});
|
|
3097
|
+
|
|
3098
|
+
document.getElementById('promptBgColor')?.addEventListener('input', (e) => {
|
|
3099
|
+
if (!widgetConfig.promptAnimation) widgetConfig.promptAnimation = {};
|
|
3100
|
+
widgetConfig.promptAnimation.backgroundColor = e.target.value;
|
|
3101
|
+
applyButtonStyles(document.getElementById('mockButton'));
|
|
3102
|
+
updateConfigCode();
|
|
3103
|
+
});
|
|
3104
|
+
|
|
3105
|
+
document.getElementById('promptTextColor')?.addEventListener('input', (e) => {
|
|
3106
|
+
if (!widgetConfig.promptAnimation) widgetConfig.promptAnimation = {};
|
|
3107
|
+
widgetConfig.promptAnimation.textColor = e.target.value;
|
|
3108
|
+
applyButtonStyles(document.getElementById('mockButton'));
|
|
3109
|
+
updateConfigCode();
|
|
3110
|
+
});
|
|
3111
|
+
|
|
3112
|
+
document.getElementById('promptAnimationType')?.addEventListener('change', (e) => {
|
|
3113
|
+
if (!widgetConfig.promptAnimation) widgetConfig.promptAnimation = {};
|
|
3114
|
+
// If pulse was previously selected, default to bounce
|
|
3115
|
+
const value = e.target.value === 'pulse' ? 'bounce' : e.target.value;
|
|
3116
|
+
widgetConfig.promptAnimation.animationType = value;
|
|
3117
|
+
applyButtonStyles(document.getElementById('mockButton'));
|
|
3118
|
+
updateConfigCode();
|
|
3119
|
+
});
|
|
3120
|
+
|
|
3121
|
+
|
|
3122
|
+
document.getElementById('promptHideAfterSeconds')?.addEventListener('input', (e) => {
|
|
3123
|
+
if (!widgetConfig.promptAnimation) widgetConfig.promptAnimation = {};
|
|
3124
|
+
const value = e.target.value.trim();
|
|
3125
|
+
widgetConfig.promptAnimation.hideAfterSeconds = value === '' ? null : parseInt(value) || null;
|
|
3126
|
+
updateConfigCode();
|
|
3127
|
+
});
|
|
3128
|
+
|
|
3129
|
+
document.getElementById('promptPosition')?.addEventListener('change', (e) => {
|
|
3130
|
+
if (!widgetConfig.promptAnimation) widgetConfig.promptAnimation = {};
|
|
3131
|
+
widgetConfig.promptAnimation.position = e.target.value;
|
|
3132
|
+
applyButtonStyles(document.getElementById('mockButton'));
|
|
3133
|
+
updateConfigCode();
|
|
3134
|
+
});
|
|
3135
|
+
document.getElementById('iconCustomImage')?.addEventListener('input', (e) => {
|
|
3136
|
+
widgetConfig.icon.customImage = e.target.value;
|
|
3137
|
+
applyButtonStyles(document.getElementById('mockButton'));
|
|
3138
|
+
updateConfigCode();
|
|
3139
|
+
});
|
|
3140
|
+
document.getElementById('iconEmoji')?.addEventListener('input', (e) => {
|
|
3141
|
+
widgetConfig.icon.emoji = e.target.value;
|
|
3142
|
+
applyButtonStyles(document.getElementById('mockButton'));
|
|
3143
|
+
updateConfigCode();
|
|
3144
|
+
});
|
|
3145
|
+
document.getElementById('iconText')?.addEventListener('input', (e) => {
|
|
3146
|
+
widgetConfig.icon.text = e.target.value;
|
|
3147
|
+
applyButtonStyles(document.getElementById('mockButton'));
|
|
3148
|
+
updateConfigCode();
|
|
3149
|
+
});
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
// Header controls (also applies to onlineIndicator)
|
|
3153
|
+
if (elementType === 'header' || elementType === 'onlineIndicator') {
|
|
3154
|
+
document.getElementById('headerTitle')?.addEventListener('input', (e) => {
|
|
3155
|
+
widgetConfig.header.title = e.target.value;
|
|
3156
|
+
renderPanelContent();
|
|
3157
|
+
updateConfigCode();
|
|
3158
|
+
});
|
|
3159
|
+
document.getElementById('headerBgColor')?.addEventListener('input', (e) => {
|
|
3160
|
+
const newColor = e.target.value;
|
|
3161
|
+
console.log('🎨 Header color changed to:', newColor);
|
|
3162
|
+
widgetConfig.header.backgroundColor = newColor;
|
|
3163
|
+
renderPanelContent();
|
|
3164
|
+
updateConfigCode();
|
|
3165
|
+
});
|
|
3166
|
+
document.getElementById('headerTextColor')?.addEventListener('input', (e) => {
|
|
3167
|
+
widgetConfig.header.textColor = e.target.value;
|
|
3168
|
+
renderPanelContent();
|
|
3169
|
+
updateConfigCode();
|
|
3170
|
+
});
|
|
3171
|
+
document.getElementById('headerShowClose')?.addEventListener('change', (e) => {
|
|
3172
|
+
widgetConfig.header.showCloseButton = e.target.checked;
|
|
3173
|
+
renderPanelContent();
|
|
3174
|
+
updateConfigCode();
|
|
3175
|
+
});
|
|
3176
|
+
document.getElementById('onlineIndicatorText')?.addEventListener('input', (e) => {
|
|
3177
|
+
widgetConfig.header.onlineIndicatorText = e.target.value;
|
|
3178
|
+
renderPanelContent();
|
|
3179
|
+
updateConfigCode();
|
|
3180
|
+
});
|
|
3181
|
+
document.getElementById('onlineIndicatorColor')?.addEventListener('input', (e) => {
|
|
3182
|
+
widgetConfig.header.onlineIndicatorColor = e.target.value;
|
|
3183
|
+
renderPanelContent();
|
|
3184
|
+
updateConfigCode();
|
|
3185
|
+
});
|
|
3186
|
+
document.getElementById('onlineIndicatorDotColor')?.addEventListener('input', (e) => {
|
|
3187
|
+
widgetConfig.header.onlineIndicatorDotColor = e.target.value;
|
|
3188
|
+
renderPanelContent();
|
|
3189
|
+
updateConfigCode();
|
|
3190
|
+
});
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
// Panel controls
|
|
3194
|
+
if (elementType === 'panel') {
|
|
3195
|
+
['Width', 'Height', 'Radius'].forEach(prop => {
|
|
3196
|
+
const id = `panel${prop}`;
|
|
3197
|
+
document.getElementById(id)?.addEventListener('input', (e) => {
|
|
3198
|
+
const key = prop.toLowerCase();
|
|
3199
|
+
widgetConfig.panel[key] = parseInt(e.target.value);
|
|
3200
|
+
renderPanelContent();
|
|
3201
|
+
updateConfigCode();
|
|
3202
|
+
});
|
|
3203
|
+
});
|
|
3204
|
+
// Position controls (included in panel settings)
|
|
3205
|
+
document.getElementById('positionVertical')?.addEventListener('change', (e) => {
|
|
3206
|
+
widgetConfig.position.vertical = e.target.value;
|
|
3207
|
+
updateMockWidgetPosition();
|
|
3208
|
+
updateConfigCode();
|
|
3209
|
+
});
|
|
3210
|
+
document.getElementById('positionHorizontal')?.addEventListener('change', (e) => {
|
|
3211
|
+
widgetConfig.position.horizontal = e.target.value;
|
|
3212
|
+
// Don't update mock widget position - it always stays on the right
|
|
3213
|
+
updateConfigCode();
|
|
3214
|
+
});
|
|
3215
|
+
document.getElementById('positionOffsetX')?.addEventListener('input', (e) => {
|
|
3216
|
+
widgetConfig.position.offset.x = parseInt(e.target.value) || 0;
|
|
3217
|
+
updateMockWidgetPosition();
|
|
3218
|
+
updateConfigCode();
|
|
3219
|
+
});
|
|
3220
|
+
document.getElementById('positionOffsetY')?.addEventListener('input', (e) => {
|
|
3221
|
+
widgetConfig.position.offset.y = parseInt(e.target.value) || 0;
|
|
3222
|
+
updateMockWidgetPosition();
|
|
3223
|
+
updateConfigCode();
|
|
3224
|
+
});
|
|
3225
|
+
// Direction controls (included in panel settings)
|
|
3226
|
+
document.getElementById('direction')?.addEventListener('change', (e) => {
|
|
3227
|
+
widgetConfig.direction = e.target.value;
|
|
3228
|
+
renderPanelContent(); // Re-render mock panel with new direction
|
|
3229
|
+
updateConfigCode();
|
|
3230
|
+
});
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3233
|
+
// Position controls
|
|
3234
|
+
if (elementType === 'position') {
|
|
3235
|
+
document.getElementById('positionVertical')?.addEventListener('change', (e) => {
|
|
3236
|
+
widgetConfig.position.vertical = e.target.value;
|
|
3237
|
+
updateMockWidgetPosition();
|
|
3238
|
+
updateConfigCode();
|
|
3239
|
+
});
|
|
3240
|
+
document.getElementById('positionHorizontal')?.addEventListener('change', (e) => {
|
|
3241
|
+
widgetConfig.position.horizontal = e.target.value;
|
|
3242
|
+
// Don't update mock widget position - it always stays on the right
|
|
3243
|
+
updateConfigCode();
|
|
3244
|
+
});
|
|
3245
|
+
document.getElementById('positionOffsetX')?.addEventListener('input', (e) => {
|
|
3246
|
+
widgetConfig.position.offset.x = parseInt(e.target.value) || 0;
|
|
3247
|
+
updateMockWidgetPosition();
|
|
3248
|
+
updateConfigCode();
|
|
3249
|
+
});
|
|
3250
|
+
document.getElementById('positionOffsetY')?.addEventListener('input', (e) => {
|
|
3251
|
+
widgetConfig.position.offset.y = parseInt(e.target.value) || 0;
|
|
3252
|
+
updateMockWidgetPosition();
|
|
3253
|
+
updateConfigCode();
|
|
3254
|
+
});
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
// Direction controls
|
|
3258
|
+
if (elementType === 'direction') {
|
|
3259
|
+
document.getElementById('direction')?.addEventListener('change', (e) => {
|
|
3260
|
+
widgetConfig.direction = e.target.value;
|
|
3261
|
+
renderPanelContent(); // Re-render mock panel with new direction
|
|
3262
|
+
updateConfigCode();
|
|
3263
|
+
});
|
|
3264
|
+
}
|
|
3265
|
+
|
|
3266
|
+
// Message controls
|
|
3267
|
+
if (elementType === 'userMessage' || elementType === 'agentMessage') {
|
|
3268
|
+
document.getElementById('msgUserBg')?.addEventListener('input', (e) => {
|
|
3269
|
+
widgetConfig.messages.userBackgroundColor = e.target.value;
|
|
3270
|
+
renderPanelContent();
|
|
3271
|
+
updateConfigCode();
|
|
3272
|
+
});
|
|
3273
|
+
document.getElementById('msgAgentBg')?.addEventListener('input', (e) => {
|
|
3274
|
+
widgetConfig.messages.agentBackgroundColor = e.target.value;
|
|
3275
|
+
renderPanelContent();
|
|
3276
|
+
updateConfigCode();
|
|
3277
|
+
});
|
|
3278
|
+
document.getElementById('msgUserTextColor')?.addEventListener('input', (e) => {
|
|
3279
|
+
widgetConfig.messages.userTextColor = e.target.value;
|
|
3280
|
+
renderPanelContent();
|
|
3281
|
+
updateConfigCode();
|
|
3282
|
+
});
|
|
3283
|
+
document.getElementById('msgAgentTextColor')?.addEventListener('input', (e) => {
|
|
3284
|
+
widgetConfig.messages.agentTextColor = e.target.value;
|
|
3285
|
+
renderPanelContent();
|
|
3286
|
+
updateConfigCode();
|
|
3287
|
+
});
|
|
3288
|
+
document.getElementById('msgUserAvatarIcon')?.addEventListener('input', (e) => {
|
|
3289
|
+
widgetConfig.messages.userAvatarIcon = e.target.value;
|
|
3290
|
+
renderPanelContent();
|
|
3291
|
+
updateConfigCode();
|
|
3292
|
+
});
|
|
3293
|
+
document.getElementById('msgAgentAvatarIcon')?.addEventListener('input', (e) => {
|
|
3294
|
+
widgetConfig.messages.agentAvatarIcon = e.target.value;
|
|
3295
|
+
renderPanelContent();
|
|
3296
|
+
updateConfigCode();
|
|
3297
|
+
});
|
|
3298
|
+
document.getElementById('msgRadius')?.addEventListener('input', (e) => {
|
|
3299
|
+
widgetConfig.messages.borderRadius = parseInt(e.target.value);
|
|
3300
|
+
renderPanelContent();
|
|
3301
|
+
updateConfigCode();
|
|
3302
|
+
});
|
|
3303
|
+
document.getElementById('msgFontSize')?.addEventListener('input', (e) => {
|
|
3304
|
+
widgetConfig.messages.fontSize = e.target.value;
|
|
3305
|
+
renderPanelContent();
|
|
3306
|
+
updateConfigCode();
|
|
3307
|
+
});
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
// Send button and input controls
|
|
3311
|
+
if (elementType === 'sendButton') {
|
|
3312
|
+
document.getElementById('sendButtonText')?.addEventListener('input', (e) => {
|
|
3313
|
+
widgetConfig.text.sendButtonText = e.target.value;
|
|
3314
|
+
renderPanelContent();
|
|
3315
|
+
updateConfigCode();
|
|
3316
|
+
});
|
|
3317
|
+
document.getElementById('sendBtnColor')?.addEventListener('input', (e) => {
|
|
3318
|
+
widgetConfig.text.sendButtonColor = e.target.value;
|
|
3319
|
+
renderPanelContent();
|
|
3320
|
+
updateConfigCode();
|
|
3321
|
+
});
|
|
3322
|
+
document.getElementById('sendBtnHover')?.addEventListener('input', (e) => {
|
|
3323
|
+
widgetConfig.text.sendButtonHoverColor = e.target.value;
|
|
3324
|
+
updateConfigCode();
|
|
3325
|
+
});
|
|
3326
|
+
document.getElementById('inputPlaceholder')?.addEventListener('input', (e) => {
|
|
3327
|
+
widgetConfig.text.inputPlaceholder = e.target.value;
|
|
3328
|
+
renderPanelContent();
|
|
3329
|
+
updateConfigCode();
|
|
3330
|
+
});
|
|
3331
|
+
document.getElementById('inputFocusColor')?.addEventListener('input', (e) => {
|
|
3332
|
+
widgetConfig.text.inputFocusColor = e.target.value;
|
|
3333
|
+
renderPanelContent();
|
|
3334
|
+
updateConfigCode();
|
|
3335
|
+
});
|
|
3336
|
+
}
|
|
3337
|
+
|
|
3338
|
+
// Start call button controls
|
|
3339
|
+
if (elementType === 'startCallButton') {
|
|
3340
|
+
document.getElementById('startCallBtnText')?.addEventListener('input', (e) => {
|
|
3341
|
+
widgetConfig.voice.startCallButtonText = e.target.value;
|
|
3342
|
+
renderPanelContent();
|
|
3343
|
+
updateConfigCode();
|
|
3344
|
+
});
|
|
3345
|
+
document.getElementById('startCallBtnColor')?.addEventListener('input', (e) => {
|
|
3346
|
+
widgetConfig.voice.startCallButtonColor = e.target.value;
|
|
3347
|
+
renderPanelContent();
|
|
3348
|
+
updateConfigCode();
|
|
3349
|
+
});
|
|
3350
|
+
document.getElementById('startCallBtnTextColor')?.addEventListener('input', (e) => {
|
|
3351
|
+
widgetConfig.voice.startCallButtonTextColor = e.target.value;
|
|
3352
|
+
renderPanelContent();
|
|
3353
|
+
updateConfigCode();
|
|
3354
|
+
});
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3357
|
+
// Mic button controls
|
|
3358
|
+
if (elementType === 'micButton') {
|
|
3359
|
+
document.getElementById('micBtnColor')?.addEventListener('input', (e) => {
|
|
3360
|
+
widgetConfig.voice.micButtonColor = e.target.value;
|
|
3361
|
+
renderPanelContent();
|
|
3362
|
+
updateConfigCode();
|
|
3363
|
+
});
|
|
3364
|
+
}
|
|
3365
|
+
|
|
3366
|
+
// Voice avatar controls (for voice interface)
|
|
3367
|
+
if (elementType === 'voiceAvatar') {
|
|
3368
|
+
document.getElementById('avatarBg')?.addEventListener('input', (e) => {
|
|
3369
|
+
widgetConfig.voice.avatarBackgroundColor = e.target.value;
|
|
3370
|
+
renderPanelContent();
|
|
3371
|
+
updateConfigCode();
|
|
3372
|
+
});
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3375
|
+
// Agent avatar controls (for conversation history)
|
|
3376
|
+
if (elementType === 'agentAvatar') {
|
|
3377
|
+
document.getElementById('agentAvatarType')?.addEventListener('change', (e) => {
|
|
3378
|
+
widgetConfig.voice.avatarType = e.target.value;
|
|
3379
|
+
// Show/hide relevant controls
|
|
3380
|
+
const iconControl = document.getElementById('agentAvatarIconControl');
|
|
3381
|
+
const imageControl = document.getElementById('agentAvatarImageControl');
|
|
3382
|
+
if (iconControl) iconControl.style.display = e.target.value === 'icon' ? 'block' : 'none';
|
|
3383
|
+
if (imageControl) imageControl.style.display = e.target.value === 'image' ? 'block' : 'none';
|
|
3384
|
+
renderPanelContent();
|
|
3385
|
+
updateConfigCode();
|
|
3386
|
+
});
|
|
3387
|
+
document.getElementById('agentAvatarIcon')?.addEventListener('input', (e) => {
|
|
3388
|
+
widgetConfig.voice.avatarIcon = e.target.value;
|
|
3389
|
+
renderPanelContent();
|
|
3390
|
+
updateConfigCode();
|
|
3391
|
+
});
|
|
3392
|
+
document.getElementById('agentAvatarImageUrl')?.addEventListener('input', (e) => {
|
|
3393
|
+
widgetConfig.voice.avatarImageUrl = e.target.value;
|
|
3394
|
+
renderPanelContent();
|
|
3395
|
+
updateConfigCode();
|
|
3396
|
+
});
|
|
3397
|
+
document.getElementById('agentAvatarBg')?.addEventListener('input', (e) => {
|
|
3398
|
+
widgetConfig.voice.avatarBackgroundColor = e.target.value;
|
|
3399
|
+
const avatars = document.querySelectorAll('.mock-history-avatar');
|
|
3400
|
+
avatars.forEach(avatar => {
|
|
3401
|
+
avatar.style.background = e.target.value;
|
|
3402
|
+
});
|
|
3403
|
+
updateConfigCode();
|
|
3404
|
+
});
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3407
|
+
// Status text controls
|
|
3408
|
+
if (elementType === 'statusTitle' || elementType === 'statusSubtitle') {
|
|
3409
|
+
document.getElementById('statusText')?.addEventListener('input', (e) => {
|
|
3410
|
+
widgetConfig.voice.statusText = e.target.value;
|
|
3411
|
+
// Update status text in both regular and compact views
|
|
3412
|
+
const statusEls = document.querySelectorAll('.mock-voice-status span, .mock-compact-status span');
|
|
3413
|
+
statusEls.forEach(el => el.textContent = e.target.value);
|
|
3414
|
+
updateConfigCode();
|
|
3415
|
+
});
|
|
3416
|
+
document.getElementById('statusTitleColor')?.addEventListener('input', (e) => {
|
|
3417
|
+
widgetConfig.voice.statusTitleColor = e.target.value;
|
|
3418
|
+
const statusEls = document.querySelectorAll('.mock-voice-status, .mock-compact-status');
|
|
3419
|
+
statusEls.forEach(el => el.style.color = e.target.value);
|
|
3420
|
+
updateConfigCode();
|
|
3421
|
+
});
|
|
3422
|
+
document.getElementById('statusDotColor')?.addEventListener('input', (e) => {
|
|
3423
|
+
widgetConfig.voice.statusDotColor = e.target.value;
|
|
3424
|
+
const dots = document.querySelectorAll('.mock-status-dot');
|
|
3425
|
+
dots.forEach(dot => dot.style.background = e.target.value);
|
|
3426
|
+
updateConfigCode();
|
|
3427
|
+
});
|
|
3428
|
+
document.getElementById('statusSubtitleColor')?.addEventListener('input', (e) => {
|
|
3429
|
+
widgetConfig.voice.statusSubtitleColor = e.target.value;
|
|
3430
|
+
renderPanelContent();
|
|
3431
|
+
updateConfigCode();
|
|
3432
|
+
});
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3435
|
+
// Live indicator controls
|
|
3436
|
+
if (elementType === 'liveIndicator') {
|
|
3437
|
+
document.getElementById('liveDotColor')?.addEventListener('input', (e) => {
|
|
3438
|
+
widgetConfig.voice.liveIndicatorDotColor = e.target.value;
|
|
3439
|
+
const dots = document.querySelectorAll('.mock-live-dot');
|
|
3440
|
+
dots.forEach(dot => dot.style.background = e.target.value);
|
|
3441
|
+
renderPanelContent();
|
|
3442
|
+
updateConfigCode(); // This will call updateActualWidget() with debounce
|
|
3443
|
+
});
|
|
3444
|
+
document.getElementById('liveTextColor')?.addEventListener('input', (e) => {
|
|
3445
|
+
widgetConfig.voice.liveIndicatorTextColor = e.target.value;
|
|
3446
|
+
const indicators = document.querySelectorAll('.mock-live-indicator');
|
|
3447
|
+
indicators.forEach(ind => ind.style.color = e.target.value);
|
|
3448
|
+
renderPanelContent();
|
|
3449
|
+
updateConfigCode(); // This will call updateActualWidget() with debounce
|
|
3450
|
+
});
|
|
3451
|
+
}
|
|
3452
|
+
|
|
3453
|
+
// Timer controls
|
|
3454
|
+
if (elementType === 'timer') {
|
|
3455
|
+
document.getElementById('timerDotColor')?.addEventListener('input', (e) => {
|
|
3456
|
+
const dots = document.querySelectorAll('.mock-timer-dot');
|
|
3457
|
+
dots.forEach(dot => dot.style.background = e.target.value);
|
|
3458
|
+
updateConfigCode();
|
|
3459
|
+
});
|
|
3460
|
+
document.getElementById('timerTextColor')?.addEventListener('input', (e) => {
|
|
3461
|
+
const timers = document.querySelectorAll('.mock-voice-timer, .mock-compact-timer');
|
|
3462
|
+
timers.forEach(timer => timer.style.color = e.target.value);
|
|
3463
|
+
updateConfigCode();
|
|
3464
|
+
});
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3467
|
+
// End call button controls
|
|
3468
|
+
if (elementType === 'endCallButton') {
|
|
3469
|
+
document.getElementById('endCallBtnColor')?.addEventListener('input', (e) => {
|
|
3470
|
+
widgetConfig.voice.endCallButtonColor = e.target.value;
|
|
3471
|
+
const buttons = document.querySelectorAll('[data-element-type="endCallButton"]');
|
|
3472
|
+
buttons.forEach(btn => btn.style.background = e.target.value);
|
|
3473
|
+
renderPanelContent();
|
|
3474
|
+
updateConfigCode();
|
|
3475
|
+
});
|
|
3476
|
+
}
|
|
3477
|
+
|
|
3478
|
+
// Speaker button controls
|
|
3479
|
+
if (elementType === 'speakerButton') {
|
|
3480
|
+
document.getElementById('speakerBtnColor')?.addEventListener('input', (e) => {
|
|
3481
|
+
widgetConfig.voice.speakerButtonColor = e.target.value;
|
|
3482
|
+
const buttons = document.querySelectorAll('[data-element-type="speakerButton"]');
|
|
3483
|
+
buttons.forEach(btn => btn.style.background = e.target.value);
|
|
3484
|
+
renderPanelContent();
|
|
3485
|
+
updateConfigCode();
|
|
3486
|
+
});
|
|
3487
|
+
}
|
|
3488
|
+
|
|
3489
|
+
// Waveform controls
|
|
3490
|
+
if (elementType === 'waveform') {
|
|
3491
|
+
document.getElementById('waveformType')?.addEventListener('change', (e) => {
|
|
3492
|
+
widgetConfig.voice.waveformType = e.target.value;
|
|
3493
|
+
// Show/hide relevant controls
|
|
3494
|
+
const colorControl = document.getElementById('waveformColorControl');
|
|
3495
|
+
const iconControl = document.getElementById('waveformIconControl');
|
|
3496
|
+
const imageControl = document.getElementById('waveformImageControl');
|
|
3497
|
+
if (colorControl) colorControl.style.display = e.target.value === 'waveform' ? 'block' : 'none';
|
|
3498
|
+
if (iconControl) iconControl.style.display = e.target.value === 'icon' ? 'block' : 'none';
|
|
3499
|
+
if (imageControl) imageControl.style.display = e.target.value === 'image' ? 'block' : 'none';
|
|
3500
|
+
renderPanelContent();
|
|
3501
|
+
updateConfigCode();
|
|
3502
|
+
});
|
|
3503
|
+
document.getElementById('waveformColor')?.addEventListener('input', (e) => {
|
|
3504
|
+
const bars = document.querySelectorAll('.mock-waveform-bar');
|
|
3505
|
+
bars.forEach(bar => bar.style.background = e.target.value);
|
|
3506
|
+
widgetConfig.voice.micButtonColor = e.target.value; // Store for config
|
|
3507
|
+
updateConfigCode();
|
|
3508
|
+
});
|
|
3509
|
+
document.getElementById('waveformIcon')?.addEventListener('input', (e) => {
|
|
3510
|
+
widgetConfig.voice.waveformIcon = e.target.value;
|
|
3511
|
+
renderPanelContent();
|
|
3512
|
+
updateConfigCode();
|
|
3513
|
+
});
|
|
3514
|
+
document.getElementById('waveformImageUrl')?.addEventListener('input', (e) => {
|
|
3515
|
+
widgetConfig.voice.waveformImageUrl = e.target.value;
|
|
3516
|
+
renderPanelContent();
|
|
3517
|
+
updateConfigCode();
|
|
3518
|
+
});
|
|
3519
|
+
}
|
|
3520
|
+
|
|
3521
|
+
// Live transcript controls
|
|
3522
|
+
if (elementType === 'liveTranscript' || elementType === 'liveTranscriptText') {
|
|
3523
|
+
document.getElementById('liveTranscriptColor')?.addEventListener('input', (e) => {
|
|
3524
|
+
widgetConfig.voice.liveTranscriptTextColor = e.target.value;
|
|
3525
|
+
const text = document.querySelector('.mock-live-text-collapsed');
|
|
3526
|
+
if (text) text.style.color = e.target.value;
|
|
3527
|
+
renderPanelContent();
|
|
3528
|
+
updateConfigCode(); // This will call updateActualWidget() with debounce
|
|
3529
|
+
});
|
|
3530
|
+
document.getElementById('liveTranscriptFontSize')?.addEventListener('input', (e) => {
|
|
3531
|
+
widgetConfig.voice.liveTranscriptFontSize = e.target.value;
|
|
3532
|
+
const text = document.querySelector('.mock-live-text-collapsed');
|
|
3533
|
+
if (text) text.style.fontSize = e.target.value;
|
|
3534
|
+
renderPanelContent();
|
|
3535
|
+
updateConfigCode(); // This will call updateActualWidget() with debounce
|
|
3536
|
+
});
|
|
3537
|
+
}
|
|
3538
|
+
|
|
3539
|
+
// Voice input controls
|
|
3540
|
+
if (elementType === 'voiceInput' || elementType === 'voiceSendButton') {
|
|
3541
|
+
document.getElementById('voiceInputPlaceholder')?.addEventListener('input', (e) => {
|
|
3542
|
+
widgetConfig.text.inputPlaceholder = e.target.value;
|
|
3543
|
+
renderPanelContent();
|
|
3544
|
+
updateConfigCode();
|
|
3545
|
+
});
|
|
3546
|
+
document.getElementById('voiceSendBtnColor')?.addEventListener('input', (e) => {
|
|
3547
|
+
widgetConfig.text.sendButtonColor = e.target.value;
|
|
3548
|
+
renderPanelContent();
|
|
3549
|
+
updateConfigCode();
|
|
3550
|
+
});
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3553
|
+
// Landing screen controls
|
|
3554
|
+
if (elementType === 'landingLogo') {
|
|
3555
|
+
document.getElementById('logoType')?.addEventListener('change', (e) => {
|
|
3556
|
+
widgetConfig.landing.logoType = e.target.value;
|
|
3557
|
+
// Show/hide relevant controls
|
|
3558
|
+
const iconControl = document.getElementById('logoIconControl');
|
|
3559
|
+
const imageControl = document.getElementById('logoImageControl');
|
|
3560
|
+
const backgroundControl = document.getElementById('logoBackgroundControl');
|
|
3561
|
+
if (iconControl) iconControl.style.display = e.target.value === 'icon' ? 'block' : 'none';
|
|
3562
|
+
if (imageControl) imageControl.style.display = e.target.value === 'image' ? 'block' : 'none';
|
|
3563
|
+
if (backgroundControl) backgroundControl.style.display = e.target.value === 'image' ? 'block' : 'none';
|
|
3564
|
+
renderPanelContent();
|
|
3565
|
+
updateConfigCode();
|
|
3566
|
+
});
|
|
3567
|
+
document.getElementById('landingLogoIcon')?.addEventListener('input', (e) => {
|
|
3568
|
+
widgetConfig.landing.logoIcon = e.target.value;
|
|
3569
|
+
widgetConfig.landing.logo = e.target.value; // Keep backward compatibility
|
|
3570
|
+
renderPanelContent();
|
|
3571
|
+
updateConfigCode();
|
|
3572
|
+
});
|
|
3573
|
+
document.getElementById('landingLogoImageUrl')?.addEventListener('input', (e) => {
|
|
3574
|
+
widgetConfig.landing.logoImageUrl = e.target.value;
|
|
3575
|
+
renderPanelContent();
|
|
3576
|
+
updateConfigCode();
|
|
3577
|
+
});
|
|
3578
|
+
document.getElementById('logoBackgroundEnabled')?.addEventListener('change', (e) => {
|
|
3579
|
+
widgetConfig.landing.logoBackgroundEnabled = e.target.checked;
|
|
3580
|
+
const colorControl = document.getElementById('logoBackgroundColorControl');
|
|
3581
|
+
if (colorControl) colorControl.style.display = e.target.checked ? 'block' : 'none';
|
|
3582
|
+
renderPanelContent();
|
|
3583
|
+
updateConfigCode();
|
|
3584
|
+
});
|
|
3585
|
+
document.getElementById('logoBackgroundColor')?.addEventListener('input', (e) => {
|
|
3586
|
+
widgetConfig.landing.logoBackgroundColor = e.target.value;
|
|
3587
|
+
renderPanelContent();
|
|
3588
|
+
updateConfigCode();
|
|
3589
|
+
});
|
|
3590
|
+
}
|
|
3591
|
+
|
|
3592
|
+
if (elementType === 'landingTitle') {
|
|
3593
|
+
document.getElementById('landingTitleText')?.addEventListener('input', (e) => {
|
|
3594
|
+
widgetConfig.landing.title = e.target.value;
|
|
3595
|
+
renderPanelContent();
|
|
3596
|
+
updateConfigCode();
|
|
3597
|
+
});
|
|
3598
|
+
document.getElementById('landingTitleColor')?.addEventListener('input', (e) => {
|
|
3599
|
+
widgetConfig.landing.titleColor = e.target.value;
|
|
3600
|
+
renderPanelContent();
|
|
3601
|
+
updateConfigCode();
|
|
3602
|
+
});
|
|
3603
|
+
}
|
|
3604
|
+
|
|
3605
|
+
if (elementType === 'landingSubtitle') {
|
|
3606
|
+
document.getElementById('landingSubtitleText')?.addEventListener('input', (e) => {
|
|
3607
|
+
widgetConfig.landing.subtitle = e.target.value;
|
|
3608
|
+
renderPanelContent();
|
|
3609
|
+
updateConfigCode();
|
|
3610
|
+
});
|
|
3611
|
+
document.getElementById('landingSubtitleColor')?.addEventListener('input', (e) => {
|
|
3612
|
+
widgetConfig.landing.subtitleColor = e.target.value;
|
|
3613
|
+
renderPanelContent();
|
|
3614
|
+
updateConfigCode();
|
|
3615
|
+
});
|
|
3616
|
+
}
|
|
3617
|
+
|
|
3618
|
+
if (elementType === 'landingBackground') {
|
|
3619
|
+
document.getElementById('landingBgColor')?.addEventListener('input', (e) => {
|
|
3620
|
+
widgetConfig.landing.backgroundColor = e.target.value;
|
|
3621
|
+
renderPanelContent();
|
|
3622
|
+
updateConfigCode();
|
|
3623
|
+
});
|
|
3624
|
+
}
|
|
3625
|
+
|
|
3626
|
+
if (elementType === 'modeCard') {
|
|
3627
|
+
document.getElementById('voiceCardTitle')?.addEventListener('input', (e) => {
|
|
3628
|
+
widgetConfig.landing.voiceCardTitle = e.target.value;
|
|
3629
|
+
renderPanelContent();
|
|
3630
|
+
updateConfigCode();
|
|
3631
|
+
});
|
|
3632
|
+
document.getElementById('textCardTitle')?.addEventListener('input', (e) => {
|
|
3633
|
+
widgetConfig.landing.textCardTitle = e.target.value;
|
|
3634
|
+
renderPanelContent();
|
|
3635
|
+
updateConfigCode();
|
|
3636
|
+
});
|
|
3637
|
+
document.getElementById('modeCardBg')?.addEventListener('input', (e) => {
|
|
3638
|
+
widgetConfig.landing.modeCardBackgroundColor = e.target.value;
|
|
3639
|
+
renderPanelContent();
|
|
3640
|
+
updateConfigCode();
|
|
3641
|
+
});
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3645
|
+
// Actual widget instance
|
|
3646
|
+
let actualWidgetInstance = null;
|
|
3647
|
+
let updateWidgetTimeout = null;
|
|
3648
|
+
let widgetManuallyClosed = false; // Track if widget was manually closed by user
|
|
3649
|
+
const AGENT_ID = 'agent_20e0b3047';
|
|
3650
|
+
const APP_ID = 'app_8LGyEp2cS6vn4GLad8WLNRlyrOq0MrvDRPXk';
|
|
3651
|
+
|
|
3652
|
+
function getTTPChatWidget() {
|
|
3653
|
+
// SDK exposes TTPChatWidget as window.TTPAgentSDK.TTPChatWidget
|
|
3654
|
+
if (window.TTPAgentSDK && window.TTPAgentSDK.TTPChatWidget) {
|
|
3655
|
+
return window.TTPAgentSDK.TTPChatWidget;
|
|
3656
|
+
}
|
|
3657
|
+
// Fallback for direct access (if SDK exposes it directly)
|
|
3658
|
+
if (window.TTPChatWidget) {
|
|
3659
|
+
return window.TTPChatWidget;
|
|
3660
|
+
}
|
|
3661
|
+
return null;
|
|
3662
|
+
}
|
|
3663
|
+
|
|
3664
|
+
// Default configuration values (used to compare and only show changed values)
|
|
3665
|
+
const defaultConfig = {
|
|
3666
|
+
direction: 'ltr',
|
|
3667
|
+
button: {
|
|
3668
|
+
size: 'medium',
|
|
3669
|
+
shape: 'circle',
|
|
3670
|
+
backgroundColor: '#FFFFFF',
|
|
3671
|
+
hoverColor: '#D3D3D3',
|
|
3672
|
+
shadow: true,
|
|
3673
|
+
shadowColor: 'rgba(0,0,0,0.15)'
|
|
3674
|
+
},
|
|
3675
|
+
icon: {
|
|
3676
|
+
type: 'custom',
|
|
3677
|
+
customImage: 'https://talktopc.com/logo192.png',
|
|
3678
|
+
size: 'medium',
|
|
3679
|
+
backgroundColor: '#FFFFFF'
|
|
3680
|
+
},
|
|
3681
|
+
panel: {
|
|
3682
|
+
width: 360,
|
|
3683
|
+
height: 550,
|
|
3684
|
+
borderRadius: 24,
|
|
3685
|
+
backgroundColor: '#FFFFFF',
|
|
3686
|
+
border: '1px solid #E5E7EB'
|
|
3687
|
+
},
|
|
3688
|
+
position: {
|
|
3689
|
+
vertical: 'bottom',
|
|
3690
|
+
horizontal: 'left',
|
|
3691
|
+
offset: { x: 20, y: 20 }
|
|
3692
|
+
},
|
|
3693
|
+
header: {
|
|
3694
|
+
title: 'Chat Assistant',
|
|
3695
|
+
backgroundColor: '#7C3AED',
|
|
3696
|
+
textColor: '#FFFFFF',
|
|
3697
|
+
showCloseButton: true,
|
|
3698
|
+
onlineIndicatorText: 'Online',
|
|
3699
|
+
onlineIndicatorColor: '#FFFFFF',
|
|
3700
|
+
onlineIndicatorDotColor: '#10b981'
|
|
3701
|
+
},
|
|
3702
|
+
messages: {
|
|
3703
|
+
userBackgroundColor: '#E5E7EB',
|
|
3704
|
+
agentBackgroundColor: '#F3F4F6',
|
|
3705
|
+
textColor: '#1F2937',
|
|
3706
|
+
userTextColor: '#1F2937',
|
|
3707
|
+
agentTextColor: '#1F2937',
|
|
3708
|
+
userAvatarIcon: '👤',
|
|
3709
|
+
agentAvatarIcon: '🤖',
|
|
3710
|
+
fontSize: '14px',
|
|
3711
|
+
borderRadius: 16
|
|
3712
|
+
},
|
|
3713
|
+
text: {
|
|
3714
|
+
sendButtonText: '→',
|
|
3715
|
+
sendButtonColor: '#7C3AED',
|
|
3716
|
+
sendButtonHoverColor: '#6D28D9',
|
|
3717
|
+
inputPlaceholder: 'Type your message...',
|
|
3718
|
+
inputFocusColor: '#7C3AED'
|
|
3719
|
+
},
|
|
3720
|
+
voice: {
|
|
3721
|
+
micButtonColor: '#7C3AED',
|
|
3722
|
+
micButtonActiveColor: '#EF4444',
|
|
3723
|
+
speakerButtonColor: '#FFFFFF',
|
|
3724
|
+
endCallButtonColor: '#ef4444',
|
|
3725
|
+
avatarBackgroundColor: '#667eea',
|
|
3726
|
+
avatarType: 'icon',
|
|
3727
|
+
avatarIcon: '🤖',
|
|
3728
|
+
avatarImageUrl: '',
|
|
3729
|
+
startCallButtonText: 'Start Call',
|
|
3730
|
+
startCallButtonColor: '#667eea',
|
|
3731
|
+
startCallButtonTextColor: '#FFFFFF',
|
|
3732
|
+
statusTitleColor: '#1e293b',
|
|
3733
|
+
statusSubtitleColor: '#64748b',
|
|
3734
|
+
statusDotColor: '#10b981',
|
|
3735
|
+
statusText: 'Listening...',
|
|
3736
|
+
liveTranscriptTextColor: '#64748b',
|
|
3737
|
+
liveTranscriptFontSize: '14px',
|
|
3738
|
+
liveIndicatorDotColor: '#10b981',
|
|
3739
|
+
liveIndicatorTextColor: '#10b981',
|
|
3740
|
+
waveformType: 'waveform',
|
|
3741
|
+
waveformIcon: '🎤',
|
|
3742
|
+
waveformImageUrl: ''
|
|
3743
|
+
},
|
|
3744
|
+
landing: {
|
|
3745
|
+
backgroundColor: 'linear-gradient(180deg, #ffffff 0%, rgba(168, 85, 247, 0.03) 100%)',
|
|
3746
|
+
logo: '🤖',
|
|
3747
|
+
logoType: 'icon',
|
|
3748
|
+
logoIcon: '🤖',
|
|
3749
|
+
logoImageUrl: '',
|
|
3750
|
+
logoBackgroundColor: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
3751
|
+
logoBackgroundEnabled: true,
|
|
3752
|
+
title: 'Welcome to AI Assistant',
|
|
3753
|
+
subtitle: 'Choose how you\'d like to interact',
|
|
3754
|
+
voiceCardTitle: 'Voice Call',
|
|
3755
|
+
textCardTitle: 'Text Chat',
|
|
3756
|
+
titleColor: '#1e293b',
|
|
3757
|
+
subtitleColor: '#64748b',
|
|
3758
|
+
modeCardBackgroundColor: '#FFFFFF'
|
|
3759
|
+
},
|
|
3760
|
+
promptAnimation: {
|
|
3761
|
+
enabled: false,
|
|
3762
|
+
text: 'Try me!',
|
|
3763
|
+
backgroundColor: 'linear-gradient(135deg, #7c3aed, #4f46e5)',
|
|
3764
|
+
textColor: '#ffffff',
|
|
3765
|
+
animationType: 'bounce',
|
|
3766
|
+
showShimmer: true,
|
|
3767
|
+
showPulseRings: true,
|
|
3768
|
+
hideAfterClick: true,
|
|
3769
|
+
hideAfterSeconds: null,
|
|
3770
|
+
position: 'top'
|
|
3771
|
+
}
|
|
3772
|
+
};
|
|
3773
|
+
|
|
3774
|
+
// Helper function to deep compare objects and return only changed properties
|
|
3775
|
+
function getChangedProperties(current, defaults) {
|
|
3776
|
+
if (current === null || current === undefined) {
|
|
3777
|
+
return undefined;
|
|
3778
|
+
}
|
|
3779
|
+
|
|
3780
|
+
if (typeof current !== 'object' || Array.isArray(current)) {
|
|
3781
|
+
// Primitive values or arrays - compare directly
|
|
3782
|
+
return JSON.stringify(current) === JSON.stringify(defaults) ? undefined : current;
|
|
3783
|
+
}
|
|
3784
|
+
|
|
3785
|
+
// Object comparison
|
|
3786
|
+
const result = {};
|
|
3787
|
+
let hasChanges = false;
|
|
3788
|
+
|
|
3789
|
+
// Check all keys in current object
|
|
3790
|
+
for (const key in current) {
|
|
3791
|
+
if (current.hasOwnProperty(key)) {
|
|
3792
|
+
const currentValue = current[key];
|
|
3793
|
+
const defaultValue = defaults[key];
|
|
3794
|
+
|
|
3795
|
+
if (defaultValue === undefined) {
|
|
3796
|
+
// Property doesn't exist in defaults - include it
|
|
3797
|
+
result[key] = currentValue;
|
|
3798
|
+
hasChanges = true;
|
|
3799
|
+
} else if (typeof currentValue === 'object' && currentValue !== null && !Array.isArray(currentValue) &&
|
|
3800
|
+
typeof defaultValue === 'object' && defaultValue !== null && !Array.isArray(defaultValue)) {
|
|
3801
|
+
// Nested object - recurse
|
|
3802
|
+
const nestedChanges = getChangedProperties(currentValue, defaultValue);
|
|
3803
|
+
if (nestedChanges !== undefined) {
|
|
3804
|
+
result[key] = nestedChanges;
|
|
3805
|
+
hasChanges = true;
|
|
3806
|
+
}
|
|
3807
|
+
} else {
|
|
3808
|
+
// Primitive or array comparison
|
|
3809
|
+
if (JSON.stringify(currentValue) !== JSON.stringify(defaultValue)) {
|
|
3810
|
+
result[key] = currentValue;
|
|
3811
|
+
hasChanges = true;
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
|
|
3817
|
+
return hasChanges ? result : undefined;
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3820
|
+
function updateConfigCode() {
|
|
3821
|
+
const codeOutput = document.getElementById('configCode');
|
|
3822
|
+
|
|
3823
|
+
// Start with required fields
|
|
3824
|
+
const displayConfig = {
|
|
3825
|
+
agentId: AGENT_ID,
|
|
3826
|
+
appId: APP_ID
|
|
3827
|
+
};
|
|
3828
|
+
|
|
3829
|
+
// Only add properties that differ from defaults
|
|
3830
|
+
const changedDirection = widgetConfig.direction !== defaultConfig.direction ? widgetConfig.direction : undefined;
|
|
3831
|
+
if (changedDirection !== undefined) {
|
|
3832
|
+
displayConfig.direction = changedDirection;
|
|
3833
|
+
}
|
|
3834
|
+
|
|
3835
|
+
const changedButton = getChangedProperties(widgetConfig.button, defaultConfig.button);
|
|
3836
|
+
if (changedButton !== undefined) {
|
|
3837
|
+
displayConfig.button = changedButton;
|
|
3838
|
+
}
|
|
3839
|
+
|
|
3840
|
+
const changedIcon = getChangedProperties(widgetConfig.icon, defaultConfig.icon);
|
|
3841
|
+
if (changedIcon !== undefined) {
|
|
3842
|
+
displayConfig.icon = changedIcon;
|
|
3843
|
+
}
|
|
3844
|
+
|
|
3845
|
+
const changedPanel = getChangedProperties(widgetConfig.panel, defaultConfig.panel);
|
|
3846
|
+
if (changedPanel !== undefined) {
|
|
3847
|
+
displayConfig.panel = changedPanel;
|
|
3848
|
+
}
|
|
3849
|
+
|
|
3850
|
+
const changedPosition = getChangedProperties(widgetConfig.position, defaultConfig.position);
|
|
3851
|
+
if (changedPosition !== undefined) {
|
|
3852
|
+
displayConfig.position = changedPosition;
|
|
3853
|
+
}
|
|
3854
|
+
|
|
3855
|
+
const changedHeader = getChangedProperties(widgetConfig.header, defaultConfig.header);
|
|
3856
|
+
if (changedHeader !== undefined) {
|
|
3857
|
+
displayConfig.header = changedHeader;
|
|
3858
|
+
}
|
|
3859
|
+
|
|
3860
|
+
const changedMessages = getChangedProperties(widgetConfig.messages, defaultConfig.messages);
|
|
3861
|
+
if (changedMessages !== undefined) {
|
|
3862
|
+
displayConfig.messages = changedMessages;
|
|
3863
|
+
}
|
|
3864
|
+
|
|
3865
|
+
const changedText = getChangedProperties(widgetConfig.text, defaultConfig.text);
|
|
3866
|
+
if (changedText !== undefined) {
|
|
3867
|
+
displayConfig.text = changedText;
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
const changedVoice = getChangedProperties(widgetConfig.voice, defaultConfig.voice);
|
|
3871
|
+
if (changedVoice !== undefined) {
|
|
3872
|
+
displayConfig.voice = changedVoice;
|
|
3873
|
+
}
|
|
3874
|
+
|
|
3875
|
+
const changedLanding = getChangedProperties(widgetConfig.landing, defaultConfig.landing);
|
|
3876
|
+
if (changedLanding !== undefined) {
|
|
3877
|
+
displayConfig.landing = changedLanding;
|
|
3878
|
+
}
|
|
3879
|
+
|
|
3880
|
+
const changedPromptAnimation = getChangedProperties(widgetConfig.promptAnimation, defaultConfig.promptAnimation);
|
|
3881
|
+
if (changedPromptAnimation !== undefined) {
|
|
3882
|
+
displayConfig.promptAnimation = changedPromptAnimation;
|
|
3883
|
+
}
|
|
3884
|
+
|
|
3885
|
+
// Format as proper JavaScript object
|
|
3886
|
+
let configStr = JSON.stringify(displayConfig, null, 2);
|
|
3887
|
+
|
|
3888
|
+
codeOutput.textContent = `const widget = new TTPAgentSDK.TTPChatWidget(${configStr});`;
|
|
3889
|
+
|
|
3890
|
+
// Always update actual widget when config changes (with debounce)
|
|
3891
|
+
if (getTTPChatWidget()) {
|
|
3892
|
+
// Debounce widget updates to avoid rapid recreations
|
|
3893
|
+
if (updateWidgetTimeout) {
|
|
3894
|
+
clearTimeout(updateWidgetTimeout);
|
|
3895
|
+
}
|
|
3896
|
+
updateWidgetTimeout = setTimeout(() => {
|
|
3897
|
+
updateActualWidget();
|
|
3898
|
+
}, 300); // Wait 300ms after last change
|
|
3899
|
+
} else {
|
|
3900
|
+
// Only warn if not during initial load (when SDK might not be loaded yet)
|
|
3901
|
+
if (!isInitializing) {
|
|
3902
|
+
console.warn('TTPChatWidget not available, cannot update actual widget');
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3907
|
+
// Function to sync actual widget state with mock panel state
|
|
3908
|
+
// Only called when mock panel state changes, not on customization changes
|
|
3909
|
+
function syncWidgetWithMockPanel() {
|
|
3910
|
+
if (!actualWidgetInstance) return;
|
|
3911
|
+
|
|
3912
|
+
setTimeout(() => {
|
|
3913
|
+
// Check if widget uses Shadow DOM (default behavior)
|
|
3914
|
+
const shadowHost = document.getElementById('ttp-widget-shadow-host');
|
|
3915
|
+
let widgetPanel;
|
|
3916
|
+
|
|
3917
|
+
if (shadowHost && shadowHost.shadowRoot) {
|
|
3918
|
+
// Widget uses Shadow DOM - access elements through shadow root
|
|
3919
|
+
widgetPanel = shadowHost.shadowRoot.querySelector('[id^="ttp-chat-widget-panel"], #text-chat-panel');
|
|
3920
|
+
} else {
|
|
3921
|
+
// Widget uses regular DOM - access elements directly
|
|
3922
|
+
widgetPanel = document.querySelector('[id^="ttp-chat-widget-panel"], #text-chat-panel');
|
|
3923
|
+
}
|
|
3924
|
+
|
|
3925
|
+
if (widgetPanel && actualWidgetInstance) {
|
|
3926
|
+
const isCurrentlyOpen = widgetPanel.classList.contains('open');
|
|
3927
|
+
|
|
3928
|
+
if (panelOpen) {
|
|
3929
|
+
// Mock panel is open - ensure widget is open (unless user manually closed it)
|
|
3930
|
+
if (!isCurrentlyOpen && !widgetManuallyClosed) {
|
|
3931
|
+
if (typeof actualWidgetInstance.open === 'function') {
|
|
3932
|
+
try {
|
|
3933
|
+
actualWidgetInstance.open();
|
|
3934
|
+
widgetManuallyClosed = false; // Reset since we're opening it
|
|
3935
|
+
} catch (e) {
|
|
3936
|
+
console.warn('⚠️ Error opening widget:', e);
|
|
3937
|
+
}
|
|
3938
|
+
} else if (typeof actualWidgetInstance.togglePanel === 'function') {
|
|
3939
|
+
try {
|
|
3940
|
+
actualWidgetInstance.togglePanel();
|
|
3941
|
+
widgetManuallyClosed = false; // Reset since we're opening it
|
|
3942
|
+
} catch (e) {
|
|
3943
|
+
console.warn('⚠️ Error opening widget:', e);
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
} else {
|
|
3948
|
+
// Mock panel is closed - ensure widget is closed
|
|
3949
|
+
if (isCurrentlyOpen) {
|
|
3950
|
+
// Widget is open, so close it
|
|
3951
|
+
widgetManuallyClosed = false; // Reset since we're closing via mock panel
|
|
3952
|
+
if (typeof actualWidgetInstance.togglePanel === 'function') {
|
|
3953
|
+
try {
|
|
3954
|
+
// togglePanel will close it if it's open
|
|
3955
|
+
actualWidgetInstance.togglePanel();
|
|
3956
|
+
} catch (e) {
|
|
3957
|
+
console.warn('⚠️ Error closing widget:', e);
|
|
3958
|
+
// Fallback: directly manipulate the DOM
|
|
3959
|
+
widgetPanel.classList.remove('open');
|
|
3960
|
+
if (actualWidgetInstance) {
|
|
3961
|
+
actualWidgetInstance.isOpen = false;
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
} else if (typeof actualWidgetInstance.close === 'function') {
|
|
3965
|
+
try {
|
|
3966
|
+
actualWidgetInstance.close();
|
|
3967
|
+
} catch (e) {
|
|
3968
|
+
console.warn('⚠️ Error closing widget:', e);
|
|
3969
|
+
// Fallback: directly manipulate the DOM
|
|
3970
|
+
widgetPanel.classList.remove('open');
|
|
3971
|
+
if (actualWidgetInstance) {
|
|
3972
|
+
actualWidgetInstance.isOpen = false;
|
|
3973
|
+
}
|
|
3974
|
+
}
|
|
3975
|
+
} else {
|
|
3976
|
+
// Fallback: directly manipulate the DOM
|
|
3977
|
+
widgetPanel.classList.remove('open');
|
|
3978
|
+
if (actualWidgetInstance) {
|
|
3979
|
+
actualWidgetInstance.isOpen = false;
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
}, 100); // Small delay to ensure widget is ready
|
|
3986
|
+
}
|
|
3987
|
+
|
|
3988
|
+
// Listen for widget close events to track manual closes
|
|
3989
|
+
function setupWidgetCloseTracking() {
|
|
3990
|
+
if (!actualWidgetInstance) return;
|
|
3991
|
+
|
|
3992
|
+
setTimeout(() => {
|
|
3993
|
+
const shadowHost = document.getElementById('ttp-widget-shadow-host');
|
|
3994
|
+
let closeBtn;
|
|
3995
|
+
|
|
3996
|
+
if (shadowHost && shadowHost.shadowRoot) {
|
|
3997
|
+
closeBtn = shadowHost.shadowRoot.querySelector('[id*="close"], .close-btn, [class*="close"]');
|
|
3998
|
+
} else {
|
|
3999
|
+
closeBtn = document.querySelector('[id*="close"], .close-btn, [class*="close"]');
|
|
4000
|
+
}
|
|
4001
|
+
|
|
4002
|
+
if (closeBtn) {
|
|
4003
|
+
// Remove any existing listeners by cloning
|
|
4004
|
+
const newCloseBtn = closeBtn.cloneNode(true);
|
|
4005
|
+
closeBtn.parentNode.replaceChild(newCloseBtn, closeBtn);
|
|
4006
|
+
|
|
4007
|
+
newCloseBtn.addEventListener('click', () => {
|
|
4008
|
+
widgetManuallyClosed = true;
|
|
4009
|
+
// Don't sync immediately - let the widget close naturally
|
|
4010
|
+
setTimeout(() => {
|
|
4011
|
+
// Only sync if mock panel is closed (user wants it closed)
|
|
4012
|
+
if (!panelOpen) {
|
|
4013
|
+
syncWidgetWithMockPanel();
|
|
4014
|
+
}
|
|
4015
|
+
}, 100);
|
|
4016
|
+
});
|
|
4017
|
+
}
|
|
4018
|
+
}, 300);
|
|
4019
|
+
}
|
|
4020
|
+
|
|
4021
|
+
function updateActualWidget() {
|
|
4022
|
+
const TTPChatWidget = getTTPChatWidget();
|
|
4023
|
+
if (!TTPChatWidget) {
|
|
4024
|
+
console.warn('TTPChatWidget not available yet');
|
|
4025
|
+
return;
|
|
4026
|
+
}
|
|
4027
|
+
|
|
4028
|
+
// Destroy existing widget if it exists
|
|
4029
|
+
if (actualWidgetInstance) {
|
|
4030
|
+
try {
|
|
4031
|
+
// Try to call destroy method if it exists
|
|
4032
|
+
if (typeof actualWidgetInstance.destroy === 'function') {
|
|
4033
|
+
actualWidgetInstance.destroy();
|
|
4034
|
+
}
|
|
4035
|
+
|
|
4036
|
+
// Remove widget elements from DOM (multiple possible selectors)
|
|
4037
|
+
const selectors = [
|
|
4038
|
+
'[id^="ttp-chat-widget-button"]',
|
|
4039
|
+
'[id^="ttp-chat-widget-panel"]',
|
|
4040
|
+
'[id*="ttp-chat"]',
|
|
4041
|
+
'.ttp-chat-widget-button',
|
|
4042
|
+
'.ttp-chat-widget-panel',
|
|
4043
|
+
'[class*="ttp-chat"]'
|
|
4044
|
+
];
|
|
4045
|
+
|
|
4046
|
+
let removedCount = 0;
|
|
4047
|
+
selectors.forEach(selector => {
|
|
4048
|
+
const elements = document.querySelectorAll(selector);
|
|
4049
|
+
elements.forEach(el => {
|
|
4050
|
+
if (el && el.parentNode) {
|
|
4051
|
+
el.parentNode.removeChild(el);
|
|
4052
|
+
removedCount++;
|
|
4053
|
+
}
|
|
4054
|
+
});
|
|
4055
|
+
});
|
|
4056
|
+
|
|
4057
|
+
actualWidgetInstance = null;
|
|
4058
|
+
|
|
4059
|
+
// Wait a bit to ensure DOM is cleaned up before creating new widget
|
|
4060
|
+
setTimeout(() => {
|
|
4061
|
+
createNewWidget(TTPChatWidget);
|
|
4062
|
+
}, 150);
|
|
4063
|
+
} catch (e) {
|
|
4064
|
+
console.warn('Error removing old widget:', e);
|
|
4065
|
+
actualWidgetInstance = null;
|
|
4066
|
+
setTimeout(() => {
|
|
4067
|
+
createNewWidget(TTPChatWidget);
|
|
4068
|
+
}, 150);
|
|
4069
|
+
}
|
|
4070
|
+
} else {
|
|
4071
|
+
createNewWidget(TTPChatWidget);
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
|
|
4075
|
+
function createNewWidget(TTPChatWidget) {
|
|
4076
|
+
|
|
4077
|
+
// Create config for actual widget - use the same config as mock but positioned differently
|
|
4078
|
+
// Deep copy to avoid mutations and ensure fresh config
|
|
4079
|
+
const actualConfig = {
|
|
4080
|
+
agentId: AGENT_ID,
|
|
4081
|
+
appId: APP_ID,
|
|
4082
|
+
direction: widgetConfig.direction,
|
|
4083
|
+
button: JSON.parse(JSON.stringify(widgetConfig.button)),
|
|
4084
|
+
icon: JSON.parse(JSON.stringify(widgetConfig.icon)),
|
|
4085
|
+
panel: JSON.parse(JSON.stringify(widgetConfig.panel)),
|
|
4086
|
+
header: JSON.parse(JSON.stringify(widgetConfig.header)), // Deep copy header to ensure color is included
|
|
4087
|
+
messages: JSON.parse(JSON.stringify(widgetConfig.messages)),
|
|
4088
|
+
text: JSON.parse(JSON.stringify(widgetConfig.text)),
|
|
4089
|
+
voice: JSON.parse(JSON.stringify(widgetConfig.voice)),
|
|
4090
|
+
landing: JSON.parse(JSON.stringify(widgetConfig.landing)),
|
|
4091
|
+
promptAnimation: JSON.parse(JSON.stringify(widgetConfig.promptAnimation || {
|
|
4092
|
+
enabled: false,
|
|
4093
|
+
text: 'Try me!',
|
|
4094
|
+
backgroundColor: 'linear-gradient(135deg, #7c3aed, #4f46e5)',
|
|
4095
|
+
textColor: '#ffffff',
|
|
4096
|
+
animationType: 'bounce',
|
|
4097
|
+
showShimmer: true,
|
|
4098
|
+
showPulseRings: true,
|
|
4099
|
+
hideAfterClick: true,
|
|
4100
|
+
hideAfterSeconds: null,
|
|
4101
|
+
position: 'top'
|
|
4102
|
+
})), // Include promptAnimation config
|
|
4103
|
+
behavior: {
|
|
4104
|
+
startOpen: panelOpen, // Sync with mock panel state
|
|
4105
|
+
autoOpen: panelOpen, // Sync with mock panel state
|
|
4106
|
+
mode: 'unified'
|
|
4107
|
+
},
|
|
4108
|
+
position: {
|
|
4109
|
+
...widgetConfig.position,
|
|
4110
|
+
// Use the same position as configured (no inversion)
|
|
4111
|
+
offset: {
|
|
4112
|
+
x: widgetConfig.position.offset.x,
|
|
4113
|
+
y: widgetConfig.position.offset.y
|
|
4114
|
+
}
|
|
4115
|
+
}
|
|
4116
|
+
};
|
|
4117
|
+
|
|
4118
|
+
try {
|
|
4119
|
+
// Reset manual close tracking when widget is recreated (unless user manually closed it)
|
|
4120
|
+
// Don't reset if widget was manually closed and mock panel is also closed
|
|
4121
|
+
if (panelOpen) {
|
|
4122
|
+
widgetManuallyClosed = false;
|
|
4123
|
+
}
|
|
4124
|
+
|
|
4125
|
+
// Debug: Log the config being passed to widget
|
|
4126
|
+
console.log('🔧 Creating widget with config:', {
|
|
4127
|
+
...actualConfig,
|
|
4128
|
+
promptAnimation: actualConfig.promptAnimation
|
|
4129
|
+
});
|
|
4130
|
+
|
|
4131
|
+
actualWidgetInstance = new TTPChatWidget(actualConfig);
|
|
4132
|
+
|
|
4133
|
+
// Debug: Verify promptAnimation was received
|
|
4134
|
+
if (actualWidgetInstance && actualWidgetInstance.config) {
|
|
4135
|
+
console.log('✅ Widget created. promptAnimation config:', actualWidgetInstance.config.promptAnimation);
|
|
4136
|
+
}
|
|
4137
|
+
|
|
4138
|
+
// Setup tracking for manual closes
|
|
4139
|
+
setupWidgetCloseTracking();
|
|
4140
|
+
|
|
4141
|
+
// Only sync if mock panel is open (don't force open if user closed it)
|
|
4142
|
+
if (panelOpen && !widgetManuallyClosed) {
|
|
4143
|
+
setTimeout(() => {
|
|
4144
|
+
syncWidgetWithMockPanel();
|
|
4145
|
+
}, 200);
|
|
4146
|
+
}
|
|
4147
|
+
} catch (error) {
|
|
4148
|
+
console.error('❌ Error creating actual widget:', error);
|
|
4149
|
+
console.error('Error details:', error.stack);
|
|
4150
|
+
console.error('Config used:', actualConfig);
|
|
4151
|
+
alert('Failed to create widget. Check console for details. Error: ' + error.message);
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
|
|
4155
|
+
function initActualWidget() {
|
|
4156
|
+
const TTPChatWidget = getTTPChatWidget();
|
|
4157
|
+
if (!TTPChatWidget) {
|
|
4158
|
+
setTimeout(initActualWidget, 100);
|
|
4159
|
+
return;
|
|
4160
|
+
}
|
|
4161
|
+
|
|
4162
|
+
updateActualWidget();
|
|
4163
|
+
// Mark initialization as complete
|
|
4164
|
+
isInitializing = false;
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4167
|
+
function resetToDefaults() {
|
|
4168
|
+
widgetConfig = {
|
|
4169
|
+
button: {
|
|
4170
|
+
size: 'medium',
|
|
4171
|
+
shape: 'circle',
|
|
4172
|
+
backgroundColor: '#FFFFFF',
|
|
4173
|
+
hoverColor: '#D3D3D3', // SDK default: light gray
|
|
4174
|
+
shadow: true,
|
|
4175
|
+
shadowColor: 'rgba(0,0,0,0.15)'
|
|
4176
|
+
},
|
|
4177
|
+
icon: {
|
|
4178
|
+
type: 'custom',
|
|
4179
|
+
customImage: 'https://talktopc.com/logo192.png',
|
|
4180
|
+
size: 'medium',
|
|
4181
|
+
backgroundColor: '#FFFFFF'
|
|
4182
|
+
},
|
|
4183
|
+
panel: {
|
|
4184
|
+
width: 360,
|
|
4185
|
+
height: 550, // Updated from SDK default 500
|
|
4186
|
+
borderRadius: 24,
|
|
4187
|
+
backgroundColor: '#FFFFFF',
|
|
4188
|
+
border: '1px solid #E5E7EB'
|
|
4189
|
+
},
|
|
4190
|
+
direction: 'ltr',
|
|
4191
|
+
position: {
|
|
4192
|
+
vertical: 'bottom',
|
|
4193
|
+
horizontal: 'left',
|
|
4194
|
+
offset: { x: 20, y: 20 }
|
|
4195
|
+
},
|
|
4196
|
+
header: {
|
|
4197
|
+
title: 'Chat Assistant',
|
|
4198
|
+
backgroundColor: '#7C3AED',
|
|
4199
|
+
textColor: '#FFFFFF',
|
|
4200
|
+
showCloseButton: true,
|
|
4201
|
+
onlineIndicatorText: 'Online',
|
|
4202
|
+
onlineIndicatorColor: '#FFFFFF',
|
|
4203
|
+
onlineIndicatorDotColor: '#10b981'
|
|
4204
|
+
},
|
|
4205
|
+
messages: {
|
|
4206
|
+
userBackgroundColor: '#E5E7EB',
|
|
4207
|
+
agentBackgroundColor: '#F3F4F6',
|
|
4208
|
+
textColor: '#1F2937', // Fallback for backward compatibility
|
|
4209
|
+
userTextColor: '#1F2937',
|
|
4210
|
+
agentTextColor: '#1F2937',
|
|
4211
|
+
userAvatarIcon: '👤',
|
|
4212
|
+
agentAvatarIcon: '🤖',
|
|
4213
|
+
fontSize: '14px',
|
|
4214
|
+
borderRadius: 16
|
|
4215
|
+
},
|
|
4216
|
+
text: {
|
|
4217
|
+
sendButtonText: '→',
|
|
4218
|
+
sendButtonColor: '#7C3AED',
|
|
4219
|
+
sendButtonHoverColor: '#6D28D9',
|
|
4220
|
+
inputPlaceholder: 'Type your message...',
|
|
4221
|
+
inputFocusColor: '#7C3AED'
|
|
4222
|
+
},
|
|
4223
|
+
voice: {
|
|
4224
|
+
micButtonColor: '#7C3AED',
|
|
4225
|
+
micButtonActiveColor: '#EF4444',
|
|
4226
|
+
speakerButtonColor: '#FFFFFF',
|
|
4227
|
+
endCallButtonColor: '#ef4444',
|
|
4228
|
+
avatarBackgroundColor: '#667eea',
|
|
4229
|
+
avatarType: 'icon',
|
|
4230
|
+
avatarIcon: '🤖',
|
|
4231
|
+
avatarImageUrl: '',
|
|
4232
|
+
startCallButtonText: 'Start Call',
|
|
4233
|
+
startCallButtonColor: '#667eea',
|
|
4234
|
+
startCallButtonTextColor: '#FFFFFF',
|
|
4235
|
+
statusTitleColor: '#1e293b',
|
|
4236
|
+
statusSubtitleColor: '#64748b',
|
|
4237
|
+
statusDotColor: '#10b981',
|
|
4238
|
+
statusText: 'Listening...',
|
|
4239
|
+
liveTranscriptTextColor: '#64748b',
|
|
4240
|
+
liveTranscriptFontSize: '14px',
|
|
4241
|
+
liveIndicatorDotColor: '#10b981',
|
|
4242
|
+
liveIndicatorTextColor: '#10b981',
|
|
4243
|
+
waveformType: 'waveform',
|
|
4244
|
+
waveformIcon: '🎤',
|
|
4245
|
+
waveformImageUrl: ''
|
|
4246
|
+
},
|
|
4247
|
+
landing: {
|
|
4248
|
+
logo: '🤖',
|
|
4249
|
+
logoType: 'icon',
|
|
4250
|
+
logoIcon: '🤖',
|
|
4251
|
+
logoImageUrl: '',
|
|
4252
|
+
logoBackgroundColor: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
4253
|
+
logoBackgroundEnabled: true,
|
|
4254
|
+
backgroundColor: 'linear-gradient(180deg, #ffffff 0%, rgba(168, 85, 247, 0.03) 100%)',
|
|
4255
|
+
title: 'Welcome to AI Assistant',
|
|
4256
|
+
subtitle: 'Choose how you\'d like to interact',
|
|
4257
|
+
voiceCardTitle: 'Voice Call',
|
|
4258
|
+
textCardTitle: 'Text Chat',
|
|
4259
|
+
titleColor: '#1e293b',
|
|
4260
|
+
subtitleColor: '#64748b',
|
|
4261
|
+
modeCardBackgroundColor: '#FFFFFF'
|
|
4262
|
+
}
|
|
4263
|
+
};
|
|
4264
|
+
|
|
4265
|
+
initMockWidget();
|
|
4266
|
+
updateMockWidgetPosition();
|
|
4267
|
+
updateConfigCode();
|
|
4268
|
+
selectedElement = null;
|
|
4269
|
+
document.querySelectorAll('.element-highlight').forEach(el => {
|
|
4270
|
+
el.classList.remove('element-highlight');
|
|
4271
|
+
});
|
|
4272
|
+
showCustomizationControls('default');
|
|
4273
|
+
}
|
|
4274
|
+
|
|
4275
|
+
// No edit mode toggle needed - single click = edit, double click = interact
|
|
4276
|
+
|
|
4277
|
+
// Initialize
|
|
4278
|
+
document.getElementById('resetBtn').addEventListener('click', resetToDefaults);
|
|
4279
|
+
document.getElementById('togglePanelBtn').addEventListener('click', () => {
|
|
4280
|
+
panelOpen = !panelOpen;
|
|
4281
|
+
document.getElementById('mockPanel').classList.toggle('open');
|
|
4282
|
+
if (panelOpen) {
|
|
4283
|
+
renderPanelContent();
|
|
4284
|
+
}
|
|
4285
|
+
// Update prompt bubble visibility based on panel state
|
|
4286
|
+
const mockButton = document.getElementById('mockButton');
|
|
4287
|
+
if (mockButton) {
|
|
4288
|
+
updatePromptBubble(mockButton);
|
|
4289
|
+
}
|
|
4290
|
+
// Sync actual widget state with mock panel
|
|
4291
|
+
syncWidgetWithMockPanel();
|
|
4292
|
+
});
|
|
4293
|
+
|
|
4294
|
+
// Make panel selectable (click on panel border/background)
|
|
4295
|
+
document.getElementById('mockPanel').addEventListener('click', (e) => {
|
|
4296
|
+
// Only select panel if clicking on the panel itself or empty space, not on child elements
|
|
4297
|
+
if (e.target === e.currentTarget ||
|
|
4298
|
+
(e.target.classList.contains('mock-panel-content') && !e.target.querySelector(':hover'))) {
|
|
4299
|
+
selectElement('panel', e.currentTarget);
|
|
4300
|
+
}
|
|
4301
|
+
});
|
|
4302
|
+
|
|
4303
|
+
// Add a way to select panel via a small indicator
|
|
4304
|
+
function addPanelSelector() {
|
|
4305
|
+
const panel = document.getElementById('mockPanel');
|
|
4306
|
+
if (!panel.querySelector('.panel-selector')) {
|
|
4307
|
+
const selector = document.createElement('div');
|
|
4308
|
+
selector.className = 'panel-selector';
|
|
4309
|
+
selector.style.cssText = 'position: absolute; top: 8px; right: 8px; width: 24px; height: 24px; background: rgba(102, 126, 234, 0.2); border: 2px dashed #667eea; border-radius: 4px; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 12px; z-index: 1001;';
|
|
4310
|
+
selector.textContent = '⚙️';
|
|
4311
|
+
selector.title = 'Click to customize panel';
|
|
4312
|
+
selector.addEventListener('click', (e) => {
|
|
4313
|
+
e.stopPropagation();
|
|
4314
|
+
selectElement('panel', panel);
|
|
4315
|
+
});
|
|
4316
|
+
panel.appendChild(selector);
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
|
|
4320
|
+
// Initialize mock widget and position
|
|
4321
|
+
initMockWidget();
|
|
4322
|
+
// Ensure position is set after initialization
|
|
4323
|
+
setTimeout(() => {
|
|
4324
|
+
updateMockWidgetPosition();
|
|
4325
|
+
}, 50);
|
|
4326
|
+
updateConfigCode();
|
|
4327
|
+
|
|
4328
|
+
// Initialize actual widget when SDK loads
|
|
4329
|
+
window.checkAndInitWidget = function() {
|
|
4330
|
+
const TTPChatWidget = getTTPChatWidget();
|
|
4331
|
+
if (TTPChatWidget) {
|
|
4332
|
+
initActualWidget();
|
|
4333
|
+
// Mark initialization as complete once SDK is ready
|
|
4334
|
+
isInitializing = false;
|
|
4335
|
+
} else {
|
|
4336
|
+
setTimeout(window.checkAndInitWidget, 100);
|
|
4337
|
+
}
|
|
4338
|
+
};
|
|
4339
|
+
|
|
4340
|
+
// Start checking for SDK (will also be called when script loads)
|
|
4341
|
+
window.checkAndInitWidget();
|
|
4342
|
+
|
|
4343
|
+
// Debug helper - expose widget instance globally for inspection
|
|
4344
|
+
window.debugWidget = function() {
|
|
4345
|
+
console.log('=== Widget Debug Info ===');
|
|
4346
|
+
console.log('actualWidgetInstance:', actualWidgetInstance);
|
|
4347
|
+
console.log('window.TTPAgentSDK:', window.TTPAgentSDK);
|
|
4348
|
+
console.log('TTPChatWidget available:', !!getTTPChatWidget());
|
|
4349
|
+
const widgetElements = document.querySelectorAll('[id*="ttp"], [class*="ttp"]');
|
|
4350
|
+
console.log('Widget elements in DOM:', widgetElements.length);
|
|
4351
|
+
widgetElements.forEach((el, idx) => {
|
|
4352
|
+
const style = window.getComputedStyle(el);
|
|
4353
|
+
console.log(`Element ${idx + 1}:`, {
|
|
4354
|
+
id: el.id,
|
|
4355
|
+
className: el.className,
|
|
4356
|
+
display: style.display,
|
|
4357
|
+
visibility: style.visibility,
|
|
4358
|
+
opacity: style.opacity,
|
|
4359
|
+
zIndex: style.zIndex
|
|
4360
|
+
});
|
|
4361
|
+
});
|
|
4362
|
+
return {
|
|
4363
|
+
instance: actualWidgetInstance,
|
|
4364
|
+
elements: Array.from(widgetElements),
|
|
4365
|
+
sdk: window.TTPAgentSDK
|
|
4366
|
+
};
|
|
4367
|
+
};
|
|
4368
|
+
</script>
|
|
4369
|
+
|
|
4370
|
+
<!-- Load the widget SDK -->
|
|
4371
|
+
<script src="/dist/agent-widget.js?v=2.34.2" onload="if (typeof window.checkAndInitWidget === 'function') setTimeout(window.checkAndInitWidget, 100);" onerror="console.error('❌ Failed to load SDK script from /dist/agent-widget.js'); alert('Failed to load SDK. Check console for details.');"></script>
|
|
4372
|
+
</body>
|
|
4373
|
+
</html>
|