@weldsuite/helpdesk-widget-sdk 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/angular.d.ts +36 -41
- package/dist/angular.esm.js +483 -102
- package/dist/angular.esm.js.map +1 -1
- package/dist/angular.js +483 -102
- package/dist/angular.js.map +1 -1
- package/dist/index.d.ts +60 -56
- package/dist/index.esm.js +505 -104
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +506 -103
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +506 -103
- package/dist/index.umd.js.map +1 -1
- package/dist/react.d.ts +36 -41
- package/dist/react.esm.js +483 -102
- package/dist/react.esm.js.map +1 -1
- package/dist/react.js +483 -102
- package/dist/react.js.map +1 -1
- package/dist/vue-composables.esm.js +483 -102
- package/dist/vue-composables.esm.js.map +1 -1
- package/dist/vue-composables.js +483 -102
- package/dist/vue-composables.js.map +1 -1
- package/package.json +4 -4
package/dist/index.esm.js
CHANGED
|
@@ -31,32 +31,6 @@ const DEFAULT_CONFIG = {
|
|
|
31
31
|
closeOnClick: true,
|
|
32
32
|
},
|
|
33
33
|
},
|
|
34
|
-
customization: {
|
|
35
|
-
primaryColor: '#000000',
|
|
36
|
-
accentColor: '#3b82f6',
|
|
37
|
-
backgroundColor: '#ffffff',
|
|
38
|
-
textColor: '#111827',
|
|
39
|
-
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
40
|
-
fontSize: '14px',
|
|
41
|
-
borderRadius: '12px',
|
|
42
|
-
},
|
|
43
|
-
features: {
|
|
44
|
-
attachments: true,
|
|
45
|
-
reactions: true,
|
|
46
|
-
typing: true,
|
|
47
|
-
readReceipts: true,
|
|
48
|
-
offlineMode: false,
|
|
49
|
-
fileUpload: true,
|
|
50
|
-
imageUpload: true,
|
|
51
|
-
voiceMessages: false,
|
|
52
|
-
videoMessages: false,
|
|
53
|
-
},
|
|
54
|
-
mobile: {
|
|
55
|
-
fullScreen: true,
|
|
56
|
-
scrollLock: true,
|
|
57
|
-
keyboardHandling: 'auto',
|
|
58
|
-
safeAreaInsets: true,
|
|
59
|
-
},
|
|
60
34
|
auth: {
|
|
61
35
|
enabled: true,
|
|
62
36
|
mode: 'anonymous',
|
|
@@ -100,6 +74,7 @@ function resolveConfig(config) {
|
|
|
100
74
|
validateConfig(config);
|
|
101
75
|
return {
|
|
102
76
|
widgetId: config.widgetId,
|
|
77
|
+
testMode: config.testMode,
|
|
103
78
|
api: {
|
|
104
79
|
...DEFAULT_CONFIG.api,
|
|
105
80
|
widgetId: config.widgetId,
|
|
@@ -129,18 +104,6 @@ function resolveConfig(config) {
|
|
|
129
104
|
...config.iframes?.backdrop,
|
|
130
105
|
},
|
|
131
106
|
},
|
|
132
|
-
customization: {
|
|
133
|
-
...DEFAULT_CONFIG.customization,
|
|
134
|
-
...config.customization,
|
|
135
|
-
},
|
|
136
|
-
features: {
|
|
137
|
-
...DEFAULT_CONFIG.features,
|
|
138
|
-
...config.features,
|
|
139
|
-
},
|
|
140
|
-
mobile: {
|
|
141
|
-
...DEFAULT_CONFIG.mobile,
|
|
142
|
-
...config.mobile,
|
|
143
|
-
},
|
|
144
107
|
auth: {
|
|
145
108
|
...DEFAULT_CONFIG.auth,
|
|
146
109
|
...config.auth,
|
|
@@ -383,6 +346,8 @@ class IframeManager {
|
|
|
383
346
|
this.modalContainer = null;
|
|
384
347
|
this.styleElement = null;
|
|
385
348
|
this.messageBroker = null;
|
|
349
|
+
// Guard flag to prevent double-binding event listeners
|
|
350
|
+
this.eventListenersBound = false;
|
|
386
351
|
this.config = config;
|
|
387
352
|
this.logger = new Logger(config.logging);
|
|
388
353
|
this.deviceInfo = detectDevice();
|
|
@@ -422,18 +387,27 @@ class IframeManager {
|
|
|
422
387
|
}
|
|
423
388
|
/**
|
|
424
389
|
* Create root container structure
|
|
390
|
+
* Reuses existing container if it has the same widgetId (singleton behavior)
|
|
425
391
|
*/
|
|
426
392
|
createRootContainer() {
|
|
427
|
-
|
|
428
|
-
let existingContainer = document.getElementById('weld-container');
|
|
393
|
+
const existingContainer = document.getElementById('weld-container');
|
|
429
394
|
if (existingContainer) {
|
|
430
|
-
|
|
395
|
+
// Reuse if same widgetId
|
|
396
|
+
if (existingContainer.getAttribute('data-widget-id') === this.config.widgetId) {
|
|
397
|
+
this.logger.debug('Reusing existing root container');
|
|
398
|
+
this.rootContainer = existingContainer;
|
|
399
|
+
this.appContainer = existingContainer.querySelector('.weld-app');
|
|
400
|
+
this.modalContainer = document.getElementById('weld-modal-container');
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
this.logger.warn('Weld container already exists with different widgetId, removing old instance');
|
|
431
404
|
existingContainer.remove();
|
|
432
405
|
}
|
|
433
406
|
// Create root container
|
|
434
407
|
this.rootContainer = document.createElement('div');
|
|
435
408
|
this.rootContainer.id = 'weld-container';
|
|
436
409
|
this.rootContainer.className = 'weld-namespace';
|
|
410
|
+
this.rootContainer.setAttribute('data-widget-id', this.config.widgetId);
|
|
437
411
|
// Create app container
|
|
438
412
|
this.appContainer = document.createElement('div');
|
|
439
413
|
this.appContainer.className = 'weld-app';
|
|
@@ -468,17 +442,7 @@ class IframeManager {
|
|
|
468
442
|
* Generate CSS for containers
|
|
469
443
|
*/
|
|
470
444
|
generateCSS() {
|
|
471
|
-
const { customization } = this.config;
|
|
472
445
|
return `
|
|
473
|
-
/* Weld Container */
|
|
474
|
-
#weld-container {
|
|
475
|
-
--weld-color-primary: ${customization.primaryColor};
|
|
476
|
-
--weld-color-accent: ${customization.accentColor};
|
|
477
|
-
--weld-font-family: ${customization.fontFamily};
|
|
478
|
-
--weld-font-size-base: ${customization.fontSize};
|
|
479
|
-
--weld-radius-xl: ${customization.borderRadius};
|
|
480
|
-
}
|
|
481
|
-
|
|
482
446
|
/* Import main stylesheet */
|
|
483
447
|
@import url('/styles/index.css');
|
|
484
448
|
|
|
@@ -502,20 +466,27 @@ class IframeManager {
|
|
|
502
466
|
* Create launcher iframe
|
|
503
467
|
*/
|
|
504
468
|
async createLauncherIframe() {
|
|
469
|
+
// Guard: skip if launcher iframe already exists
|
|
470
|
+
if (this.iframes.has(IframeType.LAUNCHER)) {
|
|
471
|
+
this.logger.debug('Launcher iframe already exists, skipping creation');
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
505
474
|
const { iframes } = this.config;
|
|
506
475
|
const { launcher } = iframes;
|
|
507
476
|
// Create container
|
|
508
477
|
const container = document.createElement('div');
|
|
509
478
|
container.className = 'weld-launcher-frame';
|
|
510
479
|
container.setAttribute('data-state', 'visible');
|
|
480
|
+
// Container is larger than the button to allow hover animations (scale, shadow) without clipping
|
|
481
|
+
const launcherPadding = 10;
|
|
511
482
|
container.style.cssText = `
|
|
512
483
|
position: fixed;
|
|
513
|
-
bottom: ${launcher.position.bottom};
|
|
514
|
-
right: ${launcher.position.right};
|
|
515
|
-
width: ${launcher.size};
|
|
516
|
-
height: ${launcher.size};
|
|
484
|
+
bottom: calc(${launcher.position.bottom} - ${launcherPadding}px);
|
|
485
|
+
right: calc(${launcher.position.right} - ${launcherPadding}px);
|
|
486
|
+
width: calc(${launcher.size} + ${launcherPadding * 2}px);
|
|
487
|
+
height: calc(${launcher.size} + ${launcherPadding * 2}px);
|
|
517
488
|
z-index: 2147483003;
|
|
518
|
-
pointer-events:
|
|
489
|
+
pointer-events: none;
|
|
519
490
|
display: block;
|
|
520
491
|
`;
|
|
521
492
|
// Create iframe
|
|
@@ -527,8 +498,12 @@ class IframeManager {
|
|
|
527
498
|
width: 100%;
|
|
528
499
|
height: 100%;
|
|
529
500
|
border: none;
|
|
530
|
-
background:
|
|
501
|
+
background: none;
|
|
502
|
+
color-scheme: none;
|
|
531
503
|
display: block;
|
|
504
|
+
pointer-events: auto;
|
|
505
|
+
border-radius: 50%;
|
|
506
|
+
filter: drop-shadow(rgba(9, 14, 21, 0.54) 0px 1px 6px) drop-shadow(rgba(9, 14, 21, 0.9) 0px 2px 32px);
|
|
532
507
|
`;
|
|
533
508
|
iframe.setAttribute('allow', 'clipboard-write');
|
|
534
509
|
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups');
|
|
@@ -544,20 +519,36 @@ class IframeManager {
|
|
|
544
519
|
createdAt: Date.now(),
|
|
545
520
|
});
|
|
546
521
|
// When DOM loads, notify MessageBroker to send weld:init
|
|
522
|
+
let launcherRetried = false;
|
|
547
523
|
iframe.onload = () => {
|
|
548
524
|
const metadata = this.iframes.get(IframeType.LAUNCHER);
|
|
549
525
|
if (metadata) {
|
|
550
526
|
this.logger.debug('Launcher iframe DOM loaded');
|
|
551
|
-
// Notify MessageBroker that DOM is loaded (triggers weld:init)
|
|
552
527
|
this.messageBroker?.setIframeDomLoaded(IframeType.LAUNCHER);
|
|
553
528
|
}
|
|
554
529
|
};
|
|
530
|
+
iframe.onerror = () => {
|
|
531
|
+
this.logger.error('Launcher iframe failed to load');
|
|
532
|
+
if (!launcherRetried) {
|
|
533
|
+
launcherRetried = true;
|
|
534
|
+
this.logger.info('Retrying launcher iframe load...');
|
|
535
|
+
setTimeout(() => { iframe.src = this.buildIframeUrl(launcher.url); }, 3000);
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
this.config.onError?.(new Error('Failed to load widget launcher'));
|
|
539
|
+
}
|
|
540
|
+
};
|
|
555
541
|
this.logger.debug('Launcher iframe created');
|
|
556
542
|
}
|
|
557
543
|
/**
|
|
558
544
|
* Create widget iframe
|
|
559
545
|
*/
|
|
560
546
|
async createWidgetIframe() {
|
|
547
|
+
// Guard: skip if widget iframe already exists
|
|
548
|
+
if (this.iframes.has(IframeType.WIDGET)) {
|
|
549
|
+
this.logger.debug('Widget iframe already exists, skipping creation');
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
561
552
|
const { iframes } = this.config;
|
|
562
553
|
const { widget } = iframes;
|
|
563
554
|
// Create container
|
|
@@ -638,54 +629,32 @@ class IframeManager {
|
|
|
638
629
|
createdAt: Date.now(),
|
|
639
630
|
});
|
|
640
631
|
// When DOM loads, notify MessageBroker to send weld:init
|
|
632
|
+
let widgetRetried = false;
|
|
641
633
|
iframe.onload = () => {
|
|
642
634
|
const metadata = this.iframes.get(IframeType.WIDGET);
|
|
643
635
|
if (metadata) {
|
|
644
636
|
this.logger.debug('Widget iframe DOM loaded');
|
|
645
|
-
// Notify MessageBroker that DOM is loaded (triggers weld:init)
|
|
646
637
|
this.messageBroker?.setIframeDomLoaded(IframeType.WIDGET);
|
|
647
638
|
}
|
|
648
639
|
};
|
|
640
|
+
iframe.onerror = () => {
|
|
641
|
+
this.logger.error('Widget iframe failed to load');
|
|
642
|
+
if (!widgetRetried) {
|
|
643
|
+
widgetRetried = true;
|
|
644
|
+
this.logger.info('Retrying widget iframe load...');
|
|
645
|
+
setTimeout(() => { iframe.src = this.buildIframeUrl(widget.url); }, 3000);
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
this.config.onError?.(new Error('Failed to load widget'));
|
|
649
|
+
}
|
|
650
|
+
};
|
|
649
651
|
this.logger.debug('Widget iframe created');
|
|
650
652
|
}
|
|
651
653
|
/**
|
|
652
|
-
* Create backdrop iframe
|
|
654
|
+
* Create backdrop iframe — disabled, widget stays non-modal so users can interact with the page
|
|
653
655
|
*/
|
|
654
656
|
async createBackdropIframe() {
|
|
655
|
-
|
|
656
|
-
this.logger.debug('Backdrop disabled, skipping creation');
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
// Create container
|
|
660
|
-
const container = document.createElement('div');
|
|
661
|
-
container.className = 'weld-backdrop-frame';
|
|
662
|
-
container.setAttribute('data-state', 'hidden');
|
|
663
|
-
container.style.cssText = `
|
|
664
|
-
position: fixed;
|
|
665
|
-
top: 0;
|
|
666
|
-
left: 0;
|
|
667
|
-
right: 0;
|
|
668
|
-
bottom: 0;
|
|
669
|
-
z-index: 2147483000;
|
|
670
|
-
background: transparent;
|
|
671
|
-
pointer-events: none;
|
|
672
|
-
opacity: 0;
|
|
673
|
-
transition: opacity 200ms ease;
|
|
674
|
-
`;
|
|
675
|
-
this.appContainer?.appendChild(container);
|
|
676
|
-
// Store metadata (backdrop doesn't have an iframe, just a div)
|
|
677
|
-
// We'll create a minimal "iframe" reference for consistency
|
|
678
|
-
const dummyIframe = document.createElement('iframe');
|
|
679
|
-
dummyIframe.style.display = 'none';
|
|
680
|
-
this.iframes.set(IframeType.BACKDROP, {
|
|
681
|
-
type: IframeType.BACKDROP,
|
|
682
|
-
element: dummyIframe,
|
|
683
|
-
container,
|
|
684
|
-
ready: true, // Backdrop is always ready
|
|
685
|
-
visible: false,
|
|
686
|
-
createdAt: Date.now(),
|
|
687
|
-
});
|
|
688
|
-
this.logger.debug('Backdrop created');
|
|
657
|
+
this.logger.debug('Backdrop disabled, skipping creation');
|
|
689
658
|
}
|
|
690
659
|
/**
|
|
691
660
|
* Build iframe URL with parameters
|
|
@@ -699,12 +668,21 @@ class IframeManager {
|
|
|
699
668
|
url.searchParams.set('device', this.deviceInfo.type);
|
|
700
669
|
url.searchParams.set('mobile', String(this.deviceInfo.isMobile));
|
|
701
670
|
url.searchParams.set('parentOrigin', window.location.origin);
|
|
671
|
+
if (this.config.testMode) {
|
|
672
|
+
url.searchParams.set('testMode', 'true');
|
|
673
|
+
}
|
|
702
674
|
return url.toString();
|
|
703
675
|
}
|
|
704
676
|
/**
|
|
705
677
|
* Setup event listeners
|
|
706
678
|
*/
|
|
707
679
|
setupEventListeners() {
|
|
680
|
+
// Guard: prevent double-binding
|
|
681
|
+
if (this.eventListenersBound) {
|
|
682
|
+
this.logger.debug('Event listeners already bound, skipping');
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
this.eventListenersBound = true;
|
|
708
686
|
// Window resize - use bound handler for proper cleanup
|
|
709
687
|
window.addEventListener('resize', this.boundHandleResize);
|
|
710
688
|
// Orientation change - use bound handler for proper cleanup
|
|
@@ -807,7 +785,7 @@ class IframeManager {
|
|
|
807
785
|
iframe.container.style.transform = 'scale(1) translateY(0)';
|
|
808
786
|
}
|
|
809
787
|
// Handle mobile scroll lock
|
|
810
|
-
if (this.deviceInfo.isMobile && type === IframeType.WIDGET
|
|
788
|
+
if (this.deviceInfo.isMobile && type === IframeType.WIDGET) {
|
|
811
789
|
document.body.classList.add('weld-mobile-open');
|
|
812
790
|
}
|
|
813
791
|
// Hide launcher on mobile when widget is open (full-screen mode)
|
|
@@ -906,6 +884,8 @@ class IframeManager {
|
|
|
906
884
|
this.iframes.clear();
|
|
907
885
|
// Clear messageBroker reference
|
|
908
886
|
this.messageBroker = null;
|
|
887
|
+
// Reset guard flag
|
|
888
|
+
this.eventListenersBound = false;
|
|
909
889
|
this.logger.info('IframeManager destroyed');
|
|
910
890
|
}
|
|
911
891
|
}
|
|
@@ -973,6 +953,8 @@ var MessageType;
|
|
|
973
953
|
// Events
|
|
974
954
|
MessageType["EVENT_TRACK"] = "weld:event:track";
|
|
975
955
|
MessageType["ERROR_REPORT"] = "weld:error:report";
|
|
956
|
+
// Page tracking
|
|
957
|
+
MessageType["PAGE_CHANGE"] = "weld:page:change";
|
|
976
958
|
// API responses
|
|
977
959
|
MessageType["API_SUCCESS"] = "weld:api:success";
|
|
978
960
|
MessageType["API_ERROR"] = "weld:api:error";
|
|
@@ -1551,8 +1533,6 @@ class MessageBroker {
|
|
|
1551
1533
|
iframeType,
|
|
1552
1534
|
config: {
|
|
1553
1535
|
api: this.config.api,
|
|
1554
|
-
customization: this.config.customization,
|
|
1555
|
-
features: this.config.features,
|
|
1556
1536
|
},
|
|
1557
1537
|
};
|
|
1558
1538
|
const message = createMessage('weld:init', MessageOrigin.PARENT, initPayload);
|
|
@@ -2395,7 +2375,7 @@ class StateCoordinator {
|
|
|
2395
2375
|
}
|
|
2396
2376
|
}
|
|
2397
2377
|
|
|
2398
|
-
var version = "1.0.
|
|
2378
|
+
var version = "1.0.17";
|
|
2399
2379
|
var packageJson = {
|
|
2400
2380
|
version: version};
|
|
2401
2381
|
|
|
@@ -2403,6 +2383,16 @@ var packageJson = {
|
|
|
2403
2383
|
* Weld SDK - Main Entry Point
|
|
2404
2384
|
* Public API for the Weld helpdesk widget
|
|
2405
2385
|
*/
|
|
2386
|
+
/**
|
|
2387
|
+
* Module-level singleton registry keyed by widgetId
|
|
2388
|
+
*/
|
|
2389
|
+
const sdkRegistry = new Map();
|
|
2390
|
+
/**
|
|
2391
|
+
* SessionStorage key helpers
|
|
2392
|
+
*/
|
|
2393
|
+
function openStateKey(widgetId) {
|
|
2394
|
+
return `weld-widget-open-${widgetId}`;
|
|
2395
|
+
}
|
|
2406
2396
|
/**
|
|
2407
2397
|
* SDK initialization status
|
|
2408
2398
|
*/
|
|
@@ -2425,6 +2415,8 @@ class WeldSDK {
|
|
|
2425
2415
|
this.readyResolve = null;
|
|
2426
2416
|
// Subscription IDs for cleanup
|
|
2427
2417
|
this.subscriptionIds = [];
|
|
2418
|
+
// Page tracking cleanup
|
|
2419
|
+
this.pageTrackingCleanup = null;
|
|
2428
2420
|
/**
|
|
2429
2421
|
* Update user attributes (Intercom-style, with rate limiting)
|
|
2430
2422
|
* Limited to 20 calls per page load to prevent abuse
|
|
@@ -2463,6 +2455,13 @@ class WeldSDK {
|
|
|
2463
2455
|
console.log('[Weld SDK] Received message:', event.data.type);
|
|
2464
2456
|
}
|
|
2465
2457
|
if (event.data?.type === 'launcher:clicked') {
|
|
2458
|
+
if (this.status !== SDKStatus.READY) {
|
|
2459
|
+
console.log('[Weld SDK] Launcher clicked but SDK not ready yet — waiting...');
|
|
2460
|
+
this.readyPromise?.then(() => {
|
|
2461
|
+
this.handleLauncherClickMessage(event);
|
|
2462
|
+
});
|
|
2463
|
+
return;
|
|
2464
|
+
}
|
|
2466
2465
|
// Toggle behavior - if widget is open, close it; if closed, open it
|
|
2467
2466
|
const state = this.stateCoordinator.getState();
|
|
2468
2467
|
if (state.widget.isOpen) {
|
|
@@ -2475,9 +2474,260 @@ class WeldSDK {
|
|
|
2475
2474
|
}
|
|
2476
2475
|
}
|
|
2477
2476
|
if (event.data?.type === 'weld:close') {
|
|
2477
|
+
if (this.status !== SDKStatus.READY)
|
|
2478
|
+
return;
|
|
2478
2479
|
console.log('[Weld SDK] Widget close requested');
|
|
2479
2480
|
this.close();
|
|
2480
2481
|
}
|
|
2482
|
+
if (event.data?.type === 'weld:unread-count') {
|
|
2483
|
+
const count = event.data.count ?? 0;
|
|
2484
|
+
// Forward to launcher iframe
|
|
2485
|
+
const launcherIframe = this.iframeManager.getIframe(IframeType.LAUNCHER);
|
|
2486
|
+
if (launcherIframe?.element?.contentWindow) {
|
|
2487
|
+
launcherIframe.element.contentWindow.postMessage({
|
|
2488
|
+
type: 'weld:unread-count',
|
|
2489
|
+
count
|
|
2490
|
+
}, '*');
|
|
2491
|
+
}
|
|
2492
|
+
// Update state coordinator for external API consumers
|
|
2493
|
+
this.stateCoordinator.setBadgeCount(count);
|
|
2494
|
+
}
|
|
2495
|
+
if (event.data?.type === 'weld:image:open' && event.data?.url) {
|
|
2496
|
+
this.showImageLightbox(event.data.url);
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
/**
|
|
2500
|
+
* Show fullscreen image lightbox on the parent page
|
|
2501
|
+
*/
|
|
2502
|
+
showImageLightbox(url) {
|
|
2503
|
+
// Remove existing lightbox if any
|
|
2504
|
+
const existing = document.getElementById('weld-image-lightbox');
|
|
2505
|
+
if (existing)
|
|
2506
|
+
existing.remove();
|
|
2507
|
+
// Zoom / pan state
|
|
2508
|
+
let scale = 1;
|
|
2509
|
+
let translateX = 0;
|
|
2510
|
+
let translateY = 0;
|
|
2511
|
+
let isDragging = false;
|
|
2512
|
+
let dragStartX = 0;
|
|
2513
|
+
let dragStartY = 0;
|
|
2514
|
+
let lastTranslateX = 0;
|
|
2515
|
+
let lastTranslateY = 0;
|
|
2516
|
+
const applyTransform = () => {
|
|
2517
|
+
img.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
|
|
2518
|
+
};
|
|
2519
|
+
const resetTransform = () => {
|
|
2520
|
+
scale = 1;
|
|
2521
|
+
translateX = 0;
|
|
2522
|
+
translateY = 0;
|
|
2523
|
+
applyTransform();
|
|
2524
|
+
img.style.cursor = 'zoom-in';
|
|
2525
|
+
};
|
|
2526
|
+
const overlay = document.createElement('div');
|
|
2527
|
+
overlay.id = 'weld-image-lightbox';
|
|
2528
|
+
overlay.style.cssText = `
|
|
2529
|
+
position: fixed;
|
|
2530
|
+
inset: 0;
|
|
2531
|
+
z-index: 2147483647;
|
|
2532
|
+
background: rgba(0, 0, 0, 0.92);
|
|
2533
|
+
display: flex;
|
|
2534
|
+
align-items: center;
|
|
2535
|
+
justify-content: center;
|
|
2536
|
+
padding: 16px;
|
|
2537
|
+
cursor: pointer;
|
|
2538
|
+
overflow: hidden;
|
|
2539
|
+
`;
|
|
2540
|
+
// Close button
|
|
2541
|
+
const closeBtn = document.createElement('button');
|
|
2542
|
+
closeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`;
|
|
2543
|
+
closeBtn.style.cssText = `
|
|
2544
|
+
position: absolute;
|
|
2545
|
+
top: 16px;
|
|
2546
|
+
right: 16px;
|
|
2547
|
+
width: 40px;
|
|
2548
|
+
height: 40px;
|
|
2549
|
+
border-radius: 50%;
|
|
2550
|
+
border: none;
|
|
2551
|
+
background: rgba(255, 255, 255, 0.1);
|
|
2552
|
+
color: white;
|
|
2553
|
+
cursor: pointer;
|
|
2554
|
+
display: flex;
|
|
2555
|
+
align-items: center;
|
|
2556
|
+
justify-content: center;
|
|
2557
|
+
transition: background 0.15s;
|
|
2558
|
+
`;
|
|
2559
|
+
closeBtn.onmouseenter = () => { closeBtn.style.background = 'rgba(255, 255, 255, 0.2)'; };
|
|
2560
|
+
closeBtn.onmouseleave = () => { closeBtn.style.background = 'rgba(255, 255, 255, 0.1)'; };
|
|
2561
|
+
// Download button
|
|
2562
|
+
const downloadBtn = document.createElement('a');
|
|
2563
|
+
downloadBtn.href = url;
|
|
2564
|
+
downloadBtn.download = '';
|
|
2565
|
+
downloadBtn.target = '_blank';
|
|
2566
|
+
downloadBtn.rel = 'noopener noreferrer';
|
|
2567
|
+
downloadBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>`;
|
|
2568
|
+
downloadBtn.style.cssText = `
|
|
2569
|
+
position: absolute;
|
|
2570
|
+
top: 16px;
|
|
2571
|
+
right: 64px;
|
|
2572
|
+
width: 40px;
|
|
2573
|
+
height: 40px;
|
|
2574
|
+
border-radius: 50%;
|
|
2575
|
+
border: none;
|
|
2576
|
+
background: rgba(255, 255, 255, 0.1);
|
|
2577
|
+
color: white;
|
|
2578
|
+
cursor: pointer;
|
|
2579
|
+
display: flex;
|
|
2580
|
+
align-items: center;
|
|
2581
|
+
justify-content: center;
|
|
2582
|
+
transition: background 0.15s;
|
|
2583
|
+
text-decoration: none;
|
|
2584
|
+
`;
|
|
2585
|
+
downloadBtn.onmouseenter = () => { downloadBtn.style.background = 'rgba(255, 255, 255, 0.2)'; };
|
|
2586
|
+
downloadBtn.onmouseleave = () => { downloadBtn.style.background = 'rgba(255, 255, 255, 0.1)'; };
|
|
2587
|
+
// Image
|
|
2588
|
+
const img = document.createElement('img');
|
|
2589
|
+
img.src = url;
|
|
2590
|
+
img.alt = 'Full size';
|
|
2591
|
+
img.draggable = false;
|
|
2592
|
+
img.style.cssText = `
|
|
2593
|
+
max-width: 100%;
|
|
2594
|
+
max-height: 100%;
|
|
2595
|
+
object-fit: contain;
|
|
2596
|
+
border-radius: 8px;
|
|
2597
|
+
cursor: zoom-in;
|
|
2598
|
+
transition: transform 0.2s ease;
|
|
2599
|
+
user-select: none;
|
|
2600
|
+
`;
|
|
2601
|
+
// Click to toggle zoom
|
|
2602
|
+
img.addEventListener('click', (e) => {
|
|
2603
|
+
e.stopPropagation();
|
|
2604
|
+
if (scale === 1) {
|
|
2605
|
+
// Zoom in to 2.5x centered on click position
|
|
2606
|
+
const rect = img.getBoundingClientRect();
|
|
2607
|
+
const clickX = e.clientX - rect.left - rect.width / 2;
|
|
2608
|
+
const clickY = e.clientY - rect.top - rect.height / 2;
|
|
2609
|
+
scale = 2.5;
|
|
2610
|
+
translateX = -clickX * 1.5;
|
|
2611
|
+
translateY = -clickY * 1.5;
|
|
2612
|
+
applyTransform();
|
|
2613
|
+
img.style.cursor = 'zoom-out';
|
|
2614
|
+
}
|
|
2615
|
+
else {
|
|
2616
|
+
// Zoom out - reset
|
|
2617
|
+
resetTransform();
|
|
2618
|
+
}
|
|
2619
|
+
});
|
|
2620
|
+
// Mouse wheel zoom
|
|
2621
|
+
overlay.addEventListener('wheel', (e) => {
|
|
2622
|
+
e.preventDefault();
|
|
2623
|
+
const delta = e.deltaY > 0 ? -0.25 : 0.25;
|
|
2624
|
+
const newScale = Math.min(Math.max(scale + delta, 1), 5);
|
|
2625
|
+
if (newScale === 1) {
|
|
2626
|
+
resetTransform();
|
|
2627
|
+
}
|
|
2628
|
+
else {
|
|
2629
|
+
scale = newScale;
|
|
2630
|
+
applyTransform();
|
|
2631
|
+
img.style.cursor = 'zoom-out';
|
|
2632
|
+
}
|
|
2633
|
+
}, { passive: false });
|
|
2634
|
+
// Drag to pan when zoomed
|
|
2635
|
+
img.addEventListener('mousedown', (e) => {
|
|
2636
|
+
if (scale <= 1)
|
|
2637
|
+
return;
|
|
2638
|
+
e.preventDefault();
|
|
2639
|
+
isDragging = true;
|
|
2640
|
+
dragStartX = e.clientX;
|
|
2641
|
+
dragStartY = e.clientY;
|
|
2642
|
+
lastTranslateX = translateX;
|
|
2643
|
+
lastTranslateY = translateY;
|
|
2644
|
+
img.style.cursor = 'grabbing';
|
|
2645
|
+
img.style.transition = 'none';
|
|
2646
|
+
});
|
|
2647
|
+
window.addEventListener('mousemove', (e) => {
|
|
2648
|
+
if (!isDragging)
|
|
2649
|
+
return;
|
|
2650
|
+
translateX = lastTranslateX + (e.clientX - dragStartX);
|
|
2651
|
+
translateY = lastTranslateY + (e.clientY - dragStartY);
|
|
2652
|
+
applyTransform();
|
|
2653
|
+
});
|
|
2654
|
+
window.addEventListener('mouseup', () => {
|
|
2655
|
+
if (!isDragging)
|
|
2656
|
+
return;
|
|
2657
|
+
isDragging = false;
|
|
2658
|
+
img.style.cursor = scale > 1 ? 'zoom-out' : 'zoom-in';
|
|
2659
|
+
img.style.transition = 'transform 0.2s ease';
|
|
2660
|
+
});
|
|
2661
|
+
// Touch: pinch to zoom + drag to pan
|
|
2662
|
+
let lastTouchDist = 0;
|
|
2663
|
+
let lastTouchScale = 1;
|
|
2664
|
+
overlay.addEventListener('touchstart', (e) => {
|
|
2665
|
+
if (e.touches.length === 2) {
|
|
2666
|
+
e.preventDefault();
|
|
2667
|
+
const dx = e.touches[0].clientX - e.touches[1].clientX;
|
|
2668
|
+
const dy = e.touches[0].clientY - e.touches[1].clientY;
|
|
2669
|
+
lastTouchDist = Math.hypot(dx, dy);
|
|
2670
|
+
lastTouchScale = scale;
|
|
2671
|
+
}
|
|
2672
|
+
else if (e.touches.length === 1 && scale > 1) {
|
|
2673
|
+
isDragging = true;
|
|
2674
|
+
dragStartX = e.touches[0].clientX;
|
|
2675
|
+
dragStartY = e.touches[0].clientY;
|
|
2676
|
+
lastTranslateX = translateX;
|
|
2677
|
+
lastTranslateY = translateY;
|
|
2678
|
+
img.style.transition = 'none';
|
|
2679
|
+
}
|
|
2680
|
+
}, { passive: false });
|
|
2681
|
+
overlay.addEventListener('touchmove', (e) => {
|
|
2682
|
+
if (e.touches.length === 2) {
|
|
2683
|
+
e.preventDefault();
|
|
2684
|
+
const dx = e.touches[0].clientX - e.touches[1].clientX;
|
|
2685
|
+
const dy = e.touches[0].clientY - e.touches[1].clientY;
|
|
2686
|
+
const dist = Math.hypot(dx, dy);
|
|
2687
|
+
scale = Math.min(Math.max(lastTouchScale * (dist / lastTouchDist), 1), 5);
|
|
2688
|
+
if (scale === 1) {
|
|
2689
|
+
translateX = 0;
|
|
2690
|
+
translateY = 0;
|
|
2691
|
+
}
|
|
2692
|
+
applyTransform();
|
|
2693
|
+
}
|
|
2694
|
+
else if (e.touches.length === 1 && isDragging) {
|
|
2695
|
+
e.preventDefault();
|
|
2696
|
+
translateX = lastTranslateX + (e.touches[0].clientX - dragStartX);
|
|
2697
|
+
translateY = lastTranslateY + (e.touches[0].clientY - dragStartY);
|
|
2698
|
+
applyTransform();
|
|
2699
|
+
}
|
|
2700
|
+
}, { passive: false });
|
|
2701
|
+
overlay.addEventListener('touchend', (e) => {
|
|
2702
|
+
if (e.touches.length < 2) {
|
|
2703
|
+
lastTouchDist = 0;
|
|
2704
|
+
}
|
|
2705
|
+
if (e.touches.length === 0) {
|
|
2706
|
+
isDragging = false;
|
|
2707
|
+
img.style.transition = 'transform 0.2s ease';
|
|
2708
|
+
}
|
|
2709
|
+
});
|
|
2710
|
+
const close = () => {
|
|
2711
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
2712
|
+
overlay.remove();
|
|
2713
|
+
};
|
|
2714
|
+
// Only close on backdrop click when not zoomed (prevent accidental close while panning)
|
|
2715
|
+
overlay.addEventListener('click', (e) => {
|
|
2716
|
+
if (e.target === overlay && scale <= 1)
|
|
2717
|
+
close();
|
|
2718
|
+
});
|
|
2719
|
+
downloadBtn.addEventListener('click', (e) => e.stopPropagation());
|
|
2720
|
+
closeBtn.addEventListener('click', (e) => { e.stopPropagation(); close(); });
|
|
2721
|
+
// Close on Escape
|
|
2722
|
+
const handleKeyDown = (e) => {
|
|
2723
|
+
if (e.key === 'Escape')
|
|
2724
|
+
close();
|
|
2725
|
+
};
|
|
2726
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
2727
|
+
overlay.appendChild(closeBtn);
|
|
2728
|
+
overlay.appendChild(downloadBtn);
|
|
2729
|
+
overlay.appendChild(img);
|
|
2730
|
+
document.body.appendChild(overlay);
|
|
2481
2731
|
}
|
|
2482
2732
|
/**
|
|
2483
2733
|
* Initialize the SDK and render widget
|
|
@@ -2503,6 +2753,13 @@ class WeldSDK {
|
|
|
2503
2753
|
this.logger.info('WeldSDK ready');
|
|
2504
2754
|
// Call onReady callback
|
|
2505
2755
|
this.config.onReady?.();
|
|
2756
|
+
// Start tracking page URL changes
|
|
2757
|
+
this.startPageTracking();
|
|
2758
|
+
// Auto-open if widget was previously open (persisted in sessionStorage)
|
|
2759
|
+
if (this.wasOpen()) {
|
|
2760
|
+
this.logger.info('Restoring previously open widget from sessionStorage');
|
|
2761
|
+
this.open();
|
|
2762
|
+
}
|
|
2506
2763
|
}
|
|
2507
2764
|
catch (error) {
|
|
2508
2765
|
this.status = SDKStatus.ERROR;
|
|
@@ -2581,6 +2838,58 @@ class WeldSDK {
|
|
|
2581
2838
|
isReady() {
|
|
2582
2839
|
return this.status === SDKStatus.READY;
|
|
2583
2840
|
}
|
|
2841
|
+
/**
|
|
2842
|
+
* Update callbacks on an existing instance (used by singleton reuse)
|
|
2843
|
+
*/
|
|
2844
|
+
updateCallbacks(config) {
|
|
2845
|
+
if (config.onReady !== undefined)
|
|
2846
|
+
this.config.onReady = config.onReady;
|
|
2847
|
+
if (config.onOpen !== undefined)
|
|
2848
|
+
this.config.onOpen = config.onOpen;
|
|
2849
|
+
if (config.onClose !== undefined)
|
|
2850
|
+
this.config.onClose = config.onClose;
|
|
2851
|
+
if (config.onError !== undefined)
|
|
2852
|
+
this.config.onError = config.onError;
|
|
2853
|
+
if (config.onDestroy !== undefined)
|
|
2854
|
+
this.config.onDestroy = config.onDestroy;
|
|
2855
|
+
if (config.onMinimize !== undefined)
|
|
2856
|
+
this.config.onMinimize = config.onMinimize;
|
|
2857
|
+
if (config.onMaximize !== undefined)
|
|
2858
|
+
this.config.onMaximize = config.onMaximize;
|
|
2859
|
+
}
|
|
2860
|
+
/**
|
|
2861
|
+
* Persist open/closed state to sessionStorage
|
|
2862
|
+
*/
|
|
2863
|
+
persistOpenState(isOpen) {
|
|
2864
|
+
try {
|
|
2865
|
+
sessionStorage.setItem(openStateKey(this.config.widgetId), isOpen ? 'true' : 'false');
|
|
2866
|
+
}
|
|
2867
|
+
catch {
|
|
2868
|
+
// sessionStorage might not be available
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
/**
|
|
2872
|
+
* Clear persisted state from sessionStorage
|
|
2873
|
+
*/
|
|
2874
|
+
clearPersistedState() {
|
|
2875
|
+
try {
|
|
2876
|
+
sessionStorage.removeItem(openStateKey(this.config.widgetId));
|
|
2877
|
+
}
|
|
2878
|
+
catch {
|
|
2879
|
+
// sessionStorage might not be available
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
/**
|
|
2883
|
+
* Check if widget was previously open (from sessionStorage)
|
|
2884
|
+
*/
|
|
2885
|
+
wasOpen() {
|
|
2886
|
+
try {
|
|
2887
|
+
return sessionStorage.getItem(openStateKey(this.config.widgetId)) === 'true';
|
|
2888
|
+
}
|
|
2889
|
+
catch {
|
|
2890
|
+
return false;
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2584
2893
|
/**
|
|
2585
2894
|
* Open the widget
|
|
2586
2895
|
*/
|
|
@@ -2589,8 +2898,6 @@ class WeldSDK {
|
|
|
2589
2898
|
console.log('[Weld SDK] Opening widget...');
|
|
2590
2899
|
this.stateCoordinator.openWidget();
|
|
2591
2900
|
this.iframeManager.showIframe(IframeType.WIDGET);
|
|
2592
|
-
this.iframeManager.showIframe(IframeType.BACKDROP);
|
|
2593
|
-
// Keep launcher visible so user can click it to close the widget
|
|
2594
2901
|
// Send open message to the widget iframe
|
|
2595
2902
|
const widgetIframe = this.iframeManager.getIframe(IframeType.WIDGET);
|
|
2596
2903
|
if (widgetIframe?.element?.contentWindow) {
|
|
@@ -2601,6 +2908,7 @@ class WeldSDK {
|
|
|
2601
2908
|
if (launcherIframe?.element?.contentWindow) {
|
|
2602
2909
|
launcherIframe.element.contentWindow.postMessage({ type: 'weld:widget-opened' }, '*');
|
|
2603
2910
|
}
|
|
2911
|
+
this.persistOpenState(true);
|
|
2604
2912
|
this.config.onOpen?.();
|
|
2605
2913
|
}
|
|
2606
2914
|
/**
|
|
@@ -2611,8 +2919,6 @@ class WeldSDK {
|
|
|
2611
2919
|
console.log('[Weld SDK] Closing widget...');
|
|
2612
2920
|
this.stateCoordinator.closeWidget();
|
|
2613
2921
|
this.iframeManager.hideIframe(IframeType.WIDGET);
|
|
2614
|
-
this.iframeManager.hideIframe(IframeType.BACKDROP);
|
|
2615
|
-
// Launcher stays visible
|
|
2616
2922
|
// Send close message to the widget iframe
|
|
2617
2923
|
const widgetIframe = this.iframeManager.getIframe(IframeType.WIDGET);
|
|
2618
2924
|
if (widgetIframe?.element?.contentWindow) {
|
|
@@ -2623,6 +2929,7 @@ class WeldSDK {
|
|
|
2623
2929
|
if (launcherIframe?.element?.contentWindow) {
|
|
2624
2930
|
launcherIframe.element.contentWindow.postMessage({ type: 'weld:widget-closed' }, '*');
|
|
2625
2931
|
}
|
|
2932
|
+
this.persistOpenState(false);
|
|
2626
2933
|
this.config.onClose?.();
|
|
2627
2934
|
}
|
|
2628
2935
|
/**
|
|
@@ -2811,6 +3118,8 @@ class WeldSDK {
|
|
|
2811
3118
|
});
|
|
2812
3119
|
// Broadcast logout to iframes
|
|
2813
3120
|
this.messageBroker.broadcast('weld:auth:logout', {});
|
|
3121
|
+
// Clear persisted widget state
|
|
3122
|
+
this.clearPersistedState();
|
|
2814
3123
|
// Clear any stored session data
|
|
2815
3124
|
try {
|
|
2816
3125
|
const prefix = 'weld-';
|
|
@@ -2952,6 +3261,63 @@ class WeldSDK {
|
|
|
2952
3261
|
this.logger.setLevel('warn');
|
|
2953
3262
|
this.logger.info('Debug mode disabled');
|
|
2954
3263
|
}
|
|
3264
|
+
/**
|
|
3265
|
+
* Send a page change message to the widget iframe
|
|
3266
|
+
*/
|
|
3267
|
+
sendPageChange(url, title) {
|
|
3268
|
+
const widgetIframe = this.iframeManager.getIframe(IframeType.WIDGET);
|
|
3269
|
+
if (widgetIframe?.element?.contentWindow) {
|
|
3270
|
+
widgetIframe.element.contentWindow.postMessage({
|
|
3271
|
+
type: 'weld:page:change',
|
|
3272
|
+
url,
|
|
3273
|
+
title,
|
|
3274
|
+
timestamp: Date.now(),
|
|
3275
|
+
}, '*');
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
/**
|
|
3279
|
+
* Start tracking page URL changes (SPA navigations + popstate)
|
|
3280
|
+
*/
|
|
3281
|
+
startPageTracking() {
|
|
3282
|
+
let lastUrl = window.location.href;
|
|
3283
|
+
let debounceTimer = null;
|
|
3284
|
+
const notifyChange = () => {
|
|
3285
|
+
const currentUrl = window.location.href;
|
|
3286
|
+
if (currentUrl !== lastUrl) {
|
|
3287
|
+
lastUrl = currentUrl;
|
|
3288
|
+
this.sendPageChange(currentUrl, document.title);
|
|
3289
|
+
}
|
|
3290
|
+
};
|
|
3291
|
+
const debouncedNotify = () => {
|
|
3292
|
+
if (debounceTimer)
|
|
3293
|
+
clearTimeout(debounceTimer);
|
|
3294
|
+
debounceTimer = setTimeout(notifyChange, 300);
|
|
3295
|
+
};
|
|
3296
|
+
// Send initial page
|
|
3297
|
+
this.sendPageChange(window.location.href, document.title);
|
|
3298
|
+
// Monkey-patch history.pushState and history.replaceState
|
|
3299
|
+
const origPushState = history.pushState.bind(history);
|
|
3300
|
+
const origReplaceState = history.replaceState.bind(history);
|
|
3301
|
+
history.pushState = function (...args) {
|
|
3302
|
+
origPushState(...args);
|
|
3303
|
+
debouncedNotify();
|
|
3304
|
+
};
|
|
3305
|
+
history.replaceState = function (...args) {
|
|
3306
|
+
origReplaceState(...args);
|
|
3307
|
+
debouncedNotify();
|
|
3308
|
+
};
|
|
3309
|
+
// Listen for popstate (browser back/forward)
|
|
3310
|
+
const handlePopstate = () => debouncedNotify();
|
|
3311
|
+
window.addEventListener('popstate', handlePopstate);
|
|
3312
|
+
// Store cleanup
|
|
3313
|
+
this.pageTrackingCleanup = () => {
|
|
3314
|
+
if (debounceTimer)
|
|
3315
|
+
clearTimeout(debounceTimer);
|
|
3316
|
+
window.removeEventListener('popstate', handlePopstate);
|
|
3317
|
+
history.pushState = origPushState;
|
|
3318
|
+
history.replaceState = origReplaceState;
|
|
3319
|
+
};
|
|
3320
|
+
}
|
|
2955
3321
|
/**
|
|
2956
3322
|
* Ensure SDK is ready before operation
|
|
2957
3323
|
*/
|
|
@@ -2960,11 +3326,26 @@ class WeldSDK {
|
|
|
2960
3326
|
throw new Error('SDK not ready. Call init() first.');
|
|
2961
3327
|
}
|
|
2962
3328
|
}
|
|
3329
|
+
/**
|
|
3330
|
+
* Detach from the current component lifecycle without destroying the widget.
|
|
3331
|
+
* Use this as a React useEffect cleanup — the widget stays alive across navigations.
|
|
3332
|
+
*/
|
|
3333
|
+
detach() {
|
|
3334
|
+
// No-op: widget stays alive in the singleton registry
|
|
3335
|
+
this.logger.debug('WeldSDK detached (no-op, widget stays alive)');
|
|
3336
|
+
}
|
|
2963
3337
|
/**
|
|
2964
3338
|
* Destroy SDK and cleanup
|
|
2965
3339
|
*/
|
|
2966
3340
|
destroy() {
|
|
2967
3341
|
this.logger.info('Destroying WeldSDK');
|
|
3342
|
+
// Remove from singleton registry
|
|
3343
|
+
sdkRegistry.delete(this.config.widgetId);
|
|
3344
|
+
// Clear persisted state
|
|
3345
|
+
this.clearPersistedState();
|
|
3346
|
+
// Stop page tracking
|
|
3347
|
+
this.pageTrackingCleanup?.();
|
|
3348
|
+
this.pageTrackingCleanup = null;
|
|
2968
3349
|
// Remove event listener using bound handler
|
|
2969
3350
|
window.removeEventListener('message', this.boundHandleLauncherClick);
|
|
2970
3351
|
// Unsubscribe from all message broker subscriptions
|
|
@@ -2984,13 +3365,33 @@ class WeldSDK {
|
|
|
2984
3365
|
}
|
|
2985
3366
|
}
|
|
2986
3367
|
/**
|
|
2987
|
-
* Create and initialize WeldSDK instance
|
|
3368
|
+
* Create and initialize WeldSDK instance.
|
|
3369
|
+
* Uses singleton pattern — if an instance for the same widgetId already exists
|
|
3370
|
+
* and is not destroyed, updates callbacks and returns the existing instance.
|
|
2988
3371
|
*/
|
|
2989
3372
|
async function createWeldSDK(config) {
|
|
3373
|
+
const widgetId = config.widgetId;
|
|
3374
|
+
// Check for existing, non-destroyed instance
|
|
3375
|
+
const existing = sdkRegistry.get(widgetId);
|
|
3376
|
+
if (existing && existing.getStatus() !== 'destroyed') {
|
|
3377
|
+
existing.updateCallbacks(config);
|
|
3378
|
+
return existing;
|
|
3379
|
+
}
|
|
2990
3380
|
const sdk = new WeldSDK(config);
|
|
3381
|
+
sdkRegistry.set(widgetId, sdk);
|
|
2991
3382
|
await sdk.init();
|
|
2992
3383
|
return sdk;
|
|
2993
3384
|
}
|
|
3385
|
+
/**
|
|
3386
|
+
* Explicitly destroy a WeldSDK instance by widgetId.
|
|
3387
|
+
* Use this for logout or when you need to fully remove the widget.
|
|
3388
|
+
*/
|
|
3389
|
+
function destroyWeldSDK(widgetId) {
|
|
3390
|
+
const sdk = sdkRegistry.get(widgetId);
|
|
3391
|
+
if (sdk) {
|
|
3392
|
+
sdk.destroy();
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
2994
3395
|
|
|
2995
|
-
export { DEFAULT_CONFIG, WeldSDK as HelpdeskWidget, IframeManager, IframeType, LogLevel, Logger, MessageBroker, RateLimiter, SecurityManager, StateCoordinator, TokenValidator, WeldSDK, createInitialState, createMessage, createWeldSDK, deepClone, deepMerge, WeldSDK as default, defaultLogger, formatFileSize, getStateValue, hasRequiredProperties, createWeldSDK as initHelpdeskWidget, isBaseMessage, isInRange, isPayloadMessage, isPlainObject, isValidApiKey, isValidArrayLength, isValidColor, isValidEmail, isValidFileSize, isValidFileType, isValidLength, isValidMessageText, isValidUrl, isValidWorkspaceId, resolveConfig, sanitizeHtml, sanitizeInput, setStateValue, validateConfig };
|
|
3396
|
+
export { DEFAULT_CONFIG, WeldSDK as HelpdeskWidget, IframeManager, IframeType, LogLevel, Logger, MessageBroker, RateLimiter, SecurityManager, StateCoordinator, TokenValidator, WeldSDK, createInitialState, createMessage, createWeldSDK, deepClone, deepMerge, WeldSDK as default, defaultLogger, destroyWeldSDK as destroyHelpdeskWidget, destroyWeldSDK, formatFileSize, getStateValue, hasRequiredProperties, createWeldSDK as initHelpdeskWidget, isBaseMessage, isInRange, isPayloadMessage, isPlainObject, isValidApiKey, isValidArrayLength, isValidColor, isValidEmail, isValidFileSize, isValidFileType, isValidLength, isValidMessageText, isValidUrl, isValidWorkspaceId, resolveConfig, sanitizeHtml, sanitizeInput, setStateValue, validateConfig };
|
|
2996
3397
|
//# sourceMappingURL=index.esm.js.map
|