ttp-agent-sdk 2.34.6 → 2.34.8

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