aumera-on-screen-widget 0.0.19 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/df-btn.js +272 -67
- package/package.json +1 -1
package/df-btn.js
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
const transform = `w_${width},h_${height},c_fill,f_auto,q_auto`;
|
|
32
32
|
|
|
33
33
|
// Insert transformation right after `/upload/`
|
|
34
|
-
return url.replace(
|
|
34
|
+
return url.replace("/upload/", `/upload/${transform}/`);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// Async function to fetch chatbot settings from GraphQL
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"env:",
|
|
46
46
|
env,
|
|
47
47
|
"language:",
|
|
48
|
-
language
|
|
48
|
+
language,
|
|
49
49
|
);
|
|
50
50
|
|
|
51
51
|
const query = `
|
|
@@ -81,14 +81,14 @@
|
|
|
81
81
|
console.log(
|
|
82
82
|
"[OSW Widget] Response status:",
|
|
83
83
|
response.status,
|
|
84
|
-
response.statusText
|
|
84
|
+
response.statusText,
|
|
85
85
|
);
|
|
86
86
|
|
|
87
87
|
if (!response.ok) {
|
|
88
88
|
console.error(
|
|
89
89
|
"[OSW Widget] Failed to fetch - Status:",
|
|
90
90
|
response.status,
|
|
91
|
-
response.statusText
|
|
91
|
+
response.statusText,
|
|
92
92
|
);
|
|
93
93
|
const errorText = await response
|
|
94
94
|
.text()
|
|
@@ -103,13 +103,13 @@
|
|
|
103
103
|
if (result.data?.oswChatbotSettingsPublic?.success) {
|
|
104
104
|
console.log(
|
|
105
105
|
"[OSW Widget] Settings retrieved successfully:",
|
|
106
|
-
result.data.oswChatbotSettingsPublic.settings
|
|
106
|
+
result.data.oswChatbotSettingsPublic.settings,
|
|
107
107
|
);
|
|
108
108
|
return result.data.oswChatbotSettingsPublic.settings;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
console.warn(
|
|
112
|
-
"[OSW Widget] GraphQL query succeeded but success=false or missing data"
|
|
112
|
+
"[OSW Widget] GraphQL query succeeded but success=false or missing data",
|
|
113
113
|
);
|
|
114
114
|
return null;
|
|
115
115
|
} catch (error) {
|
|
@@ -119,6 +119,17 @@
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// Helper function to determine if a color is dark based on luminance
|
|
123
|
+
function isColorDark(hexColor) {
|
|
124
|
+
if (!hexColor) return false;
|
|
125
|
+
const hex = hexColor.replace("#", "");
|
|
126
|
+
const r = parseInt(hex.substr(0, 2), 16) / 255;
|
|
127
|
+
const g = parseInt(hex.substr(2, 2), 16) / 255;
|
|
128
|
+
const b = parseInt(hex.substr(4, 2), 16) / 255;
|
|
129
|
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
130
|
+
return luminance < 0.5;
|
|
131
|
+
}
|
|
132
|
+
|
|
122
133
|
// Default configuration with fallback values
|
|
123
134
|
const defaultConfig = {
|
|
124
135
|
env: "prod",
|
|
@@ -189,10 +200,10 @@
|
|
|
189
200
|
// Fetch settings from GraphQL and merge with config
|
|
190
201
|
if (!config.orgId) {
|
|
191
202
|
console.error(
|
|
192
|
-
"[OSW Widget] No organization ID provided! Widget will not be displayed."
|
|
203
|
+
"[OSW Widget] No organization ID provided! Widget will not be displayed.",
|
|
193
204
|
);
|
|
194
205
|
console.error(
|
|
195
|
-
"[OSW Widget] Add orgId attribute to the script tag: <script id='df-btn' orgId='YOUR_ORG_ID' ...>"
|
|
206
|
+
"[OSW Widget] Add orgId attribute to the script tag: <script id='df-btn' orgId='YOUR_ORG_ID' ...>",
|
|
196
207
|
);
|
|
197
208
|
return; // Exit if no orgId
|
|
198
209
|
}
|
|
@@ -200,12 +211,12 @@
|
|
|
200
211
|
const settings = await fetchChatbotSettings(
|
|
201
212
|
config.orgId,
|
|
202
213
|
config.env,
|
|
203
|
-
detectedLang
|
|
214
|
+
detectedLang,
|
|
204
215
|
);
|
|
205
216
|
|
|
206
217
|
if (!settings) {
|
|
207
218
|
console.warn(
|
|
208
|
-
"[OSW Widget] Failed to fetch chatbot settings. Using fallback configuration."
|
|
219
|
+
"[OSW Widget] Failed to fetch chatbot settings. Using fallback configuration.",
|
|
209
220
|
);
|
|
210
221
|
console.warn("[OSW Widget] Check:");
|
|
211
222
|
console.warn("[OSW Widget] 1. Network tab for CORS errors");
|
|
@@ -224,7 +235,8 @@
|
|
|
224
235
|
config.backgroundDark =
|
|
225
236
|
settings.oswColourBackgroundDark || config.backgroundDark;
|
|
226
237
|
// Update hasDarkMode if GraphQL provides dark background
|
|
227
|
-
config.hasDarkMode =
|
|
238
|
+
config.hasDarkMode =
|
|
239
|
+
config.hasDarkMode || !!settings.oswColourBackgroundDark;
|
|
228
240
|
config.fontColor = settings.oswColourFontLight || config.fontColor;
|
|
229
241
|
config.fontColorDark = settings.oswColourFontDark || config.fontColorDark;
|
|
230
242
|
config.showNotification =
|
|
@@ -237,7 +249,7 @@
|
|
|
237
249
|
config.logoDark = cloudinaryResize(settings.chatbotMask, 64, 64);
|
|
238
250
|
console.log(
|
|
239
251
|
"[OSW Widget] Using chatbotMask as logo:",
|
|
240
|
-
settings.chatbotMask
|
|
252
|
+
settings.chatbotMask,
|
|
241
253
|
);
|
|
242
254
|
}
|
|
243
255
|
}
|
|
@@ -350,6 +362,18 @@
|
|
|
350
362
|
height: 48px;
|
|
351
363
|
}
|
|
352
364
|
|
|
365
|
+
/* Make header transparent when chat is open */
|
|
366
|
+
.df-btn:not(.df-closed) .df-btn-header {
|
|
367
|
+
background: transparent;
|
|
368
|
+
position: absolute;
|
|
369
|
+
top: 0;
|
|
370
|
+
left: 0;
|
|
371
|
+
right: 0;
|
|
372
|
+
z-index: 10001;
|
|
373
|
+
padding: 12px;
|
|
374
|
+
box-sizing: border-box;
|
|
375
|
+
}
|
|
376
|
+
|
|
353
377
|
.df-btn-text:before {
|
|
354
378
|
background-position: center;
|
|
355
379
|
background-repeat: no-repeat;
|
|
@@ -371,11 +395,12 @@
|
|
|
371
395
|
}
|
|
372
396
|
|
|
373
397
|
.df-btn:not(.df-closed){
|
|
374
|
-
border-radius: 16px
|
|
398
|
+
border-radius: 16px;
|
|
399
|
+
overflow: hidden;
|
|
375
400
|
}
|
|
376
401
|
|
|
377
|
-
.df-btn:not(.df-closed)
|
|
378
|
-
|
|
402
|
+
.df-btn:not(.df-closed) .df-btn-text:before {
|
|
403
|
+
display: none;
|
|
379
404
|
}
|
|
380
405
|
|
|
381
406
|
.df-btn-content {
|
|
@@ -392,6 +417,8 @@
|
|
|
392
417
|
${config.position === "left" ? "float: left;" : "float: right;"}
|
|
393
418
|
opacity: 1;
|
|
394
419
|
border-radius: 0 0 16px 16px;
|
|
420
|
+
/* iOS Safari keyboard fix: Explicit background to prevent transparency bleed */
|
|
421
|
+
background-color: ${config.background || "#FEFFFF"};
|
|
395
422
|
}
|
|
396
423
|
|
|
397
424
|
.df-btn:not(.df-closed) > .df-btn-content {
|
|
@@ -406,8 +433,8 @@
|
|
|
406
433
|
}
|
|
407
434
|
|
|
408
435
|
.maximize-minimize-btn {
|
|
409
|
-
width:
|
|
410
|
-
height:
|
|
436
|
+
width: auto;
|
|
437
|
+
height: auto;
|
|
411
438
|
cursor: pointer;
|
|
412
439
|
margin-right: 12px;
|
|
413
440
|
display: flex;
|
|
@@ -425,16 +452,12 @@
|
|
|
425
452
|
bottom: 0 !important;
|
|
426
453
|
margin: 0 !important;
|
|
427
454
|
border-radius: 0 !important;
|
|
428
|
-
/* Handle safe areas on mobile devices */
|
|
429
|
-
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
430
455
|
}
|
|
431
456
|
.df-btn.df-maximized .df-btn-content {
|
|
432
457
|
width: 100vw !important;
|
|
433
458
|
max-width: 100vw !important;
|
|
434
|
-
height:
|
|
435
|
-
height:
|
|
436
|
-
max-height: calc(100vh - 56px) !important;
|
|
437
|
-
max-height: calc(100dvh - env(safe-area-inset-bottom, 0px)) !important;
|
|
459
|
+
height: 100dvh !important;
|
|
460
|
+
max-height: 100dvh !important;
|
|
438
461
|
opacity: 1 !important;
|
|
439
462
|
float: none !important;
|
|
440
463
|
display: block !important;
|
|
@@ -446,8 +469,8 @@
|
|
|
446
469
|
opacity: 0 !important;
|
|
447
470
|
}
|
|
448
471
|
.close-btn {
|
|
449
|
-
width:
|
|
450
|
-
height:
|
|
472
|
+
width: auto;
|
|
473
|
+
height: auto;
|
|
451
474
|
cursor: pointer;
|
|
452
475
|
margin-left: 12px;
|
|
453
476
|
display: flex;
|
|
@@ -455,6 +478,18 @@
|
|
|
455
478
|
justify-content: center;
|
|
456
479
|
}
|
|
457
480
|
|
|
481
|
+
.df-icon-container {
|
|
482
|
+
display: flex;
|
|
483
|
+
align-items: center;
|
|
484
|
+
justify-content: center;
|
|
485
|
+
width: 36px;
|
|
486
|
+
height: 36px;
|
|
487
|
+
border-radius: 8px;
|
|
488
|
+
background-color: ${config.background || "#FFFFFF"}E6;
|
|
489
|
+
backdrop-filter: blur(4px);
|
|
490
|
+
-webkit-backdrop-filter: blur(4px);
|
|
491
|
+
}
|
|
492
|
+
|
|
458
493
|
/* Animation classes for FLIP */
|
|
459
494
|
.df-animating {
|
|
460
495
|
transition: transform .45s cubic-bezier(.4, 0, .2, 1);
|
|
@@ -474,16 +509,12 @@
|
|
|
474
509
|
.df-btn:not(.df-closed) {
|
|
475
510
|
margin: 0px;
|
|
476
511
|
border-radius: 0px;
|
|
477
|
-
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
478
512
|
}
|
|
479
513
|
|
|
480
514
|
.df-btn:not(.df-closed) > .df-btn-content {
|
|
481
515
|
width: 100vw;
|
|
482
|
-
max-height: 100vh;
|
|
483
516
|
max-height: 100dvh;
|
|
484
|
-
height:
|
|
485
|
-
height: calc(100dvh - 56px - env(safe-area-inset-bottom, 0px));
|
|
486
|
-
padding-bottom: 0px
|
|
517
|
+
height: 100dvh;
|
|
487
518
|
}
|
|
488
519
|
|
|
489
520
|
.df-btn-text {
|
|
@@ -491,14 +522,26 @@
|
|
|
491
522
|
height: 56px;
|
|
492
523
|
font-size: 16px;
|
|
493
524
|
}
|
|
525
|
+
|
|
526
|
+
/* Hide the pseudo-element on mobile when open (we use .header-logo instead) */
|
|
527
|
+
.df-btn:not(.df-closed) > .df-btn-header > .df-btn-text:before {
|
|
528
|
+
display: none;
|
|
529
|
+
}
|
|
494
530
|
}
|
|
495
531
|
|
|
496
|
-
${
|
|
532
|
+
${
|
|
533
|
+
config.hasDarkMode
|
|
534
|
+
? `
|
|
497
535
|
@media (prefers-color-scheme: dark){
|
|
498
536
|
.df-btn {
|
|
499
537
|
background-color: ${config.backgroundDark || "#171717"}
|
|
500
538
|
}
|
|
501
539
|
|
|
540
|
+
/* iOS Safari keyboard fix: Explicit background for dark mode */
|
|
541
|
+
.df-btn-content {
|
|
542
|
+
background-color: ${config.backgroundDark || "#171717"}
|
|
543
|
+
}
|
|
544
|
+
|
|
502
545
|
.df-btn-text {
|
|
503
546
|
color: ${config.fontColorDark || "#FFFFFF"}
|
|
504
547
|
}
|
|
@@ -511,10 +554,6 @@
|
|
|
511
554
|
}')
|
|
512
555
|
}
|
|
513
556
|
|
|
514
|
-
.df-btn:not(.df-closed) > .df-btn-text:before {
|
|
515
|
-
background-image: url('${origin}/assets/close_dark.svg')
|
|
516
|
-
}
|
|
517
|
-
|
|
518
557
|
.df-notification {
|
|
519
558
|
background: ${config.backgroundDark || "#171717"};
|
|
520
559
|
color: ${config.fontColorDark || "#FFFFFF"};
|
|
@@ -523,8 +562,14 @@
|
|
|
523
562
|
.df-notification:after {
|
|
524
563
|
border-top: 6px solid ${config.backgroundDark || "#171717"};
|
|
525
564
|
}
|
|
565
|
+
|
|
566
|
+
.df-icon-container {
|
|
567
|
+
background-color: ${config.backgroundDark || "#171717"}E6;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
`
|
|
571
|
+
: ""
|
|
526
572
|
}
|
|
527
|
-
` : ''}
|
|
528
573
|
|
|
529
574
|
@keyframes shake {
|
|
530
575
|
0%, 100% { transform: translateX(0); }
|
|
@@ -546,6 +591,57 @@
|
|
|
546
591
|
display: none;
|
|
547
592
|
}
|
|
548
593
|
|
|
594
|
+
/* iOS Safari keyboard background bleed fix: Override vh with dvh */
|
|
595
|
+
@supports (-webkit-touch-callout: none) {
|
|
596
|
+
.df-btn-content {
|
|
597
|
+
max-height: 100dvh !important;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
.df-btn.df-maximized {
|
|
601
|
+
height: 100dvh !important;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.df-btn.df-maximized .df-btn-content {
|
|
605
|
+
height: calc(100dvh - env(safe-area-inset-bottom, 0px)) !important;
|
|
606
|
+
max-height: calc(100dvh - env(safe-area-inset-bottom, 0px)) !important;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
@media screen and (max-width: 720px) {
|
|
610
|
+
.df-btn:not(.df-closed) > .df-btn-content {
|
|
611
|
+
max-height: 100dvh !important;
|
|
612
|
+
height: calc(100dvh - env(safe-area-inset-bottom, 0px)) !important;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/* iOS Safari: Force solid background painting to prevent transparency during keyboard animation */
|
|
618
|
+
@supports (-webkit-touch-callout: none) {
|
|
619
|
+
.df-btn {
|
|
620
|
+
background-color: ${config.background || "#FEFFFF"} !important;
|
|
621
|
+
-webkit-backface-visibility: hidden;
|
|
622
|
+
backface-visibility: hidden;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.df-btn-content {
|
|
626
|
+
background-color: ${config.background || "#FEFFFF"} !important;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
${
|
|
630
|
+
config.hasDarkMode
|
|
631
|
+
? `
|
|
632
|
+
@media (prefers-color-scheme: dark) {
|
|
633
|
+
.df-btn {
|
|
634
|
+
background-color: ${config.backgroundDark || "#171717"} !important;
|
|
635
|
+
}
|
|
636
|
+
.df-btn-content {
|
|
637
|
+
background-color: ${config.backgroundDark || "#171717"} !important;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
`
|
|
641
|
+
: ""
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
549
645
|
.df-btn-loading .df-btn-text:after {
|
|
550
646
|
content: '';
|
|
551
647
|
display: inline-block;
|
|
@@ -647,7 +743,7 @@
|
|
|
647
743
|
} catch (e) {
|
|
648
744
|
console.warn(
|
|
649
745
|
"[OSW Widget] Could not save notification dismissed state:",
|
|
650
|
-
e
|
|
746
|
+
e,
|
|
651
747
|
);
|
|
652
748
|
}
|
|
653
749
|
}
|
|
@@ -700,6 +796,56 @@
|
|
|
700
796
|
}
|
|
701
797
|
};
|
|
702
798
|
|
|
799
|
+
// iOS Safari detection for keyboard background bleed fix
|
|
800
|
+
const isMobileDevice =
|
|
801
|
+
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
|
802
|
+
navigator.userAgent,
|
|
803
|
+
);
|
|
804
|
+
|
|
805
|
+
// Store original body styles for scroll lock restoration
|
|
806
|
+
let savedBodyStyles = null;
|
|
807
|
+
|
|
808
|
+
// Lock body scroll on mobile to prevent background bleed when keyboard opens (iOS Safari fix)
|
|
809
|
+
function lockBodyScroll() {
|
|
810
|
+
if (!isMobileDevice) return;
|
|
811
|
+
|
|
812
|
+
savedBodyStyles = {
|
|
813
|
+
position: document.body.style.position,
|
|
814
|
+
top: document.body.style.top,
|
|
815
|
+
left: document.body.style.left,
|
|
816
|
+
right: document.body.style.right,
|
|
817
|
+
width: document.body.style.width,
|
|
818
|
+
height: document.body.style.height,
|
|
819
|
+
overflow: document.body.style.overflow,
|
|
820
|
+
scrollY: window.scrollY,
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
// Lock the body to prevent background scroll during keyboard open
|
|
824
|
+
document.body.style.position = "fixed";
|
|
825
|
+
document.body.style.top = `-${savedBodyStyles.scrollY}px`;
|
|
826
|
+
document.body.style.left = "0";
|
|
827
|
+
document.body.style.right = "0";
|
|
828
|
+
document.body.style.width = "100%";
|
|
829
|
+
document.body.style.overflow = "hidden";
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// Restore body scroll when chat closes
|
|
833
|
+
function unlockBodyScroll() {
|
|
834
|
+
if (!isMobileDevice || !savedBodyStyles) return;
|
|
835
|
+
|
|
836
|
+
document.body.style.position = savedBodyStyles.position;
|
|
837
|
+
document.body.style.top = savedBodyStyles.top;
|
|
838
|
+
document.body.style.left = savedBodyStyles.left;
|
|
839
|
+
document.body.style.right = savedBodyStyles.right;
|
|
840
|
+
document.body.style.width = savedBodyStyles.width;
|
|
841
|
+
document.body.style.height = savedBodyStyles.height;
|
|
842
|
+
document.body.style.overflow = savedBodyStyles.overflow;
|
|
843
|
+
|
|
844
|
+
// Restore scroll position
|
|
845
|
+
window.scrollTo(0, savedBodyStyles.scrollY);
|
|
846
|
+
savedBodyStyles = null;
|
|
847
|
+
}
|
|
848
|
+
|
|
703
849
|
// Create and inject the iframe on first open
|
|
704
850
|
function ensureIframeLoaded() {
|
|
705
851
|
return new Promise((resolve) => {
|
|
@@ -723,6 +869,29 @@
|
|
|
723
869
|
iframe.allow = "microphone *";
|
|
724
870
|
iframe.style.opacity = "0";
|
|
725
871
|
|
|
872
|
+
// iOS Safari keyboard background bleed fix: Make iframe a fixed-position repaint boundary on mobile
|
|
873
|
+
if (isMobileDevice) {
|
|
874
|
+
iframe.style.position = "fixed";
|
|
875
|
+
iframe.style.top = "0";
|
|
876
|
+
iframe.style.bottom = "0"; // Stretch from top to bottom
|
|
877
|
+
iframe.style.left = "0";
|
|
878
|
+
iframe.style.right = "0";
|
|
879
|
+
iframe.style.width = "100vw";
|
|
880
|
+
iframe.style.height = "100%"; // Fill between top and bottom
|
|
881
|
+
iframe.style.zIndex = "10000"; // Higher than widget container (999)
|
|
882
|
+
// Explicit background color to prevent transparency during iOS keyboard animation
|
|
883
|
+
const systemDarkMode =
|
|
884
|
+
config.hasDarkMode &&
|
|
885
|
+
window.matchMedia &&
|
|
886
|
+
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
887
|
+
iframe.style.backgroundColor = systemDarkMode
|
|
888
|
+
? config.backgroundDark || "#171717"
|
|
889
|
+
: config.background || "#FEFFFF";
|
|
890
|
+
// Force GPU layer for better repaint boundary
|
|
891
|
+
iframe.style.transform = "translateZ(0)";
|
|
892
|
+
iframe.style.webkitTransform = "translateZ(0)";
|
|
893
|
+
}
|
|
894
|
+
|
|
726
895
|
iframe.addEventListener("load", () => {
|
|
727
896
|
iframe.style.opacity = "";
|
|
728
897
|
iframeLoaded = true;
|
|
@@ -749,6 +918,8 @@
|
|
|
749
918
|
if (oldClose) header.removeChild(oldClose);
|
|
750
919
|
const oldMax = header.querySelector(".maximize-minimize-btn");
|
|
751
920
|
if (oldMax) header.removeChild(oldMax);
|
|
921
|
+
const oldLogo = header.querySelector(".header-logo");
|
|
922
|
+
if (oldLogo) header.removeChild(oldLogo);
|
|
752
923
|
const btnText = header.querySelector(".df-btn-text");
|
|
753
924
|
const btn = document.querySelector(".df-btn");
|
|
754
925
|
if (!btn) return;
|
|
@@ -759,38 +930,62 @@
|
|
|
759
930
|
// remove right padding from df-btn-text
|
|
760
931
|
btnText.style.paddingRight = "0";
|
|
761
932
|
|
|
762
|
-
//
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
const maxBtn = document.createElement("div");
|
|
766
|
-
maxBtn.className = "maximize-minimize-btn";
|
|
767
|
-
const maximized = btn.classList.contains("df-maximized");
|
|
768
|
-
const isDarkMode =
|
|
933
|
+
// Detect mobile view for conditional button display
|
|
934
|
+
const isMobileView = window.innerWidth <= 720;
|
|
935
|
+
const systemDarkMode =
|
|
769
936
|
config.hasDarkMode &&
|
|
770
937
|
window.matchMedia &&
|
|
771
938
|
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
939
|
+
// Use white icons only if the actual background is dark
|
|
940
|
+
const currentBg = systemDarkMode
|
|
941
|
+
? config.backgroundDark
|
|
942
|
+
: config.background;
|
|
943
|
+
const useDarkIcons = isColorDark(currentBg);
|
|
944
|
+
|
|
945
|
+
if (!isMobileView) {
|
|
946
|
+
// Desktop layout: [Expand] ... [Close]
|
|
947
|
+
|
|
948
|
+
// Expand button on LEFT
|
|
949
|
+
const maxBtn = document.createElement("div");
|
|
950
|
+
maxBtn.className = "maximize-minimize-btn";
|
|
951
|
+
const maximized = btn.classList.contains("df-maximized");
|
|
952
|
+
maxBtn.innerHTML = `<div class="df-icon-container"><img class="maximize-minimize-icon" src="${origin}/assets/${
|
|
953
|
+
maximized ? "collapse" : "expand"
|
|
954
|
+
}${useDarkIcons ? "_dark" : ""}.svg" alt="${
|
|
955
|
+
maximized ? "Minimize" : "Maximize"
|
|
956
|
+
}" style="width:24px;height:24px;cursor:pointer;" /></div>`;
|
|
957
|
+
header.insertBefore(maxBtn, btnText); // LEFT
|
|
958
|
+
maxBtn.addEventListener("click", (e) => {
|
|
959
|
+
e.stopPropagation();
|
|
960
|
+
maximizeMinimize();
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
// Close button on RIGHT
|
|
964
|
+
const closeBtn = document.createElement("div");
|
|
965
|
+
closeBtn.className = "close-btn";
|
|
966
|
+
closeBtn.innerHTML = `<div class="df-icon-container"><img src="${origin}/assets/close${
|
|
967
|
+
useDarkIcons ? "_dark" : ""
|
|
968
|
+
}.svg" alt="Close" style="width:20px;height:20px;cursor:pointer;" /></div>`;
|
|
969
|
+
header.appendChild(closeBtn); // RIGHT
|
|
970
|
+
closeBtn.addEventListener("click", (e) => {
|
|
971
|
+
e.stopPropagation();
|
|
972
|
+
dfToggle();
|
|
973
|
+
});
|
|
974
|
+
} else {
|
|
975
|
+
// Mobile layout: [Close] on right only
|
|
976
|
+
|
|
977
|
+
// Close button on RIGHT
|
|
978
|
+
const closeBtn = document.createElement("div");
|
|
979
|
+
closeBtn.className = "close-btn";
|
|
980
|
+
closeBtn.innerHTML = `<div class="df-icon-container"><img src="${origin}/assets/close${
|
|
981
|
+
useDarkIcons ? "_dark" : ""
|
|
982
|
+
}.svg" alt="Close" style="width:20px;height:20px;cursor:pointer;" /></div>`;
|
|
983
|
+
header.appendChild(closeBtn); // RIGHT
|
|
984
|
+
closeBtn.addEventListener("click", (e) => {
|
|
985
|
+
e.stopPropagation();
|
|
986
|
+
dfToggle();
|
|
987
|
+
});
|
|
988
|
+
}
|
|
794
989
|
} else {
|
|
795
990
|
// Set text to openText
|
|
796
991
|
if (btnText) btnText.innerText = config.openText || "Chat";
|
|
@@ -861,6 +1056,13 @@
|
|
|
861
1056
|
// Always remove maximized state when closing
|
|
862
1057
|
if (btn.classList.contains("df-closed")) {
|
|
863
1058
|
btn.classList.remove("df-maximized");
|
|
1059
|
+
// iOS Safari keyboard fix: Unlock body scroll when chat closes
|
|
1060
|
+
unlockBodyScroll();
|
|
1061
|
+
} else {
|
|
1062
|
+
// iOS Safari keyboard fix: Lock body scroll when chat opens on mobile
|
|
1063
|
+
if (isMobileDevice) {
|
|
1064
|
+
lockBodyScroll();
|
|
1065
|
+
}
|
|
864
1066
|
}
|
|
865
1067
|
updateHeaderButtons();
|
|
866
1068
|
clearInactivityAndShake();
|
|
@@ -900,7 +1102,7 @@
|
|
|
900
1102
|
} catch (e) {
|
|
901
1103
|
console.warn(
|
|
902
1104
|
"[OSW Widget] Could not save notification dismissed state:",
|
|
903
|
-
e
|
|
1105
|
+
e,
|
|
904
1106
|
);
|
|
905
1107
|
}
|
|
906
1108
|
|
|
@@ -912,6 +1114,9 @@
|
|
|
912
1114
|
const isMobile = window.innerWidth <= 768;
|
|
913
1115
|
|
|
914
1116
|
if (isMobile) {
|
|
1117
|
+
// iOS Safari keyboard fix: Lock body scroll when opening chat on mobile
|
|
1118
|
+
lockBodyScroll();
|
|
1119
|
+
|
|
915
1120
|
// On mobile, apply FLIP animation for open+maximize
|
|
916
1121
|
const content = btn.querySelector(".df-btn-content");
|
|
917
1122
|
|