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.
@@ -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>